@snack-uikit/table 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +60 -5
- package/dist/components/ServerTable/ServerTable.d.ts +48 -0
- package/dist/components/ServerTable/ServerTable.js +38 -0
- package/dist/components/ServerTable/constants.d.ts +2 -0
- package/dist/components/ServerTable/constants.js +2 -0
- package/dist/components/ServerTable/index.d.ts +1 -0
- package/dist/components/ServerTable/index.js +1 -0
- package/dist/components/ServerTable/utils.d.ts +2 -0
- package/dist/components/ServerTable/utils.js +7 -0
- package/dist/components/Table/Table.d.ts +2 -1
- package/dist/components/Table/Table.js +14 -6
- package/dist/components/Table/hooks.js +2 -2
- package/dist/components/TableEmptyState/TableEmptyState.d.ts +6 -4
- package/dist/components/TableEmptyState/TableEmptyState.js +16 -5
- package/dist/components/TableEmptyState/styles.module.css +0 -7
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/helperComponents/Cells/CopyCell/CopyCell.d.ts +4 -0
- package/dist/helperComponents/Cells/CopyCell/CopyCell.js +7 -0
- package/dist/helperComponents/Cells/CopyCell/components/CopyButton/CopyButton.d.ts +5 -0
- package/dist/helperComponents/Cells/CopyCell/components/CopyButton/CopyButton.js +23 -0
- package/dist/helperComponents/Cells/CopyCell/components/CopyButton/index.d.ts +1 -0
- package/dist/helperComponents/Cells/CopyCell/components/CopyButton/index.js +1 -0
- package/dist/helperComponents/Cells/CopyCell/components/index.d.ts +1 -0
- package/dist/helperComponents/Cells/CopyCell/components/index.js +1 -0
- package/dist/helperComponents/Cells/CopyCell/index.d.ts +1 -0
- package/dist/helperComponents/Cells/CopyCell/index.js +1 -0
- package/dist/helperComponents/Cells/CopyCell/styles.module.css +14 -0
- package/dist/helperComponents/Cells/RowActionsCell/RowActionsCell.d.ts +1 -1
- package/dist/helperComponents/Cells/StatusCell/StatusCell.d.ts +2 -1
- package/dist/helperComponents/Cells/index.d.ts +1 -0
- package/dist/helperComponents/Cells/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +1 -1
- package/package.json +7 -4
- package/src/components/ServerTable/ServerTable.tsx +113 -0
- package/src/components/ServerTable/constants.ts +2 -0
- package/src/components/ServerTable/index.ts +1 -0
- package/src/components/ServerTable/utils.ts +9 -0
- package/src/components/Table/Table.tsx +28 -7
- package/src/components/Table/hooks.tsx +2 -4
- package/src/components/TableEmptyState/TableEmptyState.tsx +9 -11
- package/src/components/TableEmptyState/styles.module.scss +0 -7
- package/src/components/index.ts +1 -0
- package/src/constants.ts +2 -0
- package/src/helperComponents/Cells/CopyCell/CopyCell.tsx +17 -0
- package/src/helperComponents/Cells/CopyCell/components/CopyButton/CopyButton.tsx +44 -0
- package/src/helperComponents/Cells/CopyCell/components/CopyButton/index.ts +1 -0
- package/src/helperComponents/Cells/CopyCell/components/index.ts +1 -0
- package/src/helperComponents/Cells/CopyCell/index.ts +1 -0
- package/src/helperComponents/Cells/CopyCell/styles.module.scss +21 -0
- package/src/helperComponents/Cells/RowActionsCell/RowActionsCell.tsx +2 -2
- package/src/helperComponents/Cells/StatusCell/StatusCell.tsx +3 -1
- package/src/helperComponents/Cells/index.ts +1 -0
- package/src/index.ts +2 -0
- package/src/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 0.13.0 (2024-02-07)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **FF-4187:** change local no data to InfoBlock ([542cf0d](https://github.com/cloud-ru-tech/snack-uikit/commit/542cf0de8fed7f4ab5e2bca3afff1318b30a6043))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **FF-4241:** add ServerTable; add CopyCell ([2cf16c1](https://github.com/cloud-ru-tech/snack-uikit/commit/2cf16c15e72a93f20d40c1bc54d7708c1a273079))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## 0.12.1 (2024-02-07)
|
|
7
23
|
|
|
8
24
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# Table
|
|
2
2
|
|
|
3
3
|
## Installation
|
|
4
|
+
|
|
4
5
|
`npm i @snack-uikit/table`
|
|
5
6
|
|
|
6
7
|
[Changelog](./CHANGELOG.md)
|
|
7
8
|
|
|
8
9
|
## TODO:
|
|
9
|
-
- multiple row selection with Shift key pressed
|
|
10
10
|
|
|
11
|
+
- multiple row selection with Shift key pressed
|
|
11
12
|
|
|
12
13
|
## Примечание
|
|
14
|
+
|
|
13
15
|
> Для оптимальной работы таблицы используйте `useMemo` для `columnDefinitions`
|
|
14
|
-
или выносите их определение за компонент
|
|
16
|
+
> или выносите их определение за компонент
|
|
15
17
|
|
|
16
18
|
## Example
|
|
17
19
|
|
|
@@ -88,16 +90,20 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
88
90
|
data={data}
|
|
89
91
|
rowSelection={{
|
|
90
92
|
enable: true, // false выключает выбор всех строк (состояние disabled)
|
|
91
|
-
// или можно передать функцию для вычисления
|
|
93
|
+
// или можно передать функцию для вычисления
|
|
92
94
|
// enable: row => !['Pending', 'Loading'].includes(row.original.status),
|
|
93
95
|
onChange: handleRowSelection,
|
|
94
96
|
}}
|
|
95
97
|
/>
|
|
96
98
|
```
|
|
97
99
|
|
|
98
|
-
|
|
99
100
|
[//]: DOCUMENTATION_SECTION_START
|
|
100
101
|
[//]: THIS_SECTION_IS_AUTOGENERATED_PLEASE_DONT_EDIT_IT
|
|
102
|
+
## CopyCell
|
|
103
|
+
### Props
|
|
104
|
+
| name | type | default value | description |
|
|
105
|
+
|------|------|---------------|-------------|
|
|
106
|
+
| value | `string \| number` | - | |
|
|
101
107
|
## Table
|
|
102
108
|
Компонент таблицы
|
|
103
109
|
### Props
|
|
@@ -118,6 +124,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
118
124
|
| onDelete | `(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void) => void` | - | Колбек удаления выбранных |
|
|
119
125
|
| outline | `boolean` | - | Внешний бордер для тулбара и таблицы |
|
|
120
126
|
| columnFilters | `ReactNode` | - | Фильтры |
|
|
127
|
+
| dataFiltered | `boolean` | - | |
|
|
121
128
|
| exportFileName | `string` | - | Название файла при экспорте CSV/XLSX |
|
|
122
129
|
| moreActions | `Action[]` | - | Элементы выпадающего списка кнопки с действиями |
|
|
123
130
|
| noDataState | `TableEmptyStateProps` | - | Экран при отстутствии данных |
|
|
@@ -130,7 +137,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
130
137
|
### Props
|
|
131
138
|
| name | type | default value | description |
|
|
132
139
|
|------|------|---------------|-------------|
|
|
133
|
-
| mapStatusToAppearance* | `
|
|
140
|
+
| mapStatusToAppearance* | `MapStatusToAppearanceFnType` | - | Маппинг значений статуса на цвета |
|
|
134
141
|
| accessorKey* | `string` | - | Имя ключа соответствующее полю в data |
|
|
135
142
|
| enableSorting | `boolean` | - | Включение/выключение сортировки |
|
|
136
143
|
| renderDescription | `(cellValue: string) => string` | - | Функция для отрисовки текста, если не передана, то будет отрисован только индикатор статуса |
|
|
@@ -144,6 +151,54 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
144
151
|
|------|------|---------------|-------------|
|
|
145
152
|
| actionsGenerator* | `ActionsGenerator<TData>` | - | Действия для строки |
|
|
146
153
|
| pinned | `boolean` | - | Закрепление колонки справа в таблице |
|
|
154
|
+
## ServerTable
|
|
155
|
+
### Props
|
|
156
|
+
| name | type | default value | description |
|
|
157
|
+
|------|------|---------------|-------------|
|
|
158
|
+
| search* | `{ initialValue?: string; state: string; placeholder?: string; loading?: boolean; onChange(value: string): void; }` | 'Search...'<br> <strong>loading</strong>: Состояние загрузки в строке поиска <br> <strong>onChange</strong>: Колбэк на изменение данных в строке поиска | Параметры отвечают за глобальный поиск в таблице <br> <strong>initialState</strong>: Начальное состояние строки поиска <br> <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br> <strong>placeholder</strong>: Placeholder строки поиска |
|
|
159
|
+
| onChangePage* | `(offset: number, limit: number) => void` | - | |
|
|
160
|
+
| columnDefinitions* | `ColumnDefinition<TData>[]` | - | Определение внешнего вида и функционала колонок |
|
|
161
|
+
| loading | `boolean` | - | Состояние загрузки |
|
|
162
|
+
| sorting | `{ initialState?: SortingState; state?: SortingState; onChange?(state: SortingState): void; }` | - | Параметры отвечают за возможность сортировки, их стоит использовать если нужно отслеживать состояние <br> <strong>initialState</strong>: Начальное состояние сортировки <br> <strong>state</strong>: Состояние сортировки, жестко устанавливаемое снаружи <br> <strong>onChange</strong>: Колбэк на изменение сортировки |
|
|
163
|
+
| rowSelection | `{ initialState?: RowSelectionState; state?: RowSelectionState; enable?: boolean \| ((row: Row<TData>) => boolean); multiRow?: boolean; onChange?(state: RowSelectionState): void; }` | - | Параметры отвечают за возможность выбора строк <br> <strong>initialState</strong>: Начальное состояние выбора строк <br> <strong>state</strong>: Состояние выбора строк, жестко устанавливаемое снаружи <br> <strong>enable</strong>: Колбэк определяющий можно ли выбрать строку <br> <strong>multiRow</strong>: Мульти-выбор строк (включен по-умолчанию, когда включается выбор) <br> <strong>onChange</strong>: Колбэк на выбор строк |
|
|
164
|
+
| onRowClick | `RowClickHandler<TData>` | - | Колбэк клика по строке |
|
|
165
|
+
| className | `string` | - | CSS-класс |
|
|
166
|
+
| onRefresh | `() => void` | - | Колбек обновления данных |
|
|
167
|
+
| onDelete | `(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void) => void` | - | Колбек удаления выбранных |
|
|
168
|
+
| outline | `boolean` | - | Внешний бордер для тулбара и таблицы |
|
|
169
|
+
| columnFilters | `ReactNode` | - | Фильтры |
|
|
170
|
+
| dataFiltered | `boolean` | - | |
|
|
171
|
+
| exportFileName | `string` | - | Название файла при экспорте CSV/XLSX |
|
|
172
|
+
| moreActions | `Action[]` | - | Элементы выпадающего списка кнопки с действиями |
|
|
173
|
+
| noDataState | `TableEmptyStateProps` | - | Экран при отстутствии данных |
|
|
174
|
+
| noResultsState | `TableEmptyStateProps` | - | Экран при отстутствии результатов поиска |
|
|
175
|
+
| suppressToolbar | `boolean` | - | Отключение тулбара |
|
|
176
|
+
| toolbarBefore | `ReactNode` | - | Дополнительный слот в `Toolbar` перед строкой поиска |
|
|
177
|
+
| suppressPagination | `boolean` | - | Отключение пагинации |
|
|
178
|
+
| items | `TData[]` | - | Данные для отрисовки |
|
|
179
|
+
| total | `number` | 10 | Общее кол-во строк |
|
|
180
|
+
| limit | `number` | 10 | Кол-во строк на страницу |
|
|
181
|
+
| offset | `number` | - | Смещение |
|
|
182
|
+
| pagination | `{ options?: number[]; optionsLabel?: string; }` | 'Rows volume' <br> | Параметры отвечают за пагинацию в таблице <br> <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу<br> <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу |
|
|
183
|
+
## ServerTable.getRowActionsColumnDef
|
|
184
|
+
Вспомогательная функция для создания ячейки с дополнительными действиями у строки
|
|
185
|
+
### Props
|
|
186
|
+
| name | type | default value | description |
|
|
187
|
+
|------|------|---------------|-------------|
|
|
188
|
+
| actionsGenerator* | `ActionsGenerator<TData>` | - | Действия для строки |
|
|
189
|
+
| pinned | `boolean` | - | Закрепление колонки справа в таблице |
|
|
190
|
+
## ServerTable.getStatusColumnDef
|
|
191
|
+
Вспомогательная функция для создания ячейки со статусом
|
|
192
|
+
### Props
|
|
193
|
+
| name | type | default value | description |
|
|
194
|
+
|------|------|---------------|-------------|
|
|
195
|
+
| mapStatusToAppearance* | `MapStatusToAppearanceFnType` | - | Маппинг значений статуса на цвета |
|
|
196
|
+
| accessorKey* | `string` | - | Имя ключа соответствующее полю в data |
|
|
197
|
+
| enableSorting | `boolean` | - | Включение/выключение сортировки |
|
|
198
|
+
| renderDescription | `(cellValue: string) => string` | - | Функция для отрисовки текста, если не передана, то будет отрисован только индикатор статуса |
|
|
199
|
+
| size | `number` | - | Размер ячейки |
|
|
200
|
+
| header | `ColumnDefTemplate<HeaderContext<TData, unknown>> & (string \| ((ctx: HeaderContext<TData, unknown>) => string))` | - | Заголовок колонки |
|
|
201
|
+
| enableResizing | `boolean` | - | Включение/выключение ресайза колонки |
|
|
147
202
|
## exportToCSV
|
|
148
203
|
### Props
|
|
149
204
|
| name | type | default value | description |
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { TableProps } from '../Table';
|
|
2
|
+
export type ServerTableProps<TData extends object> = Omit<TableProps<TData>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
|
|
3
|
+
/** Данные для отрисовки */
|
|
4
|
+
items?: TData[];
|
|
5
|
+
/**
|
|
6
|
+
* Общее кол-во строк
|
|
7
|
+
* @default 10
|
|
8
|
+
*/
|
|
9
|
+
total?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Кол-во строк на страницу
|
|
12
|
+
* @default 10
|
|
13
|
+
*/
|
|
14
|
+
limit?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Смещение
|
|
17
|
+
* @default 0
|
|
18
|
+
* */
|
|
19
|
+
offset?: number;
|
|
20
|
+
onChangePage(offset: number, limit: number): void;
|
|
21
|
+
/** Параметры отвечают за глобальный поиск в таблице <br>
|
|
22
|
+
* <strong>initialState</strong>: Начальное состояние строки поиска <br>
|
|
23
|
+
* <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br>
|
|
24
|
+
* <strong>placeholder</strong>: Placeholder строки поиска @default 'Search...'<br>
|
|
25
|
+
* <strong>loading</strong>: Состояние загрузки в строке поиска <br>
|
|
26
|
+
* <strong>onChange</strong>: Колбэк на изменение данных в строке поиска
|
|
27
|
+
* */
|
|
28
|
+
search: {
|
|
29
|
+
initialValue?: string;
|
|
30
|
+
state: string;
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
loading?: boolean;
|
|
33
|
+
onChange(value: string): void;
|
|
34
|
+
};
|
|
35
|
+
/** Параметры отвечают за пагинацию в таблице <br>
|
|
36
|
+
* <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу<br>
|
|
37
|
+
* <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу @default 'Rows volume' <br>
|
|
38
|
+
* */
|
|
39
|
+
pagination?: {
|
|
40
|
+
options?: number[];
|
|
41
|
+
optionsLabel?: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export declare function ServerTable<TData extends object>({ items, total, limit, offset, onChangePage, search, pagination, columnFilters, ...rest }: ServerTableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare namespace ServerTable {
|
|
46
|
+
var getRowActionsColumnDef: typeof import("../../helperComponents").getRowActionsColumnDef;
|
|
47
|
+
var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
|
|
48
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
14
|
+
import { Table } from '../Table';
|
|
15
|
+
import { DEFAULT_PAGINATION_LIMIT } from './constants';
|
|
16
|
+
import { onSearchDebounced } from './utils';
|
|
17
|
+
export function ServerTable(_a) {
|
|
18
|
+
var { items, total = DEFAULT_PAGINATION_LIMIT, limit = DEFAULT_PAGINATION_LIMIT, offset = 0, onChangePage, search, pagination, columnFilters } = _a, rest = __rest(_a, ["items", "total", "limit", "offset", "onChangePage", "search", "pagination", "columnFilters"]);
|
|
19
|
+
const [tempSearch, setTempSearch] = useState(search.initialValue || '');
|
|
20
|
+
const handleSearch = useCallback((newValue) => {
|
|
21
|
+
setTempSearch(newValue);
|
|
22
|
+
onSearchDebounced()(newValue.trim(), search.onChange);
|
|
23
|
+
}, [search.onChange]);
|
|
24
|
+
const handlePageChange = useCallback(({ pageSize, pageIndex }) => onChangePage(pageIndex * pageSize, pageSize), [onChangePage]);
|
|
25
|
+
const pageIndex = useMemo(() => Math.floor(offset / limit), [limit, offset]);
|
|
26
|
+
const pageCount = useMemo(() => Math.ceil(total / limit), [limit, total]);
|
|
27
|
+
return (_jsx(Table, Object.assign({}, rest, { data: items || [], search: {
|
|
28
|
+
state: tempSearch,
|
|
29
|
+
onChange: handleSearch,
|
|
30
|
+
loading: search.loading,
|
|
31
|
+
placeholder: search.placeholder,
|
|
32
|
+
}, columnFilters: columnFilters, pageCount: pageCount, pagination: Object.assign(Object.assign({}, pagination), { state: {
|
|
33
|
+
pageIndex,
|
|
34
|
+
pageSize: limit,
|
|
35
|
+
}, onChange: handlePageChange }), pageSize: limit })));
|
|
36
|
+
}
|
|
37
|
+
ServerTable.getRowActionsColumnDef = Table.getRowActionsColumnDef;
|
|
38
|
+
ServerTable.getStatusColumnDef = Table.getStatusColumnDef;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ServerTable';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ServerTable';
|
|
@@ -78,6 +78,7 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
78
78
|
outline?: boolean;
|
|
79
79
|
/** Фильтры */
|
|
80
80
|
columnFilters?: ReactNode;
|
|
81
|
+
dataFiltered?: boolean;
|
|
81
82
|
/** Название файла при экспорте CSV/XLSX */
|
|
82
83
|
exportFileName?: string;
|
|
83
84
|
/** Элементы выпадающего списка кнопки с действиями */
|
|
@@ -94,7 +95,7 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
94
95
|
suppressPagination?: boolean;
|
|
95
96
|
}>;
|
|
96
97
|
/** Компонент таблицы */
|
|
97
|
-
export declare function Table<TData extends object>({ data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters: columnFiltersProp, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportFileName, noDataState, noResultsState, suppressToolbar, toolbarBefore, suppressPagination, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
98
|
+
export declare function Table<TData extends object>({ data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters: columnFiltersProp, dataFiltered, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportFileName, noDataState, noResultsState, suppressToolbar, toolbarBefore, suppressPagination, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
98
99
|
export declare namespace Table {
|
|
99
100
|
var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
|
|
100
101
|
var statusAppearances: {
|
|
@@ -19,6 +19,7 @@ import { SkeletonContextProvider } from '@snack-uikit/skeleton';
|
|
|
19
19
|
import { Toolbar } from '@snack-uikit/toolbar';
|
|
20
20
|
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
21
21
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
22
|
+
import { DEFAULT_PAGE_SIZE } from '../../constants';
|
|
22
23
|
import { BodyRow, ExportButton, getColumnId, getRowActionsColumnDef, getSelectionCellColumnDef, getStatusColumnDef, HeaderRow, STATUS_APPEARANCE, TableContext, } from '../../helperComponents';
|
|
23
24
|
import { fuzzyFilter } from '../../utils';
|
|
24
25
|
import { TableEmptyState } from '../TableEmptyState';
|
|
@@ -27,7 +28,7 @@ import { useLoadingTable, useStateControl, useTableEmptyState } from './hooks';
|
|
|
27
28
|
import styles from './styles.module.css';
|
|
28
29
|
/** Компонент таблицы */
|
|
29
30
|
export function Table(_a) {
|
|
30
|
-
var { data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters: columnFiltersProp, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize =
|
|
31
|
+
var { data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters: columnFiltersProp, dataFiltered, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportFileName, noDataState, noResultsState, suppressToolbar = false, toolbarBefore, suppressPagination = false } = _a, rest = __rest(_a, ["data", "columnDefinitions", "rowSelection", "search", "sorting", "columnFilters", "dataFiltered", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportFileName", "noDataState", "noResultsState", "suppressToolbar", "toolbarBefore", "suppressPagination"]);
|
|
31
32
|
const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl(search, '');
|
|
32
33
|
const { state: rowSelection, onStateChange: onRowSelectionChange } = useStateControl(rowSelectionProp, {});
|
|
33
34
|
const defaultPaginationState = useMemo(() => ({
|
|
@@ -73,6 +74,9 @@ export function Table(_a) {
|
|
|
73
74
|
enableResizing: false,
|
|
74
75
|
minSize: 40,
|
|
75
76
|
},
|
|
77
|
+
manualSorting: Boolean(pageCount),
|
|
78
|
+
manualPagination: pageCount !== undefined,
|
|
79
|
+
manualFiltering: Boolean(pageCount),
|
|
76
80
|
globalFilterFn: fuzzyFilter,
|
|
77
81
|
onGlobalFilterChange,
|
|
78
82
|
onRowSelectionChange,
|
|
@@ -82,9 +86,7 @@ export function Table(_a) {
|
|
|
82
86
|
getFilteredRowModel: getFilteredRowModel(),
|
|
83
87
|
enableColumnResizing: true,
|
|
84
88
|
enableSorting: true,
|
|
85
|
-
|
|
86
|
-
enableMultiSort: false,
|
|
87
|
-
manualPagination: pageCount !== undefined,
|
|
89
|
+
enableMultiSort: true,
|
|
88
90
|
onSortingChange,
|
|
89
91
|
getSortedRowModel: getSortedRowModel(),
|
|
90
92
|
onPaginationChange,
|
|
@@ -142,12 +144,18 @@ export function Table(_a) {
|
|
|
142
144
|
const tablePagination = table.getState().pagination;
|
|
143
145
|
const [locales] = useLocale('Table');
|
|
144
146
|
const emptyStates = useTableEmptyState({ noDataState, noResultsState });
|
|
145
|
-
|
|
147
|
+
const cssPageSize = useMemo(() => {
|
|
148
|
+
const tempPageSize = !suppressPagination ? tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize : pageSize;
|
|
149
|
+
return !tableRows.length ? Math.min(Math.max(tempPageSize, 5), DEFAULT_PAGE_SIZE) : tempPageSize;
|
|
150
|
+
}, [pageSize, suppressPagination, tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize, tableRows.length]);
|
|
151
|
+
return (_jsx(_Fragment, { children: _jsxs("div", Object.assign({ style: {
|
|
152
|
+
'--page-size': cssPageSize,
|
|
153
|
+
}, className: cn(styles.wrapper, className) }, extractSupportProps(rest), { children: [!suppressToolbar && (_jsxs("div", { className: styles.header, children: [_jsx(Toolbar, { search: {
|
|
146
154
|
value: globalFilter,
|
|
147
155
|
onChange: onGlobalFilterChange,
|
|
148
156
|
loading: search === null || search === void 0 ? void 0 : search.loading,
|
|
149
157
|
placeholder: (search === null || search === void 0 ? void 0 : search.placeholder) || locales.searchPlaceholder,
|
|
150
|
-
}, checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, onDelete: enableSelection && onDelete ? handleOnDelete : undefined, onCheck: enableSelection ? handleOnCheck : undefined, outline: outline, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', before: toolbarBefore, after: exportFileName ? (_jsx(ExportButton, { fileName: exportFileName, columnDefinitions: columnDefinitions, data: data })) : undefined, moreActions: moreActions }), columnFiltersProp && _jsxs("div", { className: styles.filtersWrapper, children: [" ", columnFiltersProp, " "] })] })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsx(Scroll, { size: 's', className: styles.table, children: _jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [tableRows.length ? _jsx(HeaderRow, {}) : null, tableRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), !tableRows.length && globalFilter && _jsx(TableEmptyState, Object.assign({}, emptyStates.noResultsState)), !tableRows.length && !globalFilter && _jsx(TableEmptyState, Object.assign({}, emptyStates.noDataState))] })) }) }) }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount }))] })) }));
|
|
158
|
+
}, checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, onDelete: enableSelection && onDelete ? handleOnDelete : undefined, onCheck: enableSelection ? handleOnCheck : undefined, outline: outline, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', before: toolbarBefore, after: exportFileName ? (_jsx(ExportButton, { fileName: exportFileName, columnDefinitions: columnDefinitions, data: data })) : undefined, moreActions: moreActions }), columnFiltersProp && _jsxs("div", { className: styles.filtersWrapper, children: [" ", columnFiltersProp, " "] })] })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsx(Scroll, { size: 's', className: styles.table, children: _jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [tableRows.length ? _jsx(HeaderRow, {}) : null, tableRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), !tableRows.length && (globalFilter || dataFiltered) && (_jsx(TableEmptyState, Object.assign({}, emptyStates.noResultsState))), !tableRows.length && !globalFilter && !dataFiltered && (_jsx(TableEmptyState, Object.assign({}, emptyStates.noDataState)))] })) }) }) }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount }))] })) }));
|
|
151
159
|
}
|
|
152
160
|
Table.getStatusColumnDef = getStatusColumnDef;
|
|
153
161
|
Table.statusAppearances = STATUS_APPEARANCE;
|
|
@@ -40,8 +40,8 @@ export function useLoadingTable({ pageSize, columnDefinitions, columnPinning })
|
|
|
40
40
|
export function useTableEmptyState({ noDataState: noDataStateProp, noResultsState: noResultsStateProp, }) {
|
|
41
41
|
const [locales] = useLocale('Table');
|
|
42
42
|
return useMemo(() => {
|
|
43
|
-
const noDataState = noDataStateProp !== null && noDataStateProp !== void 0 ? noDataStateProp : Object.assign({ icon: CrossSVG, appearance: 'red' }, locales.noData);
|
|
44
|
-
const noResultsState = noResultsStateProp !== null && noResultsStateProp !== void 0 ? noResultsStateProp : Object.assign({ icon: SearchSVG, appearance: 'neutral' }, locales.noResults);
|
|
43
|
+
const noDataState = noDataStateProp !== null && noDataStateProp !== void 0 ? noDataStateProp : Object.assign({ icon: { icon: CrossSVG, appearance: 'red', decor: true } }, locales.noData);
|
|
44
|
+
const noResultsState = noResultsStateProp !== null && noResultsStateProp !== void 0 ? noResultsStateProp : Object.assign({ icon: { icon: SearchSVG, appearance: 'neutral', decor: true } }, locales.noResults);
|
|
45
45
|
return {
|
|
46
46
|
noDataState,
|
|
47
47
|
noResultsState,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { IconPredefinedProps } from '@snack-uikit/icon-predefined';
|
|
3
3
|
export type TableEmptyStateProps = {
|
|
4
|
-
title
|
|
5
|
-
description
|
|
4
|
+
title?: string;
|
|
5
|
+
description: string;
|
|
6
|
+
icon?: Pick<IconPredefinedProps, 'icon' | 'decor' | 'appearance'>;
|
|
7
|
+
footer?: ReactNode;
|
|
6
8
|
className?: string;
|
|
7
|
-
}
|
|
8
|
-
export declare function TableEmptyState({
|
|
9
|
+
};
|
|
10
|
+
export declare function TableEmptyState({ className, ...props }: TableEmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
13
|
import cn from 'classnames';
|
|
3
|
-
import {
|
|
4
|
-
import { Typography } from '@snack-uikit/typography';
|
|
14
|
+
import { InfoBlock } from '@snack-uikit/info-block';
|
|
5
15
|
import styles from './styles.module.css';
|
|
6
|
-
export function TableEmptyState(
|
|
7
|
-
|
|
16
|
+
export function TableEmptyState(_a) {
|
|
17
|
+
var { className } = _a, props = __rest(_a, ["className"]);
|
|
18
|
+
return (_jsx("div", { className: cn(styles.tableEmptyStateWrapper, className), children: _jsx(InfoBlock, Object.assign({}, props, { size: 'm', align: 'vertical' })) }));
|
|
8
19
|
}
|
|
@@ -5,11 +5,4 @@
|
|
|
5
5
|
align-items:center;
|
|
6
6
|
justify-content:center;
|
|
7
7
|
height:calc(var(--page-size, 10) * var(--size-table-line-height, 40px) + var(--size-table-line-height, 40px));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.textWrapper{
|
|
11
|
-
display:flex;
|
|
12
|
-
flex-direction:column;
|
|
13
|
-
align-items:center;
|
|
14
|
-
justify-content:center;
|
|
15
8
|
}
|
package/dist/components/index.js
CHANGED
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
3
|
+
import { CopyButton } from './components';
|
|
4
|
+
import styles from './styles.module.css';
|
|
5
|
+
export function CopyCell({ value }) {
|
|
6
|
+
return (_jsxs("div", { className: styles.copyCell, children: [_jsx(TruncateString, { text: String(value), maxLines: 1 }), _jsx(CopyButton, { valueToCopy: value, className: styles.copyButton })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import copyToClipboard from 'copy-to-clipboard';
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { ButtonFunction } from '@snack-uikit/button';
|
|
5
|
+
import { CheckSVG, CopySVG } from '@snack-uikit/icons';
|
|
6
|
+
export function CopyButton({ valueToCopy, className }) {
|
|
7
|
+
const [isChecked, setIsCheckedOpen] = useState(false);
|
|
8
|
+
const timerId = useRef();
|
|
9
|
+
const openChecked = () => setIsCheckedOpen(true);
|
|
10
|
+
const closeChecked = () => setIsCheckedOpen(false);
|
|
11
|
+
const handleClick = event => {
|
|
12
|
+
event.stopPropagation();
|
|
13
|
+
valueToCopy && copyToClipboard(String(valueToCopy), { format: 'text/plain' });
|
|
14
|
+
openChecked();
|
|
15
|
+
clearTimeout(timerId.current);
|
|
16
|
+
timerId.current = setTimeout(closeChecked, 1000);
|
|
17
|
+
};
|
|
18
|
+
useEffect(() => () => {
|
|
19
|
+
closeChecked();
|
|
20
|
+
clearTimeout(timerId.current);
|
|
21
|
+
}, []);
|
|
22
|
+
return (_jsx(ButtonFunction, { onClick: handleClick, "data-test-id": 'button-copy-value', type: 'button', icon: isChecked ? _jsx(CheckSVG, {}) : _jsx(CopySVG, {}), size: 's', className: className }));
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyCell';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyCell';
|
|
@@ -12,7 +12,7 @@ export type RowActionProps<TData> = Pick<BaseItemProps, 'content' | 'disabled'>
|
|
|
12
12
|
tagLabel?: string;
|
|
13
13
|
id?: string;
|
|
14
14
|
hidden?: boolean;
|
|
15
|
-
onClick(row: RowActionInfo<TData>, e: MouseEvent<
|
|
15
|
+
onClick(row: RowActionInfo<TData>, e: MouseEvent<HTMLElement>): void;
|
|
16
16
|
};
|
|
17
17
|
export type ActionsGenerator<TData> = (cell: CellContext<TData, unknown>) => RowActionProps<TData>[];
|
|
18
18
|
export type RowActionsColumnDefProps<TData> = {
|
|
@@ -3,11 +3,12 @@ import { STATUS_APPEARANCE } from './constants';
|
|
|
3
3
|
import { StatusAppearance } from './types';
|
|
4
4
|
export type { StatusAppearance };
|
|
5
5
|
export { STATUS_APPEARANCE };
|
|
6
|
+
export type MapStatusToAppearanceFnType = (value: string | number) => StatusAppearance;
|
|
6
7
|
type BaseStatusColumnDef = {
|
|
7
8
|
/** Имя ключа соответствующее полю в data */
|
|
8
9
|
accessorKey: string;
|
|
9
10
|
/** Маппинг значений статуса на цвета */
|
|
10
|
-
mapStatusToAppearance
|
|
11
|
+
mapStatusToAppearance: MapStatusToAppearanceFnType;
|
|
11
12
|
/** Включение/выключение сортировки */
|
|
12
13
|
enableSorting?: boolean;
|
|
13
14
|
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -37,5 +37,5 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
37
37
|
size: number;
|
|
38
38
|
};
|
|
39
39
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
40
|
-
export type { RowActionInfo, RowActionProps, RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, } from './helperComponents';
|
|
40
|
+
export type { RowActionInfo, RowActionProps, RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
|
|
41
41
|
export type { PaginationState, SortingState, RowSelectionState, RowSelectionOptions, TableEmptyStateProps, ToolbarProps, HeaderContext, CellContext, };
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Table",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.13.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -36,22 +36,25 @@
|
|
|
36
36
|
"@snack-uikit/chips": "0.11.1",
|
|
37
37
|
"@snack-uikit/icon-predefined": "0.4.1",
|
|
38
38
|
"@snack-uikit/icons": "0.20.1",
|
|
39
|
-
"@snack-uikit/
|
|
39
|
+
"@snack-uikit/info-block": "0.1.1",
|
|
40
|
+
"@snack-uikit/list": "0.3.0",
|
|
40
41
|
"@snack-uikit/locale": "0.1.1",
|
|
41
42
|
"@snack-uikit/pagination": "0.6.3",
|
|
42
43
|
"@snack-uikit/scroll": "0.5.0",
|
|
43
44
|
"@snack-uikit/skeleton": "0.3.2",
|
|
44
45
|
"@snack-uikit/tag": "0.7.2",
|
|
45
46
|
"@snack-uikit/toggles": "0.9.5",
|
|
46
|
-
"@snack-uikit/toolbar": "0.7.
|
|
47
|
+
"@snack-uikit/toolbar": "0.7.7",
|
|
47
48
|
"@snack-uikit/truncate-string": "0.4.7",
|
|
48
49
|
"@snack-uikit/typography": "0.6.1",
|
|
49
50
|
"@snack-uikit/utils": "3.2.0",
|
|
50
51
|
"@tanstack/match-sorter-utils": "8.8.4",
|
|
51
52
|
"@tanstack/react-table": "8.11.6",
|
|
52
53
|
"classnames": "2.3.2",
|
|
54
|
+
"copy-to-clipboard": "3.3.3",
|
|
55
|
+
"lodash.debounce": "4.0.8",
|
|
53
56
|
"uncontrollable": "8.0.0",
|
|
54
57
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz"
|
|
55
58
|
},
|
|
56
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "88a44a8f4c6490f3004f7feac3d50d17ff03f31c"
|
|
57
60
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { PaginationState } from '../../types';
|
|
4
|
+
import { Table, TableProps } from '../Table';
|
|
5
|
+
import { DEFAULT_PAGINATION_LIMIT } from './constants';
|
|
6
|
+
import { onSearchDebounced } from './utils';
|
|
7
|
+
|
|
8
|
+
export type ServerTableProps<TData extends object> = Omit<
|
|
9
|
+
TableProps<TData>,
|
|
10
|
+
'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'
|
|
11
|
+
> & {
|
|
12
|
+
/** Данные для отрисовки */
|
|
13
|
+
items?: TData[];
|
|
14
|
+
/**
|
|
15
|
+
* Общее кол-во строк
|
|
16
|
+
* @default 10
|
|
17
|
+
*/
|
|
18
|
+
total?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Кол-во строк на страницу
|
|
21
|
+
* @default 10
|
|
22
|
+
*/
|
|
23
|
+
limit?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Смещение
|
|
26
|
+
* @default 0
|
|
27
|
+
* */
|
|
28
|
+
offset?: number;
|
|
29
|
+
|
|
30
|
+
onChangePage(offset: number, limit: number): void;
|
|
31
|
+
|
|
32
|
+
/** Параметры отвечают за глобальный поиск в таблице <br>
|
|
33
|
+
* <strong>initialState</strong>: Начальное состояние строки поиска <br>
|
|
34
|
+
* <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br>
|
|
35
|
+
* <strong>placeholder</strong>: Placeholder строки поиска @default 'Search...'<br>
|
|
36
|
+
* <strong>loading</strong>: Состояние загрузки в строке поиска <br>
|
|
37
|
+
* <strong>onChange</strong>: Колбэк на изменение данных в строке поиска
|
|
38
|
+
* */
|
|
39
|
+
search: {
|
|
40
|
+
initialValue?: string;
|
|
41
|
+
state: string;
|
|
42
|
+
placeholder?: string;
|
|
43
|
+
loading?: boolean;
|
|
44
|
+
onChange(value: string): void;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Параметры отвечают за пагинацию в таблице <br>
|
|
48
|
+
* <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу<br>
|
|
49
|
+
* <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу @default 'Rows volume' <br>
|
|
50
|
+
* */
|
|
51
|
+
pagination?: {
|
|
52
|
+
options?: number[];
|
|
53
|
+
optionsLabel?: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function ServerTable<TData extends object>({
|
|
58
|
+
items,
|
|
59
|
+
total = DEFAULT_PAGINATION_LIMIT,
|
|
60
|
+
limit = DEFAULT_PAGINATION_LIMIT,
|
|
61
|
+
offset = 0,
|
|
62
|
+
onChangePage,
|
|
63
|
+
search,
|
|
64
|
+
pagination,
|
|
65
|
+
columnFilters,
|
|
66
|
+
...rest
|
|
67
|
+
}: ServerTableProps<TData>) {
|
|
68
|
+
const [tempSearch, setTempSearch] = useState(search.initialValue || '');
|
|
69
|
+
|
|
70
|
+
const handleSearch = useCallback(
|
|
71
|
+
(newValue: string) => {
|
|
72
|
+
setTempSearch(newValue);
|
|
73
|
+
onSearchDebounced()(newValue.trim(), search.onChange);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
[search.onChange],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const handlePageChange = useCallback(
|
|
80
|
+
({ pageSize, pageIndex }: PaginationState) => onChangePage(pageIndex * pageSize, pageSize),
|
|
81
|
+
[onChangePage],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const pageIndex = useMemo(() => Math.floor(offset / limit), [limit, offset]);
|
|
85
|
+
const pageCount = useMemo(() => Math.ceil(total / limit), [limit, total]);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Table
|
|
89
|
+
{...rest}
|
|
90
|
+
data={items || []}
|
|
91
|
+
search={{
|
|
92
|
+
state: tempSearch,
|
|
93
|
+
onChange: handleSearch,
|
|
94
|
+
loading: search.loading,
|
|
95
|
+
placeholder: search.placeholder,
|
|
96
|
+
}}
|
|
97
|
+
columnFilters={columnFilters}
|
|
98
|
+
pageCount={pageCount}
|
|
99
|
+
pagination={{
|
|
100
|
+
...pagination,
|
|
101
|
+
state: {
|
|
102
|
+
pageIndex,
|
|
103
|
+
pageSize: limit,
|
|
104
|
+
},
|
|
105
|
+
onChange: handlePageChange,
|
|
106
|
+
}}
|
|
107
|
+
pageSize={limit}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ServerTable.getRowActionsColumnDef = Table.getRowActionsColumnDef;
|
|
113
|
+
ServerTable.getStatusColumnDef = Table.getStatusColumnDef;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ServerTable';
|
|
@@ -21,6 +21,7 @@ import { Toolbar, ToolbarProps } from '@snack-uikit/toolbar';
|
|
|
21
21
|
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
22
22
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
23
23
|
|
|
24
|
+
import { DEFAULT_PAGE_SIZE } from '../../constants';
|
|
24
25
|
import {
|
|
25
26
|
BodyRow,
|
|
26
27
|
ExportButton,
|
|
@@ -123,6 +124,8 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
123
124
|
/** Фильтры */
|
|
124
125
|
columnFilters?: ReactNode;
|
|
125
126
|
|
|
127
|
+
dataFiltered?: boolean;
|
|
128
|
+
|
|
126
129
|
/** Название файла при экспорте CSV/XLSX */
|
|
127
130
|
exportFileName?: string;
|
|
128
131
|
|
|
@@ -152,6 +155,7 @@ export function Table<TData extends object>({
|
|
|
152
155
|
search,
|
|
153
156
|
sorting: sortingProp,
|
|
154
157
|
columnFilters: columnFiltersProp,
|
|
158
|
+
dataFiltered,
|
|
155
159
|
|
|
156
160
|
pagination: paginationProp,
|
|
157
161
|
|
|
@@ -161,7 +165,7 @@ export function Table<TData extends object>({
|
|
|
161
165
|
onRefresh,
|
|
162
166
|
onDelete,
|
|
163
167
|
|
|
164
|
-
pageSize =
|
|
168
|
+
pageSize = DEFAULT_PAGE_SIZE,
|
|
165
169
|
pageCount,
|
|
166
170
|
loading = false,
|
|
167
171
|
outline = false,
|
|
@@ -252,6 +256,10 @@ export function Table<TData extends object>({
|
|
|
252
256
|
minSize: 40,
|
|
253
257
|
},
|
|
254
258
|
|
|
259
|
+
manualSorting: Boolean(pageCount),
|
|
260
|
+
manualPagination: pageCount !== undefined,
|
|
261
|
+
manualFiltering: Boolean(pageCount),
|
|
262
|
+
|
|
255
263
|
globalFilterFn: fuzzyFilter,
|
|
256
264
|
onGlobalFilterChange,
|
|
257
265
|
|
|
@@ -265,11 +273,12 @@ export function Table<TData extends object>({
|
|
|
265
273
|
enableColumnResizing: true,
|
|
266
274
|
|
|
267
275
|
enableSorting: true,
|
|
268
|
-
|
|
269
|
-
enableMultiSort:
|
|
270
|
-
|
|
276
|
+
|
|
277
|
+
enableMultiSort: true,
|
|
278
|
+
|
|
271
279
|
onSortingChange,
|
|
272
280
|
getSortedRowModel: getSortedRowModel(),
|
|
281
|
+
|
|
273
282
|
onPaginationChange,
|
|
274
283
|
getPaginationRowModel: getPaginationRowModel(),
|
|
275
284
|
|
|
@@ -342,10 +351,18 @@ export function Table<TData extends object>({
|
|
|
342
351
|
const [locales] = useLocale('Table');
|
|
343
352
|
const emptyStates = useTableEmptyState({ noDataState, noResultsState });
|
|
344
353
|
|
|
354
|
+
const cssPageSize = useMemo(() => {
|
|
355
|
+
const tempPageSize = !suppressPagination ? tablePagination?.pageSize : pageSize;
|
|
356
|
+
|
|
357
|
+
return !tableRows.length ? Math.min(Math.max(tempPageSize, 5), DEFAULT_PAGE_SIZE) : tempPageSize;
|
|
358
|
+
}, [pageSize, suppressPagination, tablePagination?.pageSize, tableRows.length]);
|
|
359
|
+
|
|
345
360
|
return (
|
|
346
361
|
<>
|
|
347
362
|
<div
|
|
348
|
-
style={{
|
|
363
|
+
style={{
|
|
364
|
+
'--page-size': cssPageSize,
|
|
365
|
+
}}
|
|
349
366
|
className={cn(styles.wrapper, className)}
|
|
350
367
|
{...extractSupportProps(rest)}
|
|
351
368
|
>
|
|
@@ -397,8 +414,12 @@ export function Table<TData extends object>({
|
|
|
397
414
|
<BodyRow key={row.id} row={row} onRowClick={onRowClick} />
|
|
398
415
|
))}
|
|
399
416
|
|
|
400
|
-
{!tableRows.length && globalFilter
|
|
401
|
-
|
|
417
|
+
{!tableRows.length && (globalFilter || dataFiltered) && (
|
|
418
|
+
<TableEmptyState {...emptyStates.noResultsState} />
|
|
419
|
+
)}
|
|
420
|
+
{!tableRows.length && !globalFilter && !dataFiltered && (
|
|
421
|
+
<TableEmptyState {...emptyStates.noDataState} />
|
|
422
|
+
)}
|
|
402
423
|
</>
|
|
403
424
|
)}
|
|
404
425
|
</TableContext.Provider>
|
|
@@ -80,14 +80,12 @@ export function useTableEmptyState({
|
|
|
80
80
|
|
|
81
81
|
return useMemo(() => {
|
|
82
82
|
const noDataState: TableEmptyStateProps = noDataStateProp ?? {
|
|
83
|
-
icon: CrossSVG,
|
|
84
|
-
appearance: 'red',
|
|
83
|
+
icon: { icon: CrossSVG, appearance: 'red', decor: true },
|
|
85
84
|
...locales.noData,
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
const noResultsState: TableEmptyStateProps = noResultsStateProp ?? {
|
|
89
|
-
icon: SearchSVG,
|
|
90
|
-
appearance: 'neutral',
|
|
88
|
+
icon: { icon: SearchSVG, appearance: 'neutral', decor: true },
|
|
91
89
|
...locales.noResults,
|
|
92
90
|
};
|
|
93
91
|
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { IconPredefinedProps } from '@snack-uikit/icon-predefined';
|
|
5
|
+
import { InfoBlock } from '@snack-uikit/info-block';
|
|
6
6
|
|
|
7
7
|
import styles from './styles.module.scss';
|
|
8
8
|
|
|
9
9
|
export type TableEmptyStateProps = {
|
|
10
|
-
title
|
|
11
|
-
description
|
|
10
|
+
title?: string;
|
|
11
|
+
description: string;
|
|
12
|
+
icon?: Pick<IconPredefinedProps, 'icon' | 'decor' | 'appearance'>;
|
|
13
|
+
footer?: ReactNode;
|
|
12
14
|
className?: string;
|
|
13
|
-
}
|
|
15
|
+
};
|
|
14
16
|
|
|
15
|
-
export function TableEmptyState({
|
|
17
|
+
export function TableEmptyState({ className, ...props }: TableEmptyStateProps) {
|
|
16
18
|
return (
|
|
17
19
|
<div className={cn(styles.tableEmptyStateWrapper, className)}>
|
|
18
|
-
<
|
|
19
|
-
<div className={styles.textWrapper}>
|
|
20
|
-
<Typography.SansTitleM>{title}</Typography.SansTitleM>
|
|
21
|
-
{description && <Typography.SansBodyM>{description}</Typography.SansBodyM>}
|
|
22
|
-
</div>
|
|
20
|
+
<InfoBlock {...props} size='m' align='vertical' />
|
|
23
21
|
</div>
|
|
24
22
|
);
|
|
25
23
|
}
|
package/src/components/index.ts
CHANGED
package/src/constants.ts
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
2
|
+
|
|
3
|
+
import { CopyButton } from './components';
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
|
|
6
|
+
export type CopyCellProps = {
|
|
7
|
+
value?: string | number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function CopyCell({ value }: CopyCellProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.copyCell}>
|
|
13
|
+
<TruncateString text={String(value)} maxLines={1} />
|
|
14
|
+
<CopyButton valueToCopy={value} className={styles.copyButton} />
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import copyToClipboard from 'copy-to-clipboard';
|
|
2
|
+
import { MouseEventHandler, useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ButtonFunction } from '@snack-uikit/button';
|
|
5
|
+
import { CheckSVG, CopySVG } from '@snack-uikit/icons';
|
|
6
|
+
|
|
7
|
+
export type CopyButtonProps = {
|
|
8
|
+
valueToCopy?: string | number;
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function CopyButton({ valueToCopy, className }: CopyButtonProps) {
|
|
13
|
+
const [isChecked, setIsCheckedOpen] = useState(false);
|
|
14
|
+
const timerId = useRef<NodeJS.Timeout>();
|
|
15
|
+
const openChecked = () => setIsCheckedOpen(true);
|
|
16
|
+
const closeChecked = () => setIsCheckedOpen(false);
|
|
17
|
+
|
|
18
|
+
const handleClick: MouseEventHandler<HTMLButtonElement> = event => {
|
|
19
|
+
event.stopPropagation();
|
|
20
|
+
valueToCopy && copyToClipboard(String(valueToCopy), { format: 'text/plain' });
|
|
21
|
+
openChecked();
|
|
22
|
+
clearTimeout(timerId.current);
|
|
23
|
+
timerId.current = setTimeout(closeChecked, 1000);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
useEffect(
|
|
27
|
+
() => () => {
|
|
28
|
+
closeChecked();
|
|
29
|
+
clearTimeout(timerId.current);
|
|
30
|
+
},
|
|
31
|
+
[],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<ButtonFunction
|
|
36
|
+
onClick={handleClick}
|
|
37
|
+
data-test-id='button-copy-value'
|
|
38
|
+
type='button'
|
|
39
|
+
icon={isChecked ? <CheckSVG /> : <CopySVG />}
|
|
40
|
+
size='s'
|
|
41
|
+
className={className}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CopyCell';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@import '@snack-uikit/figma-tokens/build/scss/styles-theme-variables';
|
|
2
|
+
|
|
3
|
+
.copyCell {
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: $dimension-050m;
|
|
8
|
+
align-items: center;
|
|
9
|
+
|
|
10
|
+
min-width: 0;
|
|
11
|
+
max-width: 100%;
|
|
12
|
+
.copyButton {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
.copyButton {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -22,7 +22,7 @@ export type RowActionProps<TData> = Pick<BaseItemProps, 'content' | 'disabled'>
|
|
|
22
22
|
tagLabel?: string;
|
|
23
23
|
id?: string;
|
|
24
24
|
hidden?: boolean;
|
|
25
|
-
onClick(row: RowActionInfo<TData>, e: MouseEvent<
|
|
25
|
+
onClick(row: RowActionInfo<TData>, e: MouseEvent<HTMLElement>): void;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
type RowActionsCellProps<TData> = {
|
|
@@ -34,7 +34,7 @@ function RowActionsCell<TData>({ row, actions }: RowActionsCellProps<TData>) {
|
|
|
34
34
|
const { droplistOpened, setDroplistOpen } = useRowContext();
|
|
35
35
|
const triggerRef = useRef(null);
|
|
36
36
|
|
|
37
|
-
const handleItemClick = (item: RowActionProps<TData>) => (e: MouseEvent<
|
|
37
|
+
const handleItemClick = (item: RowActionProps<TData>) => (e: MouseEvent<HTMLElement>) => {
|
|
38
38
|
item.onClick({ rowId: row.id, itemId: item.id, data: row.original }, e);
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -16,11 +16,13 @@ type StatusCellProps = {
|
|
|
16
16
|
appearance: StatusAppearance;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
export type MapStatusToAppearanceFnType = (value: string | number) => StatusAppearance;
|
|
20
|
+
|
|
19
21
|
type BaseStatusColumnDef = {
|
|
20
22
|
/** Имя ключа соответствующее полю в data */
|
|
21
23
|
accessorKey: string;
|
|
22
24
|
/** Маппинг значений статуса на цвета */
|
|
23
|
-
mapStatusToAppearance
|
|
25
|
+
mapStatusToAppearance: MapStatusToAppearanceFnType;
|
|
24
26
|
/** Включение/выключение сортировки */
|
|
25
27
|
enableSorting?: boolean;
|
|
26
28
|
};
|
package/src/index.ts
CHANGED