Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
00b787b
feat: add hand-drawn animation to FamilyTreePlaceholder
jthrilly Mar 19, 2026
75c9dac
fix(e2e): resolve SILOS WebKit failures with announcement-based DnD
jthrilly Mar 19, 2026
b92b77d
refactor: update QuickStart wizard types for parent relationship rede…
jthrilly Mar 19, 2026
5a19a1c
refactor: replace biological toggle with raised/bio/auxiliary fields …
jthrilly Mar 19, 2026
7b4858a
refactor: add donor/surrogate selection to BioParentsStep
jthrilly Mar 19, 2026
1048c12
feat: add SiblingParentMappingStep for per-sibling parent assignment
jthrilly Mar 19, 2026
79a0fce
feat: add ParentGroupingStep for zero-siblings parent grouping
jthrilly Mar 19, 2026
e02906d
feat: wire SiblingParentMappingStep and ParentGroupingStep into wizard
jthrilly Mar 19, 2026
9cbd0b2
use node context menu; animate pedigree census placeholder
jthrilly Mar 19, 2026
de418c3
refactor: update generateQuickStartNetwork for flexible parent relati…
jthrilly Mar 19, 2026
135a1f7
fix: resolve lint errors in new wizard steps
jthrilly Mar 19, 2026
cc06454
feat: add pre-populated family stories for FamilyTreeCensus
jthrilly Mar 19, 2026
b983720
refactor: remove filled/unfilled distinction and label from FamilyTre…
jthrilly Mar 19, 2026
061398c
feat: load FamilyTree store from stageMetadata when available
jthrilly Mar 19, 2026
7554d1a
fix: let DropdownMenu handle its own open state for NodeContextMenu
jthrilly Mar 19, 2026
1de109b
fix: use onPointerUp for context menu to bypass drag source click sup…
jthrilly Mar 19, 2026
4f4a29c
feat: add bio-parent edge type, parent partnership step, and flatten …
jthrilly Mar 19, 2026
649cccc
refactor: rebuild FamilyTreeNode with labeled node pattern and ego icon
jthrilly Mar 19, 2026
571bce4
feat: show relationship to ego as fallback label on FamilyTreeNode
jthrilly Mar 19, 2026
ef8bd36
refactor: only show label below node when name is unknown
jthrilly Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions lib/interviewer/Interfaces/FamilyTreeCensus/FamilyTreeCensus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ const FamilyTreeCensus = (props: FamilyTreeCensusProps) => {
return (
<>
<div className="interface">
<Prompts
prompts={allPrompts}
currentPromptId={allPrompts[currentStepIndex]?.id}
className="shrink-0"
/>
{!showQuickStart && (
<Prompts
prompts={allPrompts}
currentPromptId={allPrompts[currentStepIndex]?.id}
className="shrink-0"
/>
)}
<div className="relative flex grow items-center justify-center">
{showQuickStart ? (
<QuickStartForm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,18 @@ describe('generateQuickStartNetwork', () => {
store.getState().generateQuickStartNetwork(
quickStart({
parents: [
{ name: '', nameKnown: false, edgeType: 'parent' },
{ name: '', nameKnown: false, edgeType: 'parent' },
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
],
}),
);
Expand All @@ -288,8 +298,18 @@ describe('generateQuickStartNetwork', () => {
store.getState().generateQuickStartNetwork(
quickStart({
parents: [
{ name: '', nameKnown: false, edgeType: 'parent' },
{ name: '', nameKnown: false, edgeType: 'parent' },
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
],
siblings: [{ name: '' }, { name: '' }],
}),
Expand Down Expand Up @@ -372,8 +392,18 @@ describe('integration: full flow', () => {

store.getState().generateQuickStartNetwork({
parents: [
{ name: '', nameKnown: false, edgeType: 'parent' },
{ name: '', nameKnown: false, edgeType: 'parent' },
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
{
name: '',
nameKnown: false,
raisedYou: true,
biologicallyRelated: true,
},
],
bioParents: [],
siblings: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,104 +1,137 @@
'use client';

import { motion } from 'motion/react';

/**
* Decorative placeholder illustration shown on the FamilyTreeCensus interface
* before the participant has started building their family tree. Renders a
* ghost pedigree (squares and circles connected by lines) at low opacity to
* preview what the completed tree will look like.
*
* Each stroke animates in sequentially with a hand-drawn drawing effect.
* Uses Motion variants so the parent can orchestrate the animation by
* toggling between "initial" and "animate" states.
*
* Layout (all positions derived from node centers):
*
* Gen 1: [Father]----[Mother] y = 28
* Gen 1: [Father]----[Mother] y = 280
* |
* ───────┼─────── y = 80
* ───────┼─────── y = 800
* | | |
* Gen 2: (Sib) [Ego] (Sib) y = 110
* Gen 2: (Sib) [Ego] (Sib) y = 1100
*/

const BASE_DELAY = 0.5;

const draw = (delay: number) => ({
initial: { pathLength: 0 },
animate: {
pathLength: 1,
transition: {
pathLength: {
type: 'spring' as const,
delay: BASE_DELAY + delay,
// Increase damping to prevent overshoot (which causes weird visual glitches with lines)
damping: 20,
},
},
},
});

export default function FamilyTreePlaceholder({
className,
}: {
className?: string;
}) {
// Node geometry
const r = 17; // circle radius & half side length for squares
// Node geometry (10x scale to avoid sub-pixel rounding errors)
const r = 170; // circle radius & half side length for squares
const d = r * 2; // node diameter / side length
const rx = 10; // square corner radius
const rx = 100; // square corner radius

// Node centers
const father = { x: 100, y: 28 };
const mother = { x: 180, y: 28 };
const midX = (father.x + mother.x) / 2; // 140
const railY = 80;
const child1 = { x: 60, y: 110 }; // circle
const ego = { x: 140, y: 110 }; // square (slightly brighter)
const child3 = { x: 220, y: 110 }; // circle
const father = { x: 1000, y: 280 };
const mother = { x: 1800, y: 280 };
const midX = (father.x + mother.x) / 2; // 1400
const railY = 800;
const child1 = { x: 600, y: 1100 }; // circle
const ego = { x: 1400, y: 1100 }; // square (slightly brighter)
const child3 = { x: 2200, y: 1100 }; // circle

const stroke = 'var(--color-platinum)';
const lineStroke = 'var(--color-platinum)';
const sw = 2.5; // stroke width
const sw = 25; // stroke width

return (
<svg
viewBox="0 0 280 145"
<motion.svg
viewBox="0 0 2800 1450"
xmlns="http://www.w3.org/2000/svg"
className={className}
aria-hidden
initial="initial"
animate="animate"
>
{/* --- Lines (behind nodes) --- */}
{/* Partner line: right edge of father to left edge of mother */}
<line
<motion.line
x1={father.x + r}
y1={father.y}
x2={mother.x - r}
y2={mother.y}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(0.4)}
/>
{/* Vertical from partner midpoint to sibling rail */}
<line
<motion.line
x1={midX}
y1={father.y}
x2={midX}
y2={railY}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(0.65)}
/>
{/* Sibling rail */}
<line
<motion.line
x1={child1.x}
y1={railY}
x2={child3.x}
y2={railY}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(0.9)}
/>
{/* Drop lines to children */}
<line
<motion.line
x1={child1.x}
y1={railY}
y1={railY - 10} // slight overlap with rail to avoid gaps
x2={child1.x}
y2={child1.y - r}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(1.15)}
/>
<line
<motion.line
x1={ego.x}
y1={railY}
y1={railY - 10} // slight overlap with rail to avoid gaps
x2={ego.x}
y2={ego.y - r}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(1.15)}
/>
<line
<motion.line
x1={child3.x}
y1={railY}
y1={railY - 10} // slight overlap with rail to avoid gaps
x2={child3.x}
y2={child3.y - r}
stroke={lineStroke}
stroke={stroke}
strokeWidth={sw}
variants={draw(1.15)}
/>

{/* --- Nodes --- */}
{/* Father (square) */}
<rect
<motion.rect
x={father.x - r}
y={father.y - r}
width={d}
Expand All @@ -107,27 +140,30 @@ export default function FamilyTreePlaceholder({
fill="none"
stroke={stroke}
strokeWidth={sw}
variants={draw(0)}
/>
{/* Mother (circle) */}
<circle
<motion.circle
cx={mother.x}
cy={mother.y}
r={r}
fill="none"
stroke={stroke}
strokeWidth={sw}
variants={draw(0.15)}
/>
{/* Sibling left (circle) */}
<circle
<motion.circle
cx={child1.x}
cy={child1.y}
r={r}
fill="none"
stroke={stroke}
strokeWidth={sw}
variants={draw(1.4)}
/>
{/* Ego (square, slightly more visible) */}
<rect
<motion.rect
x={ego.x - r}
y={ego.y - r}
width={d}
Expand All @@ -136,16 +172,18 @@ export default function FamilyTreePlaceholder({
fill="none"
stroke={stroke}
strokeWidth={sw}
variants={draw(1.4)}
/>
{/* Sibling right (circle) */}
<circle
<motion.circle
cx={child3.x}
cy={child3.y}
r={r}
fill="none"
stroke={stroke}
strokeWidth={sw}
variants={draw(1.4)}
/>
</svg>
</motion.svg>
);
}
Loading
Loading