2023-08-22 14:12:55 +00:00
|
|
|
import React from "react";
|
2023-08-22 15:17:36 +00:00
|
|
|
import f3, { f3Data } from "family-chart";
|
2023-08-22 14:12:55 +00:00
|
|
|
import "./family-chart.css";
|
2023-08-23 08:27:51 +00:00
|
|
|
import { FamilyTreeNode } from "../../utils/family_tree";
|
2023-08-23 08:58:25 +00:00
|
|
|
import { Member, fmtDate } from "../../api/MemberApi";
|
2023-08-23 09:25:33 +00:00
|
|
|
import { Couple } from "../../api/CoupleApi";
|
2023-08-22 14:12:55 +00:00
|
|
|
|
2023-08-23 08:27:51 +00:00
|
|
|
export function ComplexFamilyTree(p: {
|
|
|
|
tree: FamilyTreeNode;
|
2023-08-23 08:58:25 +00:00
|
|
|
isUp: boolean;
|
2023-08-23 08:27:51 +00:00
|
|
|
}): React.ReactElement {
|
2023-08-22 15:08:58 +00:00
|
|
|
const refCb = (container: HTMLDivElement) => {
|
|
|
|
if (!container) return;
|
2023-08-22 14:12:55 +00:00
|
|
|
|
|
|
|
const store = f3.createStore({
|
2023-08-23 08:58:25 +00:00
|
|
|
data: treeToF3Data(p.tree, p.isUp),
|
2023-08-22 14:12:55 +00:00
|
|
|
node_separation: 250,
|
|
|
|
level_separation: 150,
|
|
|
|
}),
|
|
|
|
view = f3.d3AnimationView({
|
|
|
|
store,
|
|
|
|
cont: document.querySelector("#FamilyChart"),
|
|
|
|
}),
|
|
|
|
Card = f3.elements.Card({
|
|
|
|
store,
|
|
|
|
svg: view.svg,
|
|
|
|
card_dim: {
|
2023-08-23 09:25:33 +00:00
|
|
|
w: 230,
|
2023-08-22 14:12:55 +00:00
|
|
|
h: 70,
|
|
|
|
text_x: 75,
|
|
|
|
text_y: 15,
|
|
|
|
img_w: 60,
|
|
|
|
img_h: 60,
|
|
|
|
img_x: 5,
|
|
|
|
img_y: 5,
|
|
|
|
},
|
|
|
|
card_display: [
|
2023-08-23 09:25:33 +00:00
|
|
|
(d) =>
|
|
|
|
`${d.data.first_name || ""} ${d.data.last_name || ""} ${
|
|
|
|
d.data.dead ? "✝" : ""
|
|
|
|
}`,
|
|
|
|
(d) => {
|
|
|
|
let s = `${d.data.birthday || ""} ${
|
|
|
|
d.data.deathday ? " ✝ " + d.data.deathday : ""
|
|
|
|
}`;
|
|
|
|
|
|
|
|
if (d.data.wedding_state || d.data.dateOfWedding) {
|
|
|
|
let weddingInfo = [];
|
|
|
|
if (d.data.wedding_state) weddingInfo.push(d.data.wedding_state);
|
|
|
|
if (d.data.dateOfWedding)
|
|
|
|
weddingInfo.push("Mariage : " + d.data.dateOfWedding);
|
|
|
|
s += `</tspan> <tspan x="0" dy="14" font-size="10">${weddingInfo.join(
|
|
|
|
" - "
|
|
|
|
)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
},
|
2023-08-22 14:12:55 +00:00
|
|
|
],
|
|
|
|
mini_tree: true,
|
|
|
|
link_break: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
view.setCard(Card);
|
|
|
|
store.setOnUpdate((props) => view.update(props || {}));
|
|
|
|
store.update.tree({ initial: true });
|
2023-08-22 15:08:58 +00:00
|
|
|
};
|
2023-08-22 14:12:55 +00:00
|
|
|
|
2023-08-22 15:08:58 +00:00
|
|
|
return <div className="f3" id="FamilyChart" ref={refCb}></div>;
|
|
|
|
}
|
2023-08-22 14:57:41 +00:00
|
|
|
|
2023-08-23 08:58:25 +00:00
|
|
|
function treeToF3Data(node: FamilyTreeNode, isUp: boolean): f3Data[] {
|
2023-08-23 08:27:51 +00:00
|
|
|
const availableMembers = new Set<number>();
|
|
|
|
getAvailableMembers(node, availableMembers);
|
|
|
|
|
|
|
|
const list: f3Data[] = [];
|
2023-08-23 09:25:33 +00:00
|
|
|
if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
|
|
|
|
else treeToF3DataDownRecurse(node, list, availableMembers);
|
2023-08-23 08:27:51 +00:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAvailableMembers(t: FamilyTreeNode, s: Set<number>) {
|
|
|
|
s.add(t.member.id);
|
|
|
|
|
|
|
|
t.couples?.forEach((c) => {
|
|
|
|
s.add(c.member.id);
|
|
|
|
c.down.forEach((e) => getAvailableMembers(e, s));
|
|
|
|
});
|
|
|
|
|
|
|
|
t.down?.forEach((e) => getAvailableMembers(e, s));
|
|
|
|
}
|
|
|
|
|
2023-08-23 09:25:33 +00:00
|
|
|
function memberData(m: Member, c?: Couple): f3.f3DataData {
|
2023-08-23 08:58:25 +00:00
|
|
|
return {
|
|
|
|
first_name: m.first_name ?? "_",
|
|
|
|
last_name: m.last_name ?? "_",
|
|
|
|
gender: m.sex ?? "M",
|
|
|
|
avatar: m.thumbnailURL ?? undefined,
|
2023-08-23 09:25:33 +00:00
|
|
|
dead: m.dead,
|
2023-08-23 08:58:25 +00:00
|
|
|
birthday: m.dateOfBirth ? fmtDate(m.dateOfBirth) : undefined,
|
2023-08-23 09:25:33 +00:00
|
|
|
deathday: m.dateOfDeath ? fmtDate(m.dateOfDeath) : undefined,
|
|
|
|
wedding_state: c?.stateFr,
|
|
|
|
dateOfWedding: c?.dateOfWedding ? fmtDate(c?.dateOfWedding) : undefined,
|
2023-08-23 08:58:25 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function treeToF3DataUpRecurse(
|
|
|
|
node: FamilyTreeNode,
|
|
|
|
array: f3Data[],
|
|
|
|
availableMembers: Set<number>,
|
|
|
|
child?: number,
|
|
|
|
spouses?: number[]
|
|
|
|
) {
|
|
|
|
array.push({
|
|
|
|
data: memberData(node.member),
|
|
|
|
id: node.member.id.toString(),
|
|
|
|
rels: {
|
|
|
|
father:
|
|
|
|
node.member.father && availableMembers.has(node.member.father)
|
|
|
|
? node.member.father.toString()
|
|
|
|
: undefined,
|
|
|
|
mother:
|
|
|
|
node.member.mother && availableMembers.has(node.member.mother)
|
|
|
|
? node.member.mother.toString()
|
|
|
|
: undefined,
|
|
|
|
|
|
|
|
spouses: spouses
|
|
|
|
?.filter((c) => c !== node.member.id)
|
|
|
|
.map((c) => c.toString()),
|
|
|
|
children: child ? [child.toString()] : undefined,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const parentSpouses = node.down?.map((c) => c.member.id);
|
|
|
|
|
|
|
|
node.down?.forEach((d) =>
|
|
|
|
treeToF3DataUpRecurse(
|
|
|
|
d,
|
|
|
|
array,
|
|
|
|
availableMembers,
|
|
|
|
node.member.id,
|
|
|
|
parentSpouses
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function treeToF3DataDownRecurse(
|
2023-08-23 08:27:51 +00:00
|
|
|
node: FamilyTreeNode,
|
|
|
|
array: f3Data[],
|
|
|
|
availableMembers: Set<number>
|
|
|
|
) {
|
|
|
|
// Get all members ids
|
|
|
|
const children = node?.down?.map((c) => c.member.id.toString()) ?? [];
|
|
|
|
node.couples?.map((c) =>
|
|
|
|
c.down.forEach((m) => children.push(m.member.id.toString()))
|
|
|
|
);
|
|
|
|
|
|
|
|
array.push({
|
2023-08-23 08:58:25 +00:00
|
|
|
data: memberData(node.member),
|
2023-08-23 08:27:51 +00:00
|
|
|
id: node.member.id.toString(),
|
2023-08-22 15:17:36 +00:00
|
|
|
rels: {
|
2023-08-23 08:27:51 +00:00
|
|
|
father:
|
|
|
|
node.member.father && availableMembers.has(node.member.father)
|
|
|
|
? node.member.father.toString()
|
|
|
|
: undefined,
|
|
|
|
mother:
|
|
|
|
node.member.mother && availableMembers.has(node.member.mother)
|
|
|
|
? node.member.mother.toString()
|
|
|
|
: undefined,
|
|
|
|
|
|
|
|
spouses: node.couples?.map((c) => c.member.id.toString()),
|
|
|
|
children: children,
|
2023-08-22 15:17:36 +00:00
|
|
|
},
|
2023-08-23 08:27:51 +00:00
|
|
|
});
|
|
|
|
|
2023-08-23 08:58:25 +00:00
|
|
|
node?.down?.forEach((e) =>
|
|
|
|
treeToF3DataDownRecurse(e, array, availableMembers)
|
|
|
|
);
|
2023-08-23 08:27:51 +00:00
|
|
|
|
|
|
|
if (node.couples) {
|
|
|
|
for (const c of node.couples) {
|
|
|
|
array.push({
|
2023-08-23 09:25:33 +00:00
|
|
|
data: memberData(c.member, c.couple),
|
2023-08-23 08:27:51 +00:00
|
|
|
id: c.member.id.toString(),
|
|
|
|
rels: {
|
|
|
|
father:
|
|
|
|
c.member.father && availableMembers.has(c.member.father)
|
|
|
|
? c.member.father.toString()
|
|
|
|
: undefined,
|
|
|
|
mother:
|
|
|
|
c.member.mother && availableMembers.has(c.member.mother)
|
|
|
|
? c.member.mother.toString()
|
|
|
|
: undefined,
|
|
|
|
spouses: [node.member.id.toString()],
|
|
|
|
children: c.down.map((c) => c.member.id.toString()),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-08-23 08:58:25 +00:00
|
|
|
c.down.forEach((e) =>
|
|
|
|
treeToF3DataDownRecurse(e, array, availableMembers)
|
|
|
|
);
|
2023-08-23 08:27:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-22 15:08:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
import { jsPDF } from "jspdf";
|
|
|
|
import "svg2pdf.js";
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2023-08-22 14:57:41 +00:00
|
|
|
const doc = new jsPDF({
|
|
|
|
orientation: "l",
|
|
|
|
format: [1000, 1000],
|
|
|
|
});
|
|
|
|
|
|
|
|
const element = document.getElementById("FamilyChart");
|
|
|
|
console.log(element);
|
|
|
|
doc.svg(element!.children[0], { height: 1000, width: 1000 }).then(() => {
|
|
|
|
// save the created pdf
|
2023-08-22 15:08:58 +00:00
|
|
|
//doc.save("myPDF.pdf");
|
2023-08-22 14:57:41 +00:00
|
|
|
});
|
2023-08-22 15:08:58 +00:00
|
|
|
}, 6000);*/
|