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-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: {
|
|
|
|
w: 220,
|
|
|
|
h: 70,
|
|
|
|
text_x: 75,
|
|
|
|
text_y: 15,
|
|
|
|
img_w: 60,
|
|
|
|
img_h: 60,
|
|
|
|
img_x: 5,
|
|
|
|
img_y: 5,
|
|
|
|
},
|
|
|
|
card_display: [
|
2023-08-22 15:17:36 +00:00
|
|
|
(d) => `${d.data.first_name || ""} ${d.data.last_name || ""}`,
|
2023-08-22 14:12:55 +00:00
|
|
|
(d) => `${d.data["birthday"] || ""}`,
|
|
|
|
],
|
|
|
|
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 08:58:25 +00:00
|
|
|
if (isUp) {
|
|
|
|
treeToF3DataUpRecurse(node, list, availableMembers);
|
|
|
|
} else treeToF3DataDownRecurse(node, list, availableMembers);
|
2023-08-23 08:27:51 +00:00
|
|
|
console.info(list);
|
|
|
|
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 08:58:25 +00:00
|
|
|
function memberData(m: Member): f3.f3DataData {
|
|
|
|
return {
|
|
|
|
first_name: m.first_name ?? "_",
|
|
|
|
last_name: m.last_name ?? "_",
|
|
|
|
gender: m.sex ?? "M",
|
|
|
|
avatar: m.thumbnailURL ?? undefined,
|
|
|
|
birthday: m.dateOfBirth ? fmtDate(m.dateOfBirth) : undefined,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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 08:58:25 +00:00
|
|
|
data: memberData(c.member),
|
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);*/
|