createSortableTree
중첩 데이터의 영역 매핑과 불변 갱신을 위한 3개 트리 메서드.
import { createSortableTree } from "comins-sortable/core";export declare function createSortableTree<T>(options: SortableTreeOptions<T>): SortableTree<T>;SortableTreeOptions
섹션 제목: “SortableTreeOptions”export interface SortableTreeOptions<T> { rootAreaId: string; getNodeId(node: T): SortableId; getChildren(node: T): readonly T[]; withChildren(node: T, children: readonly T[]): T; getChildrenAreaId(node: T): string;}| 옵션 | 입력과 책임 |
|---|---|
rootAreaId |
비어 있지 않은 root 영역 ID. 전체 영역에서 유일해야 합니다. |
getNodeId |
노드를 안정적인 string 또는 number ID로 변환합니다. 트리 전체에서 유일해야 합니다. |
getChildren |
자식 배열을 반환합니다. 말단 노드도 빈 배열을 반환합니다. |
withChildren |
기존 노드를 변형하지 않고 전달한 자식을 가진 노드를 반환합니다. |
getChildrenAreaId |
노드의 자식 영역 ID. root와 모든 다른 자식 영역 ID와 겹치지 않아야 합니다. |
모든 옵션은 필수입니다. 배열 순서나 렌더 횟수에 따라 ID를 다시 만들지 마십시오. 이 helper가 소비자의 withChildren 내부 mutation을 막지는 않습니다.
SortableTreeArea
섹션 제목: “SortableTreeArea”export interface SortableTreeArea<T> { areaId: string; items: readonly T[]; parent?: SortableParentLocation;}SortableTree
섹션 제목: “SortableTree”getAreas
섹션 제목: “getAreas”getAreas(nodes: readonly T[]): readonly SortableTreeArea<T>[];트리 전체를 순회하여 root 영역과 모든 노드의 자식 영역을 반환합니다. 빈 자식 영역도 포함합니다. items는 해당 입력 배열을 참조하며 root에는 parent가 없습니다. 하위 영역의 parent는 부모 항목이 속한 area ID와 item ID입니다. DOM 등록이나 상태 변경은 수행하지 않습니다.
updateArea
섹션 제목: “updateArea”updateArea(nodes: readonly T[], areaId: string, items: readonly T[]): readonly T[];현재 트리에 존재하는 단일 areaId의 항목 배열을 items로 교체하고 전체 트리 결과를 반환합니다. withChildren이 불변 갱신을 지키면 기존 입력을 유지하고 변경 없는 가지는 재사용합니다. 여러 영역 사이의 이동은 한쪽씩 호출하면 중간 중복 ID가 생길 수 있으므로 applyChange로 함께 반영합니다.
applyChange
섹션 제목: “applyChange”applyChange(nodes: readonly T[], change: FrameworkSortableChange<T>): readonly T[];프레임워크 어댑터가 전달한 change.updates를 하나의 트리 재구성에 반영합니다. 반환된 트리를 소비자 상태로 저장해야 합니다. orders만 보고 데이터를 추측하지 않습니다. 같은 update 배치에 area ID를 두 번 넣거나 현재 트리에 없는 영역을 넣으면 실패합니다. 트리 간 데이터 처리 도우미이며 DOM 드래그를 시작하거나 mount하지 않습니다.
검증과 오류
섹션 제목: “검증과 오류”| 조건 | 오류 코드 |
|---|---|
| 빈 area ID, 배열이 아닌 children, 알 수 없는 update 영역 또는 중복 update 영역 | INVALID_OPTION |
| 전체 트리에서 같은 영역 ID 재사용 | DUPLICATE_AREA_ID |
| 전체 트리에서 같은 노드 ID 재사용 | DUPLICATE_ITEM_ID |
| string·number가 아닌 노드 ID | MISSING_ITEM_ID |
SortableError는 동기적으로 throw됩니다. callback이 던진 예외도 소비자가 처리해야 합니다. 잘못된 트리나 순환 구조를 넘기지 마십시오. 변경 결과에 대해 다시 검사하지만, 임의 입력을 안전한 트리로 변환하는 sanitizer는 아닙니다.
입력·반환 예제
섹션 제목: “입력·반환 예제”아래는 트리 데이터 연산 예제입니다. updateArea는 root 순서를 뒤집고, applyChange는 자식 하나를 a에서 b로 옮깁니다. 실제 드래그가 만든 change는 어댑터 콜백에서 setNodes(current => tree.applyChange(current, change))로 적용합니다. DOM 연결과 중첩 드래그는 트리 가이드를 참고하십시오.
import { createSortableTree, type FrameworkSortableChange } from 'comins-sortable/core';
export type TreeNode = { id: string; children: readonly TreeNode[] };export const tree = createSortableTree<TreeNode>({ rootAreaId: 'root', getNodeId: node => node.id, getChildren: node => node.children, withChildren: (node, children) => ({ ...node, children }), getChildrenAreaId: node => `children:${node.id}`,});
export const initialNodes: readonly TreeNode[] = [ { id: 'a', children: [{ id: 'child', children: [] }] }, { id: 'b', children: [] },];
export function reverseRoots(nodes: readonly TreeNode[]) { return tree.updateArea(nodes, 'root', [...nodes].reverse());}
// A controlled adapter emits this shape after moving child from a to b.export function moveChild(nodes: readonly TreeNode[]) { const source = nodes.find(node => node.id === 'a'); const destination = nodes.find(node => node.id === 'b'); const child = source?.children.find(node => node.id === 'child'); if (!source || !destination || !child) return nodes; const sourceItems = source.children.filter(node => node.id !== child.id); const destinationItems = [...destination.children, child]; const change: FrameworkSortableChange<TreeNode> = { operation: 'transfer', itemId: child.id, source: { areaId: 'children:a', index: source.children.indexOf(child) }, destination: { areaId: 'children:b', index: destination.children.length }, orders: [ { areaId: 'children:a', itemIds: sourceItems.map(node => node.id) }, { areaId: 'children:b', itemIds: destinationItems.map(node => node.id) }, ], updates: [ { areaId: 'children:a', items: sourceItems }, { areaId: 'children:b', items: destinationItems }, ], }; // Real integrations pass the adapter's change directly in a state updater. return tree.applyChange(nodes, change);}실행 예제
섹션 제목: “실행 예제”import { useState } from 'react';import { initialNodes, moveChild, reverseRoots, tree } from '../../examples/sortable-tree';
export default function SortableTreeDemo({ lang = 'ko' }: { lang?: string }) { const ko = lang === 'ko'; const [nodes, setNodes] = useState(initialNodes); return <div className="demo" data-demo="sortable-tree"> <div className="demo-toolbar"> <button onClick={() => setNodes(reverseRoots)}>updateArea</button> <button onClick={() => setNodes(moveChild)}>applyChange</button> <button onClick={() => setNodes(initialNodes)}>{ko ? '초기화' : 'Reset'}</button> </div> <p>{ko ? 'updateArea: 최상위 순서 반전 · applyChange: a의 child를 b로 이동' : 'updateArea: reverse root order · applyChange: move child from a to b'}</p> <pre data-tree-state>{JSON.stringify(nodes, null, 2)}</pre> <details><summary>getAreas</summary><pre>{JSON.stringify(tree.getAreas(nodes), null, 2)}</pre></details> </div>;}아래 npm 선언과 고정 소스 snapshot을 대조했습니다. snapshot을 npm 게시 커밋으로 단정하지 않습니다.
comins-sortable/dist/core/tree.d.ts- npm 0.1.6 · Source snapshot