createSortableScope · Core
Shared area registration, option updates, cancellation, teardown, and consumer commit ownership.
Creation
Section titled “Creation”import { createSortableScope, type SortableScope } from "comins-sortable/core";export declare function createSortableScope(options?: SortableScopeOptions): SortableScope;options defaults to an empty object. Creation does not create a DOM area; registering a browser element connects the environment. This page covers the /core entry. The same-named Svelte API is documented separately in the Svelte guide.
Core is the low-level contract that reports drag proposals. Use framework adapters for controlled framework state or createSortable for automatic DOM ordering in plain JS. The Core example below explicitly applies the proposed single-list order to DOM to demonstrate consumer responsibility.
SortableScopeOptions
Section titled “SortableScopeOptions”export interface SortableScopeOptions { onBeforeDragStart?: (context: DragContext) => boolean | void; onDragStart?: (context: DragContext) => void; onDrag?: (context: DragContext) => void; onInsertDragArea?: (event: InsertDragAreaEvent) => void; onChange?: (change: SortableChange) => void; onAfterDrag?: (result: AfterDragResult) => void; onError?: (error: unknown) => void;}| Callback | Timing and return contract |
|---|---|
onBeforeDragStart |
Runs before activation. Return false to refuse the start; this path does not enter an active drag’s afterDrag cycle. |
onDragStart |
Receives DragContext when activation starts. Its return value is not a state update instruction. |
onDrag |
Receives context with pointer and destination information during a drag. |
onInsertDragArea |
Receives changes to the insertion area during dragging. |
onChange |
Receives a proposed reorder, transfer, copy, or swap. Core consumers must commit it to DOM or controlled state. |
onAfterDrag |
Receives status, reason, and optionally change after cleanup. Check for dropped to identify completion. |
onError |
Receives errors reported during callback/session handling. Synchronous registration and option errors do not all flow through this callback. |
All callbacks are optional. Except for onBeforeDragStart, their return type is void. An onChange call alone is not success: an uncommitted state or DOM proposal can be rejected with state-not-committed.
SortableScope
Section titled “SortableScope”registerArea
Section titled “registerArea”registerArea(element: Element, options: SortableAreaOptions): () => void;Registers a DOM Element and area options, returning a function that unregisters that area only. Core does not accept a selector string. areaId must be unique in the scope and item IDs must remain stable. The cleanup function is repeat-safe; unregistering an active source or destination ends the drag.
updateArea
Section titled “updateArea”updateArea(areaId: string, patch: SortableAreaPatch): void;Patches the options of a registered area without changing its areaId. Omitted fields retain their values; explicitly undefined values follow option normalization. Disabling the active source or destination terminates that drag. Returns void.
refreshArea
Section titled “refreshArea”refreshArea?(areaId: string): void;Invalidates geometry after an area’s DOM changes and plays the configured layout animation. Does not reorder data or register a new area. This method is optional on the public SortableScope type; call scope.refreshArea?.(id). Returns void.
cancel
Section titled “cancel”cancel(): void;Cancels an active drag or a pending activation. An active drag ends with reason escape. Does nothing when idle and keeps registered areas. Returns void.
destroy
Section titled “destroy”destroy(): void;Tears down the owned scope, cleaning up drags, area listeners, frames, animations, and temporary feedback. Repeated calls are allowed. Create a new scope instead of registering on a destroyed one. Returns void.
Registration options and errors
Section titled “Registration options and errors”Required area options are areaId and the item selector. Sortable items are matching direct children of the area. The default getItemId reads data-sortable-id. Defaults are direction: "auto", activationDistance: 4, and emptyInsertThreshold: 8. See core concepts and advanced sorting for more policies.
| Situation | Result |
|---|---|
updateArea / refreshArea before initial registration |
SortableError, INVALID_ELEMENT |
| Updating a missing or unregistered area ID | INVALID_ELEMENT |
| Registering an area on a destroyed scope | INVALID_OPTION |
| Invalid options / selector | Option validation error; handle it at the synchronous call site. |
Destroy a scope you own when the screen/component is removed. If you own only an area in a shared scope, call the cleanup returned by registerArea. See lifecycle and errors for termination states.
Live example
Section titled “Live example”import { useEffect, useRef, useState } from 'react';import { createSortable, type Sortable } from 'comins-sortable';import { createSortableScope, type SortableScope, type SortableScopeOptions } from 'comins-sortable/core';import 'comins-sortable/styles.css';
export default function SortableLifecycleDemo({ lang = 'ko', mode = 'vanilla' }: { lang?: string; mode?: 'vanilla' | 'core' }) { const ko = lang === 'ko'; const host = useRef<HTMLDivElement>(null); const instance = useRef<Sortable | SortableScope | null>(null); const [generation, setGeneration] = useState(0); const [disabled, setDisabled] = useState(false); const [status, setStatus] = useState('');
useEffect(() => { const element = host.current; if (!element) return; // React owns the host. Its children are owned by this imperative example. const list = document.createElement('div'); list.className = 'task-list'; list.setAttribute('role', 'list'); for (const id of ['alpha', 'beta', 'gamma']) { const item = document.createElement('div'); item.dataset.sortableId = id; item.className = 'task transfer-item'; item.setAttribute('role', 'listitem'); item.textContent = id; list.append(item); } element.replaceChildren(list); const options: SortableScopeOptions = { onChange: change => { if (mode === 'core') { // Core reports the proposed order; the consumer must commit it. const order = change.orders.find(order => order.areaId === 'tasks'); const items = new Map(Array.from(list.children, item => [item.getAttribute('data-sortable-id'), item])); for (const id of order?.itemIds ?? []) { const item = items.get(String(id)); if (item) list.append(item); } } }, onAfterDrag: result => setStatus(`${result.status}: ${result.reason}`), onError: error => setStatus(error instanceof Error ? error.message : String(error)), }; const area = { areaId: 'tasks', item: '.transfer-item', direction: 'vertical' as const }; let unregister: (() => void) | undefined; const sortable = mode === 'core' ? createSortableScope(options) : createSortable(list, { ...area, ...options }); if (mode === 'core') unregister = sortable.registerArea(list, area); instance.current = sortable; setDisabled(false); setStatus(ko ? '준비됨. 항목을 드래그하십시오.' : 'Ready. Drag an item.'); return () => { unregister?.(); sortable.destroy(); instance.current = null; element.replaceChildren(); }; }, [generation, ko, mode]);
return <div className="demo" data-demo={`sortable-lifecycle-${mode}`}> <div className="demo-toolbar"> <button disabled={!instance.current} onClick={() => { instance.current?.updateArea('tasks', { disabled: !disabled }); setDisabled(!disabled); setStatus(!disabled ? 'disabled' : 'enabled'); }}>{disabled ? (ko ? '정렬 허용' : 'Enable sorting') : (ko ? '정렬 비활성화' : 'Disable sorting')}</button> <button disabled={!instance.current} onClick={() => { instance.current?.refreshArea?.('tasks'); setStatus('refreshArea'); }}>refreshArea</button> <button disabled={!instance.current} onClick={() => { instance.current?.cancel(); setStatus('cancel'); }}>cancel</button> <button disabled={!instance.current} onClick={() => { instance.current?.destroy(); instance.current = null; setStatus('destroyed'); }}>destroy</button> <button onClick={() => setGeneration(value => value + 1)}>{ko ? '새 인스턴스' : 'New instance'}</button> </div> <div ref={host} /> <p role="status">{status}</p> </div>;}Evidence
Section titled “Evidence”The npm declarations below were compared with a fixed source snapshot. The snapshot is not asserted to be the npm publishing commit.
comins-sortable/dist/core/scope.d.tscomins-sortable/dist/core/model.d.ts- npm 0.1.6 · Source snapshot