Sortable
Headless drag-to-reorder over a list of stable keys: pointer and touch dragging from the engine, plus a full APG grab-and-move keyboard model.
Order:
<NaviusSortable @bind-Values="_order" class="flex flex-col gap-2">
@foreach (var key in _order)
{
<NaviusSortableItem @key="key" Value="@key" Label="@_labels[key]"
class="flex items-center gap-2 rounded-md border px-3 py-2
data-[dragging]:opacity-60 data-[drop-target]:border-primary
data-[keyboard-grabbed]:ring-2 data-[keyboard-grabbed]:ring-primary">
<NaviusSortableItemHandle class="cursor-grab"><GripIcon /></NaviusSortableItemHandle>
<span>@_labels[key]</span>
</NaviusSortableItem>
}
</NaviusSortable>Features
- The root owns an ordered
List<string>of keys and re-renders into the new order; the consumer iterates the same list to render one item per key. - Pointer visuals (
data-draggingon the dragged item,data-drop-targeton the hovered slot) are painted by the engine straight onto the DOM, so a C# re-render never fights them. - The keyboard reducer (Space grabs, arrows move live, Space drops, Escape restores) announces each transition through a visually-hidden live region.
- An optional Handle scopes pointer drag to a grip; a per-item
Disabledrow is skipped by roving navigation. Bind with@bind-Valuesand hookOnReorder(old, new).
Installation
Install the brain, or copy just this primitive in with the CLI.
navius add sortableAnatomy
Iterate the bound key list and render one item per key. A Handle is optional.
@using Navius.Primitives.Components.Sortable
<NaviusSortable @bind-Values="_order">
@foreach (var key in _order)
{
<NaviusSortableItem @key="key" Value="@key">
<NaviusSortableItemHandle />
@* row content *@
</NaviusSortableItem>
}
</NaviusSortable>API Reference
Root
NaviusSortable. Owns the key order, the keyboard grab/roving state and the drag engine. Renders a div with role=list plus a visually-hidden live region. Values is two-way bindable.
| Prop | Type | Default |
|---|---|---|
| Values | IReadOnlyList<string>? | - |
| ValuesChanged | EventCallback<IReadOnlyList<string>> | - |
| DefaultValues | IReadOnlyList<string>? | - |
| Orientation | SortableOrientation | Vertical |
| Disabled | bool | false |
| OnReorder | EventCallback<SortableReorderEventArgs> | - |
| ChildContent | RenderFragment? | - |
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-sortable] | Present on the list |
| [data-orientation] | "vertical" | "horizontal" | "grid" |
| [data-disabled] | Present when disabled |
| [data-dragging] | Present while a drag or grab is active |
Item
NaviusSortableItem. One reorderable row: the drag target and the roving keyboard focus target. Value is the stable key. Renders a div with role=listitem.
| Prop | Type | Default |
|---|---|---|
| Value | string | required |
| Label | string? | Value |
| Disabled | bool | false |
| ChildContent | RenderFragment? | - |
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-sortable-item] | Present on every row |
| [data-navius-sortable-id] | Mirrors Value |
| [data-dragging] | Painted by the engine on the dragged item |
| [data-drop-target] | Painted by the engine on the hovered slot |
| [data-keyboard-grabbed] | Present while grabbed for keyboard reordering |
| [data-disabled] | Present when the row or list is disabled |
ItemHandle
NaviusSortableItemHandle. An optional drag grip; its presence scopes pointer drag to the handle. aria-hidden (keyboard reordering acts on the whole item). Renders a span.
| Prop | Type | Default |
|---|---|---|
| ChildContent | RenderFragment? | - |
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-sortable-handle] | Present on the handle; scopes pointer drag to it |
Orientation is a SortableOrientation (Vertical / Horizontal / Grid). OnReorder fires a SortableReorderEventArgs(OldIndex, NewIndex). Cross-list transfer is deliberately not supported (the engine reports indices for a single container).
Accessibility
Follows the APG "grab and move" pattern. Each row is a roving tab stop (aria-roledescription="sortable item"); grabbing sets aria-grabbed and data-keyboard-grabbed. Every grab, move, drop and cancel is announced through a polite live region.
Keyboard interactions
| Key | Description |
|---|---|
| Space / Enter | Grabs the focused item, or drops a grabbed item at its new position. |
| Arrow keys | While grabbed, moves the item live; otherwise moves the roving focus. |
| Home / End | Moves the item (or focus) to the first / last position. |
| Escape | Cancels a grab and restores the original order. |