createSortable · Vanilla
Vanilla DOM 정렬 인스턴스 생성과 5개 생명주기 메서드.
생성과 필수 입력
섹션 제목: “생성과 필수 입력”import { createSortable, type Sortable } from "comins-sortable";export declare function createSortable(element: Element | string, options: VanillaSortableOptions): Sortable;element는 이미 존재하는 browser Element 또는 이를 찾는 CSS selector입니다. options는 필수이며 areaId와 item selector를 포함해야 합니다. 최초 영역을 즉시 등록하고 Sortable 인스턴스를 반환합니다. DOM이 준비된 뒤 호출하고 소유 화면을 제거할 때 해제하십시오.
| 입력 | 계약 |
|---|---|
areaId |
인스턴스 내에서 유일한 영역 ID. 갱신으로 변경할 수 없습니다. |
item |
정렬 대상 직접 자식을 찾는 CSS selector. |
getItemId |
생략하면 각 항목의 data-sortable-id를 읽습니다. 안정적인 string 또는 number ID를 제공하십시오. |
copyElement |
복사 모드에서 새 DOM 항목을 만드는 선택 콜백. 반환 요소의 ID도 새 항목을 식별해야 합니다. |
| 영역 정책 | group, direction, disabled, handle, ignore, animation·placeholder 등은 Vanilla 가이드와 복사 가이드를 따릅니다. |
| 생명주기 콜백 | SortableScopeOptions를 상속합니다. |
VanillaSortableOptions는 Vanilla 영역 옵션과 scope 콜백을 합친 타입입니다. VanillaSortableAreaPatch는 영역 ID를 제외한 부분 갱신 타입입니다. 이 페이지는 생성·생명주기 계약을 다루며 옵션 전체 사전은 후속 상세 수록 대상입니다.
DOM 반영과 완료 시점
섹션 제목: “DOM 반영과 완료 시점”Vanilla는 onChange를 호출하기 전에 DOM 변경을 적용합니다. onChange가 throw하면 변경을 rollback합니다. 이후 onAfterDrag에서 status === "dropped"인지 확인합니다. 취소·거부에서는 임시 변경을 되돌립니다. React/Vue/Svelte가 같은 항목 DOM을 관리하는 구성에는 해당 프레임워크 어댑터를 사용하십시오.
Sortable
섹션 제목: “Sortable”registerArea
섹션 제목: “registerArea”registerArea(element: Element | string, options: VanillaSortableAreaOptions): () => void;추가 DOM 영역을 같은 인스턴스에 등록합니다. Element 또는 CSS selector를 받으며, 그 영역만 해제하는 함수를 반환합니다. 각 영역은 고유한 areaId를 사용합니다. 최초 영역과 추가 영역은 같은 scope에서 드래그 정책을 공유합니다.
updateArea
섹션 제목: “updateArea”updateArea(areaId: string, patch: VanillaSortableAreaPatch): void;등록된 영역의 옵션을 부분 변경합니다. areaId는 변경할 수 없습니다. 값 생략은 기존 설정 유지이며 undefined를 명시한 옵션은 정규화 규칙을 따릅니다. 현재 드래그의 출발지·목적지를 disabled로 바꾸면 해당 드래그를 종료합니다. 반환값 void.
refreshArea
섹션 제목: “refreshArea”refreshArea(areaId: string): void;이미 등록된 영역의 DOM을 외부 코드가 바꾼 뒤 geometry와 설정된 레이아웃 애니메이션을 갱신합니다. Vanilla의 메서드는 필수이며 직접 호출합니다. 반환값 void.
cancel
섹션 제목: “cancel”cancel(): void;현재 드래그 또는 활성화 대기를 취소합니다. 활성 드래그의 종료 사유는 escape입니다. 진행 중인 작업이 없으면 동작하지 않으며 등록 영역은 유지합니다. 반환값 void.
destroy
섹션 제목: “destroy”destroy(): void;인스턴스가 소유한 전체 영역과 진행 중인 드래그를 해제합니다. listener·frame·임시 피드백을 정리하고 중복 호출을 허용합니다. 정상적으로 확정된 DOM 순서를 초기 배열로 돌리는 reset 기능은 아닙니다. 반환값 void.
오류 처리
섹션 제목: “오류 처리”요소를 찾지 못하거나 selector가 잘못되었으면 생성 시 SortableError의 INVALID_ELEMENT로 throw합니다. 존재하지 않는 영역을 갱신·refresh하는 호출도 실패합니다. 설정·등록 시의 동기 예외는 호출 지점에서 처리하고, 세션 중 보고 오류는 onError로 처리하십시오. 영역 해제와 전체 인스턴스 해제의 소유 범위를 구분합니다.
아래 예제에서 정렬 비활성화, refresh, destroy, 새 인스턴스 생성을 확인할 수 있습니다. destroy 후 DOM은 남지만 드래그 listener는 해제됩니다. 두 영역 이동 예제는 Vanilla 실행 예제를 참고하십시오.
실행 예제
섹션 제목: “실행 예제”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>;}아래 npm 선언과 고정 소스 snapshot을 대조했습니다. snapshot을 npm 게시 커밋으로 단정하지 않습니다.
comins-sortable/dist/vanilla/create-sortable.d.tscomins-sortable/dist/vanilla/types.d.ts- npm 0.1.6 · Source snapshot