Can customize shown depth
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		@@ -5,22 +5,27 @@ import {
 | 
				
			|||||||
  FormControlLabel,
 | 
					  FormControlLabel,
 | 
				
			||||||
  FormLabel,
 | 
					  FormLabel,
 | 
				
			||||||
  IconButton,
 | 
					  IconButton,
 | 
				
			||||||
 | 
					  InputLabel,
 | 
				
			||||||
 | 
					  MenuItem,
 | 
				
			||||||
  Paper,
 | 
					  Paper,
 | 
				
			||||||
  Radio,
 | 
					  Radio,
 | 
				
			||||||
  RadioGroup,
 | 
					  RadioGroup,
 | 
				
			||||||
 | 
					  Select,
 | 
				
			||||||
  Tab,
 | 
					  Tab,
 | 
				
			||||||
  Tabs,
 | 
					  Tabs,
 | 
				
			||||||
} from "@mui/material";
 | 
					} from "@mui/material";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { useParams } from "react-router-dom";
 | 
					import { useParams } from "react-router-dom";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  FamilyTreeNode,
 | 
				
			||||||
  buildAscendingTree,
 | 
					  buildAscendingTree,
 | 
				
			||||||
  buildDescendingTree,
 | 
					  buildDescendingTree,
 | 
				
			||||||
 | 
					  treeHeight,
 | 
				
			||||||
} from "../../utils/family_tree";
 | 
					} from "../../utils/family_tree";
 | 
				
			||||||
import { useFamily } from "../../widgets/BaseFamilyRoute";
 | 
					import { useFamily } from "../../widgets/BaseFamilyRoute";
 | 
				
			||||||
 | 
					import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
 | 
				
			||||||
import { MemberItem } from "../../widgets/MemberItem";
 | 
					import { MemberItem } from "../../widgets/MemberItem";
 | 
				
			||||||
import { RouterLink } from "../../widgets/RouterLink";
 | 
					import { RouterLink } from "../../widgets/RouterLink";
 | 
				
			||||||
import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
 | 
					 | 
				
			||||||
import { ComplexFamilyTree } from "../../widgets/complex_family_tree/ComplexFamilyTree";
 | 
					import { ComplexFamilyTree } from "../../widgets/complex_family_tree/ComplexFamilyTree";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum CurrTab {
 | 
					enum CurrTab {
 | 
				
			||||||
@@ -43,15 +48,17 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const member = family.members.get(Number(memberId));
 | 
					  const member = family.members.get(Number(memberId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const tree = React.useMemo(
 | 
					  const memo: [FamilyTreeNode, number] | null = React.useMemo(() => {
 | 
				
			||||||
    () =>
 | 
					    if (!member) return null;
 | 
				
			||||||
      !member
 | 
					    const tree =
 | 
				
			||||||
        ? null
 | 
					      currMode === TreeMode.Ascending
 | 
				
			||||||
        : currMode === TreeMode.Ascending
 | 
					 | 
				
			||||||
        ? buildAscendingTree(member.id, family.members, family.couples)
 | 
					        ? buildAscendingTree(member.id, family.members, family.couples)
 | 
				
			||||||
        : buildDescendingTree(member.id, family.members, family.couples),
 | 
					        : buildDescendingTree(member.id, family.members, family.couples);
 | 
				
			||||||
    [member, currMode, family.members, family.couples]
 | 
					
 | 
				
			||||||
  );
 | 
					    return [tree, treeHeight(tree)];
 | 
				
			||||||
 | 
					  }, [member, currMode, family.members, family.couples]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [currDepth, setCurrDepth] = React.useState(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!member) {
 | 
					  if (!member) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
@@ -61,6 +68,10 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [tree, maxDepth] = memo!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currDepth === 0) setCurrDepth(maxDepth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      style={{
 | 
					      style={{
 | 
				
			||||||
@@ -90,7 +101,10 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
 | 
				
			|||||||
          <RadioGroup
 | 
					          <RadioGroup
 | 
				
			||||||
            row
 | 
					            row
 | 
				
			||||||
            value={currMode}
 | 
					            value={currMode}
 | 
				
			||||||
            onChange={(_e, v) => setCurrMode(Number(v))}
 | 
					            onChange={(_e, v) => {
 | 
				
			||||||
 | 
					              setCurrDepth(0);
 | 
				
			||||||
 | 
					              setCurrMode(Number(v));
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <FormControlLabel
 | 
					            <FormControlLabel
 | 
				
			||||||
              value={TreeMode.Descending}
 | 
					              value={TreeMode.Descending}
 | 
				
			||||||
@@ -105,7 +119,26 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
 | 
				
			|||||||
          </RadioGroup>
 | 
					          </RadioGroup>
 | 
				
			||||||
        </FormControl>
 | 
					        </FormControl>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div style={{ flex: "2" }}></div>
 | 
					        <div style={{ flex: "1" }}></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
 | 
				
			||||||
 | 
					          <InputLabel>Profondeur</InputLabel>
 | 
				
			||||||
 | 
					          <Select
 | 
				
			||||||
 | 
					            value={currDepth}
 | 
				
			||||||
 | 
					            onChange={(v) => setCurrDepth(Number(v.target.value))}
 | 
				
			||||||
 | 
					            label="Profondeur"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            {Array(maxDepth)
 | 
				
			||||||
 | 
					              .fill(0)
 | 
				
			||||||
 | 
					              .map((_v, index) => (
 | 
				
			||||||
 | 
					                <MenuItem key={index} value={index + 1}>
 | 
				
			||||||
 | 
					                  {index + 1}
 | 
				
			||||||
 | 
					                </MenuItem>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					          </Select>
 | 
				
			||||||
 | 
					        </FormControl>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div style={{ flex: "1" }}></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <Tabs
 | 
					        <Tabs
 | 
				
			||||||
          value={currTab}
 | 
					          value={currTab}
 | 
				
			||||||
@@ -120,11 +153,12 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
 | 
				
			|||||||
      {/* the tree itself */}
 | 
					      {/* the tree itself */}
 | 
				
			||||||
      <Paper style={{ flex: "1" }}>
 | 
					      <Paper style={{ flex: "1" }}>
 | 
				
			||||||
        {currTab === CurrTab.BasicTree ? (
 | 
					        {currTab === CurrTab.BasicTree ? (
 | 
				
			||||||
          <BasicFamilyTree tree={tree!} />
 | 
					          <BasicFamilyTree tree={tree!} depth={currDepth} />
 | 
				
			||||||
        ) : (
 | 
					        ) : (
 | 
				
			||||||
          <ComplexFamilyTree
 | 
					          <ComplexFamilyTree
 | 
				
			||||||
            tree={tree!}
 | 
					            tree={tree!}
 | 
				
			||||||
            isUp={currMode === TreeMode.Ascending}
 | 
					            isUp={currMode === TreeMode.Ascending}
 | 
				
			||||||
 | 
					            depth={currDepth}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
      </Paper>
 | 
					      </Paper>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,6 +82,9 @@ export function buildDescendingTree(
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Sort family tree children per date of birth
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
function sortChildren(n: FamilyTreeNode[]): FamilyTreeNode[] {
 | 
					function sortChildren(n: FamilyTreeNode[]): FamilyTreeNode[] {
 | 
				
			||||||
  n.sort(
 | 
					  n.sort(
 | 
				
			||||||
    (a, b) =>
 | 
					    (a, b) =>
 | 
				
			||||||
@@ -89,3 +92,71 @@ function sortChildren(n: FamilyTreeNode[]): FamilyTreeNode[] {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
  return n;
 | 
					  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<number> {
 | 
				
			||||||
 | 
					  const s = new Set<number>();
 | 
				
			||||||
 | 
					  getAvailableMembersRecurse(t, depth, s);
 | 
				
			||||||
 | 
					  return s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getAvailableMembersRecurse(
 | 
				
			||||||
 | 
					  t: FamilyTreeNode,
 | 
				
			||||||
 | 
					  depth: number,
 | 
				
			||||||
 | 
					  s: Set<number>
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  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));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import { MemberPhoto } from "./MemberPhoto";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function BasicFamilyTree(p: {
 | 
					export function BasicFamilyTree(p: {
 | 
				
			||||||
  tree: FamilyTreeNode;
 | 
					  tree: FamilyTreeNode;
 | 
				
			||||||
 | 
					  depth: number;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <TreeView
 | 
					    <TreeView
 | 
				
			||||||
@@ -20,12 +21,15 @@ export function BasicFamilyTree(p: {
 | 
				
			|||||||
      defaultExpandIcon={<ChevronRightIcon />}
 | 
					      defaultExpandIcon={<ChevronRightIcon />}
 | 
				
			||||||
      sx={{ flexGrow: 1 }}
 | 
					      sx={{ flexGrow: 1 }}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <FamilyTreeItem n={p.tree} />
 | 
					      <FamilyTreeItem n={p.tree} depth={p.depth} />
 | 
				
			||||||
    </TreeView>
 | 
					    </TreeView>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function FamilyTreeItem(p: { n: FamilyTreeNode }): React.ReactElement {
 | 
					function FamilyTreeItem(p: {
 | 
				
			||||||
 | 
					  depth: number;
 | 
				
			||||||
 | 
					  n: FamilyTreeNode;
 | 
				
			||||||
 | 
					}): React.ReactElement {
 | 
				
			||||||
  let children = p.n.down ?? [];
 | 
					  let children = p.n.down ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (p.n.couples) {
 | 
					  if (p.n.couples) {
 | 
				
			||||||
@@ -55,9 +59,10 @@ function FamilyTreeItem(p: { n: FamilyTreeNode }): React.ReactElement {
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      {children.map((c) => (
 | 
					      {p.depth >= 2 &&
 | 
				
			||||||
        <FamilyTreeItem key={c.member.id} n={c} />
 | 
					        children.map((c) => (
 | 
				
			||||||
      ))}
 | 
					          <FamilyTreeItem depth={p.depth - 1} key={c.member.id} n={c} />
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
    </TreeItem>
 | 
					    </TreeItem>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,13 +9,19 @@ import "svg2pdf.js";
 | 
				
			|||||||
import { Couple } from "../../api/CoupleApi";
 | 
					import { Couple } from "../../api/CoupleApi";
 | 
				
			||||||
import { Member, fmtDate } from "../../api/MemberApi";
 | 
					import { Member, fmtDate } from "../../api/MemberApi";
 | 
				
			||||||
import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider";
 | 
					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 { downloadBlob } from "../../utils/files_utils";
 | 
				
			||||||
import "./family-chart.css";
 | 
					import "./family-chart.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ComplexFamilyTree(p: {
 | 
					export function ComplexFamilyTree(p: {
 | 
				
			||||||
  tree: FamilyTreeNode;
 | 
					  tree: FamilyTreeNode;
 | 
				
			||||||
  isUp: boolean;
 | 
					  isUp: boolean;
 | 
				
			||||||
 | 
					  depth: number;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
  const darkTheme = useDarkTheme();
 | 
					  const darkTheme = useDarkTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,7 +29,7 @@ export function ComplexFamilyTree(p: {
 | 
				
			|||||||
    if (!container) return;
 | 
					    if (!container) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const store = f3.createStore({
 | 
					    const store = f3.createStore({
 | 
				
			||||||
      data: treeToF3Data(p.tree, p.isUp),
 | 
					      data: treeToF3Data(p.tree, p.isUp, p.depth),
 | 
				
			||||||
      node_separation: 250,
 | 
					      node_separation: 250,
 | 
				
			||||||
      level_separation: 150,
 | 
					      level_separation: 150,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -197,41 +203,12 @@ export function ComplexFamilyTree(p: {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function treeHeight(node: FamilyTreeNode): number {
 | 
					function treeToF3Data(
 | 
				
			||||||
  let res =
 | 
					  node: FamilyTreeNode,
 | 
				
			||||||
    node.down?.reduce((prev, node) => Math.max(prev, treeHeight(node)), 0) ?? 0;
 | 
					  isUp: boolean,
 | 
				
			||||||
 | 
					  depth: number
 | 
				
			||||||
  node.couples?.forEach(
 | 
					): f3Data[] {
 | 
				
			||||||
    (c) =>
 | 
					  const availableMembers = getAvailableMembers(node, depth);
 | 
				
			||||||
      (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<number>();
 | 
					 | 
				
			||||||
  getAvailableMembers(node, availableMembers);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const list: f3Data[] = [];
 | 
					  const list: f3Data[] = [];
 | 
				
			||||||
  if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
 | 
					  if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
 | 
				
			||||||
@@ -239,17 +216,6 @@ function treeToF3Data(node: FamilyTreeNode, isUp: boolean): f3Data[] {
 | 
				
			|||||||
  return 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));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function memberData(m: Member, c?: Couple): f3.f3DataData {
 | 
					function memberData(m: Member, c?: Couple): f3.f3DataData {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    first_name: m.first_name ?? "_",
 | 
					    first_name: m.first_name ?? "_",
 | 
				
			||||||
@@ -271,6 +237,8 @@ function treeToF3DataUpRecurse(
 | 
				
			|||||||
  child?: number,
 | 
					  child?: number,
 | 
				
			||||||
  spouses?: number[]
 | 
					  spouses?: number[]
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					  if (!availableMembers.has(node.member.id)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  array.push({
 | 
					  array.push({
 | 
				
			||||||
    data: memberData(node.member),
 | 
					    data: memberData(node.member),
 | 
				
			||||||
    id: node.member.id.toString(),
 | 
					    id: node.member.id.toString(),
 | 
				
			||||||
@@ -309,11 +277,13 @@ function treeToF3DataDownRecurse(
 | 
				
			|||||||
  array: f3Data[],
 | 
					  array: f3Data[],
 | 
				
			||||||
  availableMembers: Set<number>
 | 
					  availableMembers: Set<number>
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					  if (!availableMembers.has(node.member.id)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Get all members ids
 | 
					  // Get all members ids
 | 
				
			||||||
  const children = node?.down?.map((c) => c.member.id.toString()) ?? [];
 | 
					  let children = node?.down?.map((c) => c.member.id) ?? [];
 | 
				
			||||||
  node.couples?.map((c) =>
 | 
					  node.couples?.map((c) => c.down.forEach((m) => children.push(m.member.id)));
 | 
				
			||||||
    c.down.forEach((m) => children.push(m.member.id.toString()))
 | 
					
 | 
				
			||||||
  );
 | 
					  children = children.filter((c) => availableMembers.has(c));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  array.push({
 | 
					  array.push({
 | 
				
			||||||
    data: memberData(node.member),
 | 
					    data: memberData(node.member),
 | 
				
			||||||
@@ -328,8 +298,10 @@ function treeToF3DataDownRecurse(
 | 
				
			|||||||
          ? node.member.mother.toString()
 | 
					          ? node.member.mother.toString()
 | 
				
			||||||
          : undefined,
 | 
					          : undefined,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      spouses: node.couples?.map((c) => c.member.id.toString()),
 | 
					      spouses: node.couples
 | 
				
			||||||
      children: children,
 | 
					        ?.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) {
 | 
					  if (node.couples) {
 | 
				
			||||||
    for (const c of node.couples) {
 | 
					    for (const c of node.couples) {
 | 
				
			||||||
 | 
					      if (!availableMembers.has(c.member.id)) continue;
 | 
				
			||||||
      array.push({
 | 
					      array.push({
 | 
				
			||||||
        data: memberData(c.member, c.couple),
 | 
					        data: memberData(c.member, c.couple),
 | 
				
			||||||
        id: c.member.id.toString(),
 | 
					        id: c.member.id.toString(),
 | 
				
			||||||
@@ -352,7 +325,9 @@ function treeToF3DataDownRecurse(
 | 
				
			|||||||
              ? c.member.mother.toString()
 | 
					              ? c.member.mother.toString()
 | 
				
			||||||
              : undefined,
 | 
					              : undefined,
 | 
				
			||||||
          spouses: [node.member.id.toString()],
 | 
					          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()),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user