Custom Hosts and Slots
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.
React as and Vue tag change the sortable host without changing the Area
contract. Svelte and Vanilla register the element that directly contains items.
Complete example
Section titled “Complete example”import { useState } from 'react';import { SortableArea, SortableRoot } from 'comins-sortable/react';
type Item = { id: string; label: string };
export function CustomHostExample() { const [items, setItems] = useState<Item[]>([ { id: 'task-1', label: 'Plan' }, { id: 'task-2', label: 'Build' }, ]);
return ( <SortableRoot<Item>> <SortableArea as="ul" areaId="tasks" areaProps={{ 'aria-label': 'Tasks', style: { listStyle: 'none', padding: 0 } }} items={items} itemKey="id" handle=".drag-handle" header={<li>Tasks to complete</li>} footer={<li>End of list — not a drop target</li>} onItemsChange={(next) => setItems([...next])} > {(item) => ( <li> <button type="button" className="drag-handle" aria-label={`Drag ${item.label}`} style={{ touchAction: 'none', userSelect: 'none', WebkitUserSelect: 'none' }} > ⠿ </button> <span>{item.label}</span> </li> )} </SortableArea> </SortableRoot> );}This example uses a real ul host and keeps the header/footer li elements
outside the sortable items. A consumer component passed to as must forward
the React ref to that host. Vue component hosts must expose the real root
element that will be registered.
React header and footer, Vue slots, or a narrowed Vanilla/Svelte item
selector keep non-sortable content in the same host. Non-item siblings must not
match the item selector or appear in the controlled array. Hovering such a
sibling rejects the candidate with not-accepted feedback.
For Vanilla/Svelte, item: ':scope > .task-item' limits discovery to direct
items. Selectors are evaluated in the registered Area, rather than with
matches() on each child. Do not give header/footer elements that item class.
The third-party example uses a consumer-defined host without an external UI
package dependency. Its name does not certify a third-party library integration.
Table-row and table-column Playground demos were removed; the generic host API
does not establish support for table sorting.
Playground:
/examples/third-party/react/examples/footer-slot/react/examples/header-slot/react/examples/two-list-slots/react
Playground Host examples
Section titled “Playground Host examples”When the final item leaves a populated list, its known footer boundary is retained. Refilling places both preview and item between the header and footer. React keeps headers and footers in stable render positions as the item count changes. This preserves an observed boundary; it does not infer header/footer semantics from arbitrary non-item DOM that was empty from the start.
The Custom container binding / Nested custom containers guides show the
actual DOM structure and registration API. A host directly contains sortable
items; this is not a comparison of DOM and custom events. Vanilla reports
changes through its onChange option callback.
React uses as and ref forwarding; Vue uses tag and componentProps.
Svelte attaches use:sortable to a div, and Vanilla passes a div to
createSortable. These two tabs demonstrate direct DOM registration rather
than component replacement. The functional-third-party route follows the
same distinction and connects the child area through parent.