Table
TkTable is a component that allows you to display data in a tabular manner. It's generally called a datatable.
- React
- Vue
- Angular
import { TkTable } from '@takeoff-ui/react'
import { TkTable } from '@takeoff-ui/vue'
import { TkTable } from '@takeoff-ui/angular'
Basic
The basic features of the TkTable component are demonstrated.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable columns={column} data={basicData} />
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable :columns.prop="column" :data.prop="basicData" />
</div>
</template>
<div style="padding: 8px">
<tk-table
[columns]="[
{ field: 'id', header: 'Id' },
{ field: 'name', header: 'Name' },
{ field: 'category', header: 'Category' },
{ field: 'quantity', header: 'Quantity' }
]"
[data]="[
{ id: 1, name: 'Product A', category: 'Electronics', quantity: 12 },
{ id: 2, name: 'Product B', category: 'Books', quantity: 8 },
{ id: 3, name: 'Product C', category: 'Groceries', quantity: 20 }
]"
/>
</div>
Striped
To enable striped mode, set the striped prop to true. This feature displays rows with alternating background colors.
- React
- Vue
- Angular
<TkTable columns={column} data={basicData} striped />
<TkTable :columns.prop="column" :data.prop="basicData" striped />
<tk-table
[columns]="[
{ field: 'id', header: 'Id' },
{ field: 'name', header: 'Name' },
{ field: 'category', header: 'Category' },
{ field: 'quantity', header: 'Quantity' }
]"
[data]="[
{ id: 1, name: 'Product A', category: 'Electronics', quantity: 12 },
{ id: 2, name: 'Product B', category: 'Books', quantity: 8 },
{ id: 3, name: 'Product C', category: 'Groceries', quantity: 20 }
]"
striped
/>
Header Type
The headerType prop is used to change the style of the table headers and allows you to customize the headers' style with values such as basic, primary, and dark.
headerType is basic
- React
- Vue
- Angular
<TkTable columns={column} data={headerTypeData} headerType="basic" />
<TkTable :columns.prop="column" :data.prop="headerTypeData" headerType="basic" />
<tk-table
[columns]="[
{ field: 'id', header: 'Id' },
{ field: 'name', header: 'Name' },
{ field: 'category', header: 'Category' },
{ field: 'quantity', header: 'Quantity' }
]"
[data]="[
{ id: 1, name: 'Product A', category: 'Electronics', quantity: 12 },
{ id: 2, name: 'Product B', category: 'Books', quantity: 8 },
{ id: 3, name: 'Product C', category: 'Groceries', quantity: 20 }
]"
headerType="basic"
/>
Selection
This feature allows users to select rows in the table. The selected rows are visually highlighted and can be used for various actions.
[{"id":"h456wer53","name":"Bracelet","category":"Clothing","quantity":45}]
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
const [selectionList, setSelectionList] = useState();
const [mode, setMode] = useState();
return (
<div style={{ padding: "8px" }}>
<TkTable
columns={column}
data={basicData}
dataKey="id"
selection={selectionList}
selectionMode={mode}
onTkSelectionChange={(e: CustomEvent) =>
setSelectionList(e.detail)
}
/>
<p>{JSON.stringify(selectionList)}</p>
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
import { ref } from 'vue';
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
const selectionList = ref([
{
id: 'h456wer53',
name: 'Bracelet',
category: 'Clothing',
quantity: 45,
},
]);
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable
:columns.prop="column"
:data.prop="basicData"
dataKey="id"
:selection="selectionList"
selectionMode.prop="mode"
@tkSelectionChange="e => (selectionList.value = e.detail)"
/>
<p>{{ JSON.stringify(selectionList) }}</p>
</div>
</template>
Filter and Sorting
The table supports three types of filtering:
- Text input filtering: Allows free text search in columns
- Checkbox filtering: Enables selecting multiple values from predefined options
- Radio filtering: Allows selecting a single value from predefined options
The filtering type can be configured using the filterType property in column definition. For checkbox and radio filters, you need to provide filterOptions with value-label pairs.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Input Filter',
searchable: true,
sortable: true,
sorter: (a: any, b: any) => (a.name > b.name ? 1 : -1),
filter: (value: string, row: any) =>
row.name
.toString()
.toLowerCase()
.indexOf(value.toString().toLowerCase() as string) > -1,
},
{
field: 'status',
header: 'Checkbox Filter',
searchable: true,
filterType: 'checkbox',
filterOptions: [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
{ value: 'pending', label: 'Pending' },
],
filterElements: {
icon: 'filter_list',
optionsSearchInput: { show: true, placeholder: 'Filter' },
},
},
{
field: 'group',
header: 'Radio Filter',
searchable: true,
filterType: 'radio',
filterOptions: [
{ value: 'group 1', label: 'Group 1' },
{ value: 'group 2', label: 'Group 2' },
{ value: 'group 3', label: 'Group 3' },
],
filterElements: {
optionsSearchInput: { show: true, placeholder: 'Filter' },
},
},
{
field: 'quantity',
header: 'Quantity',
sortable: true,
sorter: (a: any, b: any) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
{
field: 'date',
header: 'Date',
sortable: true,
searchable: true,
sorter: (a: any, b: any) => (a.date > b.date ? 1 : -1),
filterType: 'datepicker',
filterElements: {
optionsSearchDatepicker: {
label: 'Choose a date',
dateFormat: 'yyyy-MM-dd',
size: 'small',
},
},
},
];
const data = [
{
id: 'f230fh0g3',
name: 'Bamboo Watch',
status: 'active',
group: 'group 1',
quantity: 24,
date: '2025-09-07',
},
{
id: 'nvklal433',
name: 'Black Watch',
status: 'inactive',
group: 'group 2',
quantity: 42,
date: '2025-09-08',
},
{
id: 'zz21cz3c1',
name: 'Blue Band',
status: 'active',
group: 'group 3',
quantity: 87,
date: '2025-09-09',
},
{
id: '244wgerg2',
name: 'Blue T-Shirt',
status: 'pending',
group: 'group 1',
quantity: 12,
date: '2025-09-10',
},
{
id: 'h456wer53',
name: 'Bracelet',
status: 'inactive',
group: 'group 2',
quantity: 45,
date: '2025-09-11',
},
];
return <TkTable columns={column} data={data} />;
<script setup>
import { TkTable } from '@takeoff-ui/vue';
const column = [
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Input Filter',
searchable: true,
sortable: true,
sorter: (a, b) => (a.name > b.name ? 1 : -1),
filter: (value, row) =>
row.name
.toString()
.toLowerCase()
.indexOf(value.toString().toLowerCase()) > -1,
},
{
field: 'status',
header: 'Checkbox Filter',
searchable: true,
filterType: 'checkbox',
filterOptions: [
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
{ value: 'pending', label: 'Pending' },
],
filterElements: {
icon: 'filter_list',
optionsSearchInput: { show: true, placeholder: 'Filter' },
},
},
{
field: 'group',
header: 'Radio Filter',
searchable: true,
filterType: 'radio',
filterOptions: [
{ value: 'group 1', label: 'Group 1' },
{ value: 'group 2', label: 'Group 2' },
{ value: 'group 3', label: 'Group 3' },
],
filterElements: {
optionsSearchInput: { show: true, placeholder: 'Filter' },
},
},
{
field: 'quantity',
header: 'Quantity',
sortable: true,
sorter: (a, b) => (Number(a.quantity) > Number(b.quantity) ? 1 : -1),
},
{
field: 'date',
header: 'Date',
sortable: true,
searchable: true,
sorter: (a: any, b: any) => (a.date > b.date ? 1 : -1),
filterType: 'datepicker',
filterElements: {
optionsSearchDatepicker: {
label: 'Choose a date',
dateFormat: 'yyyy-MM-dd',
size: 'small',
},
},
},
];
const data = [
{
id: 'f230fh0g3',
name: 'Bamboo Watch',
status: 'active',
group: 'group 1',
quantity: 24,
date: '2025-09-07',
},
{
id: 'nvklal433',
name: 'Black Watch',
status: 'inactive',
group: 'group 2',
quantity: 42,
date: '2025-09-08',
},
{
id: 'zz21cz3c1',
name: 'Blue Band',
status: 'active',
group: 'group 3',
quantity: 87,
date: '2025-09-09',
},
{
id: '244wgerg2',
name: 'Blue T-Shirt',
status: 'pending',
group: 'group 1',
quantity: 12,
date: '2025-09-10',
},
{
id: 'h456wer53',
name: 'Bracelet',
status: 'inactive',
group: 'group 2',
quantity: 45,
date: '2025-09-11',
},
];
</script>
<template>
<TkTable :columns.prop="column" :data.prop="data" />
</template>
Multi Sorting
This feature allows users to sort the table by multiple columns simultaneously. Users can click on the column headers to apply sorting, and the order of sorting can be adjusted by clicking on the headers in the desired sequence.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Name',
sortable: true,
sorter: (a: any, b: any) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
},
},
{
field: 'status',
header: 'Status',
sortable: true,
sorter: (a: any, b: any) => {
if (a.status < b.status) return -1;
if (a.status > b.status) return 1;
return 0;
},
},
{
field: 'group',
header: 'Group',
sortable: true,
sorter: (a: any, b: any) => {
if (a.group < b.group) return -1;
if (a.group > b.group) return 1;
return 0;
},
},
{
field: 'quantity',
header: 'Quantity',
sortable: true,
sorter: (a: any, b: any) => {
if (a.quantity < b.quantity) return -1;
if (a.quantity > b.quantity) return 1;
return 0;
},
},
];
const data = [
{
id: 'zz21cz3c1',
name: 'Blue Band',
status: 'active',
group: 'group 3',
quantity: 87,
},
{
id: 'h456wer53',
name: 'Bracelet',
status: 'inactive',
group: 'group 2',
quantity: 45,
},
{
id: 'a789def12',
name: 'Blue Band',
status: 'pending',
group: 'group 1',
quantity: 24,
},
{
id: 'b123ghi34',
name: 'Blue Band',
status: 'inactive',
group: 'group 2',
quantity: 12,
},
{
id: 'c456jkl56',
name: 'Smart Watch',
status: 'active',
group: 'group 3',
quantity: 24,
},
{
id: 'e012pqr90',
name: 'Bracelet',
status: 'active',
group: 'group 1',
quantity: 45,
},
];
return <TkTable columns={column} data={data} multiSort={true} />;
<script setup>
import { TkTable } from '@takeoff-ui/vue';
const column = [
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Name',
sortable: true,
sorter: (a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
},
},
{
field: 'status',
header: 'Status',
sortable: true,
sorter: (a, b) => {
if (a.status < b.status) return -1;
if (a.status > b.status) return 1;
return 0;
},
},
{
field: 'group',
header: 'Group',
sortable: true,
sorter: (a, b) => {
if (a.group < b.group) return -1;
if (a.group > b.group) return 1;
return 0;
},
},
{
field: 'quantity',
header: 'Quantity',
sortable: true,
sorter: (a, b) => {
if (a.quantity < b.quantity) return -1;
if (a.quantity > b.quantity) return 1;
return 0;
},
},
];
const data = [
{
id: 'zz21cz3c1',
name: 'Blue Band',
status: 'active',
group: 'group 3',
quantity: 87,
},
{
id: 'h456wer53',
name: 'Bracelet',
status: 'inactive',
group: 'group 2',
quantity: 45,
},
{
id: 'a789def12',
name: 'Blue Band',
status: 'pending',
group: 'group 1',
quantity: 24,
},
{
id: 'b123ghi34',
name: 'Blue Band',
status: 'inactive',
group: 'group 2',
quantity: 12,
},
{
id: 'c456jkl56',
name: 'Smart Watch',
status: 'active',
group: 'group 3',
quantity: 24,
},
{
id: 'e012pqr90',
name: 'Bracelet',
status: 'active',
group: 'group 1',
quantity: 45,
},
];
</script>
<template>
<TkTable :columns.prop="column" :data.prop="data" :multiSort.prop="true" />
</template>
Clear Filters and Sorting
This example demonstrates how to clear all filters and sorting settings programmatically using the clearFilters and clearSorting methods.
- React
- Vue
- Angular
const Example = () => {
const tableRef = useRef(null);
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
sorter: (a: any, b: any) => (a.name > b.name ? 1 : -1),
filter: (value: string, row: any) =>
row.name.toString().toLowerCase().indexOf(value.toString().toLowerCase() as string) > -1,
},
{
field: "category",
header: "Category",
searchable: true,
filterType: 'checkbox',
filterOptions: [
{ value: 'Accessories', label: 'Accessories' },
{ value: 'Clothing', label: 'Clothing' },
{ value: 'Fitness', label: 'Fitness' },
],
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a: any, b: any) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
const handleClearFilters = () => {
tableRef.current?.clearFilters();
// For server-side pagination, after clearing filters,
// serverRequest method is called to trigger tkRequest event
// tableRef.current?.serverRequest();
};
const handleClearSorting = () => {
tableRef.current?.clearSorting();
// For server-side pagination, after clearing filters,
// serverRequest method is called to trigger tkRequest event
// tableRef.current?.serverRequest();
};
return (
<div>
<div style={{ marginBottom: '1rem', display: 'flex', gap: '0.5rem' }}>
<TkButton onClick={handleClearFilters} label="Clear Filters"></TkButton>
<TkButton onClick={handleClearSorting} label="Clear Sorting"></TkButton>
</div>
<TkTable ref={tableRef} columns={column} data={basicData}/>
</div>
);
};
<script setup>
import { TkTable, TkButton } from '@takeoff-ui/vue'
import { ref } from 'vue'
const tableRef = ref(null)
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
sorter: (a, b) => (a.name > b.name ? 1 : -1),
filter: (value, row) =>
row.name.toString().toLowerCase().indexOf(value.toString().toLowerCase()) > -1,
},
{
field: "category",
header: "Category",
searchable: true,
filterType: 'checkbox',
filterOptions: [
{ value: 'Accessories', label: 'Accessories' },
{ value: 'Clothing', label: 'Clothing' },
{ value: 'Fitness', label: 'Fitness' },
],
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a, b) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
const handleClearFilters = () => {
tableRef.value?.$el.clearFilters();
// For server-side pagination, after clearing filters,
// serverRequest method is called to trigger tkRequest event
// tableRef.value?.$el.serverRequest();
};
const handleClearSorting = () => {
tableRef.value?.$el.clearSorting();
// For server-side pagination, after clearing filters,
// serverRequest method is called to trigger tkRequest event
// tableRef.value?.$el.serverRequest();
};
</script>
<template>
<div>
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem">
<TkButton @click="handleClearFilters" label="Clear Filters"></TkButton>
<TkButton @click="handleClearSorting" label="Clear Sorting"></TkButton>
</div>
<TkTable ref="tableRef" :columns.prop="column" :data.prop="basicData" />
</div>
</template>
Pagination
Client side pagination
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
sorter: (a: any, b: any) => (a.name > b.name ? 1 : -1),
filter: (value: string, row: any) =>
row.name
.toString()
.toLowerCase()
.indexOf(value.toString().toLowerCase() as string) > -1,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
sorter: (a: any, b: any) => (a.category > b.category ? 1 : -1),
filter: (value: string, row: any) =>
row.category
.toString()
.toLowerCase()
.indexOf(value.toString().toLowerCase() as string) > -1,
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a: any, b: any) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
return (
<TkTable
columns={column}
data={data}
paginationMethod="client"
rowsPerPage={5}
totalItems={data.length}
/>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const data = [
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
},
{
id: "344wgerg2",
name: "Art Venere",
category: "Accessories",
quantity: 23,
},
{
id: "144wgerg3",
name: "Simona Morasca",
category: "Clothing",
quantity: 56,
},
{
id: "444wgerg6",
name: "Leota Dilliard",
category: "Fitness",
quantity: 89,
},
{
id: "k14wgerj1",
name: "Sage Wieser",
category: "Accessories",
quantity: 77,
},
{
id: "fq4wgergq",
name: "Kris Marrier",
category: "Clothing",
quantity: 65,
},
{
id: "764wger11",
name: "Abel Maclead",
category: "Clothing",
quantity: 61,
},
{
id: "08ge885f",
name: "Mattie Poquette",
category: "Fitness",
quantity: 42,
},
{
id: "wg57erg2",
name: "Meaghan Garufi",
category: "Accessories",
quantity: 99,
},
{
id: "264w3erg2",
name: "Gladys Rim",
category: "Magalhaes",
quantity: 92,
},
];
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
sorter: (a, b) => (a.name > b.name ? 1 : -1),
filter: (value, row) =>
row.name.toString().toLowerCase().indexOf(value.toString().toLowerCase()) > -1,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
sorter: (a, b) => (a.category > b.category ? 1 : -1),
filter: (value, row) =>
row.category.toString().toLowerCase().indexOf(value.toString().toLowerCase()) > -1,
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a, b) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
</script>
<template>
<TkTable
:columns.prop="column"
:data.prop="data"
paginationMethod="client"
:rowsPerPage="5"
:totalItems="data.length"
/>
</template>
Server Side
Server side sorting, filter ang pagination example.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
},
{
field: "quantity",
header: "Quantity",
sortable: true,
},
];
const tableRef = useRef<HTMLTkTableElement>(null);
const [data, setData] = useState();
const [totalItem, setTotalItem] = useState();
const [rowsPerPage, setRowsPerPage] = useState(5);
const [loading, setLoading] = useState(false);
const handleRequest = async (e: TkTableCustomEvent<ITableRequest>) => {
setLoading(true);
const result: any = await fetchFromServer(
e.detail.currentPage,
e.detail.rowsPerPage,
e.detail.filters,
e.detail.sortField,
e.detail.sortOrder
);
setTotalItem(result?.totalItem);
setRowsPerPage(e.detail.rowsPerPage);
setData(result?.data);
setLoading(false);
};
useEffect(() => {
tableRef.current.serverRequest();
}, []);
return (
<>
<TkButton
icon="refresh"
variant="neutral"
type="text"
onTkClick={async () => {
setLoading(true);
const result: any = await fetchFromServer(1, 5, [], null, null);
setTotalItem(result?.totalItem);
setData(result?.data);
setLoading(false);
tableRef.current!.setCurrentPage(1);
}}
/>
<TkTable
ref={tableRef}
columns={column}
data={data}
paginationMethod="server"
rowsPerPage={rowsPerPage}
totalItems={totalItem}
loading={loading}
onTkRequest={handleRequest}
/>
</>
);
<script setup>
import { TkTable, TkButton } from '@takeoff-ui/vue'
import { ref, onMounted } from 'vue';
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
},
{
field: "quantity",
header: "Quantity",
sortable: true,
},
];
const tableRef = ref(null);
const data = ref();
const totalItem = ref();
const rowsPerPage = ref(5);
const loading = ref(false);
const handleRequest = async (e) => {
loading.value = true;
const result = await fetchFromServer(
e.detail.currentPage,
e.detail.rowsPerPage,
e.detail.filters,
e.detail.sortField,
e.detail.sortOrder
);
totalItem.value = result?.totalItem;
rowsPerPage.value = e.detail.rowsPerPage;
data.value = result?.data;
loading.value = false;
};
onMounted(() => {
tableRef.value.serverRequest();
});
const refreshData = async () => {
loading.value = true;
const result = await fetchFromServer(1, 5, [], null, null);
totalItem.value = result?.totalItem;
data.value = result?.data;
loading.value = false;
tableRef.value.setCurrentPage(1);
};
</script>
<template>
<TkButton
icon="refresh"
variant="neutral"
type="text"
@tkClick="refreshData"
/>
<TkTable
ref="tableRef"
:columns.prop="column"
:data.prop="data"
paginationMethod="server"
:rowsPerPage="rowsPerPage"
:totalItems="totalItem"
:loading="loading"
@tkRequest="handleRequest"
/>
</template>
Custom Cell
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
html: (row) => {
return `<tk-badge label="${row.category}" ></tk-badge>`;
},
},
{
field: "quantity",
header: "Quantity",
sortable: true,
},
{
field: "-",
header: "Actions",
html: (row) => {
const tkButton: HTMLTkButtonElement =
document.createElement("tk-button");
tkButton.label = "Detail";
tkButton.type = "text";
tkButton.variant = "info";
tkButton.addEventListener("tk-click", () => {
alert("clicked row: " + JSON.stringify(row));
});
return tkButton;
},
},
];
const [data, setData] = useState();
const [totalItem, setTotalItem] = useState();
const [loading, setLoading] = useState(false);
const handleRequest = async (e: TkTableCustomEvent<ITableRequest>) => {
setLoading(true);
const result: any = await fetchFromServer(
e.detail.currentPage,
e.detail.rowsPerPage,
e.detail.filters,
e.detail.sortField,
e.detail.sortOrder
);
setData(result?.data);
setTotalItem(result?.totalItem);
setLoading(false);
};
return (
<TkTable
columns={column}
data={data}
paginationMethod="server"
rowsPerPage={5}
totalItems={totalItem}
loading={loading}
onTkRequest={handleRequest}
/>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
import { ref } from 'vue';
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
searchable: true,
sortable: true,
},
{
field: "category",
header: "Category",
searchable: true,
sortable: true,
html: (row) => {
return `<tk-badge label="${row.category}" ></tk-badge>`;
},
},
{
field: "quantity",
header: "Quantity",
sortable: true,
},
{
field: "-",
header: "Actions",
html: (row) => {
const tkButton =
document.createElement("tk-button");
tkButton.label = "Detail";
tkButton.type = "text";
tkButton.variant = "info";
tkButton.addEventListener("tk-click", () => {
alert("clicked row: " + JSON.stringify(row));
});
return tkButton;
},
},
];
const data = ref([])
const totalItem = ref(0)
const loading = ref(false)
const handleRequest = async (e) => {
loading.value = true
const result = await fetchFromServer(
e.detail.currentPage,
e.detail.rowsPerPage,
e.detail.filters,
e.detail.sortField,
e.detail.sortOrder
);
data.value = result?.data
totalItem.value = result?.totalItem
loading.value = false
};
</script>
<template>
<TkTable :columns.prop="column" :data.prop="data" paginationMethod="server" :rowsPerPage="5" :totalItems="totalItem"
:loading="loading" @tkRequest="handleRequest" />
</template>
Custom Header
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
headerHtml: () => {
return '<div style="color: red;">Custom Header</div>';
},
},
{
field: "category",
header: "Category",
headerHtml: () => {
const checkbox = document.createElement('tk-checkbox');
checkbox.label = 'Checkbox';
checkbox.addEventListener('tk-change', (e) => {
console.log('checkbox status', e.detail);
});
return checkbox;
},
},
{
field: "quantity",
header: "Quantity",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable columns={column} data={basicData} />
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
headerHtml: () => {
return '<div style="color: red;">Custom Header</div>';
},
},
{
field: "category",
header: "Category",
headerHtml: () => {
const checkbox = document.createElement('tk-checkbox');
checkbox.label = 'Checkbox';
checkbox.addEventListener('tk-change', (e) => {
console.log('checkbox status', e.detail);
});
return checkbox;
},
},
{
field: "quantity",
header: "Quantity",
},
];
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable :columns.prop="column" :data.prop="basicData" />
</div>
</template>
<div style="padding: 8px">
<tk-table
[columns]="[
{ field: 'id', header: 'Id' },
{
field: 'name',
header: 'Name',
headerHtml: () => {
return '<div style="color: red;">Custom Header</div>';
},
},
{
field: 'category',
header: 'Category',
headerHtml: () => {
const checkbox = document.createElement('tk-checkbox');
checkbox.label = 'Checkbox';
checkbox.addEventListener('tk-change', (e) => {
console.log('checkbox status', e.detail);
});
return checkbox;
},
},
{ field: 'quantity', header: 'Quantity' }
]"
[data]="[
{ id: 1, name: 'Product A', category: 'Electronics', quantity: 12 },
{ id: 2, name: 'Product B', category: 'Books', quantity: 8 },
{ id: 3, name: 'Product C', category: 'Groceries', quantity: 20 }
]"
/>
</div>
Header Style
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
style: {
background: 'var(--primary-base)',
color: 'white',
},
},
{
field: "name",
header: "Name",
style: {
background: 'var(--primary-base)',
color: 'white',
},
},
{
field: "category",
header: "Category",
style: {
background: '#222530',
color: 'white',
},
},
{
field: "quantity",
header: "Quantity",
style: {
background: '#222530',
color: 'white',
},
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable
columns={column}
data={basicData}
/>
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const basicData = [
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
},
];
const column = [
{
field: "id",
header: "Id",
style: {
background: 'var(--primary-base)',
color: 'white',
},
},
{
field: "name",
header: "Name",
style: {
background: 'var(--primary-base)',
color: 'white',
},
},
{
field: "category",
header: "Category",
style: {
background: '#222530',
color: 'white',
},
},
{
field: "quantity",
header: "Quantity",
style: {
background: '#222530',
color: 'white',
},
];
</script>
<template>
<TkTable
:columns.prop="column"
:data.prop="basicData"
/>
</template>
Row/Cell Style
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable
columns={column}
data={basicData}
cellStyle={(row, col) => {
if (col.field == "name" && row.name == "Blue Band") {
return { background: "var(--primary-base)", color: "white" };
}
}}
rowStyle={(row, index) => {
if (row.quantity > 50) {
return {
background: "var(--states-success-sub-base)",
color: "darkgreen",
};
}
if (index % 2 === 0) {
return {
background: "var(--states-info-sub-base)",
};
}
}}
/>
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const basicData = [
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
},
];
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
</script>
<template>
<TkTable
:columns.prop="column"
:data.prop="basicData"
:cellStyle.prop="(row, col) => {
if (col.field == 'name' && row.name == 'Blue Band') {
return { background: 'var(--primary-base)', color: 'white' };
}
}"
:rowStyle.prop="(row, index) => {
if (row.quantity > 50) {
return {
background: 'var(--states-success-sub-base)',
color: 'darkgreen',
};
}
if (index % 2 === 0) {
return {
background: 'var(--states-info-sub-base)',
};
}
}"
/>
</template>
Expanded Rows
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
expander: true,
field: "",
header: "",
},
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a: any, b: any) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
const [expandedRows, setExpandedRows] = useState<any[]>([]);
const handleExpandedRowsChange = (rows: any[]) => {
console.log(rows);
setExpandedRows([...rows]);
};
const renderExpandedRows = () => {
return expandedRows.map((item, index) => {
return (
<div slot={`expand-content-${item.id}`} key={"expanded-row-" + index}>
<div style={{ display: "flex", gap: "8px" }}>content</div>
</div>
);
});
};
return (
<TkTable
columns={column}
data={data}
dataKey="id"
paginationMethod="client"
rowsPerPage={5}
totalItems={data.length}
expandedRows={expandedRows}
onTkExpandedRowsChange={(e) => handleExpandedRowsChange(e.detail)}
>
{renderExpandedRows()}
</TkTable>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
import { ref } from 'vue';
const data = [
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
},
{
id: "344wgerg2",
name: "Art Venere",
category: "Accessories",
quantity: 23,
},
{
id: "144wgerg3",
name: "Simona Morasca",
category: "Clothing",
quantity: 56,
},
{
id: "444wgerg6",
name: "Leota Dilliard",
category: "Fitness",
quantity: 89,
},
{
id: "k14wgerj1",
name: "Sage Wieser",
category: "Accessories",
quantity: 77,
},
{
id: "fq4wgergq",
name: "Kris Marrier",
category: "Clothing",
quantity: 65,
},
{
id: "764wger11",
name: "Abel Maclead",
category: "Clothing",
quantity: 61,
},
{
id: "08ge885f",
name: "Mattie Poquette",
category: "Fitness",
quantity: 42,
},
{
id: "wg57erg2",
name: "Meaghan Garufi",
category: "Accessories",
quantity: 99,
},
{
id: "264w3erg2",
name: "Gladys Rim",
category: "Magalhaes",
quantity: 92,
},
];
const column = [
{
expander: true,
field: "",
header: "",
},
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "quantity",
header: "Quantity",
sortable: true,
sorter: (a, b) =>
Number(a.quantity) > Number(b.quantity) ? 1 : -1,
},
];
const expandedRows = ref([])
const handleExpandedRowsChange = (rows) => {
console.log(rows);
expandedRows.value = [...rows]
};
</script>
<template>
<TkTable
:columns.prop="column"
:data.prop="data"
dataKey="id"
paginationMethod="client"
:rowsPerPage="5"
:totalItems="data.length"
:expandedRows="expandedRows"
@tkExpandedRowsChange="(e) => handleExpandedRowsChange(e.detail)"
>
<div v-for="item, index in expandedRows" :slot="`expand-content-${item.id}`" :key="'expanded-row-' + index">
<div :style="{ display: 'flex', gap: '8px' }">content</div>
</div>
</TkTable>
</template>
Export File
The basic features of the TkTable component are demonstrated.
- React
- Vue
- Angular
const tableRef = useRef<HTMLTkTableElement>(null);
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
const exportOptions = [
{
label: "Pdf",
value: "pdf",
},
{
label: "Excel",
value: "excel",
},
{
label: "Csv",
value: "csv",
},
];
const handleItemClick = (e) => {
tableRef.current?.exportFile({
type: e.detail.value,
fileName: "custom_file_name",
} as ITableExportOptions);
};
return (
<div style={{ padding: "8px" }}>
<TkTable ref={tableRef} columns={column} data={basicData}>
<div slot="header-right">
<TkDropdown
options={exportOptions}
position="bottom-end"
onTkItemClick={handleItemClick}
>
<TkButton
slot="trigger"
label="Export"
icon="keyboard_arrow_down"
iconPosition="right"
type="outlined"
/>
</TkDropdown>
</div>
</TkTable>
</div>
);
<script setup>
import { TkTable, TkDropdown, TkButton } from '@takeoff-ui/vue'
import { ref } from 'vue';
const basicData = [
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
},
];
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
const exportOptions = [
{
label: "Pdf",
value: "pdf",
},
{
label: "Excel",
value: "excel",
},
{
label: "Csv",
value: "csv",
},
];
const tableRef = ref()
const handleItemClick = (e) => {
tableRef.value?.$el.exportFile({
type: e.detail.value,
fileName: "custom_file_name",
});
};
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable ref="tableRef" :columns.prop="column" :data.prop="basicData">
<div slot="header-right">
<TkDropdown
:options.prop="exportOptions"
position="bottom-end"
@tkItemClick="handleItemClick"
>
<TkButton
slot="trigger"
label="Export"
icon="keyboard_arrow_down"
iconPosition="right"
type="outlined"
/>
</TkDropdown>
</div>
</TkTable>
</div>
</template>
Sticky Column
This feature allows the selected columns stay in their position in case of having multiple columns that extands the space.
- React
- Vue
- Angular
const tableRef = useRef<HTMLTkTableElement>(null);
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
fixed: "left",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
{
field: "startDate",
header: "Start Date",
},
{
field: "endDate",
header: "End Date",
},
{
field: "duration",
header: "Dration",
},
{
field: "place",
header: "Place",
},
{
field: "status",
header: "Status",
fixed: "right",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable ref={tableRef} cardTitle="Sticky" columns={column} data={stickyData}>
</TkTable>
</div>
);
<script setup>
import { TkTable, TkDropdown, TkButton } from '@takeoff-ui/vue'
import { ref } from 'vue';
const stickyData=[
{
id: "f230fh0g3",
name: "Bamboo Watch",
category: "Accessories",
quantity: 24,
startDate:"12.20",
endDate:"13.20",
duration:"60 minutes",
place:"Ankara",
status:"Onboard"
},
{
id: "nvklal433",
name: "Black Watch",
category: "Onyama",
quantity: 42,
startDate:"11.40",
endDate:"15.20",
duration:"220 minutes",
place:"Istanbul",
status:"Boarding"
},
{
id: "zz21cz3c1",
name: "Blue Band",
category: "Accessories",
quantity: 87,
startDate:"09.00",
endDate:"15.00",
duration:"360 minutes",
place:"Paris",
status:"Departed"
},
{
id: "244wgerg2",
name: "Blue T-Shirt",
category: "Fitness",
quantity: 12,
startDate:"07.30",
endDate:"14.20",
duration:"410 minutes",
place:"London",
status:"Arrived"
},
{
id: "h456wer53",
name: "Bracelet",
category: "Clothing",
quantity: 45,
startDate:"18.20",
endDate:"06.20",
duration:"720 minutes",
place:"Tokyo",
status:"Checked-in"
},
]
const column = [
{
field: "id",
header: "Id",
fixed: "left",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
{
field: "startDate",
header: "Start Date",
},
{
field: "endDate",
header: "End Date",
},
{
field: "duration",
header: "Dration",
},
{
field: "place",
header: "Place",
},
{
field: "status",
header: "Status",
fixed: "right",
},
];
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable ref="tableRef" cardTitle="Sticky" :columns.prop="column" :data.prop="stickyData">
</TkTable>
</div>
</template>
Sticky Header
This feature allows the table header to remain fixed at the top when scrolling through large datasets. To enable sticky header functionality, you must provide a height value through the containerStyle prop.
- React
- Vue
- Angular
const tableRef = useRef<HTMLTkTableElement>(null);
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
{
field: "place",
header: "Place",
},
{
field: "status",
header: "Status",
},
];
return (
<TkTable columns={column} data={stickyData} containerStyle={{height: "300px"}}>
</TkTable>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
{
field: "place",
header: "Place",
},
{
field: "status",
header: "Status",
fixed: "right",
},
];
</script>
<template>
<TkTable :columns.prop="column" :data.prop="stickyData" :containerStyle="{height: "300px"}">
</TkTable>
</template>
Column Arrangement
This feature allows the user to drag and drop the columns to the desired position.
- React
- Vue
- Angular
const [columns, setColumns] = useState<ITableColumn[]>([
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Name',
},
{
field: 'category',
header: 'Category',
},
{
field: 'quantity',
header: 'Quantity',
},
]);
const [selectedColumns, setSelectedColumns] = useState<ITableColumn[]>(
columns.filter((item) => ['id', 'name'].includes(item.field)),
);
const handleDragStart = (e: React.DragEvent, index: number) => {
const parentElement = e.currentTarget.closest('div');
if (parentElement) {
e.dataTransfer.setData('text/plain', index.toString());
parentElement.classList.add('dragging');
e.dataTransfer.setDragImage(parentElement, 0, 0);
}
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
const handleDragEnd = (e: React.DragEvent) => {
const parentElement = e.currentTarget.closest('div');
if (parentElement) {
parentElement.classList.remove('dragging');
}
};
const handleDrop = (e: React.DragEvent, targetIndex: number) => {
e.preventDefault();
const sourceIndex = parseInt(e.dataTransfer.getData('text/plain'));
const newColumns = [...columns];
const [movedItem] = newColumns.splice(sourceIndex, 1);
newColumns.splice(targetIndex, 0, movedItem);
setColumns(newColumns);
setSelectedColumns(
newColumns.filter((item) =>
selectedColumns
.map((selectedCol) => selectedCol.field)
.includes(item.field),
),
);
};
return (
<div className="flex flex-col gap-2">
<TkPopover className="flex justify-end" position="bottom" trigger="click">
<TkButton
variant="neutral"
type="outlined"
slot="trigger"
label="Arrange Columns"
/>
<div slot="content">
<div className="flex flex-col gap-2">
{columns.map((col, index) => (
<div
key={col.field}
className="flex justify-between gap-2 py-2 px-3 hover:bg-gray-100 transition-colors"
>
<TkCheckbox
label={col.header}
value={
selectedColumns.findIndex(
(item) => item.field == col.field,
) > -1
}
onTkChange={(e) => {
if (e.detail) {
setSelectedColumns([...selectedColumns, col]);
} else {
setSelectedColumns(
selectedColumns.filter(
(item) => item.field != col.field,
),
);
}
}}
/>
<TkIcon
icon="drag_indicator"
variant="neutral"
draggable
onDragStart={(e) => handleDragStart(e, index)}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDrop={(e) => handleDrop(e, index)}
style={{ cursor: 'move' }}
/>
</div>
))}
</div>
</div>
</TkPopover>
<TkTable columns={selectedColumns} data={data} />
</div>
);
<script setup lang="ts">
import { ref } from 'vue';
import { ITableColumn } from '@takeoff-ui/core';
import {
TkTable,
TkCheckbox,
TkIcon,
TkPopover,
TkButton,
} from '@takeoff-ui/vue';
const columns = ref<ITableColumn[]>([
{ field: 'id', header: 'Id' },
{ field: 'name', header: 'Name' },
{ field: 'category', header: 'Category' },
{ field: 'quantity', header: 'Quantity' },
]);
const selectedColumns = ref<ITableColumn[]>(
columns.value.filter((item) => ['id', 'name'].includes(item.field)),
);
const handleDragStart = (e: DragEvent, index: number) => {
const parentElement = (e.currentTarget as HTMLElement).closest('div');
if (parentElement) {
e.dataTransfer?.setData('text/plain', index.toString());
parentElement.classList.add('dragging');
e.dataTransfer?.setDragImage(parentElement, 0, 0);
}
};
const handleDragOver = (e: DragEvent) => {
e.preventDefault();
};
const handleDragEnd = (e: DragEvent) => {
const parentElement = (e.currentTarget as HTMLElement).closest('div');
if (parentElement) {
parentElement.classList.remove('dragging');
}
};
const handleDrop = (e: DragEvent, targetIndex: number) => {
e.preventDefault();
const sourceIndex = parseInt(e.dataTransfer?.getData('text/plain') || '0');
const newColumns = [...columns.value];
const [movedItem] = newColumns.splice(sourceIndex, 1);
newColumns.splice(targetIndex, 0, movedItem);
columns.value = newColumns;
selectedColumns.value = newColumns.filter((item) =>
selectedColumns.value
.map((selectedCol) => selectedCol.field)
.includes(item.field),
);
};
const handleCheckboxChange = (e: any, col: ITableColumn) => {
if (e.detail) {
selectedColumns.value = [...selectedColumns.value, col];
} else {
selectedColumns.value = selectedColumns.value.filter(
(item) => item.field !== col.field,
);
}
};
</script>
<template>
<div class="flex flex-col gap-2">
<TkPopover
style="display: flex; justify-content: flex-end"
position="bottom"
trigger="click"
>
<TkButton
variant="neutral"
type="outlined"
slot="trigger"
label="Arrange Columns"
/>
<div slot="content">
<div
style="display: flex; flex-direction: column; gap: 8px; padding: 8px"
>
<div
v-for="(col, index) in columns"
:key="col.field"
style="
display: flex;
justify-content: space-between;
gap: 8px;
padding: 8px;
transition: background-color 0.3s ease;
"
>
<TkCheckbox
:label="col.header"
:value="
selectedColumns.findIndex((item) => item.field === col.field) >
-1
"
@tk-change="(e) => handleCheckboxChange(e, col)"
/>
<TkIcon
icon="drag_indicator"
variant="neutral"
draggable="true"
@dragstart="(e) => handleDragStart(e, index)"
@dragover="handleDragOver"
@dragend="handleDragEnd"
@drop="(e) => handleDrop(e, index)"
style="cursor: move"
/>
</div>
</div>
</div>
</TkPopover>
<TkTable :columns.prop="selectedColumns" :data.prop="data" />
</div>
</template>
Size
This feature allows the user to use alternative sizes.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable columns={column} data={basicData} size="small"/>
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable :columns.prop="column" :data.prop="basicData" size="small" />
</div>
</template>
<div style="padding: 8px">
<tk-table
[columns]="[
{ field: 'id', header: 'Id' },
{ field: 'name', header: 'Name' },
{ field: 'category', header: 'Category' },
{ field: 'quantity', header: 'Quantity' }
]"
[data]="[
{ id: 1, name: 'Product A', category: 'Electronics', quantity: 12 },
{ id: 2, name: 'Product B', category: 'Books', quantity: 8 },
{ id: 3, name: 'Product C', category: 'Groceries', quantity: 20 }
]"
size="small"
/>
</div>
Empty Data Slot
This feature allows you to customize the content displayed when there are no data in the table.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
const [data, setData] = useState([]);
return (
<div className="p-2">
<div style={{ marginBottom: '1rem', display: 'flex', gap: '0.5rem' }}>
<TkButton label="Load Data" onTkClick={() => setData(basicData)} className="mt-2" />
<TkButton label="Show Empty Data Slot" onTkClick={() => setData([])} className="mt-2" />
</div>
<TkTable columns={column} data={data}>
<div slot="empty-data" className="p-4 text-center text-gray-500">
No data found...
</div>
</TkTable>
</div>
);
<script setup>
import { TkButton, TkTable } from '@takeoff-ui/vue';
import { ref } from 'vue';
import { basicData } from './data';
const data = ref([]);
const columns = [
{
field: 'id',
header: 'Id',
},
{
field: 'name',
header: 'Name',
},
{
field: 'category',
header: 'Category',
},
{
field: 'quantity',
header: 'Quantity',
},
];
const loadData = () => {
data.value = basicData;
};
const showEmptySlot = () => {
data.value = [];
};
</script>
<template>
<div class="p-2">
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem">
<TkButton label="Load Data" @tk-click="loadData" class="mt-2" />
<TkButton label="Show Empty Data Slot" @tk-click="showEmptySlot" class="mt-2" />
</div>
<TkTable :columns.prop="columns" :data.prop="data">
<div slot="empty-data" class="p-4 text-center text-gray-500">No data found...</div>
</TkTable>
</div>
</template>
<div style="padding: 8px">
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem">
<tk-button label="Load Data" (tkClick)="loadData()" class="mt-2"></tk-button>
<tk-button label="Show Empty Data Slot" (tkClick)="showEmptySlot()" class="mt-2"></tk-button>
</div>
<tk-table [columns]="columns" [data]="data">
<div slot="empty-data" class="p-4 text-center text-gray-500">
No data found...
</div>
</tk-table>
</div>
Header and Footer Slot
This feature allows you to add custom header and footer rows within the table body (<tbody>). You can use the slot attribute to specify whether the row is a header or footer. This is useful for adding summary rows, additional information, or custom styling to specific sections of the table body.
- React
- Vue
- Angular
const column: ITableColumn[] = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
return (
<div style={{ padding: "8px" }}>
<TkTable columns={column} data={basicData}>
<tr slot="body-header" style={{ background: 'white', fontWeight: 'bold' }}>
<td style={{ padding: '0 8px' }}>Cell Sum Data</td>
<td colSpan={3} style={{ padding: '0 8px' }}>This is a custom header row in tbody</td>
</tr>
<tr slot="body-footer" style={{ background: 'white', fontWeight: 'bold' }}>
<td style={{ padding: '0 8px' }}>Cell Sum Data</td>
<td colSpan={3} style={{ padding: '0 8px' }}>This is a custom footer row in tbody</td>
</tr>
</TkTable>
</div>
);
<script setup>
import { TkTable } from '@takeoff-ui/vue'
const column = [
{
field: "id",
header: "Id",
},
{
field: "name",
header: "Name",
},
{
field: "category",
header: "Category",
},
{
field: "quantity",
header: "Quantity",
},
];
</script>
<template>
<div :style="{ padding: '8px' }">
<TkTable columns={column} data={basicData}>
<tr slot="body-header" style="background: white; font-weight: bold">
<td style="padding:0 8px">Cell Sum Data</td>
<td colSpan={3} style="padding:0 8px">This is a custom header row in tbody</td>
</tr>
<tr slot="body-footer" style="background: 'white'; font-weight: bold;">
<td style="padding:0 8px">Cell Sum Data</td>
<td colSpan={3} style="padding:0 8px">This is a custom footer row in tbody</td>
</tr>
</TkTable>
</div>
</template>
Grouped Rows
The TkTable component supports row grouping functionality that allows you to organize table data by specific column values. This feature provides visual grouping with headers showing group names and item counts, and works seamlessly with pagination, sorting, and selection.
Key Features
- Visual Group Headers: Automatically generated headers for each group showing the group value and count
- Pagination Support: Groups work correctly with both client-side and server-side pagination
- Selection Persistence: Row selections are maintained across grouping changes
- Sorting Integration: Grouped data respects existing sort configurations
- Event Handling: Listen to grouping changes with the
onTkGroupByChangeevent
Two Implementation Approaches
1. Controlled Grouping (State-driven) Use the groupBy prop with component
state for external control. This approach gives you full control over the
grouping state and allows integration with other application state management.
2. Uncontrolled Grouping (Method-driven)
Use imperative methods like groupByColumn() and clearGrouping() for internal
control. This approach is simpler when you don't need to manage grouping state
externally.
🎛️ Controlled Grouping (State-driven)
Current groupBy value: status
🔧 Uncontrolled Grouping (Method-driven)
Grouping is controlled internally via imperative methods. Use the buttons below to trigger grouping changes.
💡 Key Features:
- Controlled Grouping: Use the
groupByprop with React state for external control - Uncontrolled Grouping: Use
groupByColumn()andclearGrouping()methods - Event Handling: Listen to
onTkGroupByChangefor grouping state changes - Visual Grouping: Rows are visually grouped with headers showing group name and count
- Pagination Support: Grouping works seamlessly with both client and server-side pagination
- Selection Persistence: Row selection is maintained across grouping changes
- React
- Vue
- Angular
import { ITableColumn } from '@takeoff-ui/core';
import { TkButton, TkTable } from '@takeoff-ui/react';
import { useRef, useState } from 'react';
function GroupedRowsExample() {
const controlledTableRef = useRef<HTMLTkTableElement>(null);
const uncontrolledTableRef = useRef<HTMLTkTableElement>(null);
// Controlled table state
const [controlledGroupBy, setControlledGroupBy] = useState<string>('status');
const [controlledSelectedRows, setControlledSelectedRows] = useState<any[]>([]);
const [uncontrolledSelectedRows, setUncontrolledSelectedRows] = useState<any[]>([]);
// Sample data
const sampleData = [
{ id: 1, name: 'Website Redesign', status: 'In Progress', priority: 'High', department: 'Design' },
{ id: 2, name: 'API Development', status: 'In Progress', priority: 'High', department: 'Engineering' },
{ id: 3, name: 'Database Migration', status: 'Completed', priority: 'Critical', department: 'Engineering' },
{ id: 4, name: 'Marketing Campaign', status: 'Planning', priority: 'Medium', department: 'Marketing' },
// ... more data
];
const columns: ITableColumn[] = [
{ header: 'Project Name', field: 'name', sortable: true, searchable: true },
{ header: 'Status', field: 'status', sortable: true, searchable: true },
{ header: 'Priority', field: 'priority', sortable: true, searchable: true },
{ header: 'Department', field: 'department', sortable: true, searchable: true },
];
// Controlled grouping handlers
const handleControlledGrouping = (field: string) => {
setControlledGroupBy(field);
};
const clearControlledGrouping = () => {
setControlledGroupBy('');
};
// Uncontrolled grouping handlers (using imperative methods)
const handleUncontrolledGrouping = async (field: string) => {
await uncontrolledTableRef.current?.groupByColumn(field);
};
const clearUncontrolledGrouping = async () => {
await uncontrolledTableRef.current?.clearGrouping();
};
// Event handlers
const handleControlledSelectionChange = (e: any) => {
setControlledSelectedRows(e.detail);
};
const handleControlledGroupByChange = (e: any) => {
console.log('GroupBy changed to:', e.detail || 'none');
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
{/* Controlled Table */}
<div>
<h3>🎛️ Controlled Grouping (State-driven)</h3>
<p>Current groupBy: <strong>{controlledGroupBy || 'none'}</strong></p>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
<TkButton onTkClick={() => handleControlledGrouping('status')} label="Group by Status" type="outlined" />
<TkButton onTkClick={() => handleControlledGrouping('priority')} label="Group by Priority" type="outlined" />
<TkButton onTkClick={() => handleControlledGrouping('department')} label="Group by Department" type="outlined" />
<TkButton onTkClick={clearControlledGrouping} label="Clear Grouping" type="text" />
</div>
<TkTable
ref={controlledTableRef}
dataKey="id"
cardTitle="Controlled Table"
rowsPerPage={8}
paginationMethod="client"
columns={columns}
data={sampleData}
selectionMode="checkbox"
selection={controlledSelectedRows}
onTkSelectionChange={handleControlledSelectionChange}
onTkGroupByChange={handleControlledGroupByChange}
groupBy={controlledGroupBy}
striped={true}
/>
</div>
{/* Uncontrolled Table */}
<div>
<h3>🔧 Uncontrolled Grouping (Method-driven)</h3>
<p>Grouping controlled internally via imperative methods</p>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
<TkButton onTkClick={() => handleUncontrolledGrouping('status')} label="Group by Status" type="outlined" />
<TkButton onTkClick={() => handleUncontrolledGrouping('priority')} label="Group by Priority" type="outlined" />
<TkButton onTkClick={() => handleUncontrolledGrouping('department')} label="Group by Department" type="outlined" />
<TkButton onTkClick={clearUncontrolledGrouping} label="Clear Grouping" type="text" />
</div>
<TkTable
ref={uncontrolledTableRef}
dataKey="id"
cardTitle="Uncontrolled Table"
rowsPerPage={8}
paginationMethod="client"
columns={columns}
data={sampleData}
selectionMode="checkbox"
selection={uncontrolledSelectedRows}
onTkSelectionChange={handleUncontrolledSelectionChange}
onTkGroupByChange={handleUncontrolledGroupByChange}
striped={true}
/>
</div>
</div>
);
}
<script setup>
import { ITableColumn } from '@takeoff-ui/core';
import { TkButton, TkTable } from '@takeoff-ui/vue';
import { ref } from 'vue';
const controlledTableRef = ref(null);
const uncontrolledTableRef = ref(null);
// Controlled table state
const controlledGroupBy = ref('status');
const controlledSelectedRows = ref([]);
const uncontrolledSelectedRows = ref([]);
// Sample data
const sampleData = [
{ id: 1, name: 'Website Redesign', status: 'In Progress', priority: 'High', department: 'Design' },
{ id: 2, name: 'API Development', status: 'In Progress', priority: 'High', department: 'Engineering' },
{ id: 3, name: 'Database Migration', status: 'Completed', priority: 'Critical', department: 'Engineering' },
{ id: 4, name: 'Marketing Campaign', status: 'Planning', priority: 'Medium', department: 'Marketing' },
// ... more data
];
const columns = [
{ header: 'Project Name', field: 'name', sortable: true, searchable: true },
{ header: 'Status', field: 'status', sortable: true, searchable: true },
{ header: 'Priority', field: 'priority', sortable: true, searchable: true },
{ header: 'Department', field: 'department', sortable: true, searchable: true },
];
// Controlled grouping handlers
const handleControlledGrouping = (field) => {
controlledGroupBy.value = field;
};
const clearControlledGrouping = () => {
controlledGroupBy.value = '';
};
// Uncontrolled grouping handlers
const handleUncontrolledGrouping = async (field) => {
await uncontrolledTableRef.value?.groupByColumn(field);
};
const clearUncontrolledGrouping = async () => {
await uncontrolledTableRef.value?.clearGrouping();
};
// Event handlers
const handleControlledSelectionChange = (e) => {
controlledSelectedRows.value = e.detail;
};
const handleControlledGroupByChange = (e) => {
console.log('GroupBy changed to:', e.detail || 'none');
};
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 24px;">
<!-- Controlled Table -->
<div>
<h3>🎛️ Controlled Grouping (State-driven)</h3>
<p>Current groupBy: <strong>{{ controlledGroupBy || 'none' }}</strong></p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<TkButton @tk-click="() => handleControlledGrouping('status')" label="Group by Status" type="outlined" />
<TkButton @tk-click="() => handleControlledGrouping('priority')" label="Group by Priority" type="outlined" />
<TkButton @tk-click="() => handleControlledGrouping('department')" label="Group by Department" type="outlined" />
<TkButton @tk-click="clearControlledGrouping" label="Clear Grouping" type="text" />
</div>
<TkTable
ref="controlledTableRef"
:data-key.prop="'id'"
card-title="Controlled Table"
:rows-per-page.prop="8"
pagination-method="client"
:columns.prop="columns"
:data.prop="sampleData"
selection-mode="checkbox"
:selection.prop="controlledSelectedRows"
@tk-selection-change="handleControlledSelectionChange"
@tk-group-by-change="handleControlledGroupByChange"
:group-by.prop="controlledGroupBy"
:striped.prop="true"
/>
</div>
<!-- Uncontrolled Table -->
<div>
<h3>🔧 Uncontrolled Grouping (Method-driven)</h3>
<p>Grouping controlled internally via imperative methods</p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<TkButton @tk-click="() => handleUncontrolledGrouping('status')" label="Group by Status" type="outlined" />
<TkButton @tk-click="() => handleUncontrolledGrouping('priority')" label="Group by Priority" type="outlined" />
<TkButton @tk-click="() => handleUncontrolledGrouping('department')" label="Group by Department" type="outlined" />
<TkButton @tk-click="clearUncontrolledGrouping" label="Clear Grouping" type="text" />
</div>
<TkTable
ref="uncontrolledTableRef"
:data-key.prop="'id'"
card-title="Uncontrolled Table"
:rows-per-page.prop="8"
pagination-method="client"
:columns.prop="columns"
:data.prop="sampleData"
selection-mode="checkbox"
:selection.prop="uncontrolledSelectedRows"
@tk-selection-change="handleUncontrolledSelectionChange"
@tk-group-by-change="handleUncontrolledGroupByChange"
:striped.prop="true"
/>
</div>
</div>
</template>
import { Component, ViewChild, ElementRef } from '@angular/core';
import { ITableColumn } from '@takeoff-ui/core';
@Component({
selector: 'app-grouped-rows',
template: `
<div style="display: flex; flex-direction: column; gap: 24px;">
<!-- Controlled Table -->
<div>
<h3>🎛️ Controlled Grouping (State-driven)</h3>
<p>Current groupBy: <strong>{{ controlledGroupBy || 'none' }}</strong></p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<tk-button (tk-click)="handleControlledGrouping('status')" label="Group by Status" type="outlined"></tk-button>
<tk-button (tk-click)="handleControlledGrouping('priority')" label="Group by Priority" type="outlined"></tk-button>
<tk-button (tk-click)="handleControlledGrouping('department')" label="Group by Department" type="outlined"></tk-button>
<tk-button (tk-click)="clearControlledGrouping()" label="Clear Grouping" type="text"></tk-button>
</div>
<tk-table
#controlledTableRef
[dataKey]="'id'"
cardTitle="Controlled Table"
[rowsPerPage]="8"
paginationMethod="client"
[columns]="columns"
[data]="sampleData"
selectionMode="checkbox"
[selection]="controlledSelectedRows"
(tk-selection-change)="handleControlledSelectionChange($event)"
(tk-group-by-change)="handleControlledGroupByChange($event)"
[groupBy]="controlledGroupBy"
[striped]="true">
</tk-table>
</div>
<!-- Uncontrolled Table -->
<div>
<h3>🔧 Uncontrolled Grouping (Method-driven)</h3>
<p>Grouping controlled internally via imperative methods</p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<tk-button (tk-click)="handleUncontrolledGrouping('status')" label="Group by Status" type="outlined"></tk-button>
<tk-button (tk-click)="handleUncontrolledGrouping('priority')" label="Group by Priority" type="outlined"></tk-button>
<tk-button (tk-click)="handleUncontrolledGrouping('department')" label="Group by Department" type="outlined"></tk-button>
<tk-button (tk-click)="clearUncontrolledGrouping()" label="Clear Grouping" type="text"></tk-button>
</div>
<tk-table
#uncontrolledTableRef
[dataKey]="'id'"
cardTitle="Uncontrolled Table"
[rowsPerPage]="8"
paginationMethod="client"
[columns]="columns"
[data]="sampleData"
selectionMode="checkbox"
[selection]="uncontrolledSelectedRows"
(tk-selection-change)="handleUncontrolledSelectionChange($event)"
(tk-group-by-change)="handleUncontrolledGroupByChange($event)"
[striped]="true">
</tk-table>
</div>
</div>
`
})
export class GroupedRowsComponent {
@ViewChild('controlledTableRef') controlledTableRef!: ElementRef;
@ViewChild('uncontrolledTableRef') uncontrolledTableRef!: ElementRef;
controlledGroupBy: string = 'status';
controlledSelectedRows: any[] = [];
uncontrolledSelectedRows: any[] = [];
sampleData = [
{ id: 1, name: 'Website Redesign', status: 'In Progress', priority: 'High', department: 'Design' },
{ id: 2, name: 'API Development', status: 'In Progress', priority: 'High', department: 'Engineering' },
{ id: 3, name: 'Database Migration', status: 'Completed', priority: 'Critical', department: 'Engineering' },
{ id: 4, name: 'Marketing Campaign', status: 'Planning', priority: 'Medium', department: 'Marketing' },
// ... more data
];
columns: ITableColumn[] = [
{ header: 'Project Name', field: 'name', sortable: true, searchable: true },
{ header: 'Status', field: 'status', sortable: true, searchable: true },
{ header: 'Priority', field: 'priority', sortable: true, searchable: true },
{ header: 'Department', field: 'department', sortable: true, searchable: true },
];
// Controlled grouping handlers
handleControlledGrouping(field: string) {
this.controlledGroupBy = field;
}
clearControlledGrouping() {
this.controlledGroupBy = '';
}
// Uncontrolled grouping handlers
async handleUncontrolledGrouping(field: string) {
await this.uncontrolledTableRef.nativeElement.groupByColumn(field);
}
async clearUncontrolledGrouping() {
await this.uncontrolledTableRef.nativeElement.clearGrouping();
}
// Event handlers
handleControlledSelectionChange(event: any) {
this.controlledSelectedRows = event.detail;
}
handleControlledGroupByChange(event: any) {
console.log('GroupBy changed to:', event.detail || 'none');
}
handleUncontrolledSelectionChange(event: any) {
this.uncontrolledSelectedRows = event.detail;
}
handleUncontrolledGroupByChange(event: any) {
console.log('GroupBy changed to:', event.detail || 'none');
}
}
API
Props
| Name | Type | Default | Description |
|---|---|---|---|
string | '' | ||
(row: any, column: ITableColumn)=>any | null | Provides a function to customize cell styles. This function takes the row and column information and returns the style object for a specific cell. | |
ITableColumn[] | [] | The column definitions (Array of Objects) | |
CSSStyleProperties | null | The style attribute of container element | |
any[] | [] | Rows of data to display | |
string | null | Property of each row that defines the unique key of each row | |
(row: any)=>any | null | Provides a function to customize expanded row styles. This function takes row information and returns the style object for the expanded row content. | |
any[] | [] | Specifies which rows are expanded to show additional content. | |
string | null | Column field name to group the table data by. When specified, the table will automatically group rows by unique values in this column. Set to null or undefined to disable grouping. This makes the component controlled - changes should be handled via tkGroupByChange event. | |
"basic", "dark", "primary" | 'basic' | Style to apply to header of table | |
boolean | null | Displays a loading indicator while data is being fetched or processed. | |
boolean | false | Enables multi-column sorting. | |
string | null | Defines whether pagination is handled on the client or server side. | |
"grouped", "outlined", "text" | 'outlined' | The type of the pagination | |
(row: any, index?: number)=>any | null | Provides a function to customize row styles. This function takes row information and row index, and returns the style object for a specific row. | |
number | 6 | Number of items per page. | |
number[] | null | Number of rows per page options | |
any | [] | List of the selected | |
"checkbox", "radio" | null | Determines how rows can be selected, either with radio buttons (single selection) or checkboxes (multiple selection). | |
Function | null | A function that returns true if the row should be disabled | |
"base", "small", "xsmall" | 'base' | Sets size for the component. | |
boolean | false | Enables or disables alternating row background colors for easier readability. | |
number | null | Number of total items. |
Events
| Name | Description |
|---|---|
| tk-cell-edit | Emitted when a cell is edited. |
| tk-expanded-rows-change | Emitted when the expanded rows change. |
| tk-group-by-change | Emitted when the groupBy value changes. Always emitted for both controlled and uncontrolled components. For controlled components, handle this event to update the groupBy prop. |
| tk-request | Emitted when a request needs to be made to the server. |
| tk-row-click | Emitted when a row is clicked. |
| tk-selection-change |
Methods
| Name | Description |
|---|---|
| clearFilters | Clears all filters or specific column filters |
| clearGrouping | Clears the current grouping and returns to normal table view Always emits tkGroupByChange event with null value. For uncontrolled components, also clears internal state. |
| clearSorting | Clears all sorting for server side pagination |
| exportFile | Exports the table data to a file |
| getFilters | Returns the current filters |
| getSorting | Returns the current sorting settings |
| groupByColumn | Groups table data by the specified column field Creates group header rows that display the unique value and count of items in that group. For example, if you have a 'status' column with values 'Open' and 'Closed', this will create group headers like "Open (5)" and "Closed (3)". Always emits tkGroupByChange event. For uncontrolled components, also updates internal state. |
| runFilters | Applies the current filters to the data for client side pagination |
| serverRequest | Allows tk-request event to be triggered manually |
| setCurrentPage | Sets the current page for pagination |
| setFilters | Sets the current filter settings |
| setSorting | Sets the current sorting settings |
Slots
| Name | Description |
|---|---|
| body-footer | Custom independent rows at the bottom of tbody (e.g., totals, summary, or additional data rows) |
| body-header | Custom independent rows at the top of tbody (e.g., summary, totals, or custom data rows) |
| empty-data | Set how the table will appear when there is no data |
Interfaces
ITableColumn
Defines the columns for the table
interface ITableColumn {
/** Defines the field for the column */
field: string;
/** Defines heading for the column */
header: string;
/** Defines sub heading for the column */
subHeader?: string;
/** Defines width for the column */
width?: string;
/** Indicates if the column supports sorting */
sortable?: boolean;
/** Custom sort function for the column, mandatory when using client-side sorting. */
sorter?: Function;
/** Custom filter function for the column, mandatory when using client-side filtering. */
filter?: Function;
/** Indicates if the column is searchable */
searchable?: boolean;
/** Indicates if the column is editable */
editable?: boolean;
/** Specifies the input type for editable columns */
inputType?: string;
/** Indicates if the column contains selection checkboxes */
selectColumn?: boolean;
/** Indicates if the column acts as an expander */
expander?: boolean;
/** Custom rendering function for HTML content in the column header */
headerHtml?: Function;
/** Custom rendering function for HTML content in the column cells */
html?: Function;
/** */
fixed?: 'left' | 'right';
/** Allows styling to be applied to the th element of the column */
style?: CSSStyleProperties;
/** When true, search and sort icons will only be displayed when hovering over the th element */
showIconsOnHover?: boolean;
/** Defines the filter type for this column (text, checkbox or radio) */
filterType?: 'text' | 'checkbox' | 'radio' | 'datepicker';
/** Defines options for checkbox or radio filter type */
filterOptions?: IFilterOption[];
/** Defines the label of the buttons */
filterButtons?: {
searchButton?: { label?: string };
cancelButton?: { label?: string };
selectAllCheckbox?: { label?: string };
};
filterElements?: {
icon?: string;
searchInput?: { placeholder?: string };
searchButton?: { label?: string };
cancelButton?: { label?: string };
selectAllCheckbox?: { label?: string };
optionsSearchInput?: { show?: boolean; placeholder?: string };
optionsSearchDatepicker?: {
label?: string;
placeholder?: string;
mode?: 'single' | 'range';
dateFormat?: string;
timeFormat?: '24' | '12';
minDate?: string;
maxDate?: string;
hourStep?: number;
minuteStep?: number;
locale?: string;
showTimePicker?: boolean;
size?: 'small' | 'base' | 'large';
};
};
headerActionsOptions?: {
direction?: 'vertical' | 'horizontal';
};
}
ITableRequest
It is the return type of the tkRequest event.
interface ITableRequest {
/** The current page number */
currentPage: number;
/** The total number of pages */
totalPages: number;
/** The starting index of the items on the current page */
startItem: number;
/** The ending index of the items on the current page */
endItem: number;
/** The number of rows per page */
rowsPerPage: number;
/** The field by which the table is sorted */
sortField?: string;
/** The order of sorting: 'asc' or 'desc' */
sortOrder?: string;
/** Array of sort information for multi-sort functionality. When multiple sorts are applied, they are processed in priority order. */
sorts?: ITableSort[];
/** A list of filters applied to the table */
filters: ITableFilter[];
}
ITableCellEdit
It is the return type of the tkCellEdit event.
interface ITableCellEdit {
/** The unique identifier of the row being edited. Contains the value of the dataKey prop in the edited row */
rowId: string;
/** The index of the row being edited */
rowIndex: number;
/** The field being edited */
field: string;
/** The new value for the field */
value: string;
}
ITableExportOptions
interface ITableExportOptions {
/** only works when type is `pdf`. Default value is `vertical` */
orientation?: 'horizontal' | 'vertical';
/** */
fileName?: string;
/** */
type?: 'csv' | 'pdf' | 'excel';
/** `all` working only client side pagination. Default value is `current-page` */
scope?: 'current-page' | 'selected' | 'all';
/** Ignore Columns Fields array for only excel export */
ignoreColumnsFields?: string[];
/** Columns for only excel export */
columns?: ITableExportExcelColumn[];
/** */
externalData?: any[];
}
ITableFilter
Represents a filter applied to a table
interface ITableFilter {
/** The value of the filter - string for text/radio/datepciker filter, string array for checkbox filter, IDateSelection for datepicker filter */
value?: string | string[] | IDateSelection;
/** The field to which the filter is applied */
field: string;
/** The type of the filter (text, checkbox, radio, or datepicker) */
type?: 'text' | 'checkbox' | 'radio' | 'datepicker';
}
ITableSort
interface ITableSort {
/** The field name being sorted */
field: string;
/** The sort direction */
order: 'asc' | 'desc';
}
IFilterOption
Defines options for checkbox filter
interface IFilterOption {
/** The value of the option */
value: string;
/** The display label of the option */
label?: string;
}
ICustomElement
interface ICustomElement {
ref: HTMLElement;
element: HTMLElement;
}
ITableExportExcelColumn
interface ITableExportExcelColumn {
header: string;
field: string;
width: number;
}
ITableGroup
Represents a group of table rows with associated metadata
interface ITableGroup {
/** The value that this group represents (e.g., "Active", "Completed") */
groupValue: any;
/** The number of rows in this group */
groupCount: number;
/** The array of row data objects belonging to this group */
rows: any[];
}