Files
provenance/frontend/patches/family-chart+0.9.0.patch
T
justin 182a5dab16 Tree view: center a person between multiple spouses
family-chart 0.9.0 stacks all of a person's spouses on one gender-determined
side, so someone with two spouses (e.g. a woman with two husbands) renders
with both spouses piled above/below her and ambiguous child lines.

Patch the library (via patch-package) so the person stays centered and their
spouses split to alternating sides — spouse 1 above, spouse 2 below, further
spouses farther out — and order each couple's children to match, so children
descend from between the correct pair without crossed lines:

- setupSpouses: keep the person centered; place spouses at alternating
  offsets and recenter the cluster on the person's slot.
- sortChildrenWithSpouses: order children by spouse order (gender-independent)
  to match the new spouse positions.

Adds patch-package + a postinstall hook, and COPY patches into the Dockerfile
deps stage so the patch applies during `npm ci` in CI. Verified the patch
re-applies on a clean install and the production build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
2026-06-08 19:56:17 -04:00

119 lines
6.7 KiB
Diff

diff --git a/node_modules/family-chart/dist/family-chart.esm.js b/node_modules/family-chart/dist/family-chart.esm.js
index 3867be0..560c99e 100644
--- a/node_modules/family-chart/dist/family-chart.esm.js
+++ b/node_modules/family-chart/dist/family-chart.esm.js
@@ -10,10 +10,10 @@ function sortChildrenWithSpouses(children, datum, data) {
const b_p2 = otherParent(b, datum, data);
const a_i = a_p2 ? spouses.indexOf(a_p2.id) : -1;
const b_i = b_p2 ? spouses.indexOf(b_p2.id) : -1;
- if (datum.data.gender === "M")
- return a_i - b_i;
- else
- return b_i - a_i;
+ // Provenance patch: order children by spouse order (ascending), gender-
+ // independent, so each couple's children sit next to the matching spouse
+ // in the centered-spouse layout (see setupSpouses).
+ return a_i - b_i;
});
}
function sortAddNewChildren(children) {
@@ -701,20 +701,38 @@ function calculateTree(data, { main_id = null, node_separation = 250, level_sepa
if (spouses.length > 0) {
if (one_level_rels && d.depth > 0)
continue;
- const side = d.data.data.gender === "M" ? -1 : 1; // female on right
- d.x += spouses.length / 2 * node_separation * side;
+ // Provenance patch: keep the person in the MIDDLE and split
+ // their spouses to alternating sides, then recenter the whole
+ // cluster on the person's slot, so each couple's children
+ // descend from between the correct pair instead of all
+ // spouses stacking on one side. Ordered by relationship order
+ // (spouse 1 on top, spouse 2 below, further spouses farther
+ // out) — NOT gender; children are sorted to match in
+ // sortChildrenWithSpouses.
+ const offsets = spouses.map((_sp, i) => {
+ const dir = i % 2 === 0 ? 1 : -1;
+ const dist = Math.floor(i / 2) + 1; // 1,1,2,2,3,3...
+ return -(node_separation * dist) * dir;
+ });
+ // Mean of the person (offset 0) + every spouse, so the
+ // cluster stays centered on the person's original slot.
+ const center = offsets.reduce((a, b) => a + b, 0) / (offsets.length + 1);
+ const base_x = d.x;
+ d.x = base_x - center;
spouses.forEach((sp_id, i) => {
const spouse = {
data: data_stash.find(d0 => d0.id === sp_id),
added: true,
depth: d.depth,
spouse: d,
- x: d.x - (node_separation * (i + 1)) * side,
+ x: base_x + offsets[i] - center,
y: d.y,
tid: `${d.data.id}-spouse-${i}`,
};
- spouse.sx = i > 0 ? spouse.x : spouse.x + (node_separation / 2) * side;
- spouse.sy = i > 0 ? spouse.y : spouse.y + (node_separation / 2) * side;
+ // Anchor links at the midpoint toward the person.
+ const toward = spouse.x < d.x ? 1 : -1;
+ spouse.sx = spouse.x + toward * (node_separation / 2);
+ spouse.sy = spouse.y;
if (!d.spouses)
d.spouses = [];
d.spouses.push(spouse);
diff --git a/node_modules/family-chart/dist/family-chart.js b/node_modules/family-chart/dist/family-chart.js
index 1c750d4..47efcc2 100644
--- a/node_modules/family-chart/dist/family-chart.js
+++ b/node_modules/family-chart/dist/family-chart.js
@@ -33,10 +33,9 @@
const b_p2 = otherParent(b, datum, data);
const a_i = a_p2 ? spouses.indexOf(a_p2.id) : -1;
const b_i = b_p2 ? spouses.indexOf(b_p2.id) : -1;
- if (datum.data.gender === "M")
- return a_i - b_i;
- else
- return b_i - a_i;
+ // Provenance patch: order children by spouse order (ascending),
+ // gender-independent, to match the centered-spouse layout.
+ return a_i - b_i;
});
}
function sortAddNewChildren(children) {
@@ -724,20 +723,31 @@
if (spouses.length > 0) {
if (one_level_rels && d.depth > 0)
continue;
- const side = d.data.data.gender === "M" ? -1 : 1; // female on right
- d.x += spouses.length / 2 * node_separation * side;
+ // Provenance patch: keep the person in the MIDDLE and
+ // split spouses to alternating sides, recentered on the
+ // person's slot, so each couple's children descend from
+ // between the correct pair (see esm build for details).
+ const offsets = spouses.map((_sp, i) => {
+ const dir = i % 2 === 0 ? 1 : -1;
+ const dist = Math.floor(i / 2) + 1;
+ return -(node_separation * dist) * dir;
+ });
+ const center = offsets.reduce((a, b) => a + b, 0) / (offsets.length + 1);
+ const base_x = d.x;
+ d.x = base_x - center;
spouses.forEach((sp_id, i) => {
const spouse = {
data: data_stash.find(d0 => d0.id === sp_id),
added: true,
depth: d.depth,
spouse: d,
- x: d.x - (node_separation * (i + 1)) * side,
+ x: base_x + offsets[i] - center,
y: d.y,
tid: `${d.data.id}-spouse-${i}`,
};
- spouse.sx = i > 0 ? spouse.x : spouse.x + (node_separation / 2) * side;
- spouse.sy = i > 0 ? spouse.y : spouse.y + (node_separation / 2) * side;
+ const toward = spouse.x < d.x ? 1 : -1;
+ spouse.sx = spouse.x + toward * (node_separation / 2);
+ spouse.sy = spouse.y;
if (!d.spouses)
d.spouses = [];
d.spouses.push(spouse);