Powered by Svelte 5's Runes, an extremely flexible and simple drag-and-drop api.
bun add -D runic-reorder
<script lang='ts'>
import reorder, { type ItemState } from 'runic-reorder'
let array = $state([
'a',
'b',
'c'
])
type Item = typeof array[number]
const area = reorder(content) // Reference the snippet
</script>
{#snippet content(item: Item, state: ItemState)}
<div use:state.handle>
{item}
</div>
{/if}
<div use:area>
{@render area(array)}
</div>
That's as simple as it gets. You can move items between each use:area
. The @render area(...)
takes an array as input, which are the items rendered.
[!IMPORTANT]
Entries must be unique.
[!NOTE]
Sadly, theanimate:
directive is not currently supported, as the@render area(...)
creates the{@each items as item, i (item)}
loop.
Theanimate:
directive can currently not be used for snippet elements.I'm currently planning on finding an alternative like
use:state.animate={flip}
.
const reorder: (snippet: Snippet<[item: T, state: ItemState<T>]>) =>
| function(node: HTMLElement, options: AreaOptions): { destroy(): void }
| function(array: T[]): ReturnType<Snippet>
When you create an area using reorder(snippet)
it serves as a svelte action:
<div use:area={areaOptions}>
and a snippet:
{@render area(array)}
The action marks the dropable area for the rendered list, giving you flexibility for structure- and accessibility considerations.
The element with use:area
will be provided the following data-attributes
:
Attribute | Value(s) | Description |
---|---|---|
data-area-condition |
'true' | 'false' |
Does the area meet the condition of the dragged item? |
data-area-class |
options.class |
The classes you provide in AreaOptions
|
data-area-target |
true |
Present if the area the current target of the dragged item |
data-area-origin |
true |
Present if the area is the origin for the dragged item |
Using selectors such as div[data-area-condition='true'] {...}
,
div[data-area-target]
and div[data-area-class~='...']
you can style your area according to the situation.
[!NOTE]
The reason ofdata-area-class
is so that you can also style the dragged item, which is moved to the body doing dragging. Read more about the~='...'
selector
You can provide each area with custom options, just for that specific area:
Property | Type | Description |
---|---|---|
axis | 'x' | 'y' |
Lock element movement to one axis |
class | string |
data-area-class attribute |
condition | (item: T) => boolean |
Acceptance criteria, whether an item can be dropped here |
onDrop | (item: T) => void |
Modify (or what not) the item when it is dropped into the area. |
get | (areaState: AreaState<T>) => void |
Get the AreaState; let area = $state() as undefined | AreaState and <div use:order.area.array={{ get: a => area = a }}>
|
Property | Type | Description |
---|---|---|
clickable | boolean | Should you be able to click on the handle? |
cursor | string | A custom cursor instead of the default "grab" Note: This will override the pointer cursor when clicking on a clickable element. |
Property | Type | Description |
---|---|---|
dragging |
boolean |
Is this item dragged? |
positioning |
boolean |
Is this item being positioned somewhere? |
draggedIs |
undefined | 'before' | 'after' |
Is the dragged item the next or previous item in the same array? |
handle |
(element: HTMLElement, options?: HandleOptions) => void |
The handle is the element that can be dragged upon |
anchor |
(element: HTMLElement) => void |
(optional) The anchor is the element that a dragged item will referencee to position itself. If not provided, the use:state.handle becomes the anchor. |
area |
AreaState<T> |
The area this item is in. |
index |
number |
The index of this item in its array |
array |
T[] |
The array this item is in |
value |
T |
The value of this item |
Property | Type | Description |
---|---|---|
node |
HTMLElement |
The area element |
options |
AreaOptions<T> |
The area options |
class |
string[] |
An array of strigns that represent the class-attribute separated by space |
isTarget |
boolean |
Is the dragged item targeting this area? |
isOrigin |
boolean |
Did the dragged item come from here? |
items |
ItemState<T>[] |
The items (ItemState) that are within this area |
array |
T[] |
The array associated with this area |