- Add custom placeholder and drag-overlay content renderers for all four adapters.
- Fix long-lived session history and completed disposer reference retention.
- Suppress incidental text selection during accepted mouse-handle attempts.
- Synchronize README and release documentation automatically on version changes.
Full changelog · Release status
Release status (2026-10-25): 0.1.7 completed canonical staging. The public registry now resolves 0.1.7; post-publication closure has not been verified in this record. See status and evidence.
Drag-and-drop sorting for Vanilla JavaScript, React, Vue, and Svelte, built on one TypeScript core with zero runtime dependencies. Build sortable lists, grids, kanban boards, and nested trees with controlled application state.
npm package · English docs · 한국어 가이드
The animation shows real React Playground interactions: emptying and refilling a list between fixed slots, changing drag start areas, comparing accepted and rejected drop styles, updating a child list, and moving a complete subtree. By default, drag a card's left handle to move it and select its body text to copy it. The Drag start areas example also demonstrates title-only and whole-card dragging.
| Capability | What to try in the Playground |
|---|---|
| Sortable lists and kanban boards | Reorder items, transfer them between lists, or copy them to another list. |
| Drag start areas and text selection | Compare handle, title-only, and whole-card dragging; use handle mode to select and copy body text. |
| Multi-drag | Command/Ctrl-click handles, or use Shift for a range, then move the selected items together. |
| Sorting thresholds | Change the shaded target region to see when a drag changes order; this differs from the distance needed to start dragging. |
| Grid and Swap Grid | Grid inserts and shifts items from the dragged card’s center, including off-center handle grabs. Swap Grid exchanges only the dragged and highlighted cells. |
| Nested lists and Tree | Per-list state control updates separate arrays in React, Vue, and Svelte; Vanilla demonstrates direct DOM updates. Subtree movement preserves descendants when a node changes parent. Both include structure diagrams and step-by-step instructions. |
| Drag feedback | Compare accepted/rejected drop styles and their CSS; customize animation, placeholders, skeleton feedback, and auto-scroll. |
Vanilla uses no framework peer. React, React DOM, Vue, and Svelte integrations
use optional peers (react, react-dom, vue, and svelte).
npm install comins-sortableInstall the peers used by your chosen adapter: React >=18.2 <20 with React DOM
>=18.2 <20, Vue >=3.5 <4, or Svelte >=5 <6. Vanilla needs no framework peer.
Use the repository Playground below to try the examples.
Comins Sortable is controlled: the application owns the item array and commits
each proposed order through onItemsChange.
import { useState } from 'react';
import { SortableArea, SortableRoot } from 'comins-sortable/react';
import 'comins-sortable/styles.css';
type Item = { id: string; label: string };
export function TaskList() {
const [items, setItems] = useState<Item[]>([
{ id: 'task-1', label: 'Plan' },
{ id: 'task-2', label: 'Build' },
]);
return (
<SortableRoot<Item>>
<SortableArea
areaId="tasks"
items={items}
itemKey="id"
handle=".task-handle"
onItemsChange={(nextItems) => setItems([...nextItems])}
>
{(item) => (
<div>
<button
type="button"
className="task-handle"
aria-label={`Drag ${item.label}`}
style={{ touchAction: 'none', userSelect: 'none', WebkitUserSelect: 'none' }}
>
⠿
</button>
<span>{item.label}</span>
</div>
)}
</SortableArea>
</SortableRoot>
);
}The handle confines pointer activation to the button, so the label remains available for text selection. A labelled button does not add keyboard sorting; this module's sorting interaction is pointer-based. See the handle and text-selection guide for styling and multi-selection behavior.
| Adapter | State updates | Guide |
|---|---|---|
| Vanilla JavaScript | Direct DOM sorting with createSortable |
Vanilla guide |
| React | Controlled items and onItemsChange |
React guide |
| Vue | v-model and a typed item slot |
Vue guide |
| Svelte | Controlled items and update callbacks | Svelte guide |
Use stable string or number IDs. Framework adapters propose updates; the application commits them to its state. Cross-list moves need a shared root.
- English quick start
- Documentation index · 한국어 시작하기
- Reorder and transfer · Copy and clone
- Handles and text selection · Animation and auto-scroll
- Nested lists and tree API · Placeholder CSS and rejected targets
- Public API reference · 한글 API
createSortableTree maps your tree schema to sortable areas and applies area
updates to an immutable tree value. It preserves descendants when a node moves;
it does not render a tree component. Placeholder classes and CSS variables
customize insertion and rejection feedback without changing sorting behavior.
The Playground currently provides 25 routes per adapter, covering Vanilla JavaScript, React, Vue, and Svelte. Clone the repository to run the examples:
git clone https://github.com/kim1124/comins-sortable.git
cd comins-sortable
npm ci --ignore-scripts
npm run build
npm run devOpen the React simple-sorting example. The development server uses port 4003.
Use Reset to restore the example and clear multi-selection, including its Shift-range anchor, in all four adapters.
Automated tests cover Chromium, Firefox, and WebKit. Sorting uses pointer input; keyboard reordering is not implemented. Browser touch emulation is distinct from physical-device testing. See the changelog for published changes and the performance verification guide for reproducible large-list and resource measurements.
Native Safari title-only selection remains a separately recorded limitation. Version 0.1.7 suppresses selection initiation during accepted explicit mouse-handle attempts. Use a dedicated handle when selecting and copying body text matters; see the handle guide. Automated WebKit results do not establish native Safari or Galaxy coverage.
