vael-ui v0.3.10

Selection

Tree

A collapsible, indented list for hierarchical data with single or multi-selection.

Install

import { Tree } from 'vael-ui'

Playground

package.json
README.md
import { Tree } from 'vael-ui'

<Tree selectionMode="single" selectableFolders filterable filterPlaceholder="Search..." emptyText="No results found" motionCss :forceMount="false" :reorderable="false" reorderSiblings :autoExpandDelay="600" previewMode="clone" :touchDragDelay="150" :expandOnRowClick="false" :stickyScroll="false" id="" query="" :items="items">Tree</Tree>

Props

NameTypeDefaultDescription
itemsreadonly T[]The tree data — `TreeNode` (`value`, `label`, optional `children`) or your own extension of it.
selectionMode"multiple" | "single" | "checkbox""single"`'single'`: clicking replaces the selection. `'multiple'`: clicking toggles that node only. `'checkbox'`: checkboxes with cascading parent/child toggles.
selectableFoldersboolean | undefinedtrue`false` keeps a node with children out of the selection entirely — click, keyboard Enter/Space, and expandOnRowClick's own select-on-expand all skip it, only a leaf can become the value. Has no effect in `selectionMode="checkbox"`, which already only ever puts leaves in the model. Default: true (a folder can be selected like any other node).
filterableboolean | undefinedtrueShows a built-in label search box atop the tree, auto-expanding ancestors of any match. Default: on.
filterPlaceholderstring | undefined"Search..."
emptyTextstring | undefined"No results found"
motionCssboolean | undefinedtrue`false` skips all built-in motion (row transitions, chevron rotation, and cross-folder move).
forceMountboolean | undefinedfalseKeeps every folder's children block mounted (visibility via `v-show`, `data-state="open"/"closed"` on it) instead of mounting/unmounting on expand/collapse, so a consumer can drive the collapse with their own animation library instead of the built-in CSS transition.
reorderableboolean | undefinedfalseDrag rows to reorder and to nest, VS Code style: drop on a row's middle to move INTO it, on an edge to place beside it.
canNestInto((node: T) => boolean) | undefinedundefinedWhich rows accept children. Defaults to any row that already has some — pass your own to let empty folders take drops.
reorderSiblingsboolean | undefinedtrue`false` drops the sibling-reorder mode entirely — dragging only ever offers moving INTO a folder, with no indicator at all when hovering a row that can't hold children.
canDrop((details: SortableDropDetails) => boolean) | undefinedundefinedStructural veto re-run while dragging; `false` marks the target invalid.
beforeDrop((details: SortableDropDetails) => boolean | Promise<boolean>) | undefinedundefinedAsync gate at drop time — return `false` (or a promise of it) to cancel. Composes with `confirmAction().result`.
autoExpandDelaynumber | undefined600Hovering a collapsed row this long opens it mid-drag.
previewMode"element" | "clone""clone"`'clone'` (default): a floating copy follows the cursor, real row hidden until drop — the built-in behavior since before this prop existed. `'element'` moves the real row itself instead, so there's only one instance of it on screen; safe here since a tree row is a plain element, not a `<table>` row.
touchDragDelaynumber | undefined150Ms a touch pointer must hold a row still before a drag starts — a row is also tap-to-select/expand here, so touch needs a hold to tell the two apart; mouse/pen are unaffected. Default `150`.
expandOnRowClickboolean | undefinedfalseWhen true, clicking anywhere on a folder row also toggles its expansion, not just the chevron — it still selects too (unless `selectableFolders` is off), so picking the folder itself (without opening it to reach a file inside) still works. Off by default since it changes what a plain row click does.
stickyScrollboolean | undefinedfalseWhen true, each expanded ancestor's row pins to the top of the list as its own children scroll past, VS Code-style, so deeply nested content never loses its folder context. Uses native `position: sticky` — each row is a real, nested DOM level, not a JS-measured overlay.
idstring | undefinedundefinedId for the role="tree" list element. Auto-generated if omitted.
uiPartial<{ list: UiPartValue; node: UiPartValue; filter: UiPartValue; empty: UiPartValue; chevron: UiPartValue; label: UiPartValue; }> | undefinedundefined
modelValuestring | number | (string | number)[] | null | undefinednull
querystring | undefined""
nodeT | T[] | null | undefinednull

Slots

NameTypeDescription
node{ node: T; depth: number; expanded: boolean; checked: boolean; indeterminate: boolean; disabled: boolean; toggleExpand: () => void; toggleSelect: () => void; findNode: (value: string | number) => T | undefined; findParent: (value: string | number) => T | null; removeNode: (value: string | number) => boolean; }Row content (inside the library's role="treeitem" wrapper). findNode/findParent/removeNode are shorthand for findTreeNode/findTreeParent/removeTreeNode bound to this instance's own `items`, for the common case of looking up a sibling/parent/self without importing them.
emptyanyReplaces the default empty-state row shown when nothing survives the filter.

Events

NameTypeDescription
select[node: T]
change[value: string | number | (string | number)[] | null]
drop-error[error: unknown, details: SortableDropDetails]
expand-change[value: string | number, expanded: boolean]
reorder[value: string | number, to: DropPosition]
update:modelValue[value: string | number | (string | number)[] | null]
update:query[value: string]
update:node[value: T | T[] | null]

Exposed

NameTypeDescription
listElunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
filterInputRefunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
focusFirstRowunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
initRovingunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
expandAllunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
collapseAllunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
expandNodeunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
collapseNodeunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
findNodeunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
findParentunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
removeNodeunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
isReorderingunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.
cancelReorderunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.