vael-ui v0.3.10

Data Display

DataTable

A data grid with sorting, selection, resizing, and virtualization, composed from Columns.

Install

import { DataTable } from 'vael-ui'

Playground

import { DataTable } from 'vael-ui'

<DataTable :loading="false" :selectable="false" selectionMode="checkbox" :single="false" scrollHeight="" stackedBreakpoint="" size="md" :stripedRows="false" :showGridlines="false" :resizableColumns="false" :frozenColumns="0" :rows="0" :manualSort="false" :lazy="false" :total="0" :virtualize="false" motionCss :reorderableColumns="false" columnGripVisibility="always" previewMode="clone" :touchDragDelay="150" :page="1">DataTable</DataTable>

Props

NameTypeDefaultDescription
dataT[]Row objects. Column content is read from these via each `<Column>`'s `field`.
rowKeykeyof T | ((row: T) => string | number)Stable row identity — a key on `T`, or a function for composite/derived keys.
loadingboolean | undefinedfalseShows the `#loading` slot instead of rows/empty state.
selectableboolean | undefinedfalseAdds a leading checkbox/radio column wired to the `selected` state.
selectionMode"checkbox" | "row""checkbox"`'checkbox'` (default): leading selection column. `'row'`: click row to toggle selection.
singleboolean | undefinedfalseSingle-selection mode — `selected` holds at most one key. In `'checkbox'` mode uses `Radio`.
scrollHeightstring | undefinedCSS length (`'400px'`, `'60vh'`). When set, body scrolls with sticky header; unset uses natural flow.
stackedBreakpointstring | undefinedCSS length (`'640px'`). Below this viewport width, switches to stacked card layout.
size"md" | "sm" | "lg""md"Row-density variant.
stripedRowsboolean | undefinedfalseAlternating row background via CSS (selected > hover > stripe precedence).
showGridlinesboolean | undefinedfalseAdds inline-end border to every cell.
resizableColumnsboolean | undefinedfalseAdds a drag handle to every column header's edge for resizing.
frozenColumnsnumber | undefined0Freezes the first N columns sticky-left against horizontal scroll.
rowsnumber | undefinedRows per page. Default: all rows render. Set: internal slicing; pair with `v-model:page`. Also the page-size divisor for `lazy`'s `total`.
manualSortboolean | undefinedfalse`data` is already sorted server-side — DataTable stops sorting it locally and only reflects `v-model:sort`, so a header click tells you what to refetch instead of re-sorting what you gave it.
lazyboolean | undefinedfalse`data` is already just the current page — DataTable stops slicing it locally. Pair with `total` (the real across-all-pages count) so `#footer`/`Pagination` math stays correct.
totalnumber | undefinedReal row count across all pages. Only meaningful with `lazy`; falls back to `sortedData.length` (i.e. `data.length`) when unset.
virtualizeboolean | { itemSize?: number | undefined; overscan?: number | undefined; estimateSize?: number | undefined; } | undefinedWindows rendering to the visible rows + overscan, for very large `data`. Requires `scrollHeight`. `true` measures each row's real height (rows may vary, e.g. wrapping `#cell` content or `stackedBreakpoint`); pass an object to tune it.
motionCssboolean | undefinedtrueGates the built-in row enter/exit/reorder transition (sort, paging, row expansion). `false` skips it entirely — reach for `@row-enter`/`@row-leave` instead if you want a consumer-owned animation (GSAP, motion-v) in its place. No effect while `virtualize` is active: a virtualized list's rows are measured/recycled by height, which a CSS enter/exit transition would fight, so that mode never animates row presence regardless of this prop.
reorderableColumnsboolean | undefinedfalseDrag column headers to reorder them. Pair with `v-model:columnOrder` to control or persist the order.
columnGripVisibility"hover" | "always""always"`'always'` (default): the drag grip is always shown, so a reorderable column reads as such at a glance. `'hover'`: fades in on hover/focus instead, matching the resize handle's own restraint — reach for this once a table has enough reorderable columns that permanent grips would clutter the header.
canDrop((details: SortableDropDetails) => boolean) | undefinedundefinedStructural veto re-run while a column drags; `false` marks the target invalid. A pinned column is already protected regardless of this.
beforeDrop((details: SortableDropDetails) => boolean | Promise<boolean>) | undefinedundefinedAsync gate at drop time for a column reorder — return `false` (or a promise of it) to cancel. Composes with `confirmAction().result` for a confirm-before-move dialog.
previewMode"element" | "clone""clone"`'clone'` (default): a floating copy of the dragged column header follows the cursor, the real `<th>` hidden until drop. `'element'` moves the real header cell itself instead — **don't use this**: a `<th>`'s `:style` binding is keyed by column index, and lifting the real element out to `position: fixed` mid-drag corrupts that binding badly enough that a column can be lost from the DOM entirely on drop. Kept only for interface symmetry with `Sortable`/`Tree`, where it's safe.
touchDragDelaynumber | undefined150Ms a touch pointer must hold a column header still before a drag starts. A sortable column's header is also a tap-to-sort button, so touch needs a hold to tell the two apart; mouse/pen are unaffected. Default `150`.
uiPartial<{ root: UiPartValue; toolbar: UiPartValue; table: UiPartValue; thead: UiPartValue; th: UiPartValue; sortButton: UiPartValue; grip: UiPartValue; resizeHandle: UiPartValue; tbody: UiPartValue; tr: UiPartValue; td: UiPartValue; expansionRow: UiPartValue; expansionContent: UiPartValue; footer: UiPartValue; }> | undefined
pagenumber | undefined1
sort{ field: keyof T | null; dir: "asc" | "desc" | null; } | undefined{ field: null, dir: null }Uncontrolled by default (works exactly as before). Bind `v-model:sort` — required with `manualSort` — to see every header click and know what to refetch.
columnOrder(keyof T)[] | undefined[]Empty means "follow the DOM" (pre-reordering behavior); once a drag sets it, it outranks DOM order so the onUpdated resort below doesn't undo it.

Slots

NameTypeDescription
columns{ Column: TypedColumn; columnData: T[]; }Declare `<Column>` children. `columnData` is the table's `:data`, handed back for type-inference.
toolbar{ selected: Set<string | number>; count: number; }Toolbar content (search, bulk actions, …).
loadinganyReplaces row area while `loading` is true.
emptyanyReplaces row area when data is empty and not loading.
footer{ data: T[]; page: number; pageCount: number; total: number; }Footer content (pagination, …). `data` is sorted (not paginated); `page`/`pageCount` are always provided.
expansion{ row: T; }Full-width row beneath an expanded row. In stacked mode, always renders (no toggle).

Events

NameTypeDescription
reach-end[]
update:selection[rows: T[]]
row-click[row: T]
reach-start[]
column-reorder[order: (keyof T)[]]
row-enter[el: Element, done: () => void]
row-leave[el: Element, done: () => void]
drop-error[error: unknown, details: SortableDropDetails]
update:page[value: number]
update:sort[value: { field: keyof T | null; dir: "asc" | "desc" | null; }]
update:columnOrder[value: (keyof T)[]]

Exposed

NameTypeDescription
elunknownType inference unavailable — vue-component-meta cannot resolve defineExpose on this generic component.