Cascader
Accessible, unstyled Cascader: typed multi-level option paths in adjacent listbox columns.
|
|
| Package |
@vizejs/ui/cascader |
| Maturity |
stable |
| Own the source |
vize lib pull cascader |
| Requires |
context, controllable-state, dismissable-layer, id, portal, positioner, presence |
| Aliases |
cascader, cascade select, multi-level select, hierarchical picker |
| Covers |
Ant Design Cascader, Element Plus Cascader, PrimeVue CascadeSelect |
Usage
import { Cascader, CascaderRoot, CascaderColumn, CascaderContent, CascaderItem, CascaderTrigger, CascaderValue } from "@vizejs/ui/cascader";
Or copy the source into your project with vize lib pull cascader (see Source Distribution).
API
Cascader<T, Multiple extends boolean = false>
Source: src/families/selection/cascader/cascader-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
options (required) |
readonly T[] |
required |
Top-level options. |
modelValue |
CascaderModelValue<T, Multiple> |
undefined |
Controlled selection: one path, or a list of leaf paths when multiple is true. |
defaultValue |
CascaderModelValue<T, Multiple> |
undefined |
Initial selection for uncontrolled use and the value restored by reset. |
multiple |
Multiple |
false |
Select several leaf paths. The literal type decides the model type. |
getChildren |
(node: T) => readonly T[] | undefined |
undefined |
Child accessor. Defaults to an array-valued children property. |
isLeaf |
(node: T) => boolean |
undefined |
Leaf predicate. Defaults to "has no children and nothing to load". |
loadChildren |
(node: T, context: CascaderLoadContext) => Promise<readonly T[]> |
undefined |
Lazily load a branch's children the first time it expands. |
by |
CascaderBy<T> |
undefined |
Compare nodes by a property key or with a custom equality function. |
itemText |
(node: T) => string |
undefined |
Human-readable text for a node, used by the value display, typeahead, and search. |
itemDisabled |
(node: T) => boolean |
undefined |
Disable individual nodes. |
formValue |
(node: T) => string |
undefined |
Serialize one node for form submission; path segments are joined by separator. |
changeOnSelect |
boolean |
false |
Allow choosing branch options, not only leaves (single mode). |
expandTrigger |
CascaderExpandTrigger |
"click" |
Expand branches on click or on pointer hover. |
separator |
string |
" / " |
Separator between path segments in display text and form values. |
search |
string |
undefined |
Controlled search text; a non-empty query publishes searchResults. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable the trigger and every option. |
required |
boolean |
false |
Mark a selection as required for assistive technology. |
name |
string |
undefined |
Form field name; each selected path submits one hidden input. |
form |
string |
undefined |
Id of the owning form when rendered outside it. |
placeholder |
string |
undefined |
Text shown by CascaderValue while nothing is selected. |
typeaheadTimeout |
number |
500 |
Idle time before buffered typeahead starts a new query. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: CascaderModelValue<T, Multiple>] |
Fired when the selection requests a new controlled value (a path, or a list of paths). |
change |
[ value: CascaderModelValue<T, Multiple>, previous: CascaderModelValue<T, Multiple>, nativeEvent: Event | null, ] |
Fired after a user interaction changes the selection: next value, previous value, native event. |
update:open |
[open: boolean] |
Fired when the popup requests a controlled open value. |
update:search |
[query: string] |
Fired when the search text requests a new controlled value. |
load-error |
[node: T, error: unknown] |
Fired when loadChildren rejects for a branch, with the error. |
Slots
| Slot |
Slot props |
Description |
default |
CascaderSlotState<T> |
Trigger, value, and popup parts. Receives columns, selection, and search results. |
Exposed
| Member |
Type |
Description |
clear |
— |
|
expandPath |
— |
|
focus |
— |
|
open |
— |
|
reset |
— |
|
selected |
— |
|
selectPath |
— |
|
setOpen |
— |
|
CascaderRoot<T, Multiple extends boolean = false>
Source: src/families/selection/cascader/cascader-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
options (required) |
readonly T[] |
required |
Top-level options. |
modelValue |
CascaderModelValue<T, Multiple> |
undefined |
Controlled selection: one path, or a list of leaf paths when multiple is true. |
defaultValue |
CascaderModelValue<T, Multiple> |
undefined |
Initial selection for uncontrolled use and the value restored by reset. |
multiple |
Multiple |
false |
Select several leaf paths. The literal type decides the model type. |
getChildren |
(node: T) => readonly T[] | undefined |
undefined |
Child accessor. Defaults to an array-valued children property. |
isLeaf |
(node: T) => boolean |
undefined |
Leaf predicate. Defaults to "has no children and nothing to load". |
loadChildren |
(node: T, context: CascaderLoadContext) => Promise<readonly T[]> |
undefined |
Lazily load a branch's children the first time it expands. |
by |
CascaderBy<T> |
undefined |
Compare nodes by a property key or with a custom equality function. |
itemText |
(node: T) => string |
undefined |
Human-readable text for a node, used by the value display, typeahead, and search. |
itemDisabled |
(node: T) => boolean |
undefined |
Disable individual nodes. |
formValue |
(node: T) => string |
undefined |
Serialize one node for form submission; path segments are joined by separator. |
changeOnSelect |
boolean |
false |
Allow choosing branch options, not only leaves (single mode). |
expandTrigger |
CascaderExpandTrigger |
"click" |
Expand branches on click or on pointer hover. |
separator |
string |
" / " |
Separator between path segments in display text and form values. |
search |
string |
undefined |
Controlled search text; a non-empty query publishes searchResults. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
disabled |
boolean |
false |
Disable the trigger and every option. |
required |
boolean |
false |
Mark a selection as required for assistive technology. |
name |
string |
undefined |
Form field name; each selected path submits one hidden input. |
form |
string |
undefined |
Id of the owning form when rendered outside it. |
placeholder |
string |
undefined |
Text shown by CascaderValue while nothing is selected. |
typeaheadTimeout |
number |
500 |
Idle time before buffered typeahead starts a new query. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: CascaderModelValue<T, Multiple>] |
Fired when the selection requests a new controlled value (a path, or a list of paths). |
change |
[ value: CascaderModelValue<T, Multiple>, previous: CascaderModelValue<T, Multiple>, nativeEvent: Event | null, ] |
Fired after a user interaction changes the selection: next value, previous value, native event. |
update:open |
[open: boolean] |
Fired when the popup requests a controlled open value. |
update:search |
[query: string] |
Fired when the search text requests a new controlled value. |
load-error |
[node: T, error: unknown] |
Fired when loadChildren rejects for a branch, with the error. |
Slots
| Slot |
Slot props |
Description |
default |
CascaderSlotState<T> |
Trigger, value, and popup parts. Receives columns, selection, and search results. |
Exposed
| Member |
Type |
Description |
selected |
readonly (readonly T[])[] |
Selected paths. |
open |
boolean |
Whether the popup is open. |
setOpen |
(open: boolean) => boolean |
Request a specific open state. |
selectPath |
(path: readonly T[]) => boolean |
Choose a path (toggle in multiple mode). |
expandPath |
(path: readonly T[]) => void |
Expand the branch at the end of path, loading it when needed. |
clear |
() => boolean |
Clear the selection. |
reset |
() => boolean |
Restore defaultValue. |
focus |
(options?: FocusOptions) => void |
Focus the trigger. |
CascaderColumn
Source: src/families/selection/cascader/cascader-column.vue
Props
| Prop |
Type |
Default |
Description |
level (required) |
number |
required |
Zero-based depth of the options this column lists. |
ariaLabel |
string |
undefined |
Accessible name; defaults to the parent option's text (or "Options" at the top level). |
Slots
| Slot |
Slot props |
Description |
default |
CascaderColumnSlotState |
CascaderItem options for this level; render them from the root columns state. |
CascaderContent
Source: src/families/selection/cascader/cascader-content.vue
Props
| Prop |
Type |
Default |
Description |
placement |
Placement |
"bottom-start" |
Preferred placement relative to the trigger before collision handling. |
strategy |
PositionerStrategy |
"fixed" |
CSS positioning mode published on the floating host. |
offset |
number |
4 |
Gap between the trigger and the popup. |
collisionPadding |
number |
8 |
Viewport padding the popup should not cross. |
flip |
boolean |
true |
Flip to the opposite side when the preferred side overflows more. |
shift |
boolean |
true |
Shift the popup back into the viewport. |
size |
boolean |
true |
Publish available-size CSS variables and constrain the host. |
to |
string | HTMLElement |
"body" |
CSS selector or element the popup is moved into. |
portalDisabled |
boolean |
false |
Render in place instead of teleporting. |
defer |
boolean |
true |
Keep content in place until the portal target exists, avoiding SSR mismatch. |
forceMount |
boolean |
false |
Keep items mounted (hidden) while closed. |
ariaLabel |
string |
undefined |
Accessible name of the popup region holding the columns. |
Events
| Event |
Payload |
Description |
escape-key-down |
[event: DismissableLayerEscapeKeyDownEvent] |
Fired before Escape closes the popup. Call preventDefault() to keep it open. |
pointer-down-outside |
[event: DismissableLayerPointerDownOutsideEvent] |
Fired before an outside pointer-down closes the popup. Call preventDefault() to keep it open. |
dismiss |
[event: DismissableLayerDismissEvent] |
Fired after an unprevented dismissal request closes the popup. |
Slots
| Slot |
Slot props |
Description |
default |
CascaderContentSlotState |
One CascaderColumn per entry of the root columns. Receives open state and placement. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
CascaderItem<T>
Source: src/families/selection/cascader/cascader-item.vue
Props
| Prop |
Type |
Default |
Description |
value (required) |
T |
required |
Node rendered by this option; must belong to the column's options. |
ariaLabel |
string |
undefined |
Accessible name when the option text is not enough. |
Slots
| Slot |
Slot props |
Description |
default |
CascaderItemSlotState<T> |
Option content. Receives branch, expansion, selection, and loading state. |
CascaderTrigger
Source: src/families/selection/cascader/cascader-trigger.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name when no visible label supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids of visible labels. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the cascader. |
Slots
| Slot |
Slot props |
Description |
default |
{ readonly open: boolean; readonly empty: boolean } |
Trigger content, usually CascaderValue. Receives open and emptiness state. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
CascaderValue
Source: src/families/selection/cascader/cascader-value.vue
Props
| Prop |
Type |
Default |
Description |
pathSeparator |
string |
", " |
Text placed between paths when several are selected. |
Slots
| Slot |
Slot props |
Description |
default |
CascaderValueSlotState |
Custom rendering of the selected paths. |
placeholder |
CascaderValueSlotState |
Custom placeholder rendering while nothing is selected. |
Behavior
Contract
Cascader is a headless, strongly typed multi-level picker. cascader-root.vue
is generic over the node type T and a Multiple literal: the model is one
option path (readonly T[], empty when nothing is selected) or, with
multiple: true, a list of leaf paths (readonly (readonly T[])[]). Children
come from getChildren (default: an array children property) or lazily from
loadChildren(node, { signal }); nodes compare through by.
cascader-trigger.vue renders a native <button role="combobox" aria-haspopup="listbox"> that keeps DOM focus and exposes the highlighted
option through aria-activedescendant; aria-controls lists every open
column. cascader-content.vue renders the popup through Portal, Presence,
Positioner, and a dismissable layer. The root publishes typed columns (the
top level plus one per expanded branch); each cascader-column.vue is a
role="listbox" labelled by its parent option and each cascader-item.vue a
role="option" with aria-expanded on branches. cascader-value.vue renders
the selected path text or a placeholder. Named cascaders submit one hidden
input per selected path (formValue/by segments joined by separator).
Option ids derive from the root id plus level and index. No CSS ships.
Normative Behavior
| # |
State |
Input |
Outcome |
Proven by |
| K1 |
closed |
render |
combobox trigger with aria-haspopup="listbox", aria-expanded="false", placeholder text, no columns |
renders a combobox trigger with placeholder and no popup while closed |
| K2 |
open |
click branch / click leaf |
branches open child columns labelled by their parent and set aria-expanded; a leaf selects its path, closes, and shows A / B / C |
click expands branches into new columns and selecting a leaf closes |
| K3 |
selected |
reopen |
expands the selected path, highlights its last node, marks ancestors data-state="partial" |
reopening restores the selected path and marks ancestors as partial |
| K4 |
closed / open |
ArrowDown / Up / Home / End / ArrowRight / Enter / ArrowLeft / Space |
opens; moves within a column skipping disabled nodes; ArrowRight and Enter on a branch descend to its first option; ArrowLeft returns to the parent and closes deeper columns; Enter/Space on a leaf selects and closes |
keyboard matrix: open, arrows skip disabled, right/left traverse levels, Enter selects |
| K5 |
open |
printable keys / Escape |
typeahead per column (navigation keys reset the query); Escape closes without selecting |
typeahead moves within the active column and Escape closes without selecting |
| K6 |
changeOnSelect |
click branch |
commits the branch path and keeps the popup open |
changeOnSelect commits branch paths while keeping the popup open |
| K7 |
multiple |
click leaves |
toggles leaf paths, stays open, marks columns aria-multiselectable, submits one hidden input per path |
multiple mode toggles leaf paths and stays open |
| K8 |
expandTrigger=hover |
pointer move over a branch |
highlights and expands it |
hover expandTrigger expands branches on pointer movement |
| L1 |
loadChildren |
expand / expand a sibling / resolve |
loads once, marks the option data-loading and its column aria-busy, aborts a superseded sibling load, descends after a keyboard-triggered load |
loadChildren loads lazily, marks loading, descends by keyboard, and aborts superseded loads |
| L2 |
loadChildren |
reject / unmount |
emits load-error(node, error); unmount aborts pending loads |
unmount aborts pending loads and rejected loads emit load-error |
| S1 |
search |
non-empty query |
publishes matching (loaded) leaf paths as searchResults; selectPath chooses one |
search publishes matching paths that can be selected |
| C1 |
controlled / disabled |
select / keys |
controlled values emit and wait for the parent; disabled cascaders never open |
controlled values wait for the parent; disabled cascaders never open |
| C2 |
no provider |
mount a part |
throws VIZE_UI_CONTEXT_MISSING |
parts require a Cascader provider |
| U1 |
pure helpers |
findCascaderPath / flattenCascaderPaths / searchCascaderPaths / toCascaderSelection |
resolve, flatten (optionally including branches), search accent-insensitively, and normalize models |
pure helpers resolve, flatten, search, and normalize paths |
SSR
Ids derive from the deterministic-id primitive, the popup renders in place
until the portal hydrates, and lazy loads only start after mount. Closed and
open trees render byte-identical markup and hydrate without warnings
(renders byte-identical closed Cascader markup with the selected path label,
renders byte-identical open Cascader markup with columns for the selected path, hydrates closed and open Cascaders without mismatches).