Skip to content
SortableGuides

Nested Lists and Tree

comins-sortable@0.1.6

Playground paths below are local routes for the module repository’s development server. They are not live routes on this documentation site.

Nested areas declare their parent item so collision can reject a move from a parent into its own descendant.

createSortableTree is the framework-neutral adapter for one immutable tree value. It does not render components or own state.

import { createSortableTree } from 'comins-sortable/core';
export type Node = {
id: string;
label: string;
children: readonly Node[];
};
const tree = createSortableTree<Node>({
rootAreaId: 'root',
getNodeId: (node) => node.id,
getChildren: (node) => node.children,
withChildren: (node, children) => ({ ...node, children }),
getChildrenAreaId: (node) => `children-${node.id}`,
});
export const initialNodes: readonly Node[] = [
{
id: 'planning',
label: 'Planning',
children: [{ id: 'scope', label: 'Define scope', children: [] }],
},
{ id: 'delivery', label: 'Delivery', children: [] },
];
export function getTreeAreas(nodes: readonly Node[]) {
return tree.getAreas(nodes);
}
export function updateTreeArea(
nodes: readonly Node[],
areaId: string,
nextItems: readonly Node[],
) {
return tree.updateArea(nodes, areaId, nextItems);
}

Render every result from getTreeAreas(nodes) as a framework Area. Pass its areaId, items, and optional parent without changing them, then replace the tree state with the value returned by updateTreeArea.

getAreas returns area descriptors; it does not construct nested DOM. Render child areas inside their corresponding parent item when a hierarchical view is required. A flat rendering of those descriptors is not a recursive tree view.

updateArea replaces only the direct items of the selected area. Existing nodes retain their current descendant collections, including when framework source and destination setters apply one cross-area move sequentially.

Use applyChange when one framework Root change should be folded into the whole tree transaction. Unknown areas, duplicate node or area IDs, and cycles fail without mutating the input.

  • Nested lists in React, Vue, and Svelte keep separate root and child arrays. They demonstrate DOM nesting, the parent relationship, and transfers between lists.
  • Per-list state control (nested-controlled) in React, Vue, and Svelte updates parent and child arrays separately. Reverse children changes only the child array, preserving the parent order. The initial structure and instructions explain these updates.
  • Per-list DOM updates is the Vanilla tab of the same nested-controlled route. It reorders child DOM and calls refreshArea; it does not update controlled array state. The regular Vanilla nested-list example also operates on DOM.
  • Subtree movement (tree) keeps one recursive children value and derives each area through createSortableTree. The initial three levels are Research → Review → Document/Observe, alongside an empty Design folder. Move Review into Design to carry both descendants with it. Parent relationships update after the move.

In this display, dragging a parent carries its child areas with it, so its own descendant areas are not drop targets. Moving Research toward its own Review area therefore does not demonstrate explicit cycle-rejection feedback. Core rejects a hit-tested destination with nested-cycle when its parent chain makes it a descendant of the dragged item. Observing that rejection requires a logical descendant area that remains separately exposed during the drag.

In React, Vue, and Svelte, per-list state control and subtree movement are both controlled and use the same Core sorting engine. Both reject cycles. The distinction is updating individual list arrays versus managing parent changes and subtree preservation in one tree. The structure diagram summarizes the initial state; the actual card layout shows the current state after a move.

The refreshed Playground GIF demonstrates both the independent child-array update and Review moving with its descendants.

Playground:

  • /examples/nested/react
  • /examples/nested-controlled/react
  • /examples/functional-third-party/react
  • /examples/tree/react

Runnable example · Source guide for this version