Transfer
Two lists and the arrows between them: everything that could be chosen on one side, everything that has been on the other. For a choice long enough that a field full of chips stops being readable.
import { Transfer } from 'neba';
<Transfer
items={[
{ value: 'status', label: 'Status' },
{ value: 'commit', label: 'Commit' }
]}
value={shown}
onValueChange={setShown}
/>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the surface each of the two panels draws |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Height and type scale |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| items * | readonly TransferItem[] | — | Everything that can be on either side, in the order the lists show it |
| value | readonly string[] | — | What is on the right. With onValueChange it makes the pair controlled |
| defaultValue | readonly string[] | — | What starts on the right, uncontrolled |
| onValueChange | (value: string[]) => void | — | Fired whenever something moves |
| sourceLabel | ReactNode | — | The heading over the left-hand list. Defaults to the locale's word |
| targetLabel | ReactNode | — | And over the right-hand one |
| searchable | boolean | false | Puts a filter above each list. It hides rows; it never moves them |
| height | number | string | 220 | How tall each list is. Numbers are pixels |
| disabled | boolean | false | Nothing can be ticked or moved |
| locale | string | — | BCP 47 tag the headings, buttons and filter are written in. Unsupported tags fall back to English |
Every native <div> attribute passes through, apart from color.
TransferItem
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | string | — | What identifies it, and what value is a list of |
| label * | ReactNode | — | What the row says |
| disabled | boolean | false | In the list but not movable |
Below about a dozen options a Combobox with multiple, or a column of Checkboxes, is the smaller component. This one earns its size when the answer to "what did I actually pick" needs a list of its own.
Examples
value and onValueChange
The value is what is on the right, in the order items gives, so a row does not move when it is sent across and back. Ticking a row is not choosing it: ticks say which rows the next press moves, and the value says which side they are on.
searchable
Puts a filter above each list. It hides rows; it never moves them, and a hidden row is not part of a press.
height · disabled
height is how tall each list is. A disabled item stays in its list and can never be ticked or moved; disabled on the whole component does the same to all of them.
locale · sourceLabel · targetLabel
The headings, the buttons and the filter come from locale. sourceLabel and targetLabel write the two headings out instead, which is usually what you want: "Available" and "Selected" are rarely what the two lists actually are.
Accessibility
- Every row is a real checkbox with its label wired to it, so the whole thing is reachable with Tab and Space.
- The tick above each list is
Select allfor that list, and reports a mixed state while only some rows are ticked. - Both buttons are named ("Move to selected" and "Move to available"), and go unavailable while there is nothing for them to move.