people tags #387

This commit is contained in:
Hamid Reza Mohammadi 2026-10-02 09:18:40 +03:30
parent 872a8aab61
commit a0a7558d8b

View File

@ -43,14 +43,31 @@
(spouseOf[b] = spouseOf[b] || []).push(a); (spouseOf[b] = spouseOf[b] || []).push(a);
} }
var siblingPairs = [];
(data.relations || []).forEach(function (r) { (data.relations || []).forEach(function (r) {
if (r.relationType === 0 || r.relationType === 3) { if (r.relationType === 0 || r.relationType === 3) {
addParentChild(r.person1Id, r.person2Id); addParentChild(r.person1Id, r.person2Id);
} else if (r.relationType === 2) { } else if (r.relationType === 2) {
addSpouse(r.person1Id, r.person2Id); addSpouse(r.person1Id, r.person2Id);
} else if (r.relationType === 1) {
siblingPairs.push([r.person1Id, r.person2Id]);
}
});
// A sibling pair where only one side has a recorded parent (e.g. a sibling relation was
// entered but no separate parent relation for the other sibling) used to leave the parentless
// sibling with no parent at all, turning them into their own disconnected chart root instead
// of being drawn next to their actual family. Have the parentless sibling inherit the same
// parent(s), purely so the drawing places them together - this doesn't change any real data.
siblingPairs.forEach(function (pair) {
var a = pair[0], b = pair[1];
var aHasParent = parentsOf[a] && parentsOf[a].length > 0;
var bHasParent = parentsOf[b] && parentsOf[b].length > 0;
if (aHasParent && !bHasParent) {
parentsOf[a].forEach(function (p) { addParentChild(p, b); });
} else if (bHasParent && !aHasParent) {
parentsOf[b].forEach(function (p) { addParentChild(p, a); });
} }
// relationType === 1 (Sibling) isn't needed for layout: siblings normally end up
// adjacent anyway once they share a primaryParentOf below.
}); });
Object.keys(childrenOf).forEach(function (k) { childrenOf[k] = dedup(childrenOf[k]); }); Object.keys(childrenOf).forEach(function (k) { childrenOf[k] = dedup(childrenOf[k]); });
@ -226,6 +243,7 @@
}); });
var maxX = 0, maxY = 0; var maxX = 0, maxY = 0;
var drawnPos = {}; // id -> {x,y} for every box actually drawn (primary nodes and attached spouses)
function drawBox(id, x, y, w, h, isRequested) { function drawBox(id, x, y, w, h, isRequested) {
var p = layoutInfo.persons[id]; var p = layoutInfo.persons[id];
@ -244,6 +262,7 @@
nodesGroup.appendChild(g); nodesGroup.appendChild(g);
maxX = Math.max(maxX, x + w / 2); maxX = Math.max(maxX, x + w / 2);
maxY = Math.max(maxY, y + h / 2); maxY = Math.max(maxY, y + h / 2);
drawnPos[id] = { x: x, y: y };
} }
Object.keys(layoutInfo.nodeX).forEach(function (idStr) { Object.keys(layoutInfo.nodeX).forEach(function (idStr) {
@ -296,21 +315,34 @@
} }
svg.removeAttribute('viewBox'); // pan/zoom via transform, not viewBox, once we have a canvas group svg.removeAttribute('viewBox'); // pan/zoom via transform, not viewBox, once we have a canvas group
var svgWidth = Math.max(container.clientWidth, maxX + MARGIN); // The SVG element is always sized to the CONTAINER - never to the full tree - so it acts as a
var svgHeight = Math.max(container.clientHeight, maxY + MARGIN); // fixed "viewport" onto the (possibly much larger) tree, which is panned/zoomed via the canvas
// group's transform below. Previously this grew the SVG itself to (maxX+MARGIN)/(maxY+MARGIN)
// whenever the tree was bigger than the container; combined with the page's RTL layout and the
// container's overflow:hidden, that pinned the oversized SVG's right edge to the container's
// right edge, which could hide the requested root's own subtree entirely if it was laid out
// toward the left - with nothing below ever panning the view back to it.
var svgWidth = container.clientWidth || (maxX + MARGIN);
var svgHeight = container.clientHeight || (maxY + MARGIN);
svg.setAttribute('width', svgWidth); svg.setAttribute('width', svgWidth);
svg.setAttribute('height', svgHeight); svg.setAttribute('height', svgHeight);
// pan + zoom - a small, dependency-free version of the usual SVG drag/wheel recipe // pan + zoom - a small, dependency-free version of the usual SVG drag/wheel recipe
var scale = 1; var scale = 1;
// The tree itself is only (maxX + MARGIN) x (maxY + MARGIN) "big", but the SVG element is // Center the initial view on the requested root's own drawn position (falling back to an
// stretched to fill the whole container (so there's room to pan around a small tree) - drawn // attached/spouse box's position if that's how it was drawn) so the person actually asked for
// at a bare translate(0,0), that left it pinned to the top-left corner instead of centered // is what's visible first, rather than whatever subtree happened to land at low x. When the
// whenever the tree is smaller than the container (e.g. a two-person tree in a wide panel). // whole tree already fits inside the viewport, center the whole tree instead (matches the old,
// Starting the pan offset centered - rather than at (0,0) - fixes that without changing how // still-correct behavior for small trees).
// panning/zooming themselves work. var tx, ty;
var tx = Math.max(0, (svgWidth - (maxX + MARGIN)) / 2); if ((maxX + MARGIN) <= svgWidth && (maxY + MARGIN) <= svgHeight) {
var ty = Math.max(0, (svgHeight - (maxY + MARGIN)) / 2); tx = Math.max(0, (svgWidth - (maxX + MARGIN)) / 2);
ty = Math.max(0, (svgHeight - (maxY + MARGIN)) / 2);
} else {
var focus = drawnPos[requestedRootId] || { x: (maxX + MARGIN) / 2, y: (maxY + MARGIN) / 2 };
tx = svgWidth / 2 - focus.x;
ty = svgHeight / 2 - focus.y;
}
var dragging = false, lastX = 0, lastY = 0; var dragging = false, lastX = 0, lastY = 0;
function applyTransform() { function applyTransform() {