Destination Picker
Choose where something goes — spaces at the top of a folders-only tree, recent places, a folder search, and destinations refused with a reason.
- Status
- Since
0.25.89- Accessibility pattern
- folders-only tree, refusals announced
Last updated
Recent
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Destination Picker before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify destination-pickerInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/destination-pickerRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/button, @vegastack/folder-tree, @vegastack/panel-search, @vegastack/skeleton, @vegastack/use-announcer, @vegastack/use-async-search.
Usage
import { DestinationPicker } from "@/components/ui/destination-picker";
<DialogBody>
<DestinationPicker
spaces={spaces}
loadFolders={(space, parent) => api.folders(space, parent)}
value={target}
onValueChange={setTarget}
disabledReason={(d) =>
d.folderId === item.parentId ? "It's already here" : undefined
}
recents={recentPlaces}
search={(q, signal) => api.searchFolders(q, { signal })}
/>
</DialogBody>;Scope
The body of every "Move to…" dialog. The spaces are the top of a folders-only FolderTree in
picker mode; each opens to its folders (loaded once through loadFolders), and choosing a space
means its top level. The host owns the dialog and its Move button, enabled once value is set.
Anatomy
Examples
Move to…
recents sit above the tree, search finds a folder anywhere (the tree comes back when the box
is cleared), and disabledReason keeps a destination visible while refusing it: the reason shows
on the row, under the picker, and is announced.
Recent
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
loadFolders* | (spaceId: string, parentId: string | null) => Promise<DestinationFolder[]> | — | Loads the folders directly inside a space's top (parentId: null) or a folder. |
onValueChange* | (value: Destination) => void | — | Called when a destination is chosen. |
spaces* | readonly DestinationSpace[] | — | The spaces, in order. |
className | string | — | Classes on the root. |
disabledReason | ((destination: Destination) => string | undefined) | — | Why a destination can't take this move (it is where the item already is, inside the item
itself, or the reader can't add there) — the row stays visible, the pick is refused and the
reason is shown and announced. undefined means it can. |
emptyLabel | string | "No folders match." | |
label | string | "Destinations" | The tree's accessible name. |
recentLabel | string | "Recent" | |
recents | readonly DestinationEntry[] | — | Recent destinations, above the tree. |
search | ((query: string, signal: AbortSignal) => Promise<DestinationEntry[]>) | — | Find folders by name anywhere; omit to hide the search box. |
searchPlaceholder | string | "Search folders" | |
value | Destination | null | — | The chosen destination. |
Data attributes and CSS variables on DestinationPicker
| Attribute | Values |
|---|---|
data-slot | "destination-picker" | "destination-picker-entry" | "destination-picker-recents" | "destination-picker-refused" | "destination-picker-results" |
Accessibility
- The tree is
FolderTree's picker mode: one tab stop, arrow keys move and open, Enter chooses. - A refused choice is announced through a polite live region and stays visible as text.
| Contract | States tested |
|---|---|
| Behaviour | default, loading, refused, search, recents |
| Accessibility | labeled |
| Visual | default, with-icon |