Finish vite migration
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2023-12-12 19:45:54 +01:00
parent 34efa48c3e
commit cef5d3c416
9 changed files with 6 additions and 917 deletions

View File

@ -1,95 +0,0 @@
declare module "family-chart" {
type f3data = any;
type f3tree = any;
interface f3Rels {
spouses?: string[];
father?: string;
mother?: string;
children?: string[];
}
interface f3DataData {
gender: "M" | "F";
avatar?: string;
dead: boolean;
birthday?: string;
deathday?: string;
first_name: string;
last_name: string;
dateOfWedding?: string;
wedding_state?: string;
}
interface f3Data {
id: string;
rels: f3Rels;
data: f3DataData;
}
type f3State = {
data: f3Data[];
main_id?: any;
tree?: f3tree;
node_separation?: number;
level_separation?: number;
};
interface f3Update {
tree: (props) => void;
mainId: (mainId) => void;
data: (data: f3data) => void;
}
interface f3Store {
state: f3State;
update: f3update;
getData: () => f3data;
getTree: () => f3tree;
setOnUpdate: (cb: (props) => void) => void;
methods: any;
}
function createStore(initial_state: f3State): f3Store;
function CalculateTree({
data_stash,
main_id = null,
is_vertical = true,
node_separation = 250,
level_separation = 150,
});
function d3AnimationView(p: {
store: f3Store;
cont: HTMLElement | null;
Card?: any;
});
const handlers: any;
type F3elements = {
Card: (props: {
store: f3Store;
svg: HTMLElement;
mini_tree: boolean;
link_break: boolean;
cardEditForm?: boolean;
card_dim: {
w: number;
h: number;
text_x: number;
text_y: number;
img_w: number;
img_h: number;
img_x: number;
img_y: number;
};
card_display: ((data: f3Data) => string)[];
}) => F3CardBuilder;
};
type F3CardBuilder = (p: { node; d }) => HTMLElement;
const elements: F3elements;
}

View File

