Filters and sorting
The column filter menu is set by filters and onFilter, the sorting by the sorter function. sortDirections lists the directions, defaultSortOrder sets the initial order, and filterMultiple: false switches the filter to single choice. Every change is reported by the change event.
<script setup lang="ts">
import type { ITableProps, NTable } from '@central-design-system/components';
import { CdsTable } from '@central-design-system/components';
type TDataType = { key: string; name: string; age: number; address: string };
const columns: NTable.NColumn.TColumns<TDataType> = [
{
title: 'Имя',
dataIndex: 'name',
filters: [
{ text: 'Иван', value: 'Иван' },
{ text: 'Петр', value: 'Петр' },
{
text: 'Фамилии',
value: 'Submenu',
children: [
{ text: 'Иванов', value: 'Иван Иванов' },
{ text: 'Петров', value: 'Петр Петров' }
]
}
],
onFilter: (value, record: TDataType) => record.name.indexOf(value as string) === 0,
sorter: (a: TDataType, b: TDataType) => a.name.length - b.name.length,
sortDirections: ['descend']
},
{
title: 'Возраст',
dataIndex: 'age',
defaultSortOrder: 'descend',
sorter: (a: TDataType, b: TDataType) => a.age - b.age
},
{
title: 'Адрес',
dataIndex: 'address',
filters: [
{ text: 'Москва', value: 'Москва' },
{ text: 'Санкт-Петербург', value: 'Санкт-Петербург' }
],
filterMultiple: false,
onFilter: (value, record: TDataType) => record.address.indexOf(value as string) === 0,
sorter: (a: TDataType, b: TDataType) => a.address.length - b.address.length,
sortDirections: ['descend', 'ascend']
}
];
const data: TDataType[] = [
{ key: '1', name: 'Иван Иванов', age: 40, address: 'Москва, ул. Ленина, д. 1' },
{ key: '2', name: 'Петр Петров', age: 42, address: 'Санкт-Петербург, ул. Ленина, д. 1' },
{ key: '3', name: 'Иван Иванов', age: 18, address: 'Санкт-Петербург, ул. Ленина, д. 2' },
{ key: '4', name: 'Петр Петров', age: 28, address: 'Москва, ул. Ленина, д. 2' }
];
const onChange: ITableProps<TDataType>['onChange'] = (pagination, filters, sorter) => {
console.log('onChange', pagination, filters, sorter);
};
</script>
<template>
<CdsTable :columns="columns" :data="data" @change="onChange" />
</template>Multiple sorting
A sorter object with a multiple field allows sorting by several columns at once — the number sets the column's priority in the chain.
<script setup lang="ts">
import type { ITableProps, NTable } from '@central-design-system/components';
import { CdsTable } from '@central-design-system/components';
type TDataType = { key: string; product: string; usd: number; eur: number; rub: number };
const columns: NTable.NColumn.TColumns<TDataType> = [
{ title: 'Продукт', dataIndex: 'product' },
{
title: 'Цена USD',
dataIndex: 'usd',
sorter: { compare: (a, b) => a.usd - b.usd, multiple: 3 }
},
{
title: 'Цена EUR',
dataIndex: 'eur',
sorter: { compare: (a, b) => a.eur - b.eur, multiple: 2 }
},
{
title: 'Цена RUB',
dataIndex: 'rub',
sorter: { compare: (a, b) => a.rub - b.rub, multiple: 1 }
}
];
const data: TDataType[] = [
{ key: '1', product: 'Позиция 1', usd: 100, eur: 92, rub: 9100 },
{ key: '2', product: 'Позиция 2', usd: 50, eur: 46, rub: 4530 },
{ key: '3', product: 'Позиция 3', usd: 150, eur: 138, rub: 13590 },
{ key: '4', product: 'Позиция 4', usd: 25, eur: 23, rub: 2265 }
];
const onChange: ITableProps['onChange'] = (pagination, filters, sorter, extra) => {
console.log('params', pagination, filters, sorter, extra);
};
</script>
<template>
<CdsTable :columns="columns" :data="data" @change="onChange" />
</template>Resetting filters and sorting
The filteredValue and sortOrder column props make the state controlled: keep it outside, update it in the change handler — and reset it with buttons in a single assignment.
<script setup lang="ts">
import type { ITableProps, NTable } from '@central-design-system/components';
import { CdsButton, CdsTable } from '@central-design-system/components';
import { computed, ref } from 'vue';
type TDataItem = { key: string; name: string; age: number; address: string };
const filteredInfo = ref();
const sortedInfo = ref();
const columns = computed<NTable.NColumn.TColumns<TDataItem>>(() => {
const filtered = filteredInfo.value || {};
const sorted = sortedInfo.value || {};
return [
{
title: 'Имя',
dataIndex: 'name',
key: 'name',
filters: [
{ text: 'Иван', value: 'Иван' },
{ text: 'Петр', value: 'Петр' }
],
filteredValue: filtered.name || null,
onFilter: (value, record: TDataItem) => record.name.includes(value as string),
sorter: (a: TDataItem, b: TDataItem) => a.name.length - b.name.length,
sortOrder: sorted.columnKey === 'name' && sorted.order,
ellipsis: true
},
{
title: 'Возраст',
dataIndex: 'age',
key: 'age',
sorter: (a: TDataItem, b: TDataItem) => a.age - b.age,
sortOrder: sorted.columnKey === 'age' && sorted.order
},
{
title: 'Адрес',
dataIndex: 'address',
key: 'address',
filters: [
{ text: 'Москва', value: 'Москва' },
{ text: 'Санкт-Петербург', value: 'Санкт-Петербург' }
],
filteredValue: filtered.address || null,
onFilter: (value, record: TDataItem) => record.address.includes(value as string),
sorter: (a: TDataItem, b: TDataItem) => a.address.length - b.address.length,
sortOrder: sorted.columnKey === 'address' && sorted.order,
ellipsis: true
}
] as NTable.NColumn.TColumns<TDataItem>;
});
const data: TDataItem[] = [
{ key: '1', name: 'Иван Иванов', age: 40, address: 'Москва, ул. Ленина, д. 1' },
{ key: '2', name: 'Петр Петров', age: 42, address: 'Санкт-Петербург, ул. Ленина, д. 1' },
{ key: '3', name: 'Иван Иванов', age: 18, address: 'Санкт-Петербург, ул. Ленина, д. 2' },
{ key: '4', name: 'Петр Петров', age: 28, address: 'Москва, ул. Ленина, д. 2' }
];
const handleChange: ITableProps['onChange'] = (pagination, filters, sorter) => {
filteredInfo.value = filters;
sortedInfo.value = sorter;
};
const clearFilters = () => {
filteredInfo.value = null;
};
const clearAll = () => {
filteredInfo.value = null;
sortedInfo.value = null;
};
const setAgeSort = () => {
sortedInfo.value = { order: 'descend', columnKey: 'age' };
};
</script>
<template>
<CdsTable :columns="columns" :data="data" @change="handleChange">
<template #toolbar>
<div class="flex justify-end gap-2">
<CdsButton variant="soft" color="brand" @click="clearFilters">Очистить фильтры</CdsButton>
<CdsButton variant="soft" color="brand" @click="clearAll">Очистить фильтры и сортировки</CdsButton>
<CdsButton variant="solid" color="brand" @click="setAgeSort">Сортировать возраст</CdsButton>
</div>
</template>
</CdsTable>
</template>Custom filter panel
The column's customFilterDropdown: true flag enables the customFilterDropdown and customFilterIcon slots — the filter panel and its icon are drawn by your markup, a search field for example.
<script setup lang="ts">
import type { NTable } from '@central-design-system/components';
import { CdsButton, CdsDivider, CdsIcon, CdsTable, CdsTextInput } from '@central-design-system/components';
import { reactive, ref, toRefs } from 'vue';
type TDataType = { key: string; name: string; age: number; address: string };
const state = reactive({
searchText: '',
searchedColumn: ''
});
const searchInput = ref();
const columns: NTable.NColumn.TColumns<TDataType> = [
{
title: 'Имя',
dataIndex: 'name',
key: 'name',
customFilterDropdown: true,
onFilter: (value, record) =>
record.name
.toString()
.toLowerCase()
.includes((value as string).toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
searchInput.value?.focus?.();
}, 100);
}
}
},
{ title: 'Возраст', dataIndex: 'age', key: 'age' },
{
title: 'Адрес',
dataIndex: 'address',
key: 'address',
customFilterDropdown: true,
onFilter: (value, record) =>
record.address
.toString()
.toLowerCase()
.includes((value as string).toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
searchInput.value?.focus?.();
}, 100);
}
}
}
];
const data: TDataType[] = [
{ key: '1', name: 'Иван Иванов', age: 18, address: 'г. Москва ул. Ленина д. 1' },
{ key: '2', name: 'Петр Петров', age: 25, address: 'г. Санкт-Петербург ул. Ленина д. 1' },
{ key: '3', name: 'Александр Александрович', age: 48, address: 'г. Екатеринбург ул. Ленина д. 1' },
{ key: '4', name: 'Иван Петрович', age: 42, address: 'г. Новосибирск ул. Ленина д. 1' }
];
const handleSearch = (selectedKeys: NTable.TKey[], confirm: Function, dataIndex?: NTable.TDataIndex) => {
confirm();
state.searchText = String(selectedKeys[0] ?? '');
state.searchedColumn = String(dataIndex ?? '');
};
const handleReset = (clearFilters: ({ confirm }: { confirm: boolean }) => void) => {
clearFilters({ confirm: true });
state.searchText = '';
};
const searchingRegExp = (searchText: string) => new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i');
const { searchText, searchedColumn } = toRefs(state);
</script>
<template>
<CdsTable :columns="columns" :data="data">
<template #customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<div class="p-1">
<cds-text-input
ref="searchInput"
:placeholder="'Введите ' + String(column?.title ?? '').toLowerCase()"
:model-value="selectedKeys[0]"
size="sm"
clearable
hide-messages
class="w-[176px]"
@update:model-value="(value: any) => setSelectedKeys(value ? [value] : [])"
@keydown.enter="handleSearch(selectedKeys, confirm, column?.dataIndex)"
/>
<cds-divider class="my-3" />
<div class="flex gap-2">
<cds-button text="Сбросить" size="sm" variant="soft" color="brand" @click="handleReset(clearFilters)" />
<cds-button
text="Найти"
size="sm"
prepend-icon="search"
@click="handleSearch(selectedKeys, confirm, column?.dataIndex)"
/>
</div>
</div>
</template>
<template #customFilterIcon>
<cds-icon name="search" />
</template>
<template #bodyCell="{ text, column }">
<span v-if="searchText && searchedColumn === column.dataIndex">
<template v-for="(fragment, i) in text.toString().split(searchingRegExp(searchText))">
<mark v-if="fragment.toLowerCase() === searchText.toLowerCase()" :key="i">
{{ fragment }}
</mark>
<template v-else>{{ fragment }}</template>
</template>
</span>
</template>
</CdsTable>
</template>