Reorder
Reorder groups: a list the user reorders by dragging an item, or with the keys, as Motion's
Reorder.Group. A Group holds its items by value and runs one drag at a time. A binding registers
each item with how to read its box, drives the group from a drag's samples (dragStart, dragMove,
dragEnd) or from keys (lift, moveLifted, drop, cancel), and the group calls onReorder with each
new order. check is the rule that decides when a dragged item swaps places, and
defaultAnnouncements what a screen reader hears. The binding and its Environment supply the boxes,
points, keys and scrollers.
import * as Reorder from "@wardic/core/Reorder"
const group = Reorder.make({ values: () => items, onReorder: (next) => (items = next) })
const unregister = Reorder.register(group, item, source)
// As the item is dragged:
Reorder.dragStart(group, item)
Reorder.dragMove(group, item, sample.velocity)
Reorder.dragEnd(group, item)Interfaces
AnnouncementInfo
What an announcement says about an item.
Type Parameters
V
V
Properties
count
readonly count: number;How many items the list has.
label
readonly label: string;What it's spoken as: its name, as the Environment reads it.
position
readonly position: number;Its place in the list, from 1.
value
readonly value: V;The item's value.
Announcements
What a group announces: one for each moment of a keyboard reorder, and move for a move in code, an
app's own buttons. A group overrides any of them, and its instructions, for translation or tone.
Example
const announcements: Reorder.Announcements<Task> = {
...Reorder.defaultAnnouncements,
drop: ({ label, position }) => `${label} is now number ${position}.`,
}Type Parameters
V
V
Properties
cancel
readonly cancel: (info) => string;The reorder cancelled: "Cancelled. Milk is back at position 2 of 5."
Parameters
info
Returns
string
drop
readonly drop: (info) => string;The lifted item dropped: "Dropped Milk at position 3 of 5."
Parameters
info
Returns
string
lift
readonly lift: (info) => string;An item lifted: "Picked up Milk, position 2 of 5."
Parameters
info
Returns
string
move
readonly move: (info) => string;An item moved in code: "Moved Milk to position 1 of 5."
Parameters
info
Returns
string
step
readonly step: (info) => string;The lifted item stepped: "Milk, position 3 of 5."
Parameters
info
Returns
string
Group
A reorder group: its items by value, and one drag at a time. Made by make; its operations take it
first, or in a pipe.
registeradds an item: a value has one source, and the function it returns removes it. A value that isn't registered stays at its index invalues.resolveAxisis the group's axis: theaxisit's given, elsedetectAxisover its measured items.dragStartfixes a drag's axis and direction, and gives the axis;dragMoverunscheckfor the dragged item, with the drag sample's velocity, and gives the velocity back for auto-scroll;dragEndends it.movemoves a value in code, held to the list; a value it doesn't know is left out.lift,moveLifted,dropandcancelreorder one item with the keys, each giving where the item is, for its announcement.
Its state is its own, changed only by those operations.
Extends
PipeableInspectable
Type Parameters
V
V
Properties
~@wardic/core/Reorder
readonly "~@wardic/core/Reorder": object;_V
readonly _V: Invariant<V>;options
readonly options: GroupOptions<V>;The inputs make took.
sources
readonly sources: ReadonlyMap<V, ItemSource>;Each registered item's source, by value.
GroupOptions
A group's options: make's.
Type Parameters
V
V
Properties
axis?
readonly axis?: () => Option<Axis>;The axis, when it's given (Some): read at each drag's start, over the one detectAxis finds.
Returns
Option<Axis>
debug?
readonly debug?: (event, annotations) => void;Hears what happens, for a debug log: reorder { value, from, to, axis, direction } once per reorder,
reorder waits { value } once per wait for the boxes to be measured, reorder start { value, axis,
direction }, and move { value, from, to }.
Parameters
event
string
annotations
Record<string, unknown>
Returns
void
direction?
readonly direction?: () => "ltr" | "rtl";The group's inline direction, read at each drag's start: "ltr" when it's left out.
Returns
"ltr" | "rtl"
onReorder
readonly onReorder: (values) => void;Hears each new order, which the app sets, as with Motion's.
Parameters
values
V[]
Returns
void
values
readonly values: () => readonly V[];The app's order, read when it's needed.
Returns
readonly V[]
ItemData
An item as check sees it: its value and its layout box, its place in the list, not where it paints while it's dragged.
Type Parameters
V
V
Properties
layout
readonly layout: Box;Its layout box: its place in the list.
value
readonly value: V;The item's value in the list.
ItemSource
What a group reads from one item, when it needs it: a layout projection's handle for the item gives all three.
Properties
dragOffset
readonly dragOffset: () => Point;How far it's dragged from its place.
Returns
layoutBox
readonly layoutBox: () => Option<Box>;Its layout box, its place in the list: None before it's measured.
Returns
Option<Box>
layoutVersion
readonly layoutVersion: () => number;A count that rises each time its box is measured: after a reorder, a drag waits until it does.
Returns
number
Movement
How a dragged item moves, as check reads it.
Properties
axis
readonly axis: Axis;The axis the list runs on, or "xy" for a grid.
direction?
readonly direction?: "ltr" | "rtl";Which way a line reads: "ltr", the default, or "rtl".
offset
readonly offset: Point;Where the item is from where it started.
velocity
readonly velocity: Point;Its velocity, per second.
Placement
Where an item is after a keyboard reorder's step, for its announcement.
Properties
count
readonly count: number;How many items the order has.
moved
readonly moved: boolean;Whether this step moved it: not at an end.
position
readonly position: number;Its place in the order, from 1.
waiting
readonly waiting: boolean;Whether the step waited for the boxes to be measured: after a step they're the old order's until they are, so a step read against them could go the wrong way. It moved nothing, and has nothing to announce: a held arrow's repeat in that time is dropped.
Type Aliases
Arrow
type Arrow = "left" | "right" | "up" | "down";An arrow key, by where it points on screen.
Axis
type Axis = "x" | "y" | "xy";The axis a group reorders on: a column ("y"), a row ("x"), or a grid that wraps ("xy"), as Motion's Axis.
KeyIntent
type KeyIntent =
| {
_tag: "Toggle";
}
| {
_tag: "Cancel";
}
| {
_tag: "Step";
arrow: Arrow;
}
| {
_tag: "End";
end: "first" | "last";
};What a key does to a keyboard reorder: Space or Enter lifts an item, or drops the lifted one
(Toggle); Escape cancels; an arrow steps the lifted item one place on screen; Home and End move it
to the first or last place. Any other key does nothing.
Variables
cancel
const cancel: {
<V>(value): (group) => Option<Placement>;
<V>(group, value): Option<Placement>;
};Cancels the keyboard reorder: the order from the lift comes back through onReorder, when it changed,
and the item's placement there. None when value isn't lifted.
Call Signature
<V>(value): (group) => Option<Placement>;Type Parameters
V
V
Parameters
value
V
Returns
(group) => Option<Placement>
Call Signature
<V>(group, value): Option<Placement>;Type Parameters
V
V
Parameters
group
Group<V>
value
V
Returns
Option<Placement>
DEFAULT_INSTRUCTIONS
const DEFAULT_INSTRUCTIONS: "Press Space or Enter to pick up. Use the arrow keys to move, Space or Enter to drop, Escape to cancel." = "Press Space or Enter to pick up. Use the arrow keys to move, Space or Enter to drop, Escape to cancel.";The default instructions for an item, in English: the keys. An Environment adds what its own assistive
technology needs, as @wardic/dom's DEFAULT_REORDER_INSTRUCTIONS does.
defaultAnnouncements
const defaultAnnouncements: Announcements<unknown>;The default announcements, in English: each member's example is its wording.
dragEnd
const dragEnd: {
<V>(value): (group) => void;
<V>(group, value): void;
};Ends the drag of value's item; nothing, when it isn't the one dragged.
Call Signature
<V>(value): (group) => void;Type Parameters
V
V
Parameters
value
V
Returns
(group) => void
Call Signature
<V>(group, value): void;Type Parameters
V
V
Parameters
group
Group<V>
value
V
Returns
void
dragMove
const dragMove: {
<V>(value, velocity): (group) => Point;
<V>(group, value, velocity): Point;
};A drag sample of value's item, moving at velocity, the sample's: runs check, and gives
velocity back, for auto-scroll; no motion when value isn't the one dragged.
Call Signature
<V>(value, velocity): (group) => Point;Type Parameters
V
V
Parameters
value
V
velocity
Returns
(group) => Point
Call Signature
<V>(
group,
value,
velocity
): Point;Type Parameters
V
V
Parameters
group
Group<V>
value
V
velocity
Returns
dragStart
const dragStart: {
<V>(value): (group) => Axis;
<V>(group, value): Axis;
};Starts dragging value's item: the drag's axis and direction are fixed now. Gives the axis.
Call Signature
<V>(value): (group) => Axis;Type Parameters
V
V
Parameters
value
V
Returns
(group) => Axis
Call Signature
<V>(group, value): Axis;Type Parameters
V
V
Parameters
group
Group<V>
value
V
Returns
drop
const drop: {
<V>(value): (group) => Option<Placement>;
<V>(group, value): Option<Placement>;
};Drops the lifted item where it is: its placement, None when value isn't lifted.
Call Signature
<V>(value): (group) => Option<Placement>;Type Parameters
V
V
Parameters
value
V
Returns
(group) => Option<Placement>
Call Signature
<V>(group, value): Option<Placement>;Type Parameters
V
V
Parameters
group
Group<V>
value
V
Returns
Option<Placement>
lift
const lift: {
<V>(value): (group) => Option<Placement>;
<V>(group, value): Option<Placement>;
};Lifts value's item for a keyboard reorder: its axis and direction are fixed now, as a drag's are at
its start, and the order kept for a cancel. Lifting another item drops the one lifted. None for a
value not in values.
Call Signature
<V>(value): (group) => Option<Placement>;Type Parameters
V
V
Parameters
value
V
Returns
(group) => Option<Placement>
Call Signature
<V>(group, value): Option<Placement>;Type Parameters
V
V
Parameters
group
Group<V>
value
V
Returns
Option<Placement>
move
const move: {
<V>(value, toIndex): (group) => Option<Placement>;
<V>(group, value, toIndex): Option<Placement>;
};Moves value to toIndex in code, through onReorder, held to the list, as an app's own buttons do:
where it is then, for its announcement, moved false when it was there already. None, and nothing
moved, for a value the group doesn't know.
Call Signature
<V>(value, toIndex): (group) => Option<Placement>;Type Parameters
V
V
Parameters
value
V
toIndex
number
Returns
(group) => Option<Placement>
Call Signature
<V>(
group,
value,
toIndex
): Option<Placement>;Type Parameters
V
V
Parameters
group
Group<V>
value
V
toIndex
number
Returns
Option<Placement>
Example
Reorder.move(group, item, 0) // to the topmoveLifted
const moveLifted: {
<V>(value, intent): (group) => Option<Placement>;
<V>(group, value, intent): Option<Placement>;
};One key's move of the lifted item: an arrow's step on screen (arrowTarget), or Home's or End's move
to the first or last place, through onReorder. moved is false where it had nowhere to go. Until the
boxes are measured after its last step, a step waits (waiting, nothing moved), as a drag's next
reorder does. None when value isn't the lifted item.
Call Signature
<V>(value, intent): (group) => Option<Placement>;Type Parameters
V
V
Parameters
value
V
intent
| {
_tag: "Step";
arrow: Arrow;
}
| {
_tag: "End";
end: "first" | "last";
}
Returns
(group) => Option<Placement>
Call Signature
<V>(
group,
value,
intent
): Option<Placement>;Type Parameters
V
V
Parameters
group
Group<V>
value
V
intent
| {
_tag: "Step";
arrow: Arrow;
}
| {
_tag: "End";
end: "first" | "last";
}
Returns
Option<Placement>
register
const register: {
<V>(value, source): (group) => () => void;
<V>(group, value, source): () => void;
};Adds value's item, read through source. The function it returns removes it.
Call Signature
<V>(value, source): (group) => () => void;Type Parameters
V
V
Parameters
value
V
source
Returns
(group) => () => void
Call Signature
<V>(
group,
value,
source
): () => void;Type Parameters
V
V
Parameters
group
Group<V>
value
V
source
Returns
() => void
Functions
arrowTarget()
function arrowTarget<V>(
order,
index,
arrow,
axis,
direction
): number;Where a lifted item goes for one arrow: its new index in order, by what's on screen, so an rtl row
or a reversed list moves the way the arrow points.
- One axis: only that axis's arrows act. The item takes the place of the neighbour, of the two next
to it, whose centre lies the way the arrow points, as
checkchooses. - A grid (
"xy"): Left and Right step one place in reading order, flipped in rtl, wrapping across rows; Up and Down take the place of the item in the row above or below whose centre is nearest.
With nowhere to go, at an end or for an arrow off the axis, it's index.
Type Parameters
V
V
Parameters
order
readonly ItemData<V>[]
index
number
arrow
axis
direction
"ltr" | "rtl"
Returns
number
check()
function check<V>(
order,
value,
movement
): readonly ItemData<V>[];The order after the item holding value has been dragged offset from its place, moving at
velocity: the rule for when a dragged item swaps places. The same order when nothing moves, so a
caller can compare by reference.
- One axis moves the item one place at most: toward the neighbour, of the two next to it, whose centre lies the way it's moving, once its edge facing that neighbour crosses the neighbour's centre. No velocity on the axis moves nothing.
- A grid (
"xy"), as Motion's: the dragged centre picks the nearest row. In another row, the item goes before the first item whose centre is past it indirection; in its own, it steps one place toward the nearest box.
Type Parameters
V
V
Parameters
order
readonly ItemData<V>[]
value
V
movement
Returns
readonly ItemData<V>[]
detectAxis()
function detectAxis(layouts): Axis;The axis a list's item boxes are laid out on, as Motion's detectAxis: "x" if some two don't
overlap across, "y" if some two don't overlap down, "xy" if both, a grid that wraps, and "y"
otherwise. It stops once it has found both.
Parameters
layouts
readonly Box[]
Returns
isDragging()
function isDragging<V>(group): boolean;Whether a drag gesture runs in group.
Type Parameters
V
V
Parameters
group
Group<V>
Returns
boolean
isGroup()
function isGroup(u): u is Group<unknown>;Whether u is a reorder group, made by make.
Parameters
u
unknown
Returns
u is Group<unknown>
isLifted()
function isLifted<V>(group): boolean;Whether an item of group is lifted, by its keys (a Keyboard reorder, ADR-0037).
Type Parameters
V
V
Parameters
group
Group<V>
Returns
boolean
keyIntent()
function keyIntent(key): Option<KeyIntent>;What key does to a keyboard reorder, if anything: see KeyIntent.
Parameters
key
Returns
Option<KeyIntent>
liftedValue()
function liftedValue<V>(group): Option<V>;The lifted value, while a keyboard reorder runs.
Type Parameters
V
V
Parameters
group
Group<V>
Returns
Option<V>
make()
function make<V>(options): Group<V>;A reorder group over options' values. Each drag sample reads the measured items' boxes and the dragged
item's offset, runs check, and on a new order calls onReorder with it, a value with no box kept at
its index. After a reorder it waits until the dragged item's box is measured again, so a sample read
against the boxes from before can't cross it back.
Type Parameters
V
V
Parameters
options
GroupOptions<V>
Returns
Group<V>
Example
const group = Reorder.make({ values: () => items, onReorder: (next) => (items = next) })resolveAxis()
function resolveAxis<V>(group): Axis;The group's axis: its axis, when it's given one, else detectAxis over the measured items' boxes.
Type Parameters
V
V
Parameters
group
Group<V>