Table
A responsive table component.
Prerequisites
We're going to build a table to show deployments, this is what our data looks like:
const rawData = [
{
domain: 'eihabkhan.com',
status: 'building',
branch: 'main',
},
{
domain: 'supershop.youcan.shop',
status: 'queued',
branch: 'main',
},
{
domain: 'vercel.com',
status: 'ready',
branch: 'main',
},
{
domain: 'nextjs.org',
status: 'error',
branch: 'feat/next-conf',
},
];
In order to work with the Table
Component we have to reshape our data object so that it adheres to what the Table component is expecting, we can do that by creating a new data object that is of type TableData
:
import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types';
const data: TableData[] = rawData.map(item => {
return {
row: {
domain: item.domain,
status: item.status,
branch: item.branch,
},
}
})
File Structure
Start by creating the following structure
/components
└── /deployments
├── helpers.ts
└── DeploymentsTable.vue
helpers.ts
: This is where you define yourdata
,columns
, or any other table configurations.DeploymentsTable.vue
: This is where you import and use theTable
component, adding additional elements such asPagination
,Search
, etc.
Basic Table
Let's start by building a basic table.
Define Columns
First, we'll define our columns.
ts// components/deployments/helpers.ts import type { TableColumn } from '@youcan/ui-vue3/dist/types/components/Table/types'; export const columns: TableColumn[] = [ { label: 'Domain', accessor: 'domain' }, { label: 'Status', accessor: 'status' }, { label: 'Git Branch', accessor: 'branch' }, ];
<Table/>
ComponentNext, we'll create a
<Table />
component inDeploymentsTable.vue
to render our table, we'll pass it our data and columns objects:vue<!-- components/deployments/DeploymentsTable.vue --> <script setup lang="ts"> import { Table } from '@youcan/ui-vue3'; import { columns } from './helpers.ts'; const data = await fetchDeployments(); // Fetch data from API const deployments = data.map(item => { return { row: { domain: item.domain, status: item.status, branch: item.branch, }, } }); </script> <template> <Table :columns="columns" :data="deployments" /> </template>
Render the Table
Finally, we'll render the table in a desired page:
vue<script setup lang="ts"> import DeploymentsTable from '~components/deployments/DeploymentsTable.vue' </script> <template> <DeploymentsTable /> </template>
Cell formatting
You can customize the way you want your data to be displayed inside of the <Table />
cells. You can do that by defining the variant you want to display in a given cell. The <Table />
cell supports a predefined list of variants:
string
number
boolean
TableDataComposable
:'link'
'thumbnail'
'status'
'static-status'
'percentage'
'button'
'rating'
'toggle'
'counter'
'input'
Using TableDataComposable
requires you to handle the data
property differently based on which composable you choose:
Composable | Data |
---|---|
'link' | { label?: string, href: string } |
'thumbnail' | { size: ThumbnailSize, src?: string, alt?: string } |
'status' | { statuses: StatusDefinition[], modelValue: StatusDefinition } |
'static-status' | { status: StaticStatusDefinition } |
'percentage' | { percentage: number, type: PercentageType } |
'button' | { label: string, iconName?: string, size?: ButtonSize, disabled?: boolean, iconPosition?: ButtonIconPosition, roundedFull?: boolean } |
'rating' | { score: number, ceil?: number } |
'toggle' | { modelValue: boolean } |
'counter' | { modelValue: string, max?: number, min?: number, step?: number, id?: string, disabled?: boolean } |
'input' | TableDataNumberInput | TableDataGenericInput |
Let's format the status cell to display color-coded statuses instead of plain text.
Choose your variant
Deside how you'd want to display your data. In our example, We'll use Static Status component to display color-coded statuses
Define you static statuses
vue<!-- components/deployments/DeploymentsTable.vue --> <script setup lang="ts"> import { Table } from '@youcan/ui-vue3'; import { columns } from './helpers.ts'; import type { StaticStatusDefinition } from '@youcan/ui-vue3/dist/types/components/Status/types'; const domainStatuses: { [k: string]: StaticStatusDefinition } = { ready: { color: '#52E2C0', label: 'Ready', labelColor: '#2d4236', }, error: { color: '#EE0200', label: 'Error', labelColor: '#f8dcdc', }, building: { color: '#ffc35c', label: 'Building', labelColor: '#574811', }, queued: { color: '#999999', label: 'Queued', labelColor: '#fff2f2', }, }; const data = await fetchDeployments(); // Fetch data from API // ...
Update your data definition
Now that we defined our statuses, it's time to update our Data definition:
vue<!-- components/deployments/DeploymentsTable.vue --> <script setup lang="ts"> // ... const data = await fetchDeployments(); // Fetch data from API const deployments = data.map(item => { return { row: { domain: item.domain, status: { variant: 'static-status', data: { status: domainStatuses[item.status], }, }, branch: item.branch, }, } }); </script> <template> <Table :columns="columns" :data="deployments" /> </template>
Row Actions
Let's add row actions to our table.
Define actions
In the
helpers.ts
file, let's create an actions array of typeTableActions
:ts// components/deployments/helpers.ts import type { TableActions } from '@youcan/ui-vue3/dist/types/components/Table/types'; const actions: TableActions[] = [ { label: 'Redeploy', iconName: 'i-youcan-arrows-clockwise', tooltip: 'Redeploy', events: { click(row, index) { // Do something }, }, }, { label: 'Copy URL', iconName: 'i-youcan-copy', tooltip: 'Copy URL', events: { click(row, index) { // Do something }, }, }, { label: 'Source Code', iconName: 'i-youcan-code', tooltip: 'Source Code', events: { click(row, index) { // Do something }, }, }, ];
Pass actions to table
Pass the actions you just created into the
<Table />
component:vue<!-- components/deployments/DeploymentsTable.vue --> <script setup lang="ts"> import { columns, actions } from './helpers.ts'; // ... </script> <template> <Table :columns="columns" :data="deployments" :actions="actions" /> </template>
Sorting
Let's add sorting to our table:
Define which columns are sortable
The
column
object takes asortable
property which supports one of three values:none
,desc
, andasc
:ts// components/deployments/helpers.ts import type { TableColumn } from '@youcan/ui-vue3/dist/types/components/Table/types'; export const columns: TableColumn[] = [ { label: 'Domain', accessor: 'domain', sortable: 'desc' }, { label: 'Status', accessor: 'status' }, { label: 'Git Branch', accessor: 'branch' }, ];
Add your sort logic to the
<Table />
Inside of
DeploymentsTable.vue
define your sorting logic:vue<!-- components/deployments/DeploymentsTable.vue --> <script setup lang="ts"> // ... const handleSort = (column: TableColumn) => { data.value = data.value.sort((a, b) => { const textA = (a.row[column.accessor] as string).toUpperCase(); const textB = (b.row[column.accessor] as string).toUpperCase();
if (textA < textB) { return -1; } if (textA > textB) { return 1; } return 0; }); }; </script> <template> <Table :columns="columns" :data="deployments" :actions="actions" @sort="handleSort" /> </template>
Row Selection
Let's add the ability to select rows to our table:
Add the selected rows ref
vue<script setup lang="ts"> import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types'; // ... const selectedRows = ref<TableData[]>([]); </script>
Add selection handler
vue<script setup lang="ts"> import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types'; // ... const selectedRows = ref<TableData[]>([]); const updateSelectedRows = (data: TableData[]) => { selectedRows.value = data; }; </script>
Update
<Table />
vue<template> <Table :columns="columns" :data="data" :actions="actions" :selectable="true" :selected-rows="selectedRows" @sort="handleSort" @update:selected-rows="updateSelectedRows" /> </template>
Children
You can add child-rows to your table by updating your data
object
<!-- components/deployments/DeploymentsTable.vue -->
<script setup lang="ts">
// ...
const data = await fetchDeployments(); // Fetch data from API
const deployments = data.map(item => {
return {
row: {
domain: item.domain,
status: {
variant: 'static-status',
data: {
status: domainStatuses[item.status],
},
},
branch: item.branch,
},
children: item.children // type: TableDataRow[]
}
});
</script>
<template>
<Table
:columns="columns"
:data="deployments"
:actions="actions"
/>
</template>