A drag-to-reorder wrapper for lists and rows, with a Move menu on each handle for keyboard and screen reader users.
import { Sortable } from '@oztix/roadie-components/sortable'
Wrap a List in Sortable and give each List.Item a value. The row takes
a drag handle in front. Pass the values in order as items, and store the new
order in onReorder. Drag a handle to move its row, or click it to open the
Move menu. The drag preview is the browser's own snapshot of the row.
Set orientation='horizontal' for items in a row. Drops land on the left or
right edge, and the menu moves items left and right, or to the start and end.
disabled on Sortable turns off dragging and the Move menu for every item.
disabled on one Sortable.Item stops that item being dragged or moved from
its own menu; other items can still move past it. A List.Item has no
per-row disabled, so build a row you need to hold back with Sortable.Item.
Sortable.Item renders a <div> by default. Pass render to swap the
element, such as <li /> inside your own list, and put a Sortable.Handle
anywhere inside it. Each item sets data-dragging while it is dragged and
data-drop-edge (top, bottom, left or right) while it is the drop
target, for your own styling; the dimmed item and the accent drop line are
built in. Give each item a label so the handle and menu can name
it: "Reorder Sydney", "Move Sydney up".
Drag and drop works inside a modal Popover. A long list scrolls by itself
while you drag near its top or bottom edge.
Do
Put Sortable around the List and key each row by its value, so focus returns to the handle of the row that moved.
Don’t
Don't put it inside the list or key rows by index. Sortable adds a hidden live region after its children, which can't sit inside a list, and index keys send focus to the wrong handle.
<Sortable label='columns' …><Sortable.Item value='sku' label='SKU'>…</Sortable.Item></Sortable>
Do
Give each item a label (a text List.Item title does this for you) and the root a label. The handle reads "Reorder SKU" and moves are announced as "SKU moved to position 3 of 7 columns".
<Sortable.Item value='sku'><SkuIcon /></Sortable.Item>
Don’t
Don't leave an item unnamed. Every handle then reads "Reorder", and a screen reader user can't tell the rows apart.
<List.Item value='sku' title='SKU' trailing={<Toggle … />} />
Do
Put actions in the row's trailing slot as buttons or toggles. A reorderable row is not a link or button, and ignores href, onClick, current and chevron.
<List.Item value='sku' title={<input … />} />
Don’t
Don't put text fields in a sortable item. The whole item is draggable, so its text can't be selected, and Firefox can't place the caret in a field inside it.
Each item's `value`, in order.
Called with the new order after a drag or a Move menu choice.
The direction items run in, which sets the drop edges and menu wording.
Defaults to 'vertical'.
Turns off dragging and the Move menu.
Defaults to false.
Names the collection in announcements, as in "SKU moved to position 3 of 7 columns".
Drags its item, and opens a Move menu on click or Enter for anyone not dragging with a pointer.
CSS class applied to the element, or a function that returns a class based on the component's state.
Defaults to subtler.
Icon-button sizing. Use `'xs' | 'sm' | 'md' | 'lg'` (plain) — the `'icon-*'` aliases are accepted for backwards compatibility but discouraged.
Defaults to sm.
Defaults to "Reorder {label}" from the item's `label`.
Inherited from ButtonProps
Whether the button should be focusable when disabled.
Defaults to false.
Inherited from NativeButtonProps
Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).
Defaults to true.
One reorderable item. Sets `data-dragging` and `data-drop-edge` for styling.
Identifies the item; one of the root's `items`.
The item's name in the Move menu and announcements, as in "Move SKU down".
Stops this item being dragged or moved from its own Move menu. Other items can still move past it.
Defaults to false.
Swaps the default `<div>`, e.g. `<li />` inside a list.