Public tree view: add generation depth controls (shared with member view)
The public tree chart was fixed at 3 ancestors / 2 descendants. Add the same Generations controls the member view has (slider + number stepper + "All" per direction), applied live around the focused person. Extracts the member page's inline DepthControl into a shared components/depth-control.tsx and uses it in both, so they stay in sync. The public chart gains anc/prog depth state + an apply effect (setAncestryDepth/ setProgenyDepth + updateTree) mirroring the member behavior. tsc clean; next build passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -3,9 +3,10 @@
|
||||
// Vendored family-chart styles (the package blocks the CSS subpath export).
|
||||
import "../app/trees/[id]/tree/chart.css";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import type { components } from "@/lib/api/schema";
|
||||
import { DepthControl } from "@/components/depth-control";
|
||||
|
||||
type Person = components["schemas"]["PersonRead"];
|
||||
type Relationship = components["schemas"]["RelationshipRead"];
|
||||
@@ -42,6 +43,16 @@ export function PublicTreeChart({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const chartRef = useRef<any>(null);
|
||||
|
||||
// Generations to show around the focus, each settable (or "all"). ALL_DEPTH is
|
||||
// a number bigger than any real lineage; the chart only renders real people.
|
||||
const [ancDepth, setAncDepth] = useState(3);
|
||||
const [progDepth, setProgDepth] = useState(2);
|
||||
const [ancAll, setAncAll] = useState(false);
|
||||
const [progAll, setProgAll] = useState(false);
|
||||
const ALL_DEPTH = 100;
|
||||
const effAnc = ancAll ? ALL_DEPTH : ancDepth;
|
||||
const effProg = progAll ? ALL_DEPTH : progDepth;
|
||||
|
||||
const parentsOf = useCallback(
|
||||
(id: string) =>
|
||||
rels.filter((x) => x.type === "parent_child" && x.person_to_id === id).map((x) => x.person_from_id),
|
||||
@@ -126,8 +137,8 @@ export function PublicTreeChart({
|
||||
const chart = f3.createChart(containerRef.current, data);
|
||||
chart.setCardHtml().setCardDisplay([["first name", "last name"], ["birthday"]]);
|
||||
chart.setOrientationHorizontal();
|
||||
chart.setAncestryDepth?.(3);
|
||||
chart.setProgenyDepth?.(2);
|
||||
chart.setAncestryDepth?.(effAnc);
|
||||
chart.setProgenyDepth?.(effProg);
|
||||
chart.setAfterUpdate?.(() => {
|
||||
const md = chart.getMainDatum?.();
|
||||
const id = md?.id ?? md?.data?.id;
|
||||
@@ -155,8 +166,41 @@ export function PublicTreeChart({
|
||||
}
|
||||
}, [focusId]);
|
||||
|
||||
// Apply depth changes to the live chart without a full rebuild.
|
||||
useEffect(() => {
|
||||
if (!chartRef.current) return;
|
||||
chartRef.current.setAncestryDepth?.(effAnc);
|
||||
chartRef.current.setProgenyDepth?.(effProg);
|
||||
chartRef.current.updateTree?.();
|
||||
}, [effAnc, effProg]);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 rounded-lg border border-[var(--border)] bg-[var(--surface)] px-4 py-2.5">
|
||||
<span className="text-sm font-medium">Generations</span>
|
||||
<DepthControl
|
||||
label="Ancestors"
|
||||
icon="↑"
|
||||
value={ancDepth}
|
||||
all={ancAll}
|
||||
onValue={(v) => {
|
||||
setAncAll(false);
|
||||
setAncDepth(v);
|
||||
}}
|
||||
onAll={setAncAll}
|
||||
/>
|
||||
<DepthControl
|
||||
label="Descendants"
|
||||
icon="↓"
|
||||
value={progDepth}
|
||||
all={progAll}
|
||||
onValue={(v) => {
|
||||
setProgAll(false);
|
||||
setProgDepth(v);
|
||||
}}
|
||||
onAll={setProgAll}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="f3 rounded-xl border border-[var(--border)]"
|
||||
|
||||
Reference in New Issue
Block a user