feat(ui): drag a node on the sheet to reorder it #46

Merged
TJC merged 2 commits from feat/m9-drag-reorder into main 2026-08-19 12:15:05 +01:00
Collaborator

Summary

  • Milestone 9, decision 28's first drag. Off current main - no stack this time.
  • dropTargets turns the laid-out boxes into every gap between siblings, as the line a drop snaps to. A row's gaps stand upright, a stack's lie flat, an empty container gets one across its middle so it can be filled from the sheet at all.
  • nearestTarget measures to the line rather than to its midpoint: a long drop line is the same drop anywhere along it, and aiming at the near end of one should not lose to the middle of another.
  • A press that travels 1.5mm becomes a drag and previews; below that it stays a click and still selects. The drop commits once on release, so decision 14 gets one undo entry for the gesture rather than one per frame.
  • A cancelled pointer - a touch that became a scroll, a window that lost focus - abandons the drag instead of committing a drop nobody let go of.
  • Gaps are filtered by making the move and putting it through the same parse the commit uses. A structurally legal move can still be a template that will not parse: decision 5 wants a sized ancestor over a fill repeat, and dropping a widget into a row is how you take one away. A line drawn where the drop is then refused is worse than no line.
  • Selection stays on click, so it keeps working from the keyboard and for assistive tech; the drag hook only drags.

Test plan

  • pnpm test - 664 pass, 19 new
  • pnpm lint, tsc --noEmit and pnpm build clean
  • new src/layout/drops.test.ts covers gap placement per container kind, empty containers, children that drew nothing, and that nothing inside a widget is a target (decision 18)
  • Sheet.test.tsx drives real pointer events: preview appears only past the threshold, nothing commits before release, cancel abandons
  • App.test.tsx reorders a widget on the real fixture, asserts one undo entry for a five-move gesture, and that no drop on offer is refused on release
## Summary - Milestone 9, decision 28's first drag. Off current `main` - no stack this time. - `dropTargets` turns the laid-out boxes into every gap between siblings, as the line a drop snaps to. A row's gaps stand upright, a stack's lie flat, an empty container gets one across its middle so it can be filled from the sheet at all. - `nearestTarget` measures to the line rather than to its midpoint: a long drop line is the same drop anywhere along it, and aiming at the near end of one should not lose to the middle of another. - A press that travels 1.5mm becomes a drag and previews; below that it stays a click and still selects. The drop commits once on release, so decision 14 gets one undo entry for the gesture rather than one per frame. - A cancelled pointer - a touch that became a scroll, a window that lost focus - abandons the drag instead of committing a drop nobody let go of. - Gaps are filtered by making the move and putting it through the same parse the commit uses. A structurally legal move can still be a template that will not parse: decision 5 wants a sized ancestor over a fill repeat, and dropping a widget into a row is how you take one away. A line drawn where the drop is then refused is worse than no line. - Selection stays on click, so it keeps working from the keyboard and for assistive tech; the drag hook only drags. ## Test plan - `pnpm test` - 664 pass, 19 new - `pnpm lint`, `tsc --noEmit` and `pnpm build` clean - new `src/layout/drops.test.ts` covers gap placement per container kind, empty containers, children that drew nothing, and that nothing inside a widget is a target (decision 18) - `Sheet.test.tsx` drives real pointer events: preview appears only past the threshold, nothing commits before release, cancel abandons - `App.test.tsx` reorders a widget on the real fixture, asserts one undo entry for a five-move gesture, and that no drop on offer is refused on release
- every gap between siblings, as the line the drop snaps to
- a row's gaps stand upright, a stack's lie flat, an empty container has one
- nearest measures to the line, so a long one is the same drop along its length
- a press that travels previews a drop line; the drop commits once, on release
- only gaps that survive the commit's own parse are offered
- a cancelled pointer abandons the drag rather than committing it
TJC merged commit 79dc42477d into main 2026-08-19 12:15:05 +01:00
Sign in to join this conversation.
No reviewers
No labels
chore
core
render
ui
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TJC/daily-planner-template!46
No description provided.