feat(apollo-react): alignment guide prototype for canvas drag hints#956
feat(apollo-react): alignment guide prototype for canvas drag hints#956dbacomputer wants to merge 4 commits into
Conversation
Storybook prototype for placement hints while dragging a node on the canvas: a useAlignmentGuides hook compares the dragged node's edges and center against every other node's on onNodeDrag, and AlignmentGuidesOverlay renders the matches as dashed guide lines. Visual only, nothing snaps. Demoed on a small workflow of nodes at Templates/Canvas Alignment Guides.
…e story Move the story under Components/Canvas (next to BaseCanvas, where canvas behavior is documented) instead of Templates. Add a Static Guide Preview story with hardcoded guides for visual QA without needing to drag. The match threshold was a fixed 6 flow-space units, which shrinks to a few screen pixels once the canvas is zoomed below 1x, making some axes far harder to hit by mouse than others depending on layout. Switch to a thresholdPx option (default 8) converted to flow-space via zoom, so guides are equally reachable at any zoom level.
…view Extend AlignmentGuideLine with kind (edge/center) and matchedNodeIds so variants can distinguish match types and know which nodes a line corresponds to, without changing the baseline's visual behavior. Add five comparison sub-pages under Components/Canvas/AlignmentGuides, each a self-contained fork so the "decided" hook/overlay API stays stable while these are exploratory: - Center vs Edge Styling: distinct color/dash for center-only matches - Spacing Labels: px gap label alongside the guide line - Highlighted Match: ring highlight on the node(s) a line matches - Magnetic Snap: position snaps onto the guide instead of just showing it - Threshold Playground: live slider to compare match distance/feel
…riants Three more comparison sub-pages under Components/Canvas/AlignmentGuides: - Multi-select Drag: guides compare the dragged selection's combined bounding box (outlined) against the rest of the canvas, not just one node in isolation - Grid-snap Interplay: toggleable native snapToGrid alongside the guides, demonstrating the two compose without any special integration code (grid-snap quantizes position, guides just read it) - Equal-spacing Detection: highlights both gaps with a shared "Npx equal" label when the dragged node's left/right (or top/bottom) neighbor gaps match within tolerance; scoped to immediate neighbors, not full n-way spacing across the canvas
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency License Review
License distribution
Excluded packages
|
There was a problem hiding this comment.
Pull request overview
Adds a prototype “smart guides” system to the apollo-react canvas to visualize alignment opportunities (edge/center) while dragging nodes, plus a set of Storybook variants to compare potential UX directions.
Changes:
- Introduces
useAlignmentGuides+computeAlignmentGuidesfor guide-line detection during drag. - Adds
AlignmentGuidesOverlayto render guide lines as a viewport-aware overlay. - Adds a large Storybook sandbox with multiple variants (baseline + exploratory behaviors like snap, spacing labels, multi-select, equal spacing).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/apollo-react/src/canvas/components/index.ts | Exports the new AlignmentGuides module from the canvas components barrel. |
| packages/apollo-react/src/canvas/components/AlignmentGuides/useAlignmentGuides.ts | Core guide matching logic + hook wiring for onNodeDrag/onNodeDragStop. |
| packages/apollo-react/src/canvas/components/AlignmentGuides/index.ts | Public barrel exports for the AlignmentGuides module. |
| packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuidesOverlay.tsx | Renders computed guides in screen space using viewport transform. |
| packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuides.types.ts | Types for guide lines and computed node bounds. |
| packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuides.stories.tsx | Storybook “comparison suite” for multiple alignment-guide variants. |
| for (const other of others) { | ||
| for (const draggedValue of draggedValues) { | ||
| otherValues(other).forEach((otherValue, otherIndex) => { | ||
| if (Math.abs(draggedValue - otherValue) > threshold) return; | ||
| const key = Math.round(otherValue); | ||
| const [oStart, oEnd] = otherRange(other); | ||
| const start = Math.min(draggedRange[0], oStart); | ||
| const end = Math.max(draggedRange[1], oEnd); | ||
| const isCenterMatch = otherIndex === 1; | ||
| const existing = matchesByPosition.get(key); | ||
| const nodeIds = existing?.nodeIds ?? new Set<string>(); | ||
| nodeIds.add(other.id); | ||
| matchesByPosition.set(key, { | ||
| start: existing ? Math.min(existing.start, start) : start, | ||
| end: existing ? Math.max(existing.end, end) : end, | ||
| nodeIds, | ||
| isCenterOnly: existing ? existing.isCenterOnly && isCenterMatch : isCenterMatch, | ||
| }); | ||
| }); | ||
| } | ||
| } |
| export function toBounds(node: Node): NodeBounds { | ||
| const width = node.measured?.width ?? node.width ?? DEFAULT_NODE_SIZE; | ||
| const height = node.measured?.height ?? node.height ?? DEFAULT_NODE_SIZE; | ||
| const x1 = node.position.x; | ||
| const y1 = node.position.y; | ||
| const x2 = x1 + width; | ||
| const y2 = y1 + height; | ||
| return { id: node.id, x1, y1, x2, y2, cx: (x1 + x2) / 2, cy: (y1 + y2) / 2 }; | ||
| } |
| export function computeAlignmentGuides( | ||
| dragged: NodeBounds, | ||
| others: NodeBounds[], | ||
| threshold: number | ||
| ): AlignmentGuideLine[] { | ||
| return [ | ||
| ...collectAxisGuides(dragged, others, threshold, 'vertical'), | ||
| ...collectAxisGuides(dragged, others, threshold, 'horizontal'), | ||
| ]; | ||
| } |
📊 Coverage + size by packagePer-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
"Coverage" is each package's own |
Summary
Prototype exploring drag-alignment placement hints on the canvas — Figma/Sketch-style smart guides that appear when dragging a node near other nodes, so users have a visual cue for where to align/place it. There is currently no equivalent behavior in
apollo-react's canvas (only a fixed grid-snap utility existed).This is a review branch, not a finished feature. It ships one baseline implementation plus 9 additional comparison sub-pages so the team can pick a direction together.
What's here
New
AlignmentGuidescomponent underpackages/apollo-react/src/canvas/components/AlignmentGuides/:useAlignmentGuideshook: compares the dragged node's edges/center against every other node's ononNodeDrag, zoom-aware match threshold (screen px, not flow units)AlignmentGuidesOverlay: renders matches as dashed guide lines using the existing--canvas-selection-indicatortokenTest plan
tsc --noEmitclean on the new filesbiome lintclean on the new files🤖 Generated with Claude Code