Repository navigation
fix(ui): spring the org chart's folds and stop drags sticking to the cursor - #46
Conversation
…cursor Folding and unfolding re-rendered every card and animated left/top on a CSS ease, so large teams stalled and new reports popped in. Cards now memoise apart from their position, and cards and wires share one spring (a CSS linear() curve), so new reports slide out from under their manager and folded ones tuck back beneath it. Cards and wires keep a stable DOM order so running transitions are never cancelled. The folded or dropped-on person holds still on screen while the chart reflows around them, and each card mounts its actions menu on first click. A drag listened only on the card, so a release lost to a context menu, another window, or the card moving in the DOM left the card following the cursor. Drags now listen on the window, end on blur, Escape, or any move with no button held, and canvas pans end the same way. Adds a 391-person StressTest story for folding and dragging at scale. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Review complete. 🟡 1 medium 💬 Inline comments (1)
🧹 Nitpicks (1) — 🟢 1 low
The change rebuilds the org chart's motion layer: a new spring utility drives CSS
Reviewed commit: 0ff6b49 |
There was a problem hiding this comment.
This PR reworks the Org Chart's animated scene (spring-based transitions, enter/leave choreography, marching-dash drag-preview wires) and refines the pan/zoom canvas gesture handling it sits on.
Key findings
- 🟡 Stale
revealRequestre-reveals on everyshiftchange — org-chart.tsx:171
| React.useEffect(() => { | ||
| if (!revealRequest) return; | ||
| const node = findNode(layerRef.current, revealRequest.id); | ||
| if (node) reveal(node); | ||
| }, [layout, reveal, revealRequest]); | ||
| const person = revealRequest ? layout.byId.get(revealRequest.id) : undefined; | ||
| if (person) reveal({ ...person, x: person.x + shift.x, y: person.y + shift.y }); | ||
| }, [layout, reveal, revealRequest, shift]); |
There was a problem hiding this comment.
🟡 bug · medium
Stale revealRequest re-reveals on every shift change
The reveal effect (packages/ui/src/components/org-chart/org-chart.tsx:171-174) re-runs whenever layout, reveal, revealRequest, or shift changes, but revealRequest is never cleared after being consumed — the only write is setRevealRequest({ id }) at line 191. The PR adds shift to the dependency array, and shift changes on every fold or drop, so long after a 'select and reveal' the next unrelated fold re-invokes reveal for that old person and yanks the viewport back to them, fighting a user who has since panned away and silently defeating the hold() no-camera-jump guarantee that toggle and reparent rely on.
📋 Prompt for AI Agents
In packages/ui/src/components/org-chart/org-chart.tsx lines 171-174, make the reveal effect one-shot: after resolving the person, call setRevealRequest(null) (or compare a stored generation/token so the same request is not re-applied when the shift or layout dependencies change later) so each request fires exactly once. Without this, the newly added shift dependency re-triggers reveal for a stale person on every fold or drop that shifts the layout, re-panning the camera despite hold().
Summary
Folding and unfolding large teams
linear()curve, ~0.4s, slight overshoot). New reports slide out from under their manager; folded reports tuck back beneath it, then unmount.Drags that stuck to the cursor
Shared canvas (
workspace-kit/canvas)revealaccepts a box in canvas units,contentOriginlets fitting frame shifted content, andhold()stops auto-fitting without moving the view. All are inert for the other canvases.Storybook
StressTeststory whose host saves each move after 300ms.Measurements
Production Storybook build, 391-person stress story (headless Chrome):
At 14 and 201 people both builds stay at ~60fps.
Test plan
pnpm checkpnpm build:registrySMOKE_CASE=org-chart pnpm --filter @constructive-io/registry smoke:installGenerated with Devin