The block-handle grip is now actually grabbable. Native HTML5 drag:
mousedown + drag → dragstart fires → wordsDragState.sourceIndex is
set → the BlockInserter switches to drop-target mode (line goes
prominent, "+" hides) → dragover snaps to the nearest seam → drop
commits `moveBlockTo({fromIndex, toIndex})`.
Keyboard equivalent: focus the grip → Space toggles "grabbed" mode
(grip lights up accent-filled, aria-pressed) → ArrowUp / ArrowDown
swaps with the neighbour (via the existing moveBlock command, so
each press is committed individually) → Space or Escape exits.
Architecture:
- `words-drag-state.svelte.ts` — module-level `$state` shared between
handle (writer) and inserter (reader). Lives in eidos because this
is a visual interaction pattern, not a soma/morfo contract.
- `moveBlockToAt(state, from, to)` in operations.ts — splice out, then
splice in at the post-removal destination. Normalizes + drops caret
at the moved block's new position.
- `moveBlockTo` case in commands.ts. The existing `moveBlock`
(swap-with-neighbor) stays because the click-menu uses it.
- BlockInserter dragover/drop handlers reuse `findSeamForCursor` —
same seam math as the regular insert hover, so the drop indicator
lands exactly where the "+" would have. `seam.insertIndex >
fromIndex ? -1` correction accounts for the source's removal
shifting subsequent indices down by one.
CSS:
- `[data-words-block-inserter][data-mode='drop']` makes the line solid
+ thicker + with an outer glow during drag, and hides the "+"
button entirely (the line IS the drop indicator).
- `[data-words-block-handle][data-grabbed]` paints the grip accent-
filled with a focus ring for keyboard-grab state.
aria: `aria-pressed={keyboardGrabbed}`, descriptive aria-label on the
grip mentioning Space + arrows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
parent
305fd70129
commit
a62b23538d
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Words drag-state — shared reactive store for the eidos-layer
|
||||||
|
* drag-drop reorder (block handle ↔ block inserter).
|
||||||
|
*
|
||||||
|
* BlockHandle writes `sourceIndex` on dragstart / grabbed-via-keyboard;
|
||||||
|
* BlockInserter reads it to know when to behave as a drop target
|
||||||
|
* (paint the indicator line, accept the drop). Cleared on dragend /
|
||||||
|
* Escape / commit.
|
||||||
|
*
|
||||||
|
* Lives in the eidos layer because drag-drop is a visual interaction
|
||||||
|
* pattern, not a contract surface — soma's runtime / morfo never
|
||||||
|
* observe it. Module-level `$state` (in a `.svelte.ts` file) gives
|
||||||
|
* both sibling components a shared reactive handle without going
|
||||||
|
* through soma.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class WordsDragState {
|
||||||
|
/**
|
||||||
|
* Index of the block currently being dragged via the block-handle
|
||||||
|
* grip, or undefined when no drag is in progress. Set on dragstart
|
||||||
|
* / keyboard-grabbed; cleared on dragend / drop / Escape.
|
||||||
|
*/
|
||||||
|
sourceIndex = $state<number | undefined>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const wordsDragState = new WordsDragState();
|
||||||
Loading…
Reference in new issue