Utility · DragDrop

DragDrop

Pointer + keyboard drag and drop. Soma owns the gesture coordinator and ARIA announcements via the global Announce service. Eidos paints the visual states (data-dragging ghosts the source, data-accepting highlights compatible targets, data-dragover tints the hover target with the accent color).

parts{compiled.parts.order.length} events{events.length} sizes5 cards {totalItems} columns {Object.keys(columns).length}
handleDrop(e as DropEvent<{ from: string }>)} > {#snippet children(snippetProps)} {@const { active } = snippetProps}
{#each Object.keys(columns) as colKey (colKey)} {#snippet children(_dropSnippet)}
{COL_LABELS[colKey]} · {columns[colKey].length}
{#each columns[colKey] as item (item.id)}
⋮⋮ {item.label}
{/each}
{/snippet}
{/each}
trace {#if active} {active.label} · from {(active.data as { from?: string }).from} {:else if trace.length === 0} drag cards across columns · keyboard: Tab → Space → arrows → Enter / Escape {:else} {#each trace.slice(0, 3) as entry} {entry.event} · {entry.family}{entry.intent ? ' · ' + entry.intent : ''} {fmtTime(entry.at)} {/each} {/if}
{#if active} ⋮⋮ {active.label} {/if} {/snippet}
{#if tab === 'live'}

Controls

Soma coordinates the gesture (pointer + keyboard), filters drop targets by accept, and announces via the global Announce service. Eidos paints the visual states.

soma behaviour
eidos visual props
eidos visual · DragDrop + Draggable + Droppable + Preview svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API reference

4 parts: Provider · Draggable · Droppable · Preview. The Provider does NOT own the data — consumer receives onDrop and mutates state.

Provider (DragDrop)
PropTypeDefaultDescription
onDragStart · onDragEnd · onDropcallbacks—Gesture lifecycle hooks.
announceEnabledbooleantrueEmit SR announcements via the global Announce service.
size eidos'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Cascaded visual size.
color eidosColorRole'primary'Accent for drop targets + preview border + shadow.
Draggable
PropTypeDefaultDescription
valuestringrequiredUnique identifier returned in onDrop.
dataDragData{'{}'}Arbitrary payload received in onDrop.
disabledbooleanfalseSkip from drag gesture.
moveBuffernumber5Pixels the pointer must move before drag is recognised.
textValuestringaria-label/textContentLabel for SR announcements.
Droppable
PropTypeDefaultDescription
accept(data, value) => boolean() => trueFilter — return false to skip this target.
disabledbooleanfalseDisable drops.
textValuestring—Label for SR announcements.
Snippet props
SnippetKeyTypeDescription
Provider.childrenactiveActiveDrag | nullInfo about the in-flight drag (value, data, label, pointer).
Provider.childrencancel() => voidImperative cancel.
Draggable.childrendraggingbooleanTrue while THIS draggable is the source.
Draggable.childrenhandlePropsobjectSpread on a child to restrict the drag handle.
Droppable.childrenoverbooleanPointer/focus is over THIS target.
Droppable.childrenacceptingbooleanTrue for compatible targets while ANY drag is active.
{/if} {#if tab === 'morfo'}

morfo · declarative contract

Source: src/uix/morfo/components/drag-drop.ts.

Parts
{#each partsList as part (part.kebab)} {/each}
PartMarkerElementRoleStatesOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '—'} {part.states.length ? part.states.join(' | ') : '—'} {part.optional ? 'yes' : 'no'}
Events declaration
{#each events as action (action.name)} {@const sem = action.semantic} {@const intentDecl = 'intent' in sem ? sem.intent : undefined} {@const intentStr = typeof intentDecl === 'string' ? intentDecl : intentDecl ? `fromProp:${(intentDecl as { fromProp?: string }).fromProp ?? '?'}` : '—'} {/each}
namefamilyverbsequenceintenttarget
{action.name} {sem.family} {sem.verb ?? '—'} {sem.sequence ?? 'pre'} {intentStr} {action.target}
{/if} {#if tab === 'sema'}

sema · events

{#if events.length === 0}

DragDrop's SR feedback is delivered via the global Announce service rather than sema events — the perception is verbal, not audio cues.

{:else}
{#each events as action (action.name)} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {/each}
NameFamilyVerbSequenceIntent
{action.name} {action.semantic.family} {action.semantic.verb ?? '—'} {action.semantic.sequence ?? 'pre'} {typeof intentDecl === 'string' ? intentDecl : '—'}
{/if}
{/if} {#if tab === 'recipe'}

Eidos recipe

Selectors at src/uix/eidos/components/drag-drop/drag-drop.css.

SelectorSourceWhat it paints
[data-drag-drop-draggable]eidoscursor: grab, touch-action: none.
[data-drag-drop-draggable][data-dragging]morfoSource ghosted (opacity: 0.4).
[data-drag-drop-droppable][data-accepting]morfoCompatible target outline (soft accent).
[data-drag-drop-droppable][data-dragover]morfoHover target accent fill + inset border.
[data-drag-drop-preview]eidosFloating pointer follower — card chrome with accent border + shadow.
{/if} {#if tab === 'a11y'}

Accessibility

Full keyboard parity with pointer — and SR announcements via the global Announce service when announceEnabled is on.

Keyboard
KeyAction
TabFocus the next Draggable.
SpaceLift the focused Draggable.
Arrow / TabNavigate compatible Droppables.
EnterDrop on the current target.
EscapeCancel the lift.
Announcements (via global Announce)
{/if}