import React from "react"; import f3, { f3Data } from "family-chart"; import "./family-chart.css"; import { FamilyTreeNode } from "../../utils/family_tree"; import { Member, fmtDate } from "../../api/MemberApi"; import { Couple } from "../../api/CoupleApi"; import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider"; export function ComplexFamilyTree(p: { tree: FamilyTreeNode; isUp: boolean; }): React.ReactElement { const darkTheme = useDarkTheme(); const refCb = (container: HTMLDivElement) => { if (!container) return; const store = f3.createStore({ data: treeToF3Data(p.tree, p.isUp), 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: 230, h: 70, text_x: 75, text_y: 15, img_w: 60, img_h: 60, img_x: 5, img_y: 5, }, card_display: [ (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 += ` ${weddingInfo.join( " - " )}`; } return s; }, ], mini_tree: true, link_break: false, }); view.setCard(Card); store.setOnUpdate((props) => view.update(props || {})); store.update.tree({ initial: true }); }; return (
); } function treeToF3Data(node: FamilyTreeNode, isUp: boolean): f3Data[] { const availableMembers = new Set(); getAvailableMembers(node, availableMembers); const list: f3Data[] = []; if (isUp) treeToF3DataUpRecurse(node, list, availableMembers); else treeToF3DataDownRecurse(node, list, availableMembers); return list; } function getAvailableMembers(t: FamilyTreeNode, s: Set) { 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)); } function memberData(m: Member, c?: Couple): f3.f3DataData { return { first_name: m.first_name ?? "_", last_name: m.last_name ?? "_", gender: m.sex ?? "M", avatar: m.thumbnailURL ?? undefined, dead: m.dead, birthday: m.dateOfBirth ? fmtDate(m.dateOfBirth) : undefined, deathday: m.dateOfDeath ? fmtDate(m.dateOfDeath) : undefined, wedding_state: c?.stateFr, dateOfWedding: c?.dateOfWedding ? fmtDate(c?.dateOfWedding) : undefined, }; } function treeToF3DataUpRecurse( node: FamilyTreeNode, array: f3Data[], availableMembers: Set, 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( node: FamilyTreeNode, array: f3Data[], availableMembers: Set ) { // 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({ 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: node.couples?.map((c) => c.member.id.toString()), children: children, }, }); node?.down?.forEach((e) => treeToF3DataDownRecurse(e, array, availableMembers) ); if (node.couples) { for (const c of node.couples) { array.push({ data: memberData(c.member, c.couple), 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()), }, }); c.down.forEach((e) => treeToF3DataDownRecurse(e, array, availableMembers) ); } } } /* import { jsPDF } from "jspdf"; import "svg2pdf.js"; setTimeout(() => { 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 //doc.save("myPDF.pdf"); }); }, 6000);*/