Skip to content

refactor(react): one drag state machine for the tool panel's row lists - #527

Merged
blove merged 1 commit into
mainfrom
blove/tool-panel-drag-hook
Aug 30, 2026
Merged

blove merged 1 commit into
mainfrom
blove/tool-panel-drag-hook

Conversation

@blove

@blove blove commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

The duplication

ColumnsSection.tsx and grouping/GroupingSection.tsx carried near-verbatim copies of the same pointer-drag state machine, recorded during the SP3b review: DRAG_THRESHOLD_PX, the PendingRowDrag/ActiveRowDrag shapes, the pointerdown/move/up/cancel handlers, endDrag, and the document-level Escape-cancel effect with the preventDefault interlock. Only the pure insertion geometry (tool-panel-drop-target.ts) was shared.

The extraction

The machine now lives in packages/react/src/tool-panel/useToolRowDrag.ts, parameterized over the three real differences:

  • measureRows — how the section measures its rendered rows (called fresh on every move and again at release; the never-cache rationale moved into the option's TSDoc).
  • groups — the rendered subgroups (the columns section's pin partition vs the grouping list's single constant group).
  • commit — what a finished move applies. Called only from a release over a resolved target; the commit-time getState re-read rationale stays with each section's commit, because what there is to re-read differs (live columnLayout vs the row model's rowGroups).

The hook returns { drag, gripHandlers }; each section spreads gripHandlers(columnId) onto its grip. Constraint-carrying comments moved with the code they constrain: capture-at-pointerdown with its rAF-coalescing measurement, commit-on-release-only with the coalesced-last-move cross-boundary finding, the Escape preventDefault interlock, and the generalized no-stopPropagation-on-pointerdown rule (an open popover's outside-press close must fire). GroupingSection's async-settle-aware pending-grip-focus handling stays section-side, inside its commit. Behavior-preserving by construction and by suite: no existing test was touched except to add one.

Mutation proof

  • Commit path disabled in the hook → 2 failures, one per section: the grouping suite's "pointer drag … commits … the whole reordered list" and the columns suite's new release-commit test. (The columns section previously had no jsdom pin on the pointer-release commit — only cancel paths — so this mutation was initially caught by the grouping suite alone; one test added to close that hole.)
  • Escape-cancel's endDrag removed → 2 failures, both sections' pre-existing "Escape mid-drag cancels" tests.
  • Reverted → pnpm --filter @pretable/react test tool-panel: 8 files, 119 tests green.

Verification

  • Full react suite: 110 files, 1568 tests green.
  • pnpm format, eslint, tsc --noEmit: clean.
  • api:check fresh — nothing public moved (the hook is internal, not in any barrel); no changeset.
  • Net −20 lines (246 added incl. the new test, 266 removed).

🤖 Generated with Claude Code

ColumnsSection and GroupingSection carried near-verbatim copies of the
same pointer-drag state machine: the 5px threshold, the pending/active
drag shapes, the pointerdown/move/up/cancel handlers, endDrag, and the
document-level Escape-cancel effect with its preventDefault interlock.
Only the pure geometry (tool-panel-drop-target.ts) was shared.

The machine now lives in one hook, useToolRowDrag, parameterized over
the real differences: how the section measures its rendered rows, what
subgroups they partition into (pin groups vs a single constant group),
and what a finished move commits. The constraint-carrying comments
moved with it — capture-at-pointerdown with its rAF-coalescing
measurement, commit-on-release-only with the coalesced-last-move
finding, and the Escape preventDefault interlock — while the
commit-time re-read rationale stays with each section's commit, where
what there is to re-read differs. GroupingSection's settle-aware
pending-grip-focus handling stays section-side.

One test added: the columns section's pointer-RELEASE commit had no
jsdom pin (only cancel paths were pinned), so a commit-path mutation
in the shared hook was caught only by the grouping suite. Both
sections' suites now fail on a commit mutation and on an
Escape-cancel mutation; all 119 tool-panel tests (and the full react
suite, 1568) are green on the real code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pretable Ignored Ignored Aug 30, 2026 2:46am

Request Review

@blove
blove enabled auto-merge (squash) August 30, 2026 02:49
@github-actions

Copy link
Copy Markdown
Contributor

Vercel preview ready

Preview: https://pretable-6q4u4xh9l-cacheplane.vercel.app
Commit: 11d186f4293712d416dff6c0c609986c09fc6fb8

Updated automatically by the deploy-preview job.

@blove
blove merged commit d7ffd50 into main Aug 30, 2026
20 checks passed
@blove
blove deleted the blove/tool-panel-drag-hook branch August 30, 2026 03:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant