feat(Table): conditionally apply classes to tr and td (#3866)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
andr35
2025-04-22 21:14:57 +02:00
committed by GitHub
parent d710141a1b
commit 80dfa88ea4
2 changed files with 41 additions and 7 deletions

View File

@@ -105,6 +105,13 @@ highlights:
When rendering components with `h`, you can either use the `resolveComponent` function or import from `#components`.
::
### Meta
Use the `meta` prop as an object ([TableMeta](https://tanstack.com/table/latest/docs/api/core/table#meta)) to pass properties like:
- `class`:
- `tr`: [The classes to apply to the `tr` element.]{class="text-muted"}
### Loading
Use the `loading` prop to display a loading state, the `loading-color` prop to change its color and the `loading-animation` prop to change its animation.

View File

@@ -2,7 +2,7 @@
<script lang="ts">
import type { Ref } from 'vue'
import type { AppConfig } from '@nuxt/schema'
import type { RowData } from '@tanstack/table-core'
import type { Cell, Header, RowData, TableMeta } from '@tanstack/table-core'
import type {
CellContext,
ColumnDef,
@@ -39,13 +39,20 @@ import theme from '#build/ui/table'
import type { ComponentConfig } from '../types/utils'
declare module '@tanstack/table-core' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface ColumnMeta<TData extends RowData, TValue> {
class?: {
th?: string
td?: string
th?: string | ((cell: Header<TData, TValue>) => string)
td?: string | ((cell: Cell<TData, TValue>) => string)
}
}
interface TableMeta<TData> {
class?: {
tr?: string | ((row: Row<TData>) => string)
}
}
}
type Table = ComponentConfig<typeof theme, AppConfig, 'table'>
@@ -69,6 +76,7 @@ export interface TableProps<T extends TableData> extends TableOptions<T> {
data?: T[]
columns?: TableColumn<T>[]
caption?: string
meta?: TableMeta<T>
/**
* The text to display when the table is empty.
* @defaultValue t('table.noData')
@@ -184,6 +192,7 @@ const appConfig = useAppConfig() as Table['AppConfig']
const data = computed(() => props.data ?? [])
const columns = computed<TableColumn<T>[]>(() => props.columns ?? Object.keys(data.value[0] ?? {}).map((accessorKey: string) => ({ accessorKey, header: upperFirst(accessorKey) })))
const meta = computed(() => props.meta ?? {})
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.table || {}) })({
sticky: props.sticky,
@@ -212,6 +221,7 @@ const tableApi = useVueTable({
...reactiveOmit(props, 'as', 'data', 'columns', 'caption', 'sticky', 'loading', 'loadingColor', 'loadingAnimation', 'class', 'ui'),
data,
columns: columns.value,
meta: meta.value,
getCoreRowModel: getCoreRowModel(),
...(props.globalFilterOptions || {}),
onGlobalFilterChange: updaterOrValue => valueUpdater(updaterOrValue, globalFilterState),
@@ -326,7 +336,13 @@ defineExpose({
:key="header.id"
:data-pinned="header.column.getIsPinned()"
:colspan="header.colSpan > 1 ? header.colSpan : undefined"
:class="ui.th({ class: [props.ui?.th, header.column.columnDef.meta?.class?.th], pinned: !!header.column.getIsPinned() })"
:class="ui.th({
class: [
props.ui?.th,
typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th
],
pinned: !!header.column.getIsPinned()
})"
>
<slot :name="`${header.id}-header`" v-bind="header.getContext()">
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
@@ -344,14 +360,25 @@ defineExpose({
:data-expanded="row.getIsExpanded()"
:role="props.onSelect ? 'button' : undefined"
:tabindex="props.onSelect ? 0 : undefined"
:class="ui.tr({ class: [props.ui?.tr] })"
:class="ui.tr({
class: [
props.ui?.tr,
typeof tableApi.options.meta?.class?.tr === 'function' ? tableApi.options.meta.class.tr(row) : tableApi.options.meta?.class?.tr
]
})"
@click="handleRowSelect(row, $event)"
>
<td
v-for="cell in row.getVisibleCells()"
:key="cell.id"
:data-pinned="cell.column.getIsPinned()"
:class="ui.td({ class: [props.ui?.td, cell.column.columnDef.meta?.class?.td], pinned: !!cell.column.getIsPinned() })"
:class="ui.td({
class: [
props.ui?.td,
typeof cell.column.columnDef.meta?.class?.td === 'function' ? cell.column.columnDef.meta.class.td(cell) : cell.column.columnDef.meta?.class?.td
],
pinned: !!cell.column.getIsPinned()
})"
>
<slot :name="`${cell.column.id}-cell`" v-bind="cell.getContext()">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />