Vitral 0.2
Data

TreeTable

Rows that expand into rows, with sorting that keeps the nesting and a filter that keeps the branches leading to matches.

Import

main.ts
import { TreeTable } from '@vitral/vue';

Basic

Size
Applications
300 MBFolder
editor.app
75 MBApplication
Selection: none
<script setup lang="ts">import { Column, TreeTable, type TreeNode } from '@vitral/vue';import { ref } from 'vue'; const files: TreeNode[] = [    {        key: 'apps',        data: { name: 'Applications', size: '300 MB', type: 'Folder' },        children: [            { key: 'vue', data: { name: 'Vue', size: '25 MB', type: 'Folder' }, children: [{ key: 'vue-app', data: { name: 'vue.app', size: '10 MB', type: 'Application' } }, { key: 'vue-cli', data: { name: 'cli', size: '15 MB', type: 'Application' } }] },            { key: 'editor', data: { name: 'editor.app', size: '75 MB', type: 'Application' } }        ]    },    {        key: 'docs',        data: { name: 'Documents', size: '75 KB', type: 'Folder' },        children: [            { key: 'work', data: { name: 'Work', size: '55 KB', type: 'Folder' }, children: [{ key: 'expenses', data: { name: 'Expenses.doc', size: '30 KB', type: 'Document' } }, { key: 'resume', data: { name: 'Resume.doc', size: '25 KB', type: 'Document' } }] },            { key: 'invoices', data: { name: 'Invoices.txt', size: '20 KB', type: 'Text' } }        ]    },    { key: 'music', data: { name: 'Música', size: '1.2 GB', type: 'Folder' }, children: [{ key: 'track', data: { name: 'Samba.mp3', size: '6 MB', type: 'Audio' } }] }]; const expanded = ref<Record<string, boolean>>({ apps: true });const selection = ref({});</script> <template>    <TreeTable v-model:expanded-keys="expanded" v-model:selection-keys="selection" :value="files" selection-mode="checkbox" aria-label="Files">        <Column field="name" header="Name" expander sortable />        <Column field="size" header="Size" align="right" />        <Column field="type" header="Type" sortable />    </TreeTable>    <small style="color: var(--vt-text-muted-color)">Selection: {{ Object.keys(selection).join(', ') || 'none' }}</small></template>

Filter and pages

NameType
<script setup lang="ts">import { Column, InputText, TreeTable, type TreeNode } from '@vitral/vue';import { ref } from 'vue'; const files: TreeNode[] = [    {        key: 'apps',        data: { name: 'Applications', size: '300 MB', type: 'Folder' },        children: [            { key: 'vue', data: { name: 'Vue', size: '25 MB', type: 'Folder' }, children: [{ key: 'vue-app', data: { name: 'vue.app', size: '10 MB', type: 'Application' } }, { key: 'vue-cli', data: { name: 'cli', size: '15 MB', type: 'Application' } }] },            { key: 'editor', data: { name: 'editor.app', size: '75 MB', type: 'Application' } }        ]    },    {        key: 'docs',        data: { name: 'Documents', size: '75 KB', type: 'Folder' },        children: [            { key: 'work', data: { name: 'Work', size: '55 KB', type: 'Folder' }, children: [{ key: 'expenses', data: { name: 'Expenses.doc', size: '30 KB', type: 'Document' } }, { key: 'resume', data: { name: 'Resume.doc', size: '25 KB', type: 'Document' } }] },            { key: 'invoices', data: { name: 'Invoices.txt', size: '20 KB', type: 'Text' } }        ]    },    { key: 'music', data: { name: 'Música', size: '1.2 GB', type: 'Folder' }, children: [{ key: 'track', data: { name: 'Samba.mp3', size: '6 MB', type: 'Audio' } }] }]; const query = ref('');</script> <template>    <InputText v-model="query" placeholder="Filter by name" aria-label="Filter by name" style="align-self: flex-start" />    <TreeTable :value="files" :global-filter="query" :global-filter-fields="['name']" paginator :rows="2" striped-rows show-gridlines selection-mode="single" aria-label="Files, filtered">        <Column field="name" header="Name" expander />        <Column field="type" header="Type" />    </TreeTable></template>

API

Read from packages/vue/src/components/TreeTable/types.ts, so it says what the component actually accepts.

Props

NameTypeDescription
valueTreeNodeLike[]The rows, as tree nodes whose `data` the columns read.
selectionMode'single' | 'multiple' | 'checkbox'—
sortFieldstring | nullSort by this `data` field…
sortOrder1 | -1 | null…ascending (1) or descending (-1).
removableSortbooleanA third press on a sorted column takes it out of the sort.
globalFilterstringKeep the rows (and the branches leading to them) whose `globalFilterFields` contain this text.
globalFilterFieldsstring[]The `data` fields `globalFilter` searches.
paginatorbooleanPage the top-level rows.
rowsnumberTop-level rows per page. Defaults to 10.
rowsPerPageOptionsnumber[]—
loadingboolean—
emptyMessagestring—
stripedRowsboolean—
showGridlinesboolean—
sizeSize—
scrollHeightstringA CSS height; the header stays in view.
tableStylestring | Record<string, string>—
captionstringNames the table for assistive technology. Or name it with `aria-label`.

Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.

Emits

EventPayloadDescription
node-expandnode: TreeNodeLike—
node-collapsenode: TreeNodeLike—
node-selectnode: TreeNodeLike—
node-unselectnode: TreeNodeLike—
sortevent: { sortField: string | null; sortOrder: 1 | -1 | null }—
pageevent: { page: number; first: number; rows: number; pageCount: number }—

Slots

NameSlot propsDescription
default—The `<Column>`s, the same component DataGrid reads. `expander` marks the column with the toggle.
header——
footer——
empty——
loadingicon——