# Sortable

> Sortable lets users reorder the items of a list, a table or a grid by dragging them. It wraps SortableJS in a Tabler component and styles the dragged item.

Make lists, table rows and cards draggable with the data-sortable attribute, SortableJS options as JSON and the Sortable component.

Source: https://docs.tabler.io/ui/plugins/sortable

---

## Overview

Use Sortable where the order is up to the user, such as a task list, table rows or dashboard cards. The component is part of `tabler.js`. The dragging itself comes from [SortableJS](https://sortablejs.github.io/Sortable/), which you load separately.

## Installation

Install the SortableJS library:

```shell
npm install sortablejs
yarn add sortablejs
pnpm install sortablejs
bun install sortablejs
```

If you use a bundler, put the library on `window` before Tabler loads. The component reads `window.Sortable` and does nothing without it. Imports run before the rest of a file, so do it in a file of its own and import that one first:

```javascript
// sortable-global.js
import Sortable from 'sortablejs'

window.Sortable = Sortable
```

```javascript
import './sortable-global.js'
import '@tabler/core'
```

Or include it from a CDN, before `tabler.js`:

```html
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.6.0/dist/libs/sortablejs/Sortable.min.js"></script>
```

## Usage

### Basic list

Add `data-sortable` to the parent element. Its direct children become draggable.

```html
<ul class="list-group" data-sortable="true">
  <li class="list-group-item">Design the landing page</li>
  <li class="list-group-item">Write the release notes</li>
  <li class="list-group-item">Review open pull requests</li>
</ul>
```

### Options

Pass [SortableJS options](https://github.com/SortableJS/Sortable#options) as JSON in the attribute. This list animates the items as they move.

```html
<ul class="list-group" data-sortable="{&quot;animation&quot;:150}">
  <li class="list-group-item">First</li>
  <li class="list-group-item">Second</li>
  <li class="list-group-item">Third</li>
</ul>
```

### Drag handle

Set `handle` to a selector to start the drag only from that element. The rest of the item stays clickable.

```html
<ul class="list-group" data-sortable='{"animation":150,"handle":".sortable-handle"}'>
  <li class="list-group-item d-flex gap-2">
    <span class="sortable-handle cursor-move">⋮⋮</span>
    Design the landing page
  </li>
</ul>
```

### Table rows

Put `data-sortable` on the `tbody` to reorder the rows of a table.

```html
<table class="table">
  <tbody data-sortable='{"animation":150}'>
    <tr><td>First row</td></tr>
    <tr><td>Second row</td></tr>
  </tbody>
</table>
```

### Drag styles

Tabler styles the classes SortableJS adds while dragging: `.sortable-ghost` marks the drop position, `.sortable-chosen` the picked item and `.sortable-drag` the copy that follows the pointer.

The browser draws the dragged item itself by default, and CSS cannot style that image. Turn on the `forceFallback` option and SortableJS drags an HTML copy of the item instead, which `.sortable-drag` tilts and lifts with a shadow:

```html
<ul class="list-group" data-sortable='{"forceFallback":true}'>
  …
</ul>
```

## JavaScript

Tabler wraps the plugin in a `Sortable` component that works like the Bootstrap components: it is created once per element and stored on it. On page load Tabler creates one for every element with `data-sortable`. This is the code that runs:

```ts
initAll(SELECTOR_DATA_SORTABLE, Sortable)
```

_Source: `core/js/src/sortable.ts`_

Create the component yourself for a list added later. Options come from `data-sortable`, from the config object, or both. The object wins.

```js
const list = new tabler.Sortable(document.getElementById('tasks'), { animation: 150 });
```

Method|Description
---|---
`toArray()`|Returns the `data-id` of every item, in the current order.
`sort(order, useAnimation)`|Reorders the items to match an array of ids.
`dispose()`|Destroys the SortableJS instance and removes the component.

The `sortable` getter returns the SortableJS instance, so everything from the [SortableJS API](https://github.com/SortableJS/Sortable#methods) is available:

```js
const list = tabler.Sortable.getOrCreateInstance(document.getElementById('tasks'));
list.sortable.option('disabled', true);
```

## Accessibility

- Dragging needs a pointer. SortableJS has no keyboard support, so give keyboard users another way to change the order, such as "Move up" and "Move down" buttons.
- Use a drag handle when the items contain links or buttons, so a click on them does not start a drag.
- Save the new order only after the drop, and tell the user when saving fails.
