Shared drag-reorder primitive: createDragReorder owning DOM drag plumbing #124
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#124
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split out of #117 during triage (the other two primitives — a shared
errorMessageandcreateAsyncAction— stayed there and have since merged). This half needed its own design pass before it was agent-ready.Problem: the drop verdicts are already pure, shared, and unit-tested (
elementcrud.classifyDrop,pagemanage.resolveDrop), but the DOM event plumbing around them — dragover → compute zone → set adropSpotsignal → drop → resolve → clear — is hand-wired separately per feature.Rescoped during triage (2026-07-19) after a code survey resolved the open design questions:
dropSpotsignal, and takes a per-targetzoneFor-style callback. It does not own geometry.dropSpotand its keyed clearing (dragleave/drop/dragend), which is exactly the ~15–25 lines each handler trio repeats today.rowDropZonein the element-crud module vsdropZonein the page-manage module), and deduplicate the per-test-file jsdomfireDragdrag-simulation helper into one shared test util.See the agent brief in the comments for the full contract.
Related: #117 (sibling primitives, merged).
Agent Brief
Category: enhancement
Summary: Extract a shared
createDragReorderprimitive that owns the DOM drag-and-drop plumbing (thedropSpotsignal plus the dragover/dragleave/drop/dragend skeleton) and adopt it in the three panes that hand-wire it today — PagePane, TreePane, FormPane — while unifying the duplicated zone-geometry math and the duplicated jsdom drag test helper.Current behavior:
The web editor (Solid.js, under
web/) already has pure, unit-tested drop-verdict helpers:classifyDrop(element tree, in the element-crud module) andresolveDropwithdropZone/dropZoneMode(page tree, in the page-manage module). The DOM plumbing around them is hand-wired per pane, repeating the same skeleton each time: a drag-source signal plus adropSpot-like signal; a dragover that guards on an active drag, callspreventDefault, setsdataTransfer.dropEffect = "move", computes a zone, and sets the spot; a keyed dragleave that clears the spot only if it belongs to this target; a drop that clears state and invokes a move action; a dragend that clears everything. TheeffectAllowed/dropEffectpairing and its rationale is re-commented at every site.dropSpotsignal with per-kind keyed clearing. It also carries a duplicate verdict-recompute helper that exists only because drop handlers clear the drag-source signal before recomputing.relatedTarget-containment check in dragleave to suppress indicator flicker when the pointer crosses into child elements (a real-browser behavior jsdom never exercises).Two additional duplications ride along:
rowDropZone(element-crud module) anddropZone(page-manage module) — both implementing midpoint and 25/50/25 band math.fireDrag-style jsdom helper (one also stubs aFakeDataTransferemulating browsereffectAllowed/dropEffectarbitration), because jsdom lacks a realDragEvent. The workaround is copy-pasted four times.Desired behavior:
createDragReorderprimitive owns the shared skeleton exactly once: thedropSpotsignal (generic in the spot type), the dragover guard/preventDefault/dropEffectinvariant, keyed clearing on dragleave, and full clearing on drop/dragend. Zone geometry and drop verdicts stay in the existing pure helpers — the primitive receives per-target callbacks (azoneFor-style function returning a spot or null, and an on-drop action) rather than owning geometry. It must support several target kinds in one pane sharing a singledropSpotsignal (PagePane needs four), and it must stay a thin Solid primitive — signals plus handler factories, not a framework.relatedTargetanti-flicker dragleave (either absorbed into the primitive or still expressible around it), PagePane's read-only gating and per-kind keyed clears.dataTransfer) lives in a shared test util, and the wiring test files use it instead of their local copies.Key interfaces:
createDragReorder— new primitive. Suggested shape: generic over the spot type, returning thedropSpotaccessor plus per-target handler factories (dragover/dragleave/drop) and a dragend/clear hook; exact signature is the agent's call. Lives alongside the other shared primitives (createAsyncActionsets the precedent for a flat module under the web source root).classifyDrop,resolveDrop— behavior unchanged. The geometry consolidation may moverowDropZone/dropZoneinto a shared module, but every existing verdict test must still pass with at most mechanical import updates.Acceptance criteria:
createDragReorderhas unit tests (vitest, under the web test directory) covering at minimum: spot set on dragover with a valid zone and not set when the zone callback returns null or no drag is active;dropEffectset only when valid; keyed dragleave clears only the matching spot; drop and dragend clear all state.classifyDrop/resolveDropunit tests pass unchanged apart from imports.go test ./...pass (buildweb/distfirst; toolchains vianix shell nixpkgs#go/nixpkgs#nodejsper the repo's dev-env notes).harness serve) confirms drag indicators appear and moves land — jsdom cannot catch native drag-and-drop regressions.Out of scope: