Now.js Framework Documentation
Sortable
Sortable
ภาพรวม
Sortable คือ component สำหรับ drag-and-drop sorting ใน Now.js Framework รองรับ lists, grids และ API integration
ใช้เมื่อ:
- ต้องการ reorder items ด้วย drag-drop
- ต้องการ Kanban boards
- ต้องการ sortable tables
- ต้องการ auto-save position
ทำไมต้องใช้:
- ✅ Drag and drop reordering
- ✅ Cross-container moving
- ✅ Keyboard support (accessibility)
- ✅ Touch device support
- ✅ Auto-save API integration
- ✅ Ghost element preview
- ✅ Placeholder visualization
การใช้งานพื้นฐาน
HTML Declarative
<div data-component="sortable" data-sortable-draggable=".item">
<div class="item" draggable="true">Item 1</div>
<div class="item" draggable="true">Item 2</div>
<div class="item" draggable="true">Item 3</div>
</div>With Handle
<div data-component="sortable"
data-sortable-draggable=".item"
data-sortable-handle=".handle">
<div class="item" draggable="true">
<span class="handle">☰</span>
<span>Item 1</span>
</div>
<div class="item" draggable="true">
<span class="handle">☰</span>
<span>Item 2</span>
</div>
</div>JavaScript API
const sortable = Sortable.create(container, {
draggable: '.item',
handle: '.handle',
onEnd: (evt) => {
console.log('Moved from', evt.oldIndex, 'to', evt.newIndex);
}
});Data Attributes
| Attribute | Description |
|---|---|
data-sortable-draggable |
Selector for draggable items |
data-sortable-handle |
Selector for drag handle |
data-sortable-group |
Group name for cross-container |
data-sortable-api |
API endpoint for auto-save |
data-sortable-id-attr |
Attribute for item ID |
data-sortable-update-field |
Field to update on move |
Options
Sortable.create(element, {
// Selector for draggable items
draggable: '.item',
// Selector for drag handle (optional)
handle: '.drag-handle',
// Group name for cross-container sorting
group: 'shared',
// Ghost element class
ghostClass: 'sortable-ghost',
// Placeholder class
placeholderClass: 'sortable-placeholder',
// Animation duration (ms)
animation: 150,
// Callbacks
onStart: (evt) => {},
onEnd: (evt) => {},
onChange: (evt) => {}
});API อ้างอิง
Sortable.create(element, options)
สร้าง sortable instance
| Parameter | Type | Description |
|---|---|---|
element |
HTMLElement | Container element |
options |
object | Configuration options |
Returns: Sortable - Instance
const sortable = Sortable.create(container, {
draggable: '.card'
});Sortable.getInstance(element)
รับ instance จาก element
| Parameter | Type | Description |
|---|---|---|
element |
HTMLElement | Container element |
Returns: Sortable|null
instance.enable()
เปิดใช้งาน sorting
instance.disable()
ปิดใช้งาน sorting
instance.destroy()
ทำลาย instance
เหตุการณ์
| Event | เมื่อเกิด | Detail |
|---|---|---|
sortable:start |
เริ่ม drag | {item, oldIndex} |
sortable:end |
วาง item | {item, oldIndex, newIndex, oldContainer, newContainer} |
sortable:change |
ลำดับเปลี่ยน | {items} |
element.addEventListener('sortable:end', (e) => {
console.log(`Moved from ${e.detail.oldIndex} to ${e.detail.newIndex}`);
});Auto-Save API
บันทึกตำแหน่งอัตโนมัติเมื่อ drop:
<div data-component="sortable"
data-sortable-draggable=".task"
data-sortable-api="/api/tasks/{id}/position"
data-sortable-id-attr="data-id"
data-sortable-update-field="position">
<div class="task" data-id="1" draggable="true">Task 1</div>
<div class="task" data-id="2" draggable="true">Task 2</div>
</div>API Request ที่ส่ง:
PATCH /api/tasks/1/position
{ "position": 2 }การจัดรูปแบบ CSS
/* Draggable items */
.item[draggable="true"] {
cursor: grab;
}
.item[draggable="true"]:active {
cursor: grabbing;
}
/* Ghost element (follows cursor) */
.sortable-ghost {
opacity: 0.7;
transform: scale(1.02);
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
}
/* Placeholder (where item will drop) */
.sortable-placeholder {
background: rgba(59, 130, 246, 0.1);
border: 2px dashed #3b82f6;
border-radius: 8px;
}
/* Drag handle */
.handle {
cursor: grab;
padding: 8px;
color: #9ca3af;
}
.handle:hover {
color: #374151;
}ตัวอย่างการใช้งานจริง
Kanban Board
<div class="kanban-board">
<!-- Todo Column -->
<div data-component="sortable"
data-sortable-group="kanban"
data-sortable-draggable=".card"
data-sortable-api="/api/tasks/{id}"
data-sortable-id-attr="data-id"
data-sortable-stage-attr="data-status"
data-sortable-update-field="status"
data-status="todo">
<h3>To Do</h3>
<div class="card" data-id="1" draggable="true">Task 1</div>
<div class="card" data-id="2" draggable="true">Task 2</div>
</div>
<!-- In Progress Column -->
<div data-component="sortable"
data-sortable-group="kanban"
data-sortable-draggable=".card"
data-sortable-api="/api/tasks/{id}"
data-sortable-id-attr="data-id"
data-sortable-stage-attr="data-status"
data-sortable-update-field="status"
data-status="in-progress">
<h3>In Progress</h3>
<div class="card" data-id="3" draggable="true">Task 3</div>
</div>
<!-- Done Column -->
<div data-component="sortable"
data-sortable-group="kanban"
data-sortable-draggable=".card"
data-sortable-api="/api/tasks/{id}"
data-sortable-id-attr="data-id"
data-sortable-stage-attr="data-status"
data-sortable-update-field="status"
data-status="done">
<h3>Done</h3>
</div>
</div>Todo List
<ul data-component="sortable" data-sortable-draggable="li">
<li draggable="true">Buy groceries</li>
<li draggable="true">Call mom</li>
<li draggable="true">Finish report</li>
</ul>Table Rows
<table>
<tbody data-component="sortable" data-sortable-draggable="tr">
<tr draggable="true"><td>Row 1</td></tr>
<tr draggable="true"><td>Row 2</td></tr>
<tr draggable="true"><td>Row 3</td></tr>
</tbody>
</table>Keyboard Navigation
| Key | Action |
|---|---|
Space |
เริ่ม/หยุด drag mode |
↑/↓ |
ย้าย item ขึ้น/ลง |
Enter |
Confirm position |
Escape |
Cancel drag |
ข้อควรระวัง
⚠️ 1. draggable="true" บน items
<!-- ❌ ลืม draggable -->
<div class="item">Item</div>
<!-- ✅ เพิ่ม draggable -->
<div class="item" draggable="true">Item</div>⚠️ 2. Group Name ต้องตรงกัน
<!-- ❌ ต่าง group - ย้ายข้ามไม่ได้ -->
<div data-sortable-group="a">...</div>
<div data-sortable-group="b">...</div>
<!-- ✅ group เดียวกัน -->
<div data-sortable-group="cards">...</div>
<div data-sortable-group="cards">...</div>เอกสารที่เกี่ยวข้อง
- TableManager - Sortable tables
- ApiService - API calls