๐ข Table
Basic
use NTable
component to create a powerful table and datagrids built using Tanstack. Read more about the Tanstack Table documentation.
Prop | Type | Default | Description |
---|---|---|---|
columns | Array | [] | Table columns. |
data | Array | [] | Table data. |
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Candida | Stoltenberg | 18 | 40 | complicated | 25 |
Annetta | Kiehn | 2 | 529 | complicated | 95 |
Arianna | Gleason | 25 | 329 | relationship | 44 |
Rebekah | Borer | 25 | 74 | single | 28 |
Rey | Satterfield | 0 | 548 | relationship | 39 |
Row Selection
Row selection allows you to select rows in the table. This is useful when you want to select rows in the table. Read more about row selection in the Tanstack Row Selection documentation.
Prop | Type | Default | Description |
---|---|---|---|
modelValue | Array | [] | Selected rows. |
enableRowSelection | Boolean | false | Enable row selection. |
enableMultiRowSelection | Boolean | false | Enable multiple row selection. |
rowId | String | id | Row id to uniquely identify each row. |
enableSubRowSelection | Boolean | false | Enable sub row selection. |
@select | Event | Emitted when a row is selected. | |
@select-all | Event | Emitted when all rows are selected. |
First Name | Last Name | Age | Visits | Status | Profile Progress | |
---|---|---|---|---|---|---|
Tyrell | Erdman-Dickinson | 27 | 484 | complicated | 64 | |
Darrion | Adams | 4 | 729 | single | 95 | |
Lorine | Huel | 2 | 750 | relationship | 27 | |
Felipa | Jakubowski-Hickle | 6 | 826 | complicated | 80 | |
Ben | Stroman | 10 | 864 | complicated | 96 | |
Delphine | Quigley | 34 | 104 | single | 56 | |
Caterina | Cummerata | 9 | 920 | single | 88 | |
Orlando | Nolan | 16 | 721 | relationship | 28 | |
Jakayla | Bernier | 16 | 442 | single | 76 | |
Kira | Mills | 20 | 673 | relationship | 67 | |
Loading
Loading allows you to show a loading progress indicator in the table. This is useful when you want to show a loading progress indicator in the table.
Prop | Type | Default | Description |
---|---|---|---|
loading | Boolean | false | Loading state. |
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Roberto | Fadel | 35 | 54 | single | 51 |
Stephanie | Bahringer | 29 | 19 | single | 70 |
Andreanne | Bogisich | 36 | 664 | complicated | 17 |
Darrel | Wolf | 23 | 618 | relationship | 79 |
Davin | Pagac | 23 | 718 | relationship | 95 |
Pagination
Pagination allows you to paginate rows in the table. This is useful when you want to paginate rows in the table. Read more about pagination in the Tanstack Pagination documentation.
Prop | Type | Default | Description |
---|---|---|---|
v-model:pagination | Object | {pageIndex: 0, pageSize: 10} | Pagination default configuration. |
manualPagination | Boolean | false | Enable manual pagination. |
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Grant | Mills | 10 | 512 | relationship | 24 |
Veronica | Wintheiser | 29 | 260 | relationship | 18 |
Desiree | Bogan | 21 | 92 | single | 73 |
Durward | Fritsch | 35 | 306 | complicated | 87 |
Pierce | Kilback | 37 | 349 | single | 52 |
Sorting
Sorting allows you to sort columns in ascending or descending order. This is useful when you want to sort columns in the table. Read more about sorting in the Tanstack Sorting documentation.
Status | |||||
---|---|---|---|---|---|
Bennett | Skiles | 39 | 176 | single | 68 |
Jaron | Rohan | 2 | 812 | relationship | 80 |
Angela | Rohan | 27 | 948 | single | 36 |
Clotilde | Jacobi | 27 | 922 | complicated | 42 |
Adah | Stehr | 24 | 421 | single | 95 |
Cloyd | Wintheiser | 24 | 707 | single | 11 |
Ryley | Bernier-Schimmel | 22 | 131 | relationship | 33 |
Reta | Koelpin | 25 | 753 | complicated | 33 |
Donato | Hartmann | 7 | 629 | relationship | 26 |
Daisy | Botsford | 22 | 712 | relationship | 21 |
Visibility
Visibility allows you to show or hide columns in the table. This is useful when you want to show or hide columns in the table. Read more about visibility in the Tanstack Visibility documentation.
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Steve | Kovacek | 5 | 416 | complicated | 39 |
Wyman | Pfeffer | 13 | 340 | complicated | 2 |
Cortney | Buckridge | 38 | 451 | relationship | 49 |
Delia | Marquardt-Stamm | 13 | 47 | relationship | 45 |
Elias | Lueilwitz | 7 | 227 | relationship | 86 |
Global Filtering
Global filtering allows you to filter rows based on the value entered in the filter input. This is useful when you want to filter rows in the table. Read more about global filtering in the Tanstack Global Filtering documentation.
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Zoila | Bruen | 35 | 805 | complicated | 1 |
Ladarius | Dooley-Welch | 24 | 735 | single | 4 |
Ray | Ward | 15 | 606 | complicated | 10 |
Barton | Gutmann | 3 | 748 | single | 35 |
Maximillia | Bayer | 28 | 42 | relationship | 87 |
Geovany | Stoltenberg | 39 | 668 | complicated | 94 |
Maximillian | Cremin | 9 | 805 | complicated | 87 |
Linnie | Pollich | 30 | 596 | single | 6 |
Tyson | Ruecker | 20 | 626 | relationship | 39 |
Garnett | MacGyver | 8 | 882 | single | 30 |
Column Filtering
Filtering allows you to filter columns based on the value entered in the filter input. This is useful when you want to filter columns in the table. Read more about filtering in the Tanstack Column Filtering documentation.
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Jovan | Schultz | 24 | 749 | complicated | 17 |
Gerson | Shields | 10 | 600 | single | 80 |
Eliane | Kessler | 27 | 188 | complicated | 0 |
Taurean | Schmitt | 27 | 23 | single | 57 |
Theodore | Olson | 37 | 744 | single | 84 |
Column Ordering
Column ordering allows you to reorder columns by dragging and dropping them. This is useful when you want to change the order of columns in the table. Read more about column ordering in the Tanstack Column Ordering documentation.
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Waylon | Heller | 23 | 90 | complicated | 5 |
Lorenzo | Wyman | 14 | 899 | complicated | 15 |
Isom | Nienow | 39 | 515 | complicated | 73 |
Agustina | Quitzon | 13 | 445 | relationship | 84 |
Kenton | Bashirian | 31 | 168 | complicated | 55 |
Column Pinning
Column pinning allows you to pin columns to the left
or right
of the table. This is useful when you have a large number of columns and you want to keep some columns in view while scrolling. Read more about column pinning in the Tanstack Column Pinning documentation.
Status | First Name | Last Name | Age | Visits | Profile Progress |
---|---|---|---|---|---|
complicated | Lura | Reichel | 15 | 805 | 84 |
relationship | Jerad | Daniel | 18 | 212 | 49 |
relationship | Ned | Macejkovic | 7 | 863 | 45 |
relationship | Zella | Kulas | 15 | 733 | 11 |
complicated | Ila | White | 38 | 111 | 51 |
Expandanding
Expanding allows you to expand rows to show additional information. This is useful when you want to show additional information about a row. Read more about expanding in the Tanstack Expanding documentation.
First Name | Last Name | Age | Visits | Status | Profile Progress | |
---|---|---|---|---|---|---|
Millie | Emmerich | 15 | 346 | relationship | 18 | |
Agnes | Conroy | 13 | 993 | single | 99 | |
Norma | Schmeler | 11 | 645 | single | 7 | |
Agustin | Harber | 25 | 261 | single | 31 | |
Katarina | Waelchi | 1 | 763 | complicated | 50 | |
Grouping
Grouping allows you to group rows based on a column value. This is useful when you want to group rows in the table. Read more about grouping in the Tanstack Grouping documentation.
Name | Info | ||||
---|---|---|---|---|---|
First Name | Last Name | Age | Visits | Status | Profile Progress |
Deion | Hermann | 19 | 938 | relationship | 72 |
Jillian | Frami | 21 | 993 | single | 48 |
Bailee | Mraz | 25 | 668 | relationship | 17 |
Hosea | Bogisich | 18 | 352 | single | 83 |
Adell | Trantow | 24 | 942 | relationship | 41 |
Server-side
Server-side allows you to fetch data from the server. This is useful when you want to fetch data from the server. Read more about server-side in the Tanstack Server-side documentation.
Name | Url |
---|---|
bulbasaur | https://pokeapi.co/api/v2/pokemon/1/ |
ivysaur | https://pokeapi.co/api/v2/pokemon/2/ |
venusaur | https://pokeapi.co/api/v2/pokemon/3/ |
charmander | https://pokeapi.co/api/v2/pokemon/4/ |
charmeleon | https://pokeapi.co/api/v2/pokemon/5/ |
Customization
You can customize the table by using the following props.
Prop | Type | Default | Description |
---|---|---|---|
columns.meta.meta | Object | {} | Column meta data. |
una | Object | {} | Unique name attribute. |
First Name | Last Name | Age | Visits | Status | Profile Progress |
---|---|---|---|---|---|
Dexter | Bayer | 27 | 487 | relationship | 96 |
Jacinto | Roob | 29 | 584 | complicated | 75 |
Dorian | Mraz | 37 | 297 | complicated | 94 |
Aylin | Mitchell | 12 | 799 | single | 37 |
Rodolfo | Hammes | 14 | 833 | complicated | 89 |
Slots
You can use the following slots to customize the table.
Name | Description | Props |
---|---|---|
{column}-filter | Column filter slot. | column |
{column}-header | Column header slot. | column |
{column}-cell | Column cell slot. | cell |
{column}-footer | Column footer slot. | column |
header | Header slot. | table |
body | Body slot. | table |
raw | Row slot. | row |
footer | Footer slot. | table |
expanded | Expanded slot. | row |
empty | Empty slot. | |
loading | Loading slot. |
Account | |||||
---|---|---|---|---|---|
MR Magali Rohan Joan24@yahoo.com | Magali | Rohan | single | 10% | |
TH Tina Hoppe Eliza.Berge30@yahoo.com | Tina | Hoppe | relationship | 60% | |
CS Clara Shields Ima_Corwin29@gmail.com | Clara | Shields | single | 18% | |
MG Maiya Glover Libbie8@gmail.com | Maiya | Glover | relationship | 61% | |
RL Rosalia Langosh Aniya15@hotmail.com | Rosalia | Langosh | complicated | 3% | |
CM Ciara Mills Osborne58@hotmail.com | Ciara | Mills | relationship | 66% | |
JL Justus Lang-Brown Beau_Fahey87@yahoo.com | Justus | Lang-Brown | relationship | 5% | |
GO Gilberto Oberbrunner Mariano95@yahoo.com | Gilberto | Oberbrunner | relationship | 57% | |
GS Geovany Schimmel Meda81@gmail.com | Geovany | Schimmel | relationship | 5% | |
AB Alexa Borer Khalid.Bernier99@hotmail.com | Alexa | Borer | single | 39% | |
Props
import type {
ColumnDef,
GroupColumnDef,
} from '@tanstack/vue-table'
import type { HTMLAttributes } from 'vue'
export interface NTableProps<TData, TValue> extends NTableRootProps {
/**
* @see https://tanstack.com/table/latest/docs/guide/data
*/
data: TData[]
/**
* @see https://tanstack.com/table/latest/docs/api/core/column
*/
columns: ColumnDef<TData, TValue>[] | GroupColumnDef<TData, TValue>[]
/**
* @see https://tanstack.com/table/latest/docs/api/core/table#getrowid
*/
rowId?: string
/**
* @see https://tanstack.com/table/latest/docs/api/core/table#autoresetall
*/
autoResetAll?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/row-selection#enablerowselection
*/
enableRowSelection?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/row-selection#enablemultirowselection
*/
enableMultiRowSelection?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/row-selection#enablesubrowselection
*/
enableSubRowSelection?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/column-filtering#enablecolumnfilters
*/
enableColumnFilters?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#enablesorting
*/
enableSorting?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#enablemultisort
*/
enableMultiSort?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#enablemultiremove
*/
enableMultiRemove?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#enablesortingremoval
*/
enableSortingRemoval?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#manualsorting
*/
manualSorting?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#maxmultisortcolcount
*/
maxMultiSortColCount?: number
/**
* @see https://tanstack.com/table/latest/docs/api/features/pagination#manualpagination
*/
manualPagination?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/pagination#pagecount
*/
pageCount?: number
/**
* @see https://tanstack.com/table/latest/docs/api/features/pagination#rowcount
*/
rowCount?: number
/**
* @see https://tanstack.com/table/latest/docs/api/features/pagination#autoresetpageindex
*/
autoResetPageIndex?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#sortingfns
*/
sortingFns?: Record<string, (a: any, b: any) => number>
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#sortdescfirst-1
*/
sortDescFirst?: boolean
/**
* @see https://tanstack.com/table/latest/docs/api/features/sorting#ismultisortevent
*/
isMultiSortEvent?: (e: unknown) => boolean
// sub-components props
_tableHead?: NTableHeadProps
_tableHeader?: NTableHeaderProps
_tableFooter?: NTableFooterProps
_tableBody?: NTableBodyProps
_tableCaption?: NTableCaptionProps
_tableRow?: NTableRowProps
_tableCell?: NTableCellProps
_tableEmpty?: NTableEmptyProps
_tableLoading?: NTableLoadingProps
loading?: boolean
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/table.ts
*/
una?: NTableUnaProps
}
export interface NTableRootProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableRoot' | 'tableRootWrapper'>
}
export interface NTableBodyProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableBody'>
}
export interface NTableHeadProps {
class?: HTMLAttributes['class']
dataPinned?: 'left' | 'right' | false
una?: Pick<NTableUnaProps, 'tableHead'>
}
export interface NTableHeaderProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableHeader'>
}
export interface NTableFooterProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableFooter'>
}
export interface NTableRowProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableRow'>
}
export interface NTableCellProps {
class?: HTMLAttributes['class']
dataPinned?: 'left' | 'right' | false
una?: Pick<NTableUnaProps, 'tableCell'>
}
export interface NTableEmptyProps {
class?: HTMLAttributes['class']
colspan?: number
_tableCell?: NTableCellProps
_tableRow?: NTableRowProps
una?: Pick<NTableUnaProps, 'tableEmpty' | 'tableRow' | 'tableCell'>
}
export interface NTableLoadingProps {
enabled?: boolean
class?: HTMLAttributes['class']
colspan?: number
_tableCell?: NTableCellProps
_tableRow?: NTableRowProps
una?: Pick<NTableUnaProps, 'tableLoading' | 'tableRow' | 'tableCell'>
}
export interface NTableCaptionProps {
class?: HTMLAttributes['class']
una?: Pick<NTableUnaProps, 'tableCaption'>
}
interface NTableUnaProps {
tableRoot?: HTMLAttributes['class']
tableRootWrapper?: HTMLAttributes['class']
tableBody?: HTMLAttributes['class']
tableHead?: HTMLAttributes['class']
tableHeader?: HTMLAttributes['class']
tableFooter?: HTMLAttributes['class']
tableRow?: HTMLAttributes['class']
tableCell?: HTMLAttributes['class']
tableCaption?: HTMLAttributes['class']
tableEmpty?: HTMLAttributes['class']
tableLoading?: HTMLAttributes['class']
}
Presets
type TablePrefix = 'table'
export const staticTable: Record<`${TablePrefix}-${string}` | TablePrefix, string> = {
// config
'table-default-variant': 'table-solid-gray',
'table': '',
// table-root
'table-root': 'w-full caption-bottom text-sm',
'table-root-wrapper': 'relative w-full overflow-x-auto overflow-y-hidden border border-base rounded-md',
'table-body': '[&_tr:last-child]:border-0 border-base',
'table-caption': 'mt-4 text-sm text-muted',
// table-head
'table-head': 'h-12 px-4 text-left align-middle font-medium text-muted [&:has([role=checkbox])]:pr-0',
'table-head-pinned': 'sticky bg-base',
'table-head-pinned-left': 'left-0',
'table-head-pinned-right': 'right-0',
// table-header
'table-header': '[&_tr]:border-b border-base',
// table-row
'table-row': 'border-b border-base hover:bg-muted data-[filter=true]:hover:bg-base data-[state=selected]:bg-muted',
// table-cell
'table-cell': 'p-4 align-middle [&:has([role=checkbox])]:pr-0',
'table-cell-pinned': 'sticky bg-base',
'table-cell-pinned-left': 'left-0',
'table-cell-pinned-right': 'right-0',
// table-empty
'table-empty-row': '',
'table-empty-cell': 'p-4 whitespace-nowrap align-middle text-sm text-muted hover:bg-base',
'table-empty': 'flex items-center justify-center py-10',
// table-loading
'table-loading-icon': 'i-lucide-refresh-ccw animate-spin text-lg',
'table-loading-row': 'data-[loading=true]:border-0',
'table-loading-cell': '',
'table-loading': 'absolute inset-x-0 overflow-hidden p-0',
// table-footer
'table-footer': 'border-t border-base bg-muted font-medium [&>tr]:last:border-b-0',
}
export const dynamicTable: [RegExp, (params: RegExpExecArray) => string][] = [
]
export const table = [
...dynamicTable,
staticTable,
]
Component
<script setup lang="ts" generic="TData, TValue">
import type {
ColumnFiltersState,
ColumnOrderState,
ColumnPinningState,
ExpandedState,
GroupingState,
Header,
PaginationState,
SortingState,
VisibilityState,
} from '@tanstack/vue-table'
import type { Ref } from 'vue'
import type { NTableProps } from '../../../types'
import {
FlexRender,
getCoreRowModel,
getExpandedRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useVueTable,
} from '@tanstack/vue-table'
import { computed, h } from 'vue'
import { cn, pickProps, valueUpdater } from '../../../utils'
import Button from '../../elements/Button.vue'
import Checkbox from '../../forms/Checkbox.vue'
import Input from '../../forms/Input.vue'
import TableBody from './TableBody.vue'
import TableCell from './TableCell.vue'
import TableEmpty from './TableEmpty.vue'
import TableFooter from './TableFooter.vue'
import TableHead from './TableHead.vue'
import TableHeader from './TableHeader.vue'
import TableLoading from './TableLoading.vue'
import TableRoot from './TableRoot.vue'
import TableRow from './TableRow.vue'
const props = withDefaults(defineProps <NTableProps<TData, TValue>>(), {
enableMultiRowSelection: true,
})
const emit = defineEmits(['select', 'selectAll', 'expand'])
const slots = defineSlots()
const rowSelection = defineModel<Record<string, boolean>>('modelValue')
const sorting = defineModel<SortingState>('sorting')
const columnVisibility = defineModel<VisibilityState>('columnVisibility')
const columnFilters = defineModel<ColumnFiltersState>('columnFilters')
const globalFilter = defineModel<string>('globalFilter')
const columnOrder = defineModel<ColumnOrderState>('columnOrder')
const columnPinning = defineModel<ColumnPinningState>('columnPinning')
const expanded = defineModel<ExpandedState>('expanded')
const grouping = defineModel<GroupingState>('grouping')
const pagination = defineModel<PaginationState>('pagination', {
default: () => ({
pageIndex: 0,
pageSize: 10,
}),
})
const columnsWithMisc = computed(() => {
let data = []
// add selection column
data = props.enableRowSelection
? [
{
accessorKey: 'selection',
header: props.enableMultiRowSelection
? ({ table }: any) => h(Checkbox, {
'checked': table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
'onUpdate:checked': (value: boolean) => {
table.toggleAllPageRowsSelected(!!value)
emit('selectAll', table.getRowModel().rows)
},
'areaLabel': 'Select all rows',
})
: '',
cell: ({ row }: any) => h(Checkbox, {
'checked': row.getIsSelected() ?? false,
'onUpdate:checked': (value: boolean) => {
row.toggleSelected(!!value)
emit('select', row)
},
'areaLabel': 'Select row',
}),
enableSorting: false,
enableHiding: false,
},
...props.columns,
]
: props.columns
// add expanded column
data = slots.expanded
? [
{
accessorKey: 'expanded',
header: '',
cell: ({ row }: any) => h(Button, {
size: 'xs',
icon: true,
label: 'i-radix-icons-chevron-down',
onClick: () => {
row.toggleExpanded()
emit('expand', row)
},
una: {
btnDefaultVariant: 'btn-ghost-gray btn-square',
btnIconLabel: cn(
'transform transition-transform duration-200',
row.getIsExpanded() ? '-rotate-180' : 'rotate-0',
),
},
}),
enableSorting: false,
enableHiding: false,
},
...data,
]
: data
return data
})
const table = computed(() => {
return useVueTable({
get data() {
return props.data ?? []
},
get columns() {
return columnsWithMisc.value ?? []
},
state: {
get sorting() { return sorting.value },
get columnFilters() { return columnFilters.value },
get globalFilter() { return globalFilter.value },
get rowSelection() { return rowSelection.value },
get columnVisibility() { return columnVisibility.value },
get pagination() { return pagination.value },
get columnOrder() { return columnOrder.value },
get columnPinning() { return columnPinning.value },
get expanded() { return expanded.value },
get grouping() { return grouping.value },
},
enableMultiRowSelection: props.enableMultiRowSelection,
enableSubRowSelection: props.enableSubRowSelection,
autoResetAll: props.autoResetAll,
enableRowSelection: props.enableRowSelection,
enableColumnFilters: props.enableColumnFilters,
manualPagination: props.manualPagination,
manualSorting: props.manualSorting,
pageCount: props.pageCount,
rowCount: props.rowCount,
autoResetPageIndex: props.autoResetPageIndex,
enableSorting: props.enableSorting,
enableSortingRemoval: props.enableSortingRemoval,
enableMultiSort: props.enableMultiSort,
enableMultiRemove: props.enableMultiRemove,
maxMultiSortColCount: props.maxMultiSortColCount,
sortingFns: props.sortingFns,
isMultiSortEvent: props.isMultiSortEvent,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getRowId: (row: any) => props.rowId ? row[props.rowId] : row.id,
getExpandedRowModel: getExpandedRowModel(),
onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
onRowSelectionChange: updaterOrValue => valueUpdater(updaterOrValue, rowSelection),
onColumnVisibilityChange: updaterOrValue => valueUpdater(updaterOrValue, columnVisibility),
onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
onGlobalFilterChange: updaterOrValue => valueUpdater(updaterOrValue, globalFilter),
onPaginationChange: updaterOrValue => valueUpdater(updaterOrValue, pagination),
onColumnOrderChange: updaterOrValue => valueUpdater(updaterOrValue, columnOrder),
onColumnPinningChange: updaterOrValue => valueUpdater(updaterOrValue, columnPinning),
onExpandedChange: updaterOrValue => valueUpdater(updaterOrValue, expanded),
onGroupingChange: updaterOrValue => valueUpdater(updaterOrValue, grouping),
})
}) as Ref<ReturnType<typeof useVueTable>>
function getHeaderColumnFiltersCount(headers: Header<unknown, unknown>[]): number {
let count = 0
headers.forEach((header) => {
if (header.column.columnDef.enableColumnFilter)
count++
})
return count
}
defineExpose({
...table.value,
})
</script>
<template>
<TableRoot
v-bind="pickProps(props, ['class', 'una'])"
>
<!-- header -->
<TableHeader
:una="una"
v-bind="props._tableHeader"
>
<slot name="header" :table="table">
<TableRow
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
:una="una"
v-bind="props._tableRow"
>
<!-- headers -->
<TableHead
v-for="header in headerGroup.headers"
:key="header.id"
:colspan="header.colSpan"
:data-pinned="header.column.getIsPinned()"
:una="una"
v-bind="{ ...props._tableHead, ...header.column.columnDef.meta }"
>
<Button
v-if="header.column.columnDef.enableSorting || (header.column.columnDef.enableSorting !== false && enableSorting)"
btn="ghost-gray"
size="sm"
class="font-normal -ml-0.85em"
:una="{
btnTrailing: 'text-sm',
}"
:trailing="header.column.getIsSorted() === 'asc'
? 'i-lucide-arrow-up-wide-narrow' : header.column.getIsSorted() === 'desc'
? 'i-lucide-arrow-down-narrow-wide' : 'i-lucide-arrow-up-down'"
@click="header.column.toggleSorting(
header.column.getIsSorted() === 'asc' ? undefined : header.column.getIsSorted() !== 'desc',
enableMultiSort,
)"
>
<slot
:name="`${header.id}-header`"
:column="header.column"
>
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
</slot>
</Button>
<component
:is="header.id === 'selection' ? 'div' : 'span'"
v-else
class="text-sm text-muted"
>
<slot
:name="`${header.id}-header`"
:column="header.column"
>
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
</slot>
</component>
</TableHead>
</TableRow>
<!-- column filters -->
<template
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
>
<TableRow
v-if="getHeaderColumnFiltersCount(headerGroup.headers) > 0 || enableColumnFilters"
data-filter="true"
:una="una"
v-bind="props._tableRow"
>
<TableHead
v-for="header in headerGroup.headers"
:key="header.id"
:una="una"
:colspan="header.colSpan"
:data-pinned="header.column.getIsPinned()"
v-bind="{ ...props._tableHead, ...header.column.columnDef.meta }"
>
<slot
v-if="header.id !== 'selection' && ((header.column.columnDef.enableColumnFilter !== false && enableColumnFilters) || header.column.columnDef.enableColumnFilter)"
:name="`${header.id}-filter`"
:column="header.column"
>
<Input
class="w-auto"
:model-value="header.column.getFilterValue() as string"
:placeholder="header.column.columnDef.header"
@update:model-value="header.column.setFilterValue($event)"
/>
</slot>
</TableHead>
</TableRow>
</template>
</slot>
<TableLoading
:enabled="props.loading"
:una="una"
v-bind="props._tableLoading"
>
<slot name="loading" />
</TableLoading>
</TableHeader>
<!-- body -->
<TableBody
:una="una"
v-bind="props._tableBody"
>
<slot name="body" :table="table">
<template v-if="table.getRowModel().rows?.length">
<template
v-for="row in table.getRowModel().rows"
:key="row.id"
>
<TableRow
:data-state="row.getIsSelected() && 'selected'"
:una="una"
v-bind="props._tableRow"
>
<slot
name="row"
:row="row"
>
<!-- rows -->
<TableCell
v-for="cell in row.getVisibleCells()"
:key="cell.id"
:data-pinned="cell.column.getIsPinned()"
:una="una"
v-bind="{ ...props._tableCell, ...cell.column.columnDef.meta }"
>
<slot
:name="`${cell.column.id}-cell`"
:cell="cell"
>
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</slot>
</TableCell>
</slot>
</TableRow>
<!-- expanded -->
<TableRow
v-if="row.getIsExpanded() && $slots.expanded"
:una="una"
v-bind="props._tableRow"
>
<TableCell
:colspan="row.getAllCells().length"
:una="una"
v-bind="props._tableCell"
>
<slot name="expanded" :row="row" />
</TableCell>
</TableRow>
</template>
</template>
<TableEmpty
v-else
:colspan="table.getAllLeafColumns().length"
:una="una"
v-bind="props._tableEmpty"
>
<slot name="empty" />
</TableEmpty>
</slot>
</TableBody>
<!-- footer -->
<TableFooter
v-if="table.getFooterGroups().length > 0"
:una="una"
v-bind="props._tableFooter"
>
<slot name="footer" :table="table">
<template
v-for="footerGroup in table.getFooterGroups()"
:key="footerGroup.id"
>
<TableRow
v-if="footerGroup.headers.length > 0"
:una="una"
v-bind="props._tableRow"
>
<template
v-for="header in footerGroup.headers"
:key="header.id"
>
<TableHead
v-if="header.column.columnDef.footer"
:colspan="header.colSpan"
:una="una"
v-bind="{ ...props._tableHead, ...header.column.columnDef.meta }"
>
<slot :name="`${header.id}-footer`" :column="header.column">
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.footer"
:props="header.getContext()"
/>
</slot>
</TableHead>
</template>
</TableRow>
</template>
</slot>
</TableFooter>
</TableRoot>
</template>.