diff --git a/geneit_app/src/routes/family/FamilyMemberTreeRoute.tsx b/geneit_app/src/routes/family/FamilyMemberTreeRoute.tsx
index 5d2950f..6c8e8c5 100644
--- a/geneit_app/src/routes/family/FamilyMemberTreeRoute.tsx
+++ b/geneit_app/src/routes/family/FamilyMemberTreeRoute.tsx
@@ -5,22 +5,27 @@ import {
FormControlLabel,
FormLabel,
IconButton,
+ InputLabel,
+ MenuItem,
Paper,
Radio,
RadioGroup,
+ Select,
Tab,
Tabs,
} from "@mui/material";
import React from "react";
import { useParams } from "react-router-dom";
import {
+ FamilyTreeNode,
buildAscendingTree,
buildDescendingTree,
+ treeHeight,
} from "../../utils/family_tree";
import { useFamily } from "../../widgets/BaseFamilyRoute";
+import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
import { MemberItem } from "../../widgets/MemberItem";
import { RouterLink } from "../../widgets/RouterLink";
-import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
import { ComplexFamilyTree } from "../../widgets/complex_family_tree/ComplexFamilyTree";
enum CurrTab {
@@ -43,15 +48,17 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
const member = family.members.get(Number(memberId));
- const tree = React.useMemo(
- () =>
- !member
- ? null
- : currMode === TreeMode.Ascending
+ const memo: [FamilyTreeNode, number] | null = React.useMemo(() => {
+ if (!member) return null;
+ const tree =
+ currMode === TreeMode.Ascending
? buildAscendingTree(member.id, family.members, family.couples)
- : buildDescendingTree(member.id, family.members, family.couples),
- [member, currMode, family.members, family.couples]
- );
+ : buildDescendingTree(member.id, family.members, family.couples);
+
+ return [tree, treeHeight(tree)];
+ }, [member, currMode, family.members, family.couples]);
+
+ const [currDepth, setCurrDepth] = React.useState(0);
if (!member) {
return (
@@ -61,6 +68,10 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
);
}
+ const [tree, maxDepth] = memo!;
+
+ if (currDepth === 0) setCurrDepth(maxDepth);
+
return (
setCurrMode(Number(v))}
+ onChange={(_e, v) => {
+ setCurrDepth(0);
+ setCurrMode(Number(v));
+ }}
>
-
+
+
+
+ Profondeur
+
+
+
+
{currTab === CurrTab.BasicTree ? (
-
+
) : (
)}
diff --git a/geneit_app/src/utils/family_tree.ts b/geneit_app/src/utils/family_tree.ts
index e9dba4d..5444540 100644
--- a/geneit_app/src/utils/family_tree.ts
+++ b/geneit_app/src/utils/family_tree.ts
@@ -82,6 +82,9 @@ export function buildDescendingTree(
};
}
+/**
+ * Sort family tree children per date of birth
+ */
function sortChildren(n: FamilyTreeNode[]): FamilyTreeNode[] {
n.sort(
(a, b) =>
@@ -89,3 +92,71 @@ function sortChildren(n: FamilyTreeNode[]): FamilyTreeNode[] {
);
return n;
}
+
+/**
+ * Compute family tree height
+ */
+export function treeHeight(node: FamilyTreeNode): number {
+ let res =
+ node.down?.reduce((prev, node) => Math.max(prev, treeHeight(node)), 0) ?? 0;
+
+ node.couples?.forEach(
+ (c) =>
+ (res = Math.max(
+ res,
+ c.down.reduce((prev, node) => Math.max(prev, treeHeight(node)), 0)
+ ))
+ );
+
+ return res + 1;
+}
+
+/**
+ * Compute family tree width
+ */
+export function treeWidth(node: FamilyTreeNode): number {
+ const values = new Array(treeHeight(node)).fill(0);
+ treeWidthRecurse(node, values, 0);
+ return Math.max(...values);
+}
+
+function treeWidthRecurse(node: FamilyTreeNode, vals: number[], level: number) {
+ vals[level] +=
+ 1 + (node.couples?.length ?? 0) + ((node.down?.length ?? 0) > 0 ? 1 : 0);
+
+ node.down?.forEach((n) => treeWidthRecurse(n, vals, level + 1));
+
+ node.couples?.forEach((c) =>
+ c.down.forEach((n) => treeWidthRecurse(n, vals, level + 1))
+ );
+}
+
+/**
+ * Get the list of members to be shown in a tree,
+ * depending of a specified depth
+ */
+export function getAvailableMembers(
+ t: FamilyTreeNode,
+ depth: number
+): Set {
+ const s = new Set();
+ getAvailableMembersRecurse(t, depth, s);
+ return s;
+}
+
+function getAvailableMembersRecurse(
+ t: FamilyTreeNode,
+ depth: number,
+ s: Set
+) {
+ if (depth < 1) return;
+
+ s.add(t.member.id);
+
+ t.couples?.forEach((c) => {
+ s.add(c.member.id);
+ c.down.forEach((e) => getAvailableMembersRecurse(e, depth - 1, s));
+ });
+
+ t.down?.forEach((e) => getAvailableMembersRecurse(e, depth - 1, s));
+}
diff --git a/geneit_app/src/widgets/BasicFamilyTree.tsx b/geneit_app/src/widgets/BasicFamilyTree.tsx
index ad3ef02..4c66ab4 100644
--- a/geneit_app/src/widgets/BasicFamilyTree.tsx
+++ b/geneit_app/src/widgets/BasicFamilyTree.tsx
@@ -13,6 +13,7 @@ import { MemberPhoto } from "./MemberPhoto";
export function BasicFamilyTree(p: {
tree: FamilyTreeNode;
+ depth: number;
}): React.ReactElement {
return (
}
sx={{ flexGrow: 1 }}
>
-
+
);
}
-function FamilyTreeItem(p: { n: FamilyTreeNode }): React.ReactElement {
+function FamilyTreeItem(p: {
+ depth: number;
+ n: FamilyTreeNode;
+}): React.ReactElement {
let children = p.n.down ?? [];
if (p.n.couples) {
@@ -55,9 +59,10 @@ function FamilyTreeItem(p: { n: FamilyTreeNode }): React.ReactElement {
}
>
- {children.map((c) => (
-
- ))}
+ {p.depth >= 2 &&
+ children.map((c) => (
+
+ ))}
);
}
diff --git a/geneit_app/src/widgets/complex_family_tree/ComplexFamilyTree.tsx b/geneit_app/src/widgets/complex_family_tree/ComplexFamilyTree.tsx
index c948711..0c70308 100644
--- a/geneit_app/src/widgets/complex_family_tree/ComplexFamilyTree.tsx
+++ b/geneit_app/src/widgets/complex_family_tree/ComplexFamilyTree.tsx
@@ -9,13 +9,19 @@ import "svg2pdf.js";
import { Couple } from "../../api/CoupleApi";
import { Member, fmtDate } from "../../api/MemberApi";
import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider";
-import { FamilyTreeNode } from "../../utils/family_tree";
+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();
@@ -23,7 +29,7 @@ export function ComplexFamilyTree(p: {
if (!container) return;
const store = f3.createStore({
- data: treeToF3Data(p.tree, p.isUp),
+ data: treeToF3Data(p.tree, p.isUp, p.depth),
node_separation: 250,
level_separation: 150,
});
@@ -197,41 +203,12 @@ export function ComplexFamilyTree(p: {
);
}
-function treeHeight(node: FamilyTreeNode): number {
- let res =
- node.down?.reduce((prev, node) => Math.max(prev, treeHeight(node)), 0) ?? 0;
-
- node.couples?.forEach(
- (c) =>
- (res = Math.max(
- res,
- c.down.reduce((prev, node) => Math.max(prev, treeHeight(node)), 0)
- ))
- );
-
- return res + 1;
-}
-
-function treeWidth(node: FamilyTreeNode): number {
- const values = new Array(treeHeight(node)).fill(0);
- treeWidthRecurse(node, values, 0);
- return Math.max(...values);
-}
-
-function treeWidthRecurse(node: FamilyTreeNode, vals: number[], level: number) {
- vals[level] +=
- 1 + (node.couples?.length ?? 0) + ((node.down?.length ?? 0) > 0 ? 1 : 0);
-
- node.down?.forEach((n) => treeWidthRecurse(n, vals, level + 1));
-
- node.couples?.forEach((c) =>
- c.down.forEach((n) => treeWidthRecurse(n, vals, level + 1))
- );
-}
-
-function treeToF3Data(node: FamilyTreeNode, isUp: boolean): f3Data[] {
- const availableMembers = new Set();
- getAvailableMembers(node, availableMembers);
+function treeToF3Data(
+ node: FamilyTreeNode,
+ isUp: boolean,
+ depth: number
+): f3Data[] {
+ const availableMembers = getAvailableMembers(node, depth);
const list: f3Data[] = [];
if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
@@ -239,17 +216,6 @@ function treeToF3Data(node: FamilyTreeNode, isUp: boolean): f3Data[] {
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 ?? "_",
@@ -271,6 +237,8 @@ function treeToF3DataUpRecurse(
child?: number,
spouses?: number[]
) {
+ if (!availableMembers.has(node.member.id)) return;
+
array.push({
data: memberData(node.member),
id: node.member.id.toString(),
@@ -309,11 +277,13 @@ function treeToF3DataDownRecurse(
array: f3Data[],
availableMembers: Set
) {
+ if (!availableMembers.has(node.member.id)) return;
+
// 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()))
- );
+ 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),
@@ -328,8 +298,10 @@ function treeToF3DataDownRecurse(
? node.member.mother.toString()
: undefined,
- spouses: node.couples?.map((c) => c.member.id.toString()),
- children: children,
+ spouses: node.couples
+ ?.filter((s) => availableMembers.has(s.member.id))
+ .map((c) => c.member.id.toString()),
+ children: children.map((c) => c.toString()),
},
});
@@ -339,6 +311,7 @@ function treeToF3DataDownRecurse(
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(),
@@ -352,7 +325,9 @@ function treeToF3DataDownRecurse(
? c.member.mother.toString()
: undefined,
spouses: [node.member.id.toString()],
- children: c.down.map((c) => c.member.id.toString()),
+ children: c.down
+ .filter((c) => availableMembers.has(c.member.id))
+ .map((c) => c.member.id.toString()),
},
});