Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

@vates/flexi-table-vue

Vue 3 adapter for flexi-table — a flexible, fully-typed data table with sorting, filtering, column visibility, and row grouping.

Install

npm install @vates/flexi-table-vue

Requires Vue ≥ 3.3.

Usage

<script setup lang="ts">
import { DataTable, type ColumnDef } from '@vates/flexi-table-vue'

interface Employee {
  id: number
  name: string
  department: string
  salary: number
}

const COLUMNS: ColumnDef<Employee>[] = [
  { key: 'name', label: 'Name', type: 'string' },
  { key: 'department', label: 'Department', type: 'string', groupable: true },
  {
    key: 'salary',
    label: 'Salary',
    type: 'number',
    format: (v) => Number(v).toLocaleString() + '',
  },
]
</script>

<template>
  <DataTable :data="employees" :columns="COLUMNS" row-key="id" />
</template>

Custom rendering

Use named scoped slots to customize how cells, filter labels, and group headers render.

<DataTable :data="employees" :columns="COLUMNS" row-key="id">
  <!-- Custom table cell -->
  <template #cell-department="{ value, row }">
    <Badge :label="String(value)" />
  </template>

  <!-- Custom filter checklist item -->
  <template #filter-department="{ value }">
    <Badge :label="value" />
  </template>

  <!-- Custom group header value (same slot as cell) -->
  <template #group-department="{ value }">
    <Badge :label="String(value)" />
  </template>
</DataTable>

Slot naming: #cell-{key}, #filter-{key}, #group-{key} where {key} matches the column's key.

#group-{key} applies to group header rows when that column is used for grouping, so values display with the same visual as table cells.

Multi-value (array) columns

A column whose cell value is an array — tags, genres, categories — is detected automatically, no flag required:

  • The filter checklist lists each individual item instead of the stringified whole array ("Action,RPG" becomes separate "Action" and "RPG" entries), and a row matches if it contains any selected item (multiMode: 'or', the default) or all of them (multiMode: 'and').
  • Grouping by an array column fans a row out into one group per item — a row tagged ['Action', 'RPG'] appears under both the "Action" and "RPG" groups. The #group-{key} slot and format receive the single item being grouped on, not the whole array.
  • A row with an empty array (tags: []) is bucketed under a labeled placeholder — (none) by default, customizable via the emptyValue label — instead of a blank checklist entry or an unlabeled group.
  • Cells without a custom #cell-{key} slot or format display the array joined with , .
interface Game {
  id: number
  name: string
  tags: string[]
}

const COLUMNS: ColumnDef<Game>[] = [
  { key: 'name', label: 'Name' },
  { key: 'tags', label: 'Tags', groupable: true }, // no extra config needed
]

Row selection

Pass :selectable="true" to show a checkbox column. The header checkbox selects/deselects the full filtered dataset (all pages at once). Group header checkboxes select/deselect all rows in that group. Both support indeterminate state.

<script setup lang="ts">
const selected = ref<Employee[]>([])
</script>

<template>
  <DataTable
    :data="employees"
    :columns="COLUMNS"
    row-key="id"
    :selectable="true"
    @selection-change="selected = $event"
  />
  <p v-if="selected.length > 0">{{ selected.length }} rows selected</p>
</template>

selectionChange receives the array of currently selected rows that are present in the filtered dataset. Selection uses object identity (Set<TRow>), so it persists across sort/filter changes as long as row references are stable.

DataTable props

Prop Type Default Description
data TRow[] Row data
columns ColumnDef<TRow>[] Column definitions
rowKey keyof TRow & string Unique row identifier
defaultVisibleColumns string[] all Initially visible column keys
labels Partial<DataTableLabels> English UI string overrides
defaultPageSize number 0 (off) Initial rows per page; 0 disables pagination
selectable boolean false Show checkbox column for row selection

All props accept MaybeRefOrGetter — you can pass refs, computed values, or plain values.

Events

Event Payload Description
selectionChange TRow[] Emitted when selection changes; payload is the selected rows present in the filtered dataset

Column definition

interface ColumnDef<TRow extends object> {
  key: keyof TRow & string
  label: string
  type?: 'string' | 'number' | 'date' // controls filter UI; default: 'string'
  width?: number
  format?: (value: unknown, row: TRow) => string
  sortable?: boolean // default: true
  filterable?: boolean // default: true
  groupable?: boolean // default: false
  multiMode?: 'and' | 'or' // match mode for array-valued columns; default: 'or'
}

For custom rendering, provide a #cell-{key} slot instead of a render function.

useTableState composable

If you need to build a custom layout, use the composable directly:

import { useTableState } from '@vates/flexi-table-vue'

const {
  // Reactive state (refs)
  visibleCols,
  sorts,
  filters,
  rangeFilters,
  groupBy,
  collapsedGroups,
  page,
  pageSize,
  selection, // ShallowRef<Set<TRow>> — use selection.value.has(row) to check membership
  // Computed
  processedData,
  pagedData,
  groupedData,
  activeColumns,
  stringValueMap,
  activeFilterCount,
  numPages,
  selectedRows,
  L,
  // Actions
  toggleColVisibility,
  toggleSort,
  toggleFilter,
  setRangeFilter,
  toggleGroup,
  toggleGroupCollapse,
  clearColumnFilter,
  clearSorts,
  clearFilters,
  clearGroups,
  clearAll,
  setPage,
  setPageSize,
  getSortIcon,
  getSortIndex,
  toggleRowSelection, // (row: TRow) => void
  toggleSelectAll, // (rows: TRow[]) => void — selects all if any unselected, else deselects all
  clearSelection, // () => void
} = useTableState(data, columns, options)

data, columns, and options can be refs, computed values, or plain values.

i18n

Use a built-in locale or supply any Partial<DataTableLabels> overrides (shallow-merged over English defaults):

import { LABELS_FR } from '@vates/flexi-table-vue'
<DataTable :labels="LABELS_FR" ... />

Built-in locales: LABELS_EN (default), LABELS_FR, LABELS_ES, LABELS_DE, LABELS_PT.

Theming

All colors are CSS custom properties. Define them in your own stylesheet (typically on :root). Dark mode activates automatically when the OS preference is dark via prefers-color-scheme: dark, and can be forced with a data-theme attribute:

/* in your global stylesheet */
:root {
  --color-background-primary: #ffffff;
  --color-background-secondary: #f7f6f3;
  --color-text-primary: #1a1916;
  --color-text-secondary: #6b6a66;
  /* ... other tokens ... */
}
@media (prefers-color-scheme: dark) {
  :root {
    --color-background-primary: #141413;
    --color-text-primary: #e8e7e4;
    /* ... */
  }
}
[data-theme='dark'] {
  /* same dark values */
}
[data-theme='light'] {
  /* same light values */
}
// Force dark / light / follow OS
document.documentElement.dataset.theme = 'dark'
document.documentElement.dataset.theme = 'light'
delete document.documentElement.dataset.theme

See the vanilla README for the full token reference table.

License

MIT