Usage
The Table component is built on top of TanStack Table and is powered by the useVueTable composable to provide a flexible and fully type-safe API. Some features of TanStack Table are not supported yet, we'll add more over time.
| # | Date | Status | Amount | |||
|---|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | ||
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | ||
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | ||
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | ||
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 | ||
| #4595 | Mar 10, 13:40 | refunded | ava.thomas@example.com | €428.00 | ||
| #4594 | Mar 10, 09:15 | paid | michael.wilson@example.com | €683.00 | ||
| #4593 | Mar 9, 20:25 | failed | olivia.taylor@example.com | €947.00 | ||
| #4592 | Mar 9, 18:45 | paid | benjamin.jackson@example.com | €851.00 | ||
| #4591 | Mar 9, 16:05 | paid | sophia.miller@example.com | €762.00 | ||
| #4590 | Mar 9, 14:20 | paid | noah.clark@example.com | €573.00 | ||
| #4589 | Mar 9, 11:35 | failed | isabella.lee@example.com | €389.00 | ||
| #4588 | Mar 8, 22:50 | refunded | liam.walker@example.com | €701.00 | ||
| #4587 | Mar 8, 20:15 | paid | charlotte.hall@example.com | €856.00 | ||
| #4586 | Mar 8, 17:40 | paid | mason.young@example.com | €492.00 | ||
| #4585 | Mar 8, 14:55 | failed | amelia.king@example.com | €637.00 | ||
| #4584 | Mar 8, 12:30 | paid | elijah.wright@example.com | €784.00 | ||
| #4583 | Mar 8, 09:45 | refunded | harper.scott@example.com | €345.00 | ||
| #4582 | Mar 7, 23:10 | paid | evelyn.green@example.com | €918.00 | ||
| #4581 | Mar 7, 20:25 | paid | logan.baker@example.com | €567.00 |
Table component. Check out the source code on GitHub.Data
Use the data prop as an array of objects, the columns will be generated based on the keys of the objects.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | james.anderson@example.com | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | mia.white@example.com | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | william.brown@example.com | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | emma.davis@example.com | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | 639 |
{
"wait": "Loading client-side content..."
}Columns
Use the columns prop as an array of ColumnDef objects with properties like:
accessorKey: The key of the row object to use when extracting the value for the column.header: The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).footer: The footer to display for the column. Works exactly like header, but is displayed under the table.cell: The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).meta: Extra properties for the column.class:td: The classes to apply to thetdelement.th: The classes to apply to thethelement.
style:td: The style to apply to thetdelement.th: The style to apply to thethelement.
In order to render components or other HTML elements, you will need to use the Vue h function inside the header and cell props. This is different from other components that use slots but allows for more flexibility.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
h, you can either use the resolveComponent function or import from #components.Meta
Use the meta prop as an object (TableMeta) to pass properties like:
class:tr: The classes to apply to thetrelement.
style:tr: The style to apply to thetrelement.
| ID | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | Mar 11, 03:30 PM | paid | james.anderson@example.com | $594.00 |
| 4599 | Mar 11, 10:10 AM | failed | mia.white@example.com | $276.00 |
| 4598 | Mar 11, 08:50 AM | refunded | william.brown@example.com | $315.00 |
| 4597 | Mar 10, 07:45 PM | paid | emma.davis@example.com | $529.00 |
| 4596 | Mar 10, 03:55 PM | paid | ethan.harris@example.com | $639.00 |
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.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | james.anderson@example.com | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | mia.white@example.com | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | william.brown@example.com | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | emma.davis@example.com | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | 639 |
{
"wait": "Loading client-side content..."
}Sticky
Use the sticky prop to make the header or footer sticky.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | james.anderson@example.com | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | mia.white@example.com | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | william.brown@example.com | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | emma.davis@example.com | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | 639 |
| 4595 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | 639 |
| 4594 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | 639 |
{
"wait": "Loading client-side content..."
}Examples
With row actions
You can add a new column that renders a DropdownMenu component inside the cell to render row actions.
| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
With expandable rows
You can add a new column that renders a Button component inside the cell to toggle the expandable state of a row using the TanStack Table Expanding APIs.
#expanded slot to render the expanded content which will receive the row as a parameter.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
{
"id": "4599",
"date": "2024-03-11T10:10:00",
"status": "failed",
"email": "mia.white@example.com",
"amount": 276
} | |||||
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 | |
expanded prop to control the expandable state of the rows (can be binded with v-model).DropdownMenu component inside the actions column.With grouped rows
You can group rows based on a given column value and show/hide sub rows via some button added to the cell using the TanStack Table Grouping APIs.
Important parts:
- Add
groupingprop with an array of column ids you want to group by. - Add
grouping-optionsprop. It must includegetGroupedRowModel, you can import it from@tanstack/vue-tableor implement your own. - Expand rows via
row.toggleExpanded()method on any cell of the row. Keep in mind, it also toggles#expandedslot. - Use
aggregateFnon column definition to define how to aggregate the rows. agregatedCellrenderer on column definition only works if there is nocellrenderer.
| Item | # | Date | Amount | |
|---|---|---|---|---|
Account 1 | 3 records | Mar 11, 15:30 | 3 customers | €1,548.00 |
Account 2 | 2 records | Mar 11, 10:10 | 2 customers | €805.00 |
With row selection
You can add a new column that renders a Checkbox component inside the header and cell to select rows using the TanStack Table Row Selection APIs.
| Date | Status | Amount | ||
|---|---|---|---|---|
| Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
| Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
row-selection prop to control the selection state of the rows (can be binded with v-model).With row select event
You can add a @select listener to make rows clickable with or without a checkbox column.
Event and TableRow instance as the first and second arguments respectively.| Date | Status | Amount | ||
|---|---|---|---|---|
| Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
| Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
With row context menu event
You can add a @contextmenu listener to make rows right clickable and wrap the Table in a ContextMenu component to display row actions for example.
Event and TableRow instance as the first and second arguments respectively.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
With row hover event
You can add a @hover listener to make rows hoverable and use a Popover or a Tooltip component to display row details for example.
Event and TableRow instance as the first and second arguments respectively.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
refDebounced to prevent the Popover from opening and closing too quickly when moving the cursor from one row to another.With column footer
You can add a footer property to the column definition to render a footer for the column.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
Total: €2,353.00 |
With column sorting
You can update a column header to render a Button component inside the header to toggle the sorting state using the TanStack Table Sorting APIs.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
sorting prop to control the sorting state of the columns (can be binded with v-model).You can also create a reusable component to make any column header sortable.
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
With column pinning
You can update a column header to render a Button component inside the header to toggle the pinning state using the TanStack Table Pinning APIs.
size values for your columns to ensure proper column width handling, especially with multiple pinned columns.| #4600000000000000000000000000000000000000 | 2024-03-11T15:30:00 | paid | james.anderson@example.com | €594,000.00 |
| #4599000000000000000000000000000000000000 | 2024-03-11T10:10:00 | failed | mia.white@example.com | €276,000.00 |
| #4598000000000000000000000000000000000000 | 2024-03-11T08:50:00 | refunded | william.brown@example.com | €315,000.00 |
| #4597000000000000000000000000000000000000 | 2024-03-10T19:45:00 | paid | emma.davis@example.com | €5,290,000.00 |
| #4596000000000000000000000000000000000000 | 2024-03-10T15:55:00 | paid | ethan.harris@example.com | €639,000.00 |
column-pinning prop to control the pinning state of the columns (can be binded with v-model).With column visibility
You can use a DropdownMenu component to toggle the visibility of the columns using the TanStack Table Column Visibility APIs.
| Date | Status | Amount | |
|---|---|---|---|
| Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
| Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
| Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
column-visibility prop to control the visibility state of the columns (can be binded with v-model).With column filters
You can use an Input component to filter per column the rows using the TanStack Table Column Filtering APIs.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | james.anderson@example.com | €594.00 |
column-filters prop to control the filters state of the columns (can be binded with v-model).With global filter
You can use an Input component to filter the rows using the TanStack Table Global Filtering APIs.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4599 | Mar 11, 10:10 | failed | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | william.brown@example.com | €315.00 |
| #4597 | Mar 10, 19:45 | paid | emma.davis@example.com | €529.00 |
| #4596 | Mar 10, 15:55 | paid | ethan.harris@example.com | €639.00 |
global-filter prop to control the global filter state (can be binded with v-model).With pagination
You can use a Pagination component to control the pagination state using the Pagination APIs.
There are different pagination approaches as explained in Pagination Guide. In this example, we use client-side pagination so we need to manually pass getPaginationRowModel() function.
| # | Date | Amount | |
|---|---|---|---|
| #4600 | Mar 11, 15:30 | james.anderson@example.com | €594.00 |
| #4599 | Mar 11, 10:10 | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | william.brown@example.com | €315.00 |
| #4597 | Mar 10, 19:45 | emma.davis@example.com | €529.00 |
| #4596 | Mar 10, 15:55 | ethan.harris@example.com | €639.00 |
pagination prop to control the pagination state (can be binded with v-model).With fetched data
You can fetch data from an API and use them in the Table.
| ID | Name | Company | |
|---|---|---|---|
| No data | |||
With infinite scroll
If you use server-side pagination, you can use the useInfiniteScroll composable to load more data when scrolling.
| ID | Avatar | First name | Username | |
|---|---|---|---|---|
| No data | ||||
With drag and drop
You can use the useSortable composable from @vueuse/integrations to enable drag and drop functionality on the Table. This integration wraps Sortable.js to provide a seamless drag and drop experience.
:b24ui prop to target it with useSortable (e.g. :b24ui="{ tbody: 'my-table-tbody' }").| # | Date | Amount | |
|---|---|---|---|
| #4600 | Mar 11, 15:30 | james.anderson@example.com | €594.00 |
| #4599 | Mar 11, 10:10 | mia.white@example.com | €276.00 |
| #4598 | Mar 11, 08:50 | william.brown@example.com | €315.00 |
| #4597 | Mar 10, 19:45 | emma.davis@example.com | €529.00 |
With virtualization
Use the virtualize prop to enable virtualization for large datasets as a boolean or an object with options like { estimateSize: 65, overscan: 12 }. You can also pass other TanStack Virtual options to customize the virtualization behavior.
| # | Date | Status | Amount | |
|---|---|---|---|---|
class="h-[400px]").With tree data
You can use the get-sub-rows prop to display hierarchical (tree) data in the table.
For example, if your data objects have a children array, set :get-sub-rows="row => row.children" to enable expandable rows.
| # | Date | Amount | ||
|---|---|---|---|---|
4600 | Mar 11, 15:30 | james.anderson@example.com | €594.00 | |
4599 | Mar 11, 10:10 | mia.white@example.com | €276.00 | |
4598 | Mar 11, 08:50 | william.brown@example.com | €315.00 | |
4597 | Mar 10, 19:45 | emma.davis@example.com | €529.00 | |
4589 | Mar 9, 11:35 | isabella.lee@example.com | €389.00 | |
With slots
You can use slots to customize the header and data cells of the table.
Use the #<column>-header slot to customize the header of a column. You will have access to the column, header and table properties in the slot scope.
Use the #<column>-cell slot to customize the cell of a column. You will have access to the cell, column, getValue, renderValue, row, and table properties in the slot scope.
| ID | Name | Role | ||
|---|---|---|---|---|
| 1 | Lindsay Walton Front-end Developer | lindsay.walton@example.com | Member | |
| 2 | Courtney Henry Designer | courtney.henry@example.com | Admin | |
| 3 | Tom Cook Director of Product | tom.cook@example.com | Member | |
| 4 | Whitney Francis Copywriter | whitney.francis@example.com | Admin | |
| 5 | Leonard Krasner Senior Designer | leonard.krasner@example.com | Owner | |
| 6 | Floyd Miles Principal Designer | floyd.miles@example.com | Member |
API
Props
Slots
Expose
You can access the typed component instance using useTemplateRef.
<script setup lang="ts">
const table = useTemplateRef('table')
</script>
<template>
<B24Table ref="table" />
</template>
This will give you access to the following:
Theme
export default defineAppConfig({
b24ui: {
table: {
slots: {
root: 'relative overflow-auto',
base: 'min-w-full font-[family-name:var(--ui-font-family-primary)]',
caption: 'sr-only',
thead: 'relative',
tbody: '[&>tr]:data-[selectable=true]:hover:bg-(--ui-color-bg-content-secondary) light:[&>tr]:data-[selectable=true]:hover:bg-[#f6f8f9] [&>tr]:data-[selectable=true]:focus-visible:outline-(--ui-color-accent-soft-element-blue) [&>tr]:data-[selected=true]:hover:bg-(--ui-color-bg-content-secondary) light:[&>tr]:data-[selected=true]:hover:bg-(#eff7d7) [&>tr]:data-[selected=true]:focus-visible:outline-(--ui-color-accent-soft-element-blue)',
tfoot: 'relative',
tr: 'data-[selected=true]:bg-(--ui-color-bg-content-tertiary) light:data-[selected=true]:bg-[#f4fcde]',
th: 'px-4 py-3.5 text-(length:--ui-font-size-md)/(--ui-font-line-height-md) text-(--b24ui-typography-label-color) whitespace-nowrap text-left rtl:text-right font-(--ui-font-weight-normal) [&:has([role=checkbox])]:pe-0 align-middle',
td: 'p-4 text-(length:--ui-font-size-md)/(--ui-font-line-height-md) text-(--b24ui-typography-label-color) whitespace-nowrap font-(--ui-font-weight-normal) [&:has([role=checkbox])]:pe-0 align-middle',
separator: 'absolute z-[1] left-0 w-full h-px bg-(--ui-color-design-tinted-na-stroke)',
empty: 'py-6 text-center text-(length:--ui-font-size-sm) text-(--b24ui-typography-description-color)',
loading: 'py-6 text-center'
},
variants: {
virtualize: {
true: {
tr: 'relative before:absolute before:z-1 before:w-full before:border-b before:border-b-(--ui-color-design-tinted-na-stroke)'
},
false: {
base: 'overflow-clip',
tbody: 'divide-y divide-(--ui-color-design-tinted-na-stroke)'
}
},
pinned: {
true: {
th: 'sticky bg-(--ui-color-bg-content-primary) z-[1]',
td: 'sticky bg-(--ui-color-bg-content-primary) z-[1]'
}
},
sticky: {
true: {
thead: 'sticky top-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur',
tfoot: 'sticky bottom-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
},
header: {
thead: 'sticky top-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
},
footer: {
tfoot: 'sticky bottom-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
}
},
loading: {
true: {
thead: 'after:absolute after:z-[1] after:h-px after:bg-(--b24ui-background)'
}
},
loadingAnimation: {
loading: '',
carousel: '',
'carousel-inverse': '',
swing: '',
elastic: ''
},
loadingColor: {
'air-primary': {
root: 'style-filled'
},
'air-primary-success': {
root: 'style-filled-success'
},
'air-primary-alert': {
root: 'style-filled-alert'
},
'air-primary-copilot': {
root: 'style-filled-copilot'
},
'air-primary-warning': {
root: 'style-filled-warning'
}
}
},
compoundVariants: [
{
loading: true,
loadingAnimation: 'loading',
class: {
thead: 'after:animate-[progressbar-loading_2s_infinite]'
}
},
{
loading: true,
loadingAnimation: 'carousel',
class: {
thead: 'after:animate-[carousel_2s_ease-in-out_infinite] rtl:after:animate-[carousel-rtl_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'carousel-inverse',
class: {
thead: 'after:animate-[carousel-inverse_2s_ease-in-out_infinite] rtl:after:animate-[carousel-inverse-rtl_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'swing',
class: {
thead: 'after:animate-[swing_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'elastic',
class: {
thead: 'after:animate-[elastic_2s_ease-in-out_infinite]'
}
}
],
defaultVariants: {
loadingColor: 'air-primary',
loadingAnimation: 'loading'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import bitrix24UIPluginVite from '@bitrix24/b24ui-nuxt/vite'
export default defineConfig({
plugins: [
vue(),
bitrix24UIPluginVite({
b24ui: {
table: {
slots: {
root: 'relative overflow-auto',
base: 'min-w-full font-[family-name:var(--ui-font-family-primary)]',
caption: 'sr-only',
thead: 'relative',
tbody: '[&>tr]:data-[selectable=true]:hover:bg-(--ui-color-bg-content-secondary) light:[&>tr]:data-[selectable=true]:hover:bg-[#f6f8f9] [&>tr]:data-[selectable=true]:focus-visible:outline-(--ui-color-accent-soft-element-blue) [&>tr]:data-[selected=true]:hover:bg-(--ui-color-bg-content-secondary) light:[&>tr]:data-[selected=true]:hover:bg-(#eff7d7) [&>tr]:data-[selected=true]:focus-visible:outline-(--ui-color-accent-soft-element-blue)',
tfoot: 'relative',
tr: 'data-[selected=true]:bg-(--ui-color-bg-content-tertiary) light:data-[selected=true]:bg-[#f4fcde]',
th: 'px-4 py-3.5 text-(length:--ui-font-size-md)/(--ui-font-line-height-md) text-(--b24ui-typography-label-color) whitespace-nowrap text-left rtl:text-right font-(--ui-font-weight-normal) [&:has([role=checkbox])]:pe-0 align-middle',
td: 'p-4 text-(length:--ui-font-size-md)/(--ui-font-line-height-md) text-(--b24ui-typography-label-color) whitespace-nowrap font-(--ui-font-weight-normal) [&:has([role=checkbox])]:pe-0 align-middle',
separator: 'absolute z-[1] left-0 w-full h-px bg-(--ui-color-design-tinted-na-stroke)',
empty: 'py-6 text-center text-(length:--ui-font-size-sm) text-(--b24ui-typography-description-color)',
loading: 'py-6 text-center'
},
variants: {
virtualize: {
true: {
tr: 'relative before:absolute before:z-1 before:w-full before:border-b before:border-b-(--ui-color-design-tinted-na-stroke)'
},
false: {
base: 'overflow-clip',
tbody: 'divide-y divide-(--ui-color-design-tinted-na-stroke)'
}
},
pinned: {
true: {
th: 'sticky bg-(--ui-color-bg-content-primary) z-[1]',
td: 'sticky bg-(--ui-color-bg-content-primary) z-[1]'
}
},
sticky: {
true: {
thead: 'sticky top-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur',
tfoot: 'sticky bottom-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
},
header: {
thead: 'sticky top-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
},
footer: {
tfoot: 'sticky bottom-0 inset-x-0 bg-(--ui-color-bg-content-primary) z-[2] backdrop-blur'
}
},
loading: {
true: {
thead: 'after:absolute after:z-[1] after:h-px after:bg-(--b24ui-background)'
}
},
loadingAnimation: {
loading: '',
carousel: '',
'carousel-inverse': '',
swing: '',
elastic: ''
},
loadingColor: {
'air-primary': {
root: 'style-filled'
},
'air-primary-success': {
root: 'style-filled-success'
},
'air-primary-alert': {
root: 'style-filled-alert'
},
'air-primary-copilot': {
root: 'style-filled-copilot'
},
'air-primary-warning': {
root: 'style-filled-warning'
}
}
},
compoundVariants: [
{
loading: true,
loadingAnimation: 'loading',
class: {
thead: 'after:animate-[progressbar-loading_2s_infinite]'
}
},
{
loading: true,
loadingAnimation: 'carousel',
class: {
thead: 'after:animate-[carousel_2s_ease-in-out_infinite] rtl:after:animate-[carousel-rtl_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'carousel-inverse',
class: {
thead: 'after:animate-[carousel-inverse_2s_ease-in-out_infinite] rtl:after:animate-[carousel-inverse-rtl_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'swing',
class: {
thead: 'after:animate-[swing_2s_ease-in-out_infinite]'
}
},
{
loading: true,
loadingAnimation: 'elastic',
class: {
thead: 'after:animate-[elastic_2s_ease-in-out_infinite]'
}
}
],
defaultVariants: {
loadingColor: 'air-primary',
loadingAnimation: 'loading'
}
}
}
})
]
})