@ -31,7 +31,6 @@ import { SimpleFamilyTree } from "../../widgets/simple_family_tree/SimpleFamilyT
enum CurrTab {
BasicTree,
SimpleTree,
AdvancedTree,
}
enum TreeMode {
@ -148,7 +147,6 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
>
<Tab tabIndex={CurrTab.BasicTree} label="Basique" />
<Tab tabIndex={CurrTab.SimpleTree} label="Simple" />
{/*<Tab tabIndex={CurrTab.AdvancedTree} label="Avancé" />*/}
</Tabs>
</div>
@ -156,14 +154,8 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
<Paper style={{ flex: "1", display: "flex", flexDirection: "column" }}>
{currTab === CurrTab.BasicTree ? (
<BasicFamilyTree tree={tree!} depth={currDepth} />
) : currTab === CurrTab.SimpleTree ? (
<SimpleFamilyTree tree={tree!} depth={currDepth} />
) : (
<>unimplemented</> /*<ComplexFamilyTree
tree={tree!}
isUp={currMode === TreeMode.Ascending}
depth={currDepth}
/>*/
<SimpleFamilyTree tree={tree!} depth={currDepth} />
)}
</Paper>
</div>

View File

@ -1,339 +0,0 @@
import { mdiXml } from "@mdi/js";
import Icon from "@mdi/react";
import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
import { IconButton, Tooltip } from "@mui/material";
import "family-chart";
import { jsPDF } from "jspdf";
import React from "react";
import "svg2pdf.js";
import { Couple } from "../../api/CoupleApi";
import { Member, fmtDate } from "../../api/MemberApi";
import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider";
import {
FamilyTreeNode,
getAvailableMembers,
treeHeight,
treeWidth,
} from "../../utils/family_tree";
import { downloadBlob } from "../../utils/files_utils";
import "./family-chart.css";
export function ComplexFamilyTree(p: {
tree: FamilyTreeNode;
isUp: boolean;
depth: number;
}): React.ReactElement {
const darkTheme = useDarkTheme();
console.log(f3);
const applyTree = (container: HTMLDivElement) => {
if (!container) return;
const store = f3.createStore({
data: treeToF3Data(p.tree, p.isUp, p.depth),
node_separation: 250,
level_separation: 150,
});
const view = f3.d3AnimationView({
store,
cont: container,
});
const Card = f3.elements.Card({
store,
svg: view.svg,
card_dim: {
w: 210,
h: 120,
text_x: 5,
text_y: 75,
img_w: 60,
img_h: 70,
img_x: 5,
img_y: 5,
},
card_display: [
(d) =>
`${d.data.first_name || ""} ${d.data.last_name || ""} ${
d.data.dead ? "✝" : ""
}`,
(d) => {
let birthDeath = [];
if (d.data.birthday) birthDeath.push(d.data.birthday);
if (d.data.deathday) birthDeath.push(d.data.deathday);
let s = birthDeath.join(" -> ");
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;
},
],
mini_tree: true,
link_break: false,
});
// Patch generated card
const PatchedCard: f3.F3CardBuilder = (p) => {
const res = Card(p);
// Patch card colors for PDF export
res
.querySelector(".card-male")
?.querySelector(".card-body-rect")
?.setAttribute("fill", "#add8e6");
res
.querySelector(".card-female")
?.querySelector(".card-body-rect")
?.setAttribute("fill", "#ffb6c1");
return res;
};
view.setCard(PatchedCard);
store.setOnUpdate((props) => view.update(props || {}));
store.update.tree({ initial: false, transition_time: 0 });
};
const doExport = async (onlySVG: boolean) => {
const docWidth = treeWidth(p.tree) * 65;
const docHeight = treeHeight(p.tree) * 60;
console.info(`Tree w=${treeWidth(p.tree)} h=${treeHeight(p.tree)}`);
// Clone the SVG to manipulate it
const container = document.createElement("div");
container.classList.add("f3", "f3-export");
container.style.width = docWidth + "px";
container.style.height = docHeight + "px";
document.body.appendChild(container);
applyTree(container);
const target = container.children[0];
await new Promise((res) => setTimeout(() => res(null), 100));
// SVG manipulations (adaptations to export)
let dstSVG = target.innerHTML.replaceAll(
`<path class="link" fill="none" stroke="#fff"`,
`<path class="link" fill="none" stroke="#000"`
);
dstSVG = dstSVG.replaceAll(
`class="text-overflow-mask"`,
`class="text-overflow-mask" fill="transparent"`
);
dstSVG = dstSVG.replaceAll(`>UNKNOWN<`, `fill="#000">INCONNU<`);
dstSVG = dstSVG.replaceAll(
`class="card-outline`,
`fill="transparent" class="card-outline`
);
dstSVG = dstSVG.replaceAll("✝", " ");
// Download in SVG format
if (onlySVG) {
// Fix background color (first rect background)
dstSVG = dstSVG.replace(
`fill="transparent"></rect>`,
`fill="white"></rect>`
);
const blob = new Blob([`<svg>${dstSVG}</svg>`], {
type: "image/svg+xml",
});
downloadBlob(blob, "ArbreGenealogique.svg");
return;
}
// Download in PDF format
//navigator.clipboard.writeText(dstSVG);
target.innerHTML = dstSVG;
const doc = new jsPDF({
orientation: "l",
format: [docHeight, docWidth],
});
await doc.svg(target, {
height: docHeight,
width: docWidth,
});
container.remove();
// Save the created pdf
doc.save("ArbreGenealogique.pdf");
};
const exportPDF = () => doExport(false);
const exportSVG = () => doExport(true);
return (
<div>
<div style={{ textAlign: "right" }}>
<Tooltip title="Exporter le graphique au format PDF">
<IconButton onClick={exportPDF}>
<PictureAsPdfIcon />
</IconButton>
</Tooltip>
<Tooltip title="Exporter le graphique au format SVG">
<IconButton onClick={exportSVG}>
<Icon path={mdiXml} size={1} />
</IconButton>
</Tooltip>
</div>
<div
style={{ width: "100%" }}
className={`f3 ${darkTheme.enabled ? "f3-dark" : "f3-light"}`}
id="FamilyChart"
ref={applyTree}
></div>
</div>
);
}
function treeToF3Data(
node: FamilyTreeNode,
isUp: boolean,
depth: number
): f3.f3Data[] {
const availableMembers = getAvailableMembers(node, depth);
const list: f3.f3Data[] = [];
if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
else treeToF3DataDownRecurse(node, list, availableMembers);
return list;
}
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: f3.f3Data[],
availableMembers: Set<number>,
child?: number,
spouses?: number[]
) {
if (!availableMembers.has(node.member.id)) return;
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: f3.f3Data[],
availableMembers: Set<number>
) {
if (!availableMembers.has(node.member.id)) return;
// Get all members ids
let children = node?.down?.map((c) => c.member.id) ?? [];
node.couples?.map((c) => c.down.forEach((m) => children.push(m.member.id)));
children = children.filter((c) => availableMembers.has(c));
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
?.filter((s) => availableMembers.has(s.member.id))
.map((c) => c.member.id.toString()),
children: children.map((c) => c.toString()),
},
});
node?.down?.forEach((e) =>
treeToF3DataDownRecurse(e, array, availableMembers)
);
if (node.couples) {
for (const c of node.couples) {
if (!availableMembers.has(c.member.id)) continue;
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
.filter((c) => availableMembers.has(c.member.id))
.map((c) => c.member.id.toString()),
},
});
c.down.forEach((e) =>
treeToF3DataDownRecurse(e, array, availableMembers)
);
}
}
}

View File

@ -1,111 +0,0 @@
.f3 {
height: 700px;
max-height: calc(100vh - 80px);
width: 900px;
max-width: 100%;
margin: auto;
position: relative;
}
.f3 .cursor-pointer {
cursor: pointer;
}
.f3 svg.main_svg {
width: 100%;
height: 100%;
/*background-color: #3b5560;*/
color: #3b5560;
}
.f3 svg.main_svg text {
fill: currentColor;
}
.f3 rect.card-female,
.f3 .card-female .card-body-rect,
.f3 .card-female .text-overflow-mask {
fill: lightpink;
}
.f3 rect.card-male,
.f3 .card-male .card-body-rect,
.f3 .card-male .text-overflow-mask {
fill: lightblue;
}
.f3 .card-genderless .card-body-rect,
.f3 .card-genderless .text-overflow-mask {
fill: lightgray;
}
.f3 .card_add .card-body-rect {
fill: #3b5560;
stroke-width: 4px;
stroke: #fff;
cursor: pointer;
}
.f3 g.card_add text {
fill: #fff;
}
.f3 .card-main {
stroke: #000;
}
.f3 .card_family_tree rect {
transition: 0.3s;
}
.f3 .card_family_tree:hover rect {
transform: scale(1.1);
}
.f3 .card_add_relative {
cursor: pointer;
color: #fff;
transition: 0.3s;
}
.f3 .card_add_relative circle {
fill: rgba(0, 0, 0, 0);
}
.f3 .card_add_relative:hover {
color: black;
}
.f3 .card_edit.pencil_icon {
color: #fff;
transition: 0.3s;
}
.f3 .card_edit.pencil_icon:hover {
color: black;
}
.f3 .card_break_link,
.f3 .link_upper,
.f3 .link_lower,
.f3 .link_particles {
transform-origin: 50% 50%;
transition: 1s;
}
.f3 .card_break_link {
color: #fff;
}
.f3 .card_break_link.closed .link_upper {
transform: translate(-140.5px, 655.6px);
}
.f3 .card_break_link.closed .link_upper g {
transform: rotate(-58deg);
}
.f3 .card_break_link.closed .link_particles {
transform: scale(0);
}
.f3 .input-field input {
height: 2.5rem !important;
}
.f3 .input-field > label:not(.label-icon).active {
-webkit-transform: translateY(-8px) scale(0.8);
transform: translateY(-8px) scale(0.8);
}
.f3-light .link {
stroke: black;
}
.f3-export {
position: fixed;
top: 0px;
left: 0px;
/*width: 3508px;
height: 2480px;*/
opacity: 0;
}