createSortable · Vanilla
Create a Vanilla DOM sorting instance and use its five lifecycle methods.
Creation and required inputs
Section titled “Creation and required inputs”import { createSortable, type Sortable } from "comins-sortable";export declare function createSortable(element: Element | string, options: VanillaSortableOptions): Sortable;element is an existing browser Element or a CSS selector locating it. Required options include areaId and item. The factory immediately registers the first area and returns a Sortable instance. Initialize after DOM is ready and dispose when its owning screen is removed.
| Input | Contract |
|---|---|
areaId |
Area ID unique within the instance; cannot be changed by an update. |
item |
CSS selector matching sortable direct children. |
getItemId |
Defaults to reading each item’s data-sortable-id. Supply stable string or number IDs. |
copyElement |
Optional callback creating a DOM item in copy mode. Its ID must identify the new item too. |
| Area policies | group, direction, disabled, handle, ignore, animation, and placeholder follow the Vanilla guide and copy guide. |
| Lifecycle callbacks | Inherited from SortableScopeOptions. |
VanillaSortableOptions combines Vanilla area options with scope callbacks. VanillaSortableAreaPatch allows partial area updates excluding the ID. This page covers creation and lifecycle; the full option dictionary remains a subsequent documentation item.
DOM commit and completion
Section titled “DOM commit and completion”Vanilla applies DOM changes before calling onChange. If onChange throws, it rolls back. Check status === "dropped" in the later onAfterDrag event for completion; cancelled or rejected drags roll back temporary changes. Use framework adapters when React, Vue, or Svelte owns the same item DOM.
Sortable
Section titled “Sortable”registerArea
Section titled “registerArea”registerArea(element: Element | string, options: VanillaSortableAreaOptions): () => void;Registers an additional DOM area in the same instance. Accepts an Element or CSS selector and returns a cleanup for that area only. Each area needs a unique areaId; all areas share the instance’s scope and drag policies.
updateArea
Section titled “updateArea”updateArea(areaId: string, patch: VanillaSortableAreaPatch): 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;Refreshes geometry and configured layout animation after external code changes a registered area’s DOM. This method is required on the Vanilla interface and can be called directly. 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 every area and active drag owned by the instance, cleaning listeners, frames, and temporary feedback. Repeated calls are allowed. This is not a reset that restores the initial order after a committed DOM change. Returns void.
Error handling
Section titled “Error handling”A missing element or invalid element selector throws SortableError with INVALID_ELEMENT during creation. Updating or refreshing a missing area also fails. Handle synchronous setup/registration exceptions at the call site and session-reported errors through onError. Distinguish ownership of one area from ownership of the entire instance.
Use the example to disable sorting, refresh, destroy, and create a new instance. Destroy keeps DOM in place but removes drag listeners. See the Vanilla transfer example for two areas.
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/vanilla/create-sortable.d.tscomins-sortable/dist/vanilla/types.d.ts- npm 0.1.6 · Source snapshot