DataTable
The VDataTable component is used for displaying tabular data. Features include sorting, searching, pagination, and row selection.
Usage
Basic Usage
To use the VDataTable component, you will need to provide it with an array of items to display and an array of headers, which define the columns of the table.
Here is an example of basic usage of the VDataTable component:
INFO
The VDataTable component is registered globally when you install with @morpheme/ui. So you don't need to import it manually.
Striped
To create a striped table, you can use the striped prop:
This will add alternate background colors to each row of the table, giving it a striped appearance.
Hover
To add a hover effect to the rows of the table, you can use the hover prop:
This will change the background color of the row when the mouse pointer is hovering over it.
Flat
To remove the box shadow and border of the table, you can use the flat prop:
Bordered
To add a border around the table, you can use the bordered prop:
Tile
To remove border radius of the table, you can use the tile prop:
Dense
To make the rows of the table denser, you can use the dense prop:
Loading
To display a loading state for the table, you can use the loading prop. This will display a loading spinner and disable the table until the data is loaded.
Empty
The VDataTable component can display an empty state when the items prop is an empty array. This can be useful if you want to inform the user that there are no data items to display in the table.
This will display a message indicating that the table is empty.
You can also customize the appearance and behavior of the empty state using the following props:
empty-text: Customize the text displayed in the empty state.empty-class: Add a custom class to the empty state element.
For example, to customize the text displayed in the empty state and add a custom class, you can use the following code:
<template>
<VDataTable
:items="[]"
empty-text="No data available"
empty-class="text-red-500"
/>
</template><template>
<VDataTable
:items="[]"
empty-text="No data available"
empty-class="text-red-500"
/>
</template>This will display the empty state with the text "No data available" and the class "text-red-500" applied.
Must Sort
To require that a column be sorted before the table can be displayed, you can use the must-sort prop:
Disable Sorting
To disable sorting for the table, you can use the disable-sorting prop:
Custom Wrapper Class
To add a custom class to the wrapper element of the table, you can use the wrapper-class prop:
Custom Class
To add custom classes to the various components of the table, you can use the following props:
header-class: Add a custom class to the header element.body-class: Add a custom class to the body element.footer-class: Add a custom class to the footer element.column-inactive-class: Add a custom class to the inactive column elements.hover-class: Add a custom class to the hover state of the rows.td-class: Add a custom class to the cells.tr-class: Add a custom class to the rows.
For example, to add custom classes to the header, body, footer, and rows, you can use the following code:
<template>
<VDataTable
:headers="headers"
:items="items"
header-class="bg-blue-600"
body-class="bg-gray-100"
footer-class="bg-gray-100"
column-inactive-class="text-blue-50 hover:text-blue-200"
hover
hover-class="transition duration-300 hover:bg-blue-500 hover:text-white"
td-class="group-hover:text-white"
tr-class="!hover:bg-gray-700 __TR__CLASS__"
/>
</template><template>
<VDataTable
:headers="headers"
:items="items"
header-class="bg-blue-600"
body-class="bg-gray-100"
footer-class="bg-gray-100"
column-inactive-class="text-blue-50 hover:text-blue-200"
hover
hover-class="transition duration-300 hover:bg-blue-500 hover:text-white"
td-class="group-hover:text-white"
tr-class="!hover:bg-gray-700 __TR__CLASS__"
/>
</template>This will add the classes bg-blue-600 to the header element, bg-gray-100 to the body element, bg-gray-100 to the footer element, transition duration-300 hover:bg-blue-500 hover:text-white to the hover state of the rows, group-hover:text-white to the cells, and !hover:bg-gray-700 TR__CLASS to the rows.
Selecting Rows in the DataTable
The VDataTable component provides the selectable prop that allows you to enable row selection in the table. When the selectable prop is set to true, a checkbox will be displayed in the first column of each row, and the user will be able to select multiple rows by clicking on the checkboxes.
To use row selection, you will need to bind the v-model directive to a variable that will store the selected rows. The selected rows will be stored as an array of objects, with each object representing a selected row.
For example, to enable row selection and bind the v-model directive to a selected variable, you can use the following code:
This will enable row selection and bind the selected variable to the v-model directive.
Freezing Columns in the DataTable
The VDataTable component allows you to freeze columns in place, so that they remain visible while the rest of the table is scrolled horizontally. This can be useful if you want to keep important columns, such as the first and last columns, visible at all times.
To freeze a column, you can use the freeze prop in the header object for that column. The positionFreeze prop can be used to specify whether the column should be frozen to the left or right side of the table.
This will freeze the first and last columns of the table, so that they remain visible while the rest of the table is scrolled horizontally.
Server Side
This is an example of using a server-side data table in Vue.js. The data table is connected to a server-side API to retrieve data and handle pagination, sorting, and filtering.
The example includes a function to convert ratings to star icons and displays them in the table. It also displays thumbnail images and formats price values with the appropriate currency symbol.
Search
The VDataTable component allows for searching through its items by binding a search string to its search prop.
In this example, this is done by using the VInput component to create a search bar that updates the search prop of the VDataTable component whenever the user inputs a new search query. The VDataTable component then filters its items based on this search query.
Props
| Name | Type | Default |
|---|---|---|
bodyClass | String | '' |
bordered | Boolean | false |
columnActiveClass | String | '' |
columnInactiveClass | String | '' |
dense | Boolean | false |
disableSorting | Boolean | false |
flat | Boolean | false |
footerClass | String | '' |
footerColor | String | '' |
headerClass | String | '' |
headers | Array as PropType<VDataTableHeader[]> | [] |
hideFooter | Boolean | false |
hover | Boolean | false |
hoverClass | String | '' |
items | Array as PropType<VDataTableItem[]> | [] |
itemsPerPage | Number | 10 |
loading | Boolean | false |
loadingText | String | 'Loading...' |
modelValue | Array | [] |
multiSort | Boolean | false |
mustSort | Boolean | false |
noDataText | String | 'No results' |
noShadow | Boolean | false |
page | Number | 1 |
pagination | Object | {} |
rowClass | (item: T, index: number) => string; | undefined |
search | String | '' |
searchBy | [String, Array] as PropType<string> | string[] |
selectable | Boolean | false |
serverSide | Boolean | false |
sortBy | String | '' |
sortDirection | String as PropType<SortDirection> | '' |
striped | Boolean | false |
stripedClass | String | '' |
tdClass | String | '' |
tile | Boolean | false |
totalItems | Number | 0 |
trClass | String | '' |
value | Array | [] |
wrapperClass | String | '' |
Events
update:search
This event is emitted when the search string changes. The value parameter contains the new search string.
Type:
(e: 'update:search', value: string): void;(e: 'update:search', value: string): void;update:sortBy
This event is emitted when the sortBy value changes. The value parameter contains the new sortBy value.
Type:
(e: 'update:sortBy', value: string): void;(e: 'update:sortBy', value: string): void;update:sortDirection
This event is emitted when the sortDirection value changes. The value parameter contains the new sortDirection value.
Type:
(e: 'update:sortDirection', value: SortDirection): void;(e: 'update:sortDirection', value: SortDirection): void;update:page
This event is emitted when the page value changes. The value parameter contains the new page value.
Type:
(e: 'update:page', value: number): void;(e: 'update:page', value: number): void;update:itemsPerPage
This event is emitted when the itemsPerPage value changes. The value parameter contains the new itemsPerPage value.
Type:
(e: 'update:itemsPerPage', value: number): void;(e: 'update:itemsPerPage', value: number): void;update:totalItems
This event is emitted when the totalItems value changes. The value parameter contains the new totalItems value.
Type:
(e: 'update:totalItems', value: number): void;(e: 'update:totalItems', value: number): void;update:pagination
This event is emitted when any of the pagination values (page, items per page, or total items) change. The value parameter contains an object with the updated pagination values.
Type:
(e: 'update:pagination', value: Record<string, any>): void;(e: 'update:pagination', value: Record<string, any>): void;page:change
This event is emitted when the page number changes. The value parameter contains the new page number.
Type:
(e: 'page:change', value: number): void;(e: 'page:change', value: number): void;itemsPerPage:change
This event is emitted when the number of items per page changes. The value parameter contains the new number of items per page.
Type:
(e: 'itemsPerPage:change', value: number): void;(e: 'itemsPerPage:change', value: number): void;pagination:change
This event is emitted when any of the pagination values (page, items per page, or total items) change. The value parameter contains an object with the updated pagination values.
Type:
(e: 'pagination:change', value: Record<string, any>): void;(e: 'pagination:change', value: Record<string, any>): void;update:modelValue
This event is emitted when the value of the v-model directive changes. The value parameter contains the new value.
Type:
(e: 'update:modelValue', value: any): void;(e: 'update:modelValue', value: any): void;sort
The sort event is emitted when the user sorts a table by a particular column. The payload for this event includes the name of the column that the table is being sorted by (sortBy) and the direction of the sort (direction).
Type:
(e: 'sort', payload: {sortBy: string; direction: SortDirection}): void;(e: 'sort', payload: {sortBy: string; direction: SortDirection}): void;row:click
The row:click event is emitted when the user clicks on a row in the table. The payload for this event includes the data for the clicked row (item) and the index of the row in the table (index).
Type:
(e: 'row:click', payload: {item: VDataTableItem; index: number}): void;(e: 'row:click', payload: {item: VDataTableItem; index: number}): void;Slots
loading
The loading slot allows you to customize the content shown when the table is loading data.
Type:
loading?: (props: {}) => any;loading?: (props: {}) => any;Example:
<template>
<VDataTable>
<template #loading>Loading...</template>
</VDataTable>
</template><template>
<VDataTable>
<template #loading>Loading...</template>
</VDataTable>
</template>header.selectable
The header.selectable slot allows you to customize the content of the selectable column in the table header. It receives a selectAll payload that indicates whether all rows are currently selected.
Type:
'header.selectable'?: (props: {selectAll: boolean}) => any;'header.selectable'?: (props: {selectAll: boolean}) => any;Example:
<template>
<VDataTable>
<template #header.selectable="{selectAll}">
<v-checkbox v-model="selectAll" />
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #header.selectable="{selectAll}">
<v-checkbox v-model="selectAll" />
</template>
</VDataTable>
</template>empty
The empty slot allows you to customize the content shown when the table has no data.
Type:
empty?: (props: {}) => any;empty?: (props: {}) => any;Example:
<template>
<VDataTable>
<template #empty> No Data </template>
</VDataTable>
</template><template>
<VDataTable>
<template #empty> No Data </template>
</VDataTable>
</template>item.selected
The item.selected slot allows you to customize the content of the selected column for each row in the table. It receives a selected payload that indicates whether the current row is selected.
Type:
'item.selected'?: (props: {selected: any[]; item: T}) => any;'item.selected'?: (props: {selected: any[]; item: T}) => any;Example:
<template>
<VDataTable>
<template #item.selected="selected">
<VSwitch v-model="selected" />
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #item.selected="selected">
<VSwitch v-model="selected" />
</template>
</VDataTable>
</template>item.index
The item.index slot allows you to customize the content of the index column for each row in the table. It receives an index payload that indicates the index of the current row.
Type:
'item.index'?: (props: {index: number; item: T}) => any;'item.index'?: (props: {index: number; item: T}) => any;Example:
<template>
<VDataTable>
<template #item.index="{index}">
{{ index }}
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #item.index="{index}">
{{ index }}
</template>
</VDataTable>
</template>item.{value}
The item.{value} slot allows you to customize the content of a particular column for each row in the table. The value in the slot name corresponds to the name of the column. It receives an item payload that contains the data for the current row and an index payload that indicates the index of the current row.
Type:
[K in keyof T as K extends string ? `item.${K}` : never]?: (props: {
item: T;
index: number;
}) => any;[K in keyof T as K extends string ? `item.${K}` : never]?: (props: {
item: T;
index: number;
}) => any;Example:
<template>
<VDataTable>
<template #item.id="{item}">
{{ item.id }}
</template>
<template #item.fullName="{item}">
{{ item.firstName }} {{ item.lastName }}
</template>
<template #item.website="{item}">
<Link :to="item.website"> {{ item.website }} </Link>
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #item.id="{item}">
{{ item.id }}
</template>
<template #item.fullName="{item}">
{{ item.firstName }} {{ item.lastName }}
</template>
<template #item.website="{item}">
<Link :to="item.website"> {{ item.website }} </Link>
</template>
</VDataTable>
</template>header.{value}
The header.{value} slot allows you to customize the content of a table header (th).
Type:
{
[K in keyof T as K extends string ? `header.${K}` : never]?: (props: {
header: H;
index: number;
}) => any;
}{
[K in keyof T as K extends string ? `header.${K}` : never]?: (props: {
header: H;
index: number;
}) => any;
}Example:
<script setup lang="ts">
import {items, headers} from './data';
</script>
<template>
<VDataTable :headers="headers" :items="items">
<template #header.name="{header}">
<div class="uppercase">{{ header.text }}</div>
</template>
</VDataTable>
</template><script setup lang="ts">
import {items, headers} from './data';
</script>
<template>
<VDataTable :headers="headers" :items="items">
<template #header.name="{header}">
<div class="uppercase">{{ header.text }}</div>
</template>
</VDataTable>
</template>footer
The footer slot allows you to customize the content of the table footer. It receives a pagination payload that indicates whether pagination is enabled, a perPage payload that indicates the number of items per page, a serverSide payload that indicates whether the table is using server-side data, an items payload that contains the data for the current page, a totalItems payload that indicates the total number of items in the table, a footerColor payload that indicates the color of the footer, a footerClass payload that indicates the class of the footer, and a page payload that indicates the current page.
Type:
footer?: (props: {
pagination: VDataTablePaginationProps;
perPage: number;
serverSide: boolean;
items: T[];
totalItems: number;
footerColor: string;
footerClass: string;
page: number;
}) => any;footer?: (props: {
pagination: VDataTablePaginationProps;
perPage: number;
serverSide: boolean;
items: T[];
totalItems: number;
footerColor: string;
footerClass: string;
page: number;
}) => any;Example:
<template>
<VDataTable>
<template #footer="{page}">
<VPagination v-model="page" />
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #footer="{page}">
<VPagination v-model="page" />
</template>
</VDataTable>
</template>header
The header slot alows you to customize the content of header (th) of table.
Type:
header: (props: {
headerClass: string;
headers: H[];
sortMap: Map<string, SortDirection>;
selectable: boolean;
disableSorting: boolean;
handleSort: (header: H) => void;
}) => any;header: (props: {
headerClass: string;
headers: H[];
sortMap: Map<string, SortDirection>;
selectable: boolean;
disableSorting: boolean;
handleSort: (header: H) => void;
}) => any;body
The body slot alows you to customize the content of table body (tbody) of table.
Type:
body: (props: {
bodyClass: string;
items: T[];
loading: boolean;
headers: H[];
noDataText: string;
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
loadingText: string;
stripedClass: string;
hoverClass: string;
tdClass: string;
selectable: boolean;
start: number;
}) => any;body: (props: {
bodyClass: string;
items: T[];
loading: boolean;
headers: H[];
noDataText: string;
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
loadingText: string;
stripedClass: string;
hoverClass: string;
tdClass: string;
selectable: boolean;
start: number;
}) => any;item
The item slot alows you to customize the content of table row (tr) of table.
Type:
item: (props: {
item: T;
index: number;
headers: H[];
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
}) => any;item: (props: {
item: T;
index: number;
headers: H[];
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
}) => any;Example:
<template>
<VDataTable>
<template #item="{
item,
handleRowClick,
headers,
getTdClass,
tdClass,
get
}">
<tr
@click="handleRowClick(item)"
>
<td
v-for="(header, index) in headers"
:key="index"
class="v-table-td"
:class="[
getTdClass(header),
tdClass,
header?.tdClass || '',
]"
>
{{ get(item, header.value) }}
</td>
</tr>
</template>
</VDataTable>
</template><template>
<VDataTable>
<template #item="{
item,
handleRowClick,
headers,
getTdClass,
tdClass,
get
}">
<tr
@click="handleRowClick(item)"
>
<td
v-for="(header, index) in headers"
:key="index"
class="v-table-td"
:class="[
getTdClass(header),
tdClass,
header?.tdClass || '',
]"
>
{{ get(item, header.value) }}
</td>
</tr>
</template>
</VDataTable>
</template>CSS Variables
:root {
/* spacing */
--v-table-padding-x: var(--size-spacing-6);
--v-table-padding-y: var(--size-spacing-3);
--v-table-border-color: var(--color-gray-200);
/* thead */
--v-table-thead-bg-color: var(--color-gray-50);
/* th */
--v-table-th-color: var(--color-gray-600);
--v-table-th-font-size: var(--size-font-xs);
--v-table-th-font-weight: var(--font-weight-bold);
--v-table-th-white-space: nowrap;
--v-table-th-text-align: left;
/* th active */
--v-table-th-active-color: var(--color-primary-500);
/* th active hover */
--v-table-th-active-hover-color: var(--color-primary-600);
// tr
--v-table-tr-bg-color: transparent;
/* td */
--v-table-td-color: var(--color-gray-900);
--v-table-td-bg-color: var(--color-white);
--v-table-td-font-size: var(--size-font-sm);
--v-table-td-font-weight: var(--font-weight-regular);
--v-table-td-white-space: nowrap;
--v-table-td-text-align: left;
/* dense */
--v-table-dense-padding-x: var(--size-spacing-4);
--v-table-dense-padding-y: var(--size-spacing-2);
/* striped */
--v-table-striped-bg-color: var(--color-gray-50);
--v-table-striped-even-bg-color: var(--color-white);
/* hover */
--v-table-hover-bg-color: var(--color-gray-100);
}:root {
/* spacing */
--v-table-padding-x: var(--size-spacing-6);
--v-table-padding-y: var(--size-spacing-3);
--v-table-border-color: var(--color-gray-200);
/* thead */
--v-table-thead-bg-color: var(--color-gray-50);
/* th */
--v-table-th-color: var(--color-gray-600);
--v-table-th-font-size: var(--size-font-xs);
--v-table-th-font-weight: var(--font-weight-bold);
--v-table-th-white-space: nowrap;
--v-table-th-text-align: left;
/* th active */
--v-table-th-active-color: var(--color-primary-500);
/* th active hover */
--v-table-th-active-hover-color: var(--color-primary-600);
// tr
--v-table-tr-bg-color: transparent;
/* td */
--v-table-td-color: var(--color-gray-900);
--v-table-td-bg-color: var(--color-white);
--v-table-td-font-size: var(--size-font-sm);
--v-table-td-font-weight: var(--font-weight-regular);
--v-table-td-white-space: nowrap;
--v-table-td-text-align: left;
/* dense */
--v-table-dense-padding-x: var(--size-spacing-4);
--v-table-dense-padding-y: var(--size-spacing-2);
/* striped */
--v-table-striped-bg-color: var(--color-gray-50);
--v-table-striped-even-bg-color: var(--color-white);
/* hover */
--v-table-hover-bg-color: var(--color-gray-100);
}Standalone Installation
You can also install the DataTable component individually via @morpheme/table package:
npm i @morpheme/tablenpm i @morpheme/table<script setup lang="ts">
import VDataTable from '@morpheme/table';
</script>
<template>
<VDataTable />
</template><script setup lang="ts">
import VDataTable from '@morpheme/table';
</script>
<template>
<VDataTable />
</template>Storybook
View Storybook documentation here.
Morpheme