people tags #387
This commit is contained in:
parent
1f80d03fef
commit
872a8aab61
@ -1,82 +0,0 @@
|
||||
@page "{id:int}"
|
||||
@model GanjooRazor.Pages.FamilyTreeModel
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@section Head {
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
@Model.LastError
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- #hdr2 -->
|
||||
|
||||
<div id="fa">
|
||||
|
||||
<div id="maincnt">
|
||||
|
||||
<partial name="_InlineLoginPartial" model="Model" />
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
<div class="poem">
|
||||
<h1>
|
||||
تبارنامهٔ @(string.IsNullOrEmpty(Model.RootPerson.FamilyTreeCaption) ? Model.RootPerson.Name : Model.RootPerson.FamilyTreeCaption)
|
||||
<a href="javascript:void(0)" onclick="PersonWindow.open(@Model.RootPerson.Id)" class="up-btn up-btn--ghost up-btn--sm">مشاهدهٔ اطلاعات این شخصیت</a>
|
||||
</h1>
|
||||
<p class="up-hint"><small>
|
||||
برای مشاهدهٔ توضیحات هر شخصیت، روی آن کلیک کنید. برای پیمایش نمودار آن را با ماوس بکشید و با غلتک ماوس بزرگ/کوچک کنید.
|
||||
این نمودار محدود به فرزندان این شخصیت نیست؛ تمام خویشاوندان بههمپیوسته (از جمله نیاکان و خانوادهٔ همسر) را نشان میدهد.
|
||||
</small></p>
|
||||
|
||||
<div id="familytree-container" style="position:relative;width:100%;height:70vh;border:1px solid #d8cdb8;border-radius:6px;overflow:hidden;background:#faf7f0;">
|
||||
<svg id="familytree-svg" width="100%" height="100%"></svg>
|
||||
<div id="familytree-tooltip" class="ft-tooltip-panel" style="display:none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
</div>
|
||||
|
||||
<partial name="_FooterPartial" model=@(new _FooterPartialModel(){StickyEnabled = true}) />
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.ft-box { fill: #fdf6e6; stroke: #8b6b4a; stroke-width: 1.5; }
|
||||
.ft-box-current { fill: #f3e2b8; stroke: #7a4a2a; stroke-width: 2.5; }
|
||||
.ft-box-text { font-size: 14px; fill: #4a3520; font-family: inherit; }
|
||||
.ft-heart { font-size: 13px; fill: #a0455c; }
|
||||
.ft-line { fill: none; stroke: #a99a7c; stroke-width: 1.5; }
|
||||
.ft-line-spouse { stroke-dasharray: 3,3; }
|
||||
.ft-tooltip-panel {
|
||||
position: absolute; top: 10px; left: 10px; max-width: 300px;
|
||||
background: #fffdf7; border: 1px solid #8b6b4a; border-radius: 8px;
|
||||
padding: 12px 16px; box-shadow: 0 2px 10px rgba(0,0,0,.25); z-index: 10;
|
||||
direction: rtl; text-align: right;
|
||||
}
|
||||
.ft-tooltip-close {
|
||||
float: left; border: none; background: none; font-size: 18px; cursor: pointer; color: #7a4a2a;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var familyTreeData = @Html.Raw(Model.TreeDataJson);
|
||||
var familyTreeRootId = @Model.RootPerson.Id;
|
||||
</script>
|
||||
<script src="/js/familytree.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
GanjoorFamilyTree.render('familytree-container', 'familytree-svg', 'familytree-tooltip', familyTreeData, familyTreeRootId);
|
||||
});
|
||||
</script>
|
||||
|
||||
@if (ViewData["TrackingScript"] != null && !string.IsNullOrEmpty(ViewData["TrackingScript"].ToString()))
|
||||
{
|
||||
@Html.Raw(ViewData["TrackingScript"])
|
||||
}
|
||||
}
|
||||
53
GanjooRazor/Pages/FamilyTreeWindow.cshtml
Normal file
53
GanjooRazor/Pages/FamilyTreeWindow.cshtml
Normal file
@ -0,0 +1,53 @@
|
||||
@page "{id:int}"
|
||||
@model GanjooRazor.Pages.FamilyTreeWindowModel
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
<p>@Model.LastError</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="up-modal-familytree">
|
||||
<h2>
|
||||
تبارنامهٔ @(string.IsNullOrEmpty(Model.RootPerson.FamilyTreeCaption) ? Model.RootPerson.Name : Model.RootPerson.FamilyTreeCaption)
|
||||
<a href="javascript:void(0)" onclick="PersonWindow.open(@Model.RootPerson.Id)" class="up-btn up-btn--ghost up-btn--sm">مشاهدهٔ اطلاعات این شخصیت</a>
|
||||
</h2>
|
||||
<p class="up-hint"><small>
|
||||
برای مشاهدهٔ توضیحات هر شخصیت، روی آن کلیک کنید. برای پیمایش نمودار آن را با ماوس بکشید و با غلتک ماوس بزرگ/کوچک کنید.
|
||||
این نمودار محدود به فرزندان این شخصیت نیست؛ تمام خویشاوندان بههمپیوسته (از جمله نیاکان و خانوادهٔ همسر) را نشان میدهد.
|
||||
</small></p>
|
||||
|
||||
<div id="familytree-container" style="position:relative;width:100%;height:65vh;border:1px solid #d8cdb8;border-radius:6px;overflow:hidden;background:#faf7f0;">
|
||||
<svg id="familytree-svg" width="100%" height="100%"></svg>
|
||||
<div id="familytree-tooltip" class="ft-tooltip-panel" style="display:none;"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.ft-box { fill: #fdf6e6; stroke: #8b6b4a; stroke-width: 1.5; }
|
||||
.ft-box-current { fill: #f3e2b8; stroke: #7a4a2a; stroke-width: 2.5; }
|
||||
.ft-box-text { font-size: 14px; fill: #4a3520; font-family: inherit; }
|
||||
.ft-heart { font-size: 13px; fill: #a0455c; }
|
||||
.ft-line { fill: none; stroke: #a99a7c; stroke-width: 1.5; }
|
||||
.ft-line-spouse { stroke-dasharray: 3,3; }
|
||||
.ft-tooltip-panel {
|
||||
position: absolute; top: 10px; left: 10px; max-width: 300px;
|
||||
background: #fffdf7; border: 1px solid #8b6b4a; border-radius: 8px;
|
||||
padding: 12px 16px; box-shadow: 0 2px 10px rgba(0,0,0,.25); z-index: 10;
|
||||
direction: rtl; text-align: right;
|
||||
}
|
||||
.ft-tooltip-close {
|
||||
float: left; border: none; background: none; font-size: 18px; cursor: pointer; color: #7a4a2a;
|
||||
}
|
||||
</style>
|
||||
|
||||
@* not an executable script - just a data holder, parsed and rendered explicitly by
|
||||
FamilyTreeWindow.open()'s onReady callback in personwindow.js once this fragment has
|
||||
been injected into the modal body (innerHTML never executes a <script> tag, so unlike
|
||||
the old standalone FamilyTree.cshtml page this can't just call GanjoorFamilyTree.render()
|
||||
directly here) - same pattern _PersonGraphPartial.cshtml uses for pge-data *@
|
||||
<script type="application/json" id="ftw-data">@Html.Raw(Model.TreeDataJson)</script>
|
||||
<input type="hidden" id="ftw-root-id" value="@Model.RootPerson.Id" />
|
||||
</div>
|
||||
}
|
||||
@ -9,14 +9,16 @@ using RMuseum.Models.Ganjoor.ViewModels;
|
||||
namespace GanjooRazor.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// interactive family-tree chart, drawn client-side (vanilla JS/SVG - see the <script> block
|
||||
/// in FamilyTree.cshtml) from the connected kinship component GET api/people/{id}/familytree
|
||||
/// returns. Public/read-only counterpart of the PersonWindow.open() modal's plain relatives
|
||||
/// list, reachable from there and from the PeopleExplorer.open() modal's family-tree index.
|
||||
/// interactive family-tree chart, drawn client-side (vanilla JS/SVG - see familytree.js) from
|
||||
/// the connected kinship component GET api/people/{id}/familytree returns. Inline-modal
|
||||
/// counterpart of the old standalone FamilyTree.cshtml page (now removed) - fetched as a
|
||||
/// fragment (Layout = null, no site chrome) and opened over whatever page the user is already
|
||||
/// on via FamilyTreeWindow.open(id) in personwindow.js, the same pattern PersonWindow.cshtml/
|
||||
/// PeopleExplorer.cshtml use.
|
||||
/// </summary>
|
||||
public class FamilyTreeModel : LoginPartialEnabledPageModel
|
||||
public class FamilyTreeWindowModel : LoginPartialEnabledPageModel
|
||||
{
|
||||
public FamilyTreeModel(HttpClient httpClient, IConfiguration configuration) : base(httpClient, configuration)
|
||||
public FamilyTreeWindowModel(HttpClient httpClient, IConfiguration configuration) : base(httpClient, configuration)
|
||||
{
|
||||
}
|
||||
|
||||
@ -27,9 +29,10 @@ namespace GanjooRazor.Pages
|
||||
public GanjoorFamilyTreeViewModel Tree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tree, re-serialized with an explicit camelCase contract (same convention ReviewEdits.cshtml.cs
|
||||
/// uses for AllLocationsJson/AllPeopleJson) so the client-side layout script has a predictable
|
||||
/// shape to parse, regardless of the API's own default casing
|
||||
/// Tree, re-serialized with an explicit camelCase contract (same convention
|
||||
/// _PersonGraphPartial.cshtml.cs's GraphDataJson uses) so familytree.js has a predictable
|
||||
/// shape to parse once this fragment's data block is read, regardless of the API's own
|
||||
/// default casing
|
||||
/// </summary>
|
||||
public string TreeDataJson =>
|
||||
JsonConvert.SerializeObject(
|
||||
@ -54,8 +57,6 @@ namespace GanjooRazor.Pages
|
||||
return Page();
|
||||
}
|
||||
|
||||
ViewData["Title"] = $"گنجور » تبارنامهٔ {(string.IsNullOrEmpty(RootPerson.FamilyTreeCaption) ? RootPerson.Name : RootPerson.FamilyTreeCaption)}";
|
||||
|
||||
var treeResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/people/{id}/familytree");
|
||||
if (!treeResponse.IsSuccessStatusCode)
|
||||
{
|
||||
@ -21,7 +21,7 @@ else
|
||||
<li>
|
||||
<a href="javascript:void(0)" onclick="PersonWindow.open(@root.Id)">@root.FamilyTreeCaption</a>
|
||||
<small>(@root.Name)</small>
|
||||
— <a href="/FamilyTree/@root.Id"><small>نمایش نمودار</small></a>
|
||||
— <a href="javascript:void(0)" onclick="FamilyTreeWindow.open(@root.Id)"><small>نمایش نمودار</small></a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
@ -52,7 +52,7 @@ else
|
||||
{
|
||||
<h3>
|
||||
خویشاوندان
|
||||
<a href="/FamilyTree/@Model.Person.Id" class="up-btn up-btn--ghost up-btn--sm">نمایش نمودار تبارنامه</a>
|
||||
<a href="javascript:void(0)" onclick="FamilyTreeWindow.open(@Model.Person.Id)" class="up-btn up-btn--ghost up-btn--sm">نمایش نمودار تبارنامه</a>
|
||||
@if (Model.LoggedIn)
|
||||
{
|
||||
<a href="/User/SuggestNewPersonRelation?personId=@Model.Person.Id" class="up-btn up-btn--ghost up-btn--sm">افزودن خویشاوند جدید</a>
|
||||
|
||||
@ -39,10 +39,11 @@
|
||||
</script>
|
||||
|
||||
<script src="~/js/bk.js" asp-append-version="true"></script>
|
||||
@* person profile / people-explorer inline modal - see _Layout.cshtml for the same include on
|
||||
the public site; needed here too since ReviewPersonEdits/ReviewPersonRelationEdits link to
|
||||
a person's profile *@
|
||||
@* person profile / people-explorer / family-tree inline modals - see _Layout.cshtml for the
|
||||
same includes on the public site; needed here too since ReviewPersonEdits/
|
||||
ReviewPersonRelationEdits link to a person's profile *@
|
||||
<script src="~/js/peoplegraph.js" asp-append-version="true"></script>
|
||||
<script src="~/js/familytree.js" asp-append-version="true"></script>
|
||||
<script src="~/js/personwindow.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
function editBanner(bannerId) {
|
||||
|
||||
@ -37,10 +37,12 @@
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="گنجور" href="https://ganjoor.net/search.xml" />
|
||||
<script src="/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/js/bk.js" asp-append-version="true"></script>
|
||||
@* person profile / people-explorer inline modal - global since a person's name can turn up
|
||||
almost anywhere (poem pages, family tree, suggestion forms); peoplegraph.js is the shared
|
||||
force-directed renderer both PeopleExplorer.open() and the category-scoped "شخصیتها" tab use *@
|
||||
@* person profile / people-explorer / family-tree inline modals - global since a person's
|
||||
name can turn up almost anywhere (poem pages, family tree, suggestion forms); peoplegraph.js
|
||||
and familytree.js are the shared renderers PeopleExplorer.open()/FamilyTreeWindow.open() and
|
||||
the category-scoped "شخصیتها" tab use *@
|
||||
<script src="~/js/peoplegraph.js" asp-append-version="true"></script>
|
||||
<script src="~/js/familytree.js" asp-append-version="true"></script>
|
||||
<script src="~/js/personwindow.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
var cookieValue = getCookie("playerScrollLock");
|
||||
|
||||
@ -39,10 +39,11 @@
|
||||
</script>
|
||||
<script src="/lib/diff.js"></script>
|
||||
<script src="~/js/bk.js" asp-append-version="true"></script>
|
||||
<!-- PersonWindow.open()/PeopleExplorer.open() are used by pages under Areas/User/Pages (e.g. the
|
||||
Suggest* person-editing forms), so they're loaded globally here, same as _Layout.cshtml and
|
||||
_AdminLayout.cshtml do for the rest of the site. -->
|
||||
<!-- PersonWindow.open()/PeopleExplorer.open()/FamilyTreeWindow.open() are used by pages under
|
||||
Areas/User/Pages (e.g. the Suggest* person-editing forms), so they're loaded globally here,
|
||||
same as _Layout.cshtml and _AdminLayout.cshtml do for the rest of the site. -->
|
||||
<script src="~/js/peoplegraph.js" asp-append-version="true"></script>
|
||||
<script src="~/js/familytree.js" asp-append-version="true"></script>
|
||||
<script src="~/js/personwindow.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
function compareCorrectionBlocks(originalElemenyId, editedElementId, resultElementId) {
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
// Renders the interactive family-tree chart on /FamilyTree/{id}. Pure vanilla JS + SVG - no external
|
||||
// charting library - since the shape of the problem (a handful to a few hundred nodes, drawn once,
|
||||
// Renders the interactive family-tree chart inside the FamilyTreeWindow.open(id) modal (see
|
||||
// personwindow.js and FamilyTreeWindow.cshtml). Pure vanilla JS + SVG - no external charting
|
||||
// library - since the shape of the problem (a handful to a few hundred nodes, drawn once,
|
||||
// panned/zoomed by the user) doesn't need one, and the project prefers self-hosted, dependency-light
|
||||
// front-end code.
|
||||
//
|
||||
// Input (window.familyTreeData, set inline by FamilyTree.cshtml): the JSON of
|
||||
// Input (passed as the render() call's data/requestedRootId arguments - see personwindow.js's
|
||||
// openFamilyTree(), which reads them from the #ftw-data/#ftw-root-id elements
|
||||
// FamilyTreeWindow.cshtml embeds): the JSON of
|
||||
// RMuseum.Models.Ganjoor.ViewModels.GanjoorFamilyTreeViewModel, camelCased -
|
||||
// { rootId, persons: [{id, name, birthYearInLHijri, deathYearInLHijri, validBirthDate,
|
||||
// validDeathDate, description, wikiUrl, familyTreeCaption, ...}], relations: [{person1Id,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Generic inline-modal shell, loaded on every page (see _Layout.cshtml / _AdminLayout.cshtml),
|
||||
// used to replace what used to be three standalone pages: Person.cshtml (a person's profile),
|
||||
// People.cshtml (family-tree-roots index) and PeopleGraph.cshtml (whole-site relationship graph).
|
||||
// Those pages had no independent purpose of their own - they only ever existed to show data
|
||||
// reachable from somewhere else (a poem's tagged persons, a category's "شخصیتها" tab, an admin
|
||||
// moderation queue, the family-tree chart) - so instead of navigating away, callers open one of
|
||||
// these two windows over whatever page they're already on:
|
||||
// used to replace what used to be four standalone pages: Person.cshtml (a person's profile),
|
||||
// People.cshtml (family-tree-roots index), PeopleGraph.cshtml (whole-site relationship graph) and
|
||||
// FamilyTree.cshtml (one person's family-tree chart). Those pages had no independent purpose of
|
||||
// their own - they only ever existed to show data reachable from somewhere else (a poem's tagged
|
||||
// persons, a category's "شخصیتها" tab, an admin moderation queue) - so instead of navigating
|
||||
// away, callers open one of these three windows over whatever page they're already on:
|
||||
//
|
||||
// PersonWindow.open(id) - a small modal: one person's bio/relations/affiliations/poems,
|
||||
// fetched from /PersonWindow/{id} (Person.cshtml's old content,
|
||||
@ -12,8 +12,11 @@
|
||||
// PeopleExplorer.open() - a large modal: the whole-site force-directed graph (formerly
|
||||
// /PeopleGraph) plus the family-tree-roots list (formerly /People),
|
||||
// merged into one fragment fetched from /PeopleExplorer.
|
||||
// FamilyTreeWindow.open(id) - a large modal: one person's connected-kinship-component tree
|
||||
// chart (formerly /FamilyTree/{id}), fetched from
|
||||
// /FamilyTreeWindow/{id} - see FamilyTreeWindow.cshtml.
|
||||
//
|
||||
// Both render into the same reusable overlay/modal shell built here on first use.
|
||||
// All three render into the same reusable overlay/modal shell built here on first use.
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
@ -117,6 +120,20 @@
|
||||
});
|
||||
}
|
||||
|
||||
function openFamilyTree(id) {
|
||||
openModal(true);
|
||||
loadInto('/FamilyTreeWindow/' + String(id), function () {
|
||||
var dataEl = document.getElementById('ftw-data');
|
||||
var rootIdEl = document.getElementById('ftw-root-id');
|
||||
if (dataEl && rootIdEl && window.GanjoorFamilyTree) {
|
||||
var treeData = JSON.parse(dataEl.textContent);
|
||||
var rootId = parseInt(rootIdEl.value, 10);
|
||||
GanjoorFamilyTree.render('familytree-container', 'familytree-svg', 'familytree-tooltip', treeData, rootId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.PersonWindow = { open: openPerson, close: closeModal };
|
||||
window.PeopleExplorer = { open: openExplorer, close: closeModal };
|
||||
window.FamilyTreeWindow = { open: openFamilyTree, close: closeModal };
|
||||
})();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user