@snack-uikit/table 0.6.5-preview-85c5f47b.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +376 -0
  2. package/LICENSE +201 -0
  3. package/README.md +165 -0
  4. package/dist/components/Table/Table.d.ts +104 -0
  5. package/dist/components/Table/Table.js +125 -0
  6. package/dist/components/Table/constants.d.ts +3 -0
  7. package/dist/components/Table/constants.js +14 -0
  8. package/dist/components/Table/hooks.d.ts +19 -0
  9. package/dist/components/Table/hooks.js +37 -0
  10. package/dist/components/Table/index.d.ts +1 -0
  11. package/dist/components/Table/index.js +1 -0
  12. package/dist/components/Table/styles.module.css +46 -0
  13. package/dist/components/TableEmptyState/TableEmptyState.d.ts +8 -0
  14. package/dist/components/TableEmptyState/TableEmptyState.js +8 -0
  15. package/dist/components/TableEmptyState/index.d.ts +1 -0
  16. package/dist/components/TableEmptyState/index.js +1 -0
  17. package/dist/components/TableEmptyState/styles.module.css +15 -0
  18. package/dist/components/TablePagination/TablePagination.d.ts +8 -0
  19. package/dist/components/TablePagination/TablePagination.js +19 -0
  20. package/dist/components/TablePagination/index.d.ts +1 -0
  21. package/dist/components/TablePagination/index.js +1 -0
  22. package/dist/components/TablePagination/styles.module.css +11 -0
  23. package/dist/components/index.d.ts +1 -0
  24. package/dist/components/index.js +1 -0
  25. package/dist/constants.d.ts +28 -0
  26. package/dist/constants.js +31 -0
  27. package/dist/exportTable.d.ts +9 -0
  28. package/dist/exportTable.js +51 -0
  29. package/dist/helperComponents/Cells/BodyCell/BodyCell.d.ts +7 -0
  30. package/dist/helperComponents/Cells/BodyCell/BodyCell.js +24 -0
  31. package/dist/helperComponents/Cells/BodyCell/index.d.ts +1 -0
  32. package/dist/helperComponents/Cells/BodyCell/index.js +1 -0
  33. package/dist/helperComponents/Cells/BodyCell/styles.module.css +12 -0
  34. package/dist/helperComponents/Cells/Cell.d.ts +9 -0
  35. package/dist/helperComponents/Cells/Cell.js +20 -0
  36. package/dist/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +7 -0
  37. package/dist/helperComponents/Cells/HeaderCell/HeaderCell.js +17 -0
  38. package/dist/helperComponents/Cells/HeaderCell/helpers.d.ts +2 -0
  39. package/dist/helperComponents/Cells/HeaderCell/helpers.js +12 -0
  40. package/dist/helperComponents/Cells/HeaderCell/index.d.ts +1 -0
  41. package/dist/helperComponents/Cells/HeaderCell/index.js +1 -0
  42. package/dist/helperComponents/Cells/HeaderCell/styles.module.css +53 -0
  43. package/dist/helperComponents/Cells/RowActionsCell/RowActionsCell.d.ts +22 -0
  44. package/dist/helperComponents/Cells/RowActionsCell/RowActionsCell.js +36 -0
  45. package/dist/helperComponents/Cells/RowActionsCell/index.d.ts +1 -0
  46. package/dist/helperComponents/Cells/RowActionsCell/index.js +1 -0
  47. package/dist/helperComponents/Cells/RowActionsCell/styles.module.css +34 -0
  48. package/dist/helperComponents/Cells/SelectionCell/SelectionCell.d.ts +2 -0
  49. package/dist/helperComponents/Cells/SelectionCell/SelectionCell.js +45 -0
  50. package/dist/helperComponents/Cells/SelectionCell/index.d.ts +1 -0
  51. package/dist/helperComponents/Cells/SelectionCell/index.js +1 -0
  52. package/dist/helperComponents/Cells/SelectionCell/styles.module.css +13 -0
  53. package/dist/helperComponents/Cells/StatusCell/StatusCell.d.ts +27 -0
  54. package/dist/helperComponents/Cells/StatusCell/StatusCell.js +43 -0
  55. package/dist/helperComponents/Cells/StatusCell/constants.d.ts +13 -0
  56. package/dist/helperComponents/Cells/StatusCell/constants.js +14 -0
  57. package/dist/helperComponents/Cells/StatusCell/index.d.ts +1 -0
  58. package/dist/helperComponents/Cells/StatusCell/index.js +1 -0
  59. package/dist/helperComponents/Cells/StatusCell/styles.module.css +96 -0
  60. package/dist/helperComponents/Cells/index.d.ts +5 -0
  61. package/dist/helperComponents/Cells/index.js +5 -0
  62. package/dist/helperComponents/Cells/styles.module.css +13 -0
  63. package/dist/helperComponents/ExportButton/ExportButton.d.ts +10 -0
  64. package/dist/helperComponents/ExportButton/ExportButton.js +27 -0
  65. package/dist/helperComponents/ExportButton/index.d.ts +1 -0
  66. package/dist/helperComponents/ExportButton/index.js +1 -0
  67. package/dist/helperComponents/Rows/BodyRow.d.ts +15 -0
  68. package/dist/helperComponents/Rows/BodyRow.js +25 -0
  69. package/dist/helperComponents/Rows/HeaderRow.d.ts +1 -0
  70. package/dist/helperComponents/Rows/HeaderRow.js +11 -0
  71. package/dist/helperComponents/Rows/PinnedCells.d.ts +8 -0
  72. package/dist/helperComponents/Rows/PinnedCells.js +6 -0
  73. package/dist/helperComponents/Rows/Row.d.ts +10 -0
  74. package/dist/helperComponents/Rows/Row.js +18 -0
  75. package/dist/helperComponents/Rows/index.d.ts +2 -0
  76. package/dist/helperComponents/Rows/index.js +2 -0
  77. package/dist/helperComponents/Rows/styles.module.css +87 -0
  78. package/dist/helperComponents/contexts.d.ts +14 -0
  79. package/dist/helperComponents/contexts.js +14 -0
  80. package/dist/helperComponents/helpers.d.ts +2 -0
  81. package/dist/helperComponents/helpers.js +9 -0
  82. package/dist/helperComponents/hooks.d.ts +28 -0
  83. package/dist/helperComponents/hooks.js +67 -0
  84. package/dist/helperComponents/index.d.ts +7 -0
  85. package/dist/helperComponents/index.js +7 -0
  86. package/dist/helperComponents/types.d.ts +7 -0
  87. package/dist/helperComponents/types.js +1 -0
  88. package/dist/index.d.ts +3 -0
  89. package/dist/index.js +3 -0
  90. package/dist/types.d.ts +38 -0
  91. package/dist/types.js +1 -0
  92. package/dist/utils.d.ts +2 -0
  93. package/dist/utils.js +9 -0
  94. package/package.json +55 -0
  95. package/src/components/Table/Table.tsx +385 -0
  96. package/src/components/Table/constants.ts +18 -0
  97. package/src/components/Table/hooks.tsx +68 -0
  98. package/src/components/Table/index.ts +1 -0
  99. package/src/components/Table/styles.module.scss +55 -0
  100. package/src/components/TableEmptyState/TableEmptyState.tsx +25 -0
  101. package/src/components/TableEmptyState/index.ts +1 -0
  102. package/src/components/TableEmptyState/styles.module.scss +18 -0
  103. package/src/components/TablePagination/TablePagination.tsx +70 -0
  104. package/src/components/TablePagination/index.ts +1 -0
  105. package/src/components/TablePagination/styles.module.scss +13 -0
  106. package/src/components/index.ts +1 -0
  107. package/src/constants.ts +31 -0
  108. package/src/exportTable.ts +82 -0
  109. package/src/helperComponents/Cells/BodyCell/BodyCell.tsx +32 -0
  110. package/src/helperComponents/Cells/BodyCell/index.ts +1 -0
  111. package/src/helperComponents/Cells/BodyCell/styles.module.scss +15 -0
  112. package/src/helperComponents/Cells/Cell.tsx +21 -0
  113. package/src/helperComponents/Cells/HeaderCell/HeaderCell.tsx +53 -0
  114. package/src/helperComponents/Cells/HeaderCell/helpers.tsx +14 -0
  115. package/src/helperComponents/Cells/HeaderCell/index.ts +1 -0
  116. package/src/helperComponents/Cells/HeaderCell/styles.module.scss +74 -0
  117. package/src/helperComponents/Cells/RowActionsCell/RowActionsCell.tsx +118 -0
  118. package/src/helperComponents/Cells/RowActionsCell/index.ts +1 -0
  119. package/src/helperComponents/Cells/RowActionsCell/styles.module.scss +44 -0
  120. package/src/helperComponents/Cells/SelectionCell/SelectionCell.tsx +54 -0
  121. package/src/helperComponents/Cells/SelectionCell/index.ts +1 -0
  122. package/src/helperComponents/Cells/SelectionCell/styles.module.scss +18 -0
  123. package/src/helperComponents/Cells/StatusCell/StatusCell.tsx +108 -0
  124. package/src/helperComponents/Cells/StatusCell/constants.ts +14 -0
  125. package/src/helperComponents/Cells/StatusCell/index.ts +1 -0
  126. package/src/helperComponents/Cells/StatusCell/styles.module.scss +95 -0
  127. package/src/helperComponents/Cells/index.ts +5 -0
  128. package/src/helperComponents/Cells/styles.module.scss +13 -0
  129. package/src/helperComponents/ExportButton/ExportButton.tsx +70 -0
  130. package/src/helperComponents/ExportButton/index.ts +1 -0
  131. package/src/helperComponents/Rows/BodyRow.tsx +78 -0
  132. package/src/helperComponents/Rows/HeaderRow.tsx +36 -0
  133. package/src/helperComponents/Rows/PinnedCells.tsx +17 -0
  134. package/src/helperComponents/Rows/Row.tsx +20 -0
  135. package/src/helperComponents/Rows/index.ts +2 -0
  136. package/src/helperComponents/Rows/styles.module.scss +127 -0
  137. package/src/helperComponents/contexts.ts +29 -0
  138. package/src/helperComponents/helpers.ts +13 -0
  139. package/src/helperComponents/hooks.ts +84 -0
  140. package/src/helperComponents/index.ts +7 -0
  141. package/src/helperComponents/types.ts +13 -0
  142. package/src/index.ts +3 -0
  143. package/src/types.ts +80 -0
  144. package/src/utils.ts +13 -0
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@snack-uikit/table",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "title": "Table",
7
+ "version": "0.6.5-preview-85c5f47b.0",
8
+ "sideEffects": [
9
+ "*.css",
10
+ "*.woff",
11
+ "*.woff2"
12
+ ],
13
+ "description": "",
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.js",
16
+ "homepage": "https://github.com/cloud-ru-tech/snack-uikit/tree/master/packages/table",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/cloud-ru-tech/snack-uikit.git",
20
+ "directory": "packages/table"
21
+ },
22
+ "author": "Белов Алексей <anbelov@cloud.ru>",
23
+ "contributors": [
24
+ "Белов Алексей <anbelov@cloud.ru>"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "./CHANGELOG.md",
30
+ "./LICENSE"
31
+ ],
32
+ "license": "Apache-2.0",
33
+ "scripts": {},
34
+ "dependencies": {
35
+ "@snack-uikit/button": "0.13.7-preview-85c5f47b.0",
36
+ "@snack-uikit/chips": "0.8.5-preview-85c5f47b.0",
37
+ "@snack-uikit/droplist": "0.10.13-preview-85c5f47b.0",
38
+ "@snack-uikit/icon-predefined": "0.2.6-preview-85c5f47b.0",
39
+ "@snack-uikit/icons": "0.18.2-preview-85c5f47b.0",
40
+ "@snack-uikit/pagination": "0.4.11-preview-85c5f47b.0",
41
+ "@snack-uikit/scroll": "0.3.5-preview-85c5f47b.0",
42
+ "@snack-uikit/skeleton": "0.2.5-preview-85c5f47b.0",
43
+ "@snack-uikit/toggles": "0.7.1-preview-85c5f47b.0",
44
+ "@snack-uikit/toolbar": "0.3.8-preview-85c5f47b.0",
45
+ "@snack-uikit/truncate-string": "0.2.17-preview-85c5f47b.0",
46
+ "@snack-uikit/typography": "0.4.5-preview-85c5f47b.0",
47
+ "@snack-uikit/utils": "2.0.2-preview-85c5f47b.0",
48
+ "@tanstack/match-sorter-utils": "8.8.4",
49
+ "@tanstack/react-table": "8.10.7",
50
+ "classnames": "2.3.2",
51
+ "uncontrollable": "8.0.0",
52
+ "xlsx": "0.18.5"
53
+ },
54
+ "gitHead": "6a7eef41d780d945f64f791448eaa72b9e0ae72d"
55
+ }
@@ -0,0 +1,385 @@
1
+ import {
2
+ CellContext,
3
+ ColumnPinningState,
4
+ getCoreRowModel,
5
+ getFilteredRowModel,
6
+ getPaginationRowModel,
7
+ getSortedRowModel,
8
+ PaginationState,
9
+ RowSelectionOptions,
10
+ RowSelectionState,
11
+ SortingState,
12
+ useReactTable,
13
+ } from '@tanstack/react-table';
14
+ import { ReactNode, useCallback, useMemo } from 'react';
15
+
16
+ import { IconPredefined } from '@snack-uikit/icon-predefined';
17
+ import { Scroll } from '@snack-uikit/scroll';
18
+ import { SkeletonContextProvider } from '@snack-uikit/skeleton';
19
+ import { Toolbar, ToolbarProps } from '@snack-uikit/toolbar';
20
+ import { TruncateString } from '@snack-uikit/truncate-string';
21
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
22
+
23
+ import { ColumnAlign, ColumnPinPosition } from '../../constants';
24
+ import {
25
+ BodyRow,
26
+ ExportButton,
27
+ getColumnId,
28
+ getRowActionsColumnDef,
29
+ getSelectionCellColumnDef,
30
+ getStatusColumnDef,
31
+ HeaderRow,
32
+ RowClickHandler,
33
+ StatusAppearance,
34
+ TableContext,
35
+ } from '../../helperComponents';
36
+ import { ColumnDefinition } from '../../types';
37
+ import { fuzzyFilter } from '../../utils';
38
+ import { TableEmptyState, TableEmptyStateProps } from '../TableEmptyState';
39
+ import { TablePagination } from '../TablePagination';
40
+ import { DEFAULT_NO_DATA_TABLE_STATE, DEFAULT_NO_RESULTS_TABLE_STATE } from './constants';
41
+ import { useLoadingTable, useStateControl } from './hooks';
42
+ import styles from './styles.module.scss';
43
+
44
+ export type TableProps<TData extends object> = WithSupportProps<{
45
+ /** Данные для отрисовки */
46
+ data: TData[];
47
+ /** Определение внешнего вида и функционала колонок */
48
+ columnDefinitions: ColumnDefinition<TData>[];
49
+ /** Параметры отвечают за возможность сортировки, их стоит использовать если нужно отслеживать состояние <br>
50
+ * <strong>initialState</strong>: Начальное состояние сортировки <br>
51
+ * <strong>state</strong>: Состояние сортировки, жестко устанавливаемое снаружи <br>
52
+ * <strong>onChange</strong>: Колбэк на изменение сортировки
53
+ * */
54
+ sorting?: {
55
+ initialState?: SortingState;
56
+ state?: SortingState;
57
+ onChange?(state: SortingState): void;
58
+ };
59
+ /** Параметры отвечают за возможность выбора строк <br>
60
+ * <strong>initialState</strong>: Начальное состояние выбора строк <br>
61
+ * <strong>state</strong>: Состояние выбора строк, жестко устанавливаемое снаружи <br>
62
+ * <strong>enable</strong>: Колбэк определяющий можно ли выбрать строку <br>
63
+ * <strong>multiRow</strong>: Мульти-выбор строк (включен по-умолчанию, когда включается выбор) <br>
64
+ * <strong>onChange</strong>: Колбэк на выбор строк
65
+ * */
66
+ rowSelection?: {
67
+ initialState?: RowSelectionState;
68
+ state?: RowSelectionState;
69
+ enable?: RowSelectionOptions<TData>['enableRowSelection'];
70
+ multiRow?: boolean;
71
+ onChange?(state: RowSelectionState): void;
72
+ };
73
+ /** Параметры отвечают за глобальный поиск в таблице <br>
74
+ * <strong>initialState</strong>: Начальное состояние строки поиска <br>
75
+ * <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br>
76
+ * <strong>placeholder</strong>: Placeholder строки поиска @default 'Search'<br>
77
+ * <strong>loading</strong>: Состояние загрузки в строке поиска <br>
78
+ * <strong>onChange</strong>: Колбэк на изменение данных в строке поиска
79
+ * */
80
+ search?: {
81
+ initialValue?: string;
82
+ state?: string;
83
+ placeholder?: string;
84
+ loading?: boolean;
85
+ onChange?(value: string): void;
86
+ };
87
+
88
+ /** Максимальное кол-во строк на страницу @default 10 */
89
+ pageSize?: number;
90
+
91
+ /** Параметры отвечают за пагинацию в таблице <br>
92
+ * <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br>
93
+ * <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу <br>
94
+ * <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу @default 'Rows volume: ' <br>
95
+ * <strong>onChange</strong>: Колбэк на изменение пагинации
96
+ * */
97
+ pagination?: {
98
+ state?: PaginationState;
99
+ options?: number[];
100
+ optionsLabel?: string;
101
+ onChange?(state: PaginationState): void;
102
+ };
103
+
104
+ /** Кол-во страниц (используется для внешнего управления) */
105
+ pageCount?: number;
106
+
107
+ /** Колбэк клика по строке */
108
+ onRowClick?: RowClickHandler<TData>;
109
+ /** CSS-класс */
110
+ className?: string;
111
+
112
+ /** Состояние загрузки */
113
+ loading?: boolean;
114
+
115
+ /** Колбек обновления данных */
116
+ onRefresh?(): void;
117
+
118
+ /** Колбек удаления выбранных */
119
+ onDelete?(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void): void;
120
+
121
+ /** Внешний бордер для тулбара и таблицы */
122
+ outline?: boolean;
123
+
124
+ /** Фильтры */
125
+ columnFilters?: ReactNode;
126
+
127
+ /** Название файла при экспорте CSV/XLSX */
128
+ exportFileName?: string;
129
+
130
+ /** Элементы выпадающего списка кнопки с действиями */
131
+ moreActions?: ToolbarProps['moreActions'];
132
+
133
+ /** Экран при отстутствии данных */
134
+ noDataState?: TableEmptyStateProps;
135
+ /** Экран при отстутствии результатов поиска */
136
+ noResultsState?: TableEmptyStateProps;
137
+
138
+ /** Отключение тулбара */
139
+ suppressToolbar?: boolean;
140
+ /** Отключение пагинации */
141
+ suppressPagination?: boolean;
142
+ }>;
143
+
144
+ /** Компонент таблицы */
145
+ export function Table<TData extends object>({
146
+ data,
147
+ columnDefinitions,
148
+
149
+ rowSelection: rowSelectionProp,
150
+ search,
151
+ sorting: sortingProp,
152
+ columnFilters: columnFiltersProp,
153
+
154
+ pagination: paginationProp,
155
+
156
+ className,
157
+
158
+ onRowClick,
159
+ onRefresh,
160
+ onDelete,
161
+
162
+ pageSize = 10,
163
+ pageCount,
164
+ loading = false,
165
+ outline = false,
166
+
167
+ moreActions,
168
+ exportFileName,
169
+
170
+ noDataState = DEFAULT_NO_DATA_TABLE_STATE,
171
+ noResultsState = DEFAULT_NO_RESULTS_TABLE_STATE,
172
+
173
+ suppressToolbar = false,
174
+ suppressPagination = false,
175
+
176
+ ...rest
177
+ }: TableProps<TData>) {
178
+ const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl<string>(search, '');
179
+ const { state: rowSelection, onStateChange: onRowSelectionChange } = useStateControl<RowSelectionState>(
180
+ rowSelectionProp,
181
+ {},
182
+ );
183
+
184
+ const defaultPaginationState = useMemo(
185
+ () => ({
186
+ pageIndex: 0,
187
+ pageSize,
188
+ }),
189
+ [pageSize],
190
+ );
191
+
192
+ const { state: sorting, onStateChange: onSortingChange } = useStateControl<SortingState>(sortingProp, []);
193
+ const { state: pagination, onStateChange: onPaginationChange } = useStateControl<PaginationState>(
194
+ paginationProp,
195
+ defaultPaginationState,
196
+ );
197
+
198
+ const enableSelection = Boolean(rowSelectionProp?.enable);
199
+
200
+ const tableColumns: ColumnDefinition<TData>[] = useMemo(() => {
201
+ let cols: ColumnDefinition<TData>[] = columnDefinitions.map(column =>
202
+ column.cell
203
+ ? column
204
+ : {
205
+ ...column,
206
+ cell: (cell: CellContext<TData, unknown>) => <TruncateString text={String(cell.getValue())} maxLines={1} />,
207
+ },
208
+ );
209
+
210
+ if (enableSelection) {
211
+ cols = [getSelectionCellColumnDef(), ...cols];
212
+ }
213
+
214
+ return cols;
215
+ }, [columnDefinitions, enableSelection]);
216
+
217
+ const columnPinning = useMemo(() => {
218
+ const pinningState: Required<ColumnPinningState> = { left: [], right: [] };
219
+
220
+ for (const col of tableColumns) {
221
+ const id = getColumnId(col);
222
+
223
+ if (col.pinned && id) {
224
+ pinningState[col.pinned]?.push(id);
225
+ }
226
+ }
227
+
228
+ return pinningState;
229
+ }, [tableColumns]);
230
+
231
+ const table = useReactTable<TData>({
232
+ data,
233
+ columns: tableColumns,
234
+
235
+ state: {
236
+ columnPinning,
237
+ globalFilter,
238
+ rowSelection,
239
+ sorting,
240
+ pagination,
241
+ },
242
+
243
+ pageCount,
244
+
245
+ defaultColumn: {
246
+ enableSorting: false,
247
+ },
248
+
249
+ globalFilterFn: fuzzyFilter,
250
+ onGlobalFilterChange,
251
+
252
+ onRowSelectionChange,
253
+ enableRowSelection: rowSelectionProp?.enable,
254
+ enableMultiRowSelection: rowSelectionProp?.multiRow,
255
+
256
+ enableFilters: true,
257
+ getFilteredRowModel: getFilteredRowModel(),
258
+
259
+ enableSorting: true,
260
+ manualSorting: false,
261
+ enableMultiSort: false,
262
+ manualPagination: pageCount !== undefined,
263
+ onSortingChange,
264
+ getSortedRowModel: getSortedRowModel(),
265
+ onPaginationChange,
266
+ getPaginationRowModel: getPaginationRowModel(),
267
+
268
+ getCoreRowModel: getCoreRowModel(),
269
+ });
270
+
271
+ const { loadingTable } = useLoadingTable({ pageSize, columnDefinitions: tableColumns, columnPinning });
272
+
273
+ const handleOnRefresh = useCallback(() => {
274
+ table.resetRowSelection();
275
+ onRefresh?.();
276
+ }, [onRefresh, table]);
277
+
278
+ const handleOnDelete = useCallback(() => {
279
+ if (loading) {
280
+ return;
281
+ }
282
+
283
+ if (onDelete) {
284
+ onDelete(table.getState().rowSelection, table.resetRowSelection);
285
+ }
286
+ }, [loading, onDelete, table]);
287
+
288
+ const handleOnCheck = useCallback(() => {
289
+ if (!loading && rowSelectionProp?.multiRow) {
290
+ table.toggleAllPageRowsSelected();
291
+ return;
292
+ }
293
+
294
+ if (!loading && table.getIsSomePageRowsSelected()) {
295
+ table.resetRowSelection();
296
+ return;
297
+ }
298
+ }, [loading, rowSelectionProp?.multiRow, table]);
299
+
300
+ const tableRows = table.getRowModel().rows;
301
+ const loadingTableRows = loadingTable.getRowModel().rows;
302
+ const tablePagination = table.getState().pagination;
303
+
304
+ return (
305
+ <>
306
+ <div
307
+ style={{ '--page-size': !suppressPagination ? tablePagination?.pageSize : pageSize }}
308
+ className={className}
309
+ {...extractSupportProps(rest)}
310
+ >
311
+ {!suppressToolbar && (
312
+ <div className={styles.header}>
313
+ <Toolbar
314
+ value={globalFilter}
315
+ onChange={onGlobalFilterChange}
316
+ checked={table.getIsAllPageRowsSelected()}
317
+ indeterminate={table.getIsSomePageRowsSelected()}
318
+ className={styles.toolbar}
319
+ onRefresh={handleOnRefresh}
320
+ onDelete={enableSelection && onDelete ? handleOnDelete : undefined}
321
+ onCheck={enableSelection ? handleOnCheck : undefined}
322
+ outline={outline}
323
+ loading={search?.loading}
324
+ placeholder={search?.placeholder || 'Search'}
325
+ selectionMode={
326
+ rowSelectionProp?.multiRow ? Toolbar.selectionModes.Multiple : Toolbar.selectionModes.Single
327
+ }
328
+ actions={
329
+ exportFileName ? (
330
+ <ExportButton fileName={exportFileName} columnDefinitions={columnDefinitions} data={data} />
331
+ ) : undefined
332
+ }
333
+ moreActions={moreActions}
334
+ />
335
+
336
+ {columnFiltersProp && <div className={styles.filtersWrapper}> {columnFiltersProp} </div>}
337
+ </div>
338
+ )}
339
+
340
+ <div className={styles.scrollWrapper} data-outline={outline || undefined}>
341
+ <Scroll size={Scroll.sizes.S} className={styles.table}>
342
+ <div className={styles.tableContent}>
343
+ <TableContext.Provider value={{ table }}>
344
+ {loading ? (
345
+ <SkeletonContextProvider loading>
346
+ <HeaderRow />
347
+ {loadingTableRows.map(row => (
348
+ <BodyRow key={row.id} row={row} />
349
+ ))}
350
+ </SkeletonContextProvider>
351
+ ) : (
352
+ <>
353
+ {tableRows.length ? <HeaderRow /> : null}
354
+ {tableRows.map(row => (
355
+ <BodyRow key={row.id} row={row} onRowClick={onRowClick} />
356
+ ))}
357
+
358
+ {!tableRows.length && globalFilter && <TableEmptyState {...noResultsState} />}
359
+ {!tableRows.length && !globalFilter && <TableEmptyState {...noDataState} />}
360
+ </>
361
+ )}
362
+ </TableContext.Provider>
363
+ </div>
364
+ </Scroll>
365
+ </div>
366
+
367
+ {!suppressPagination && (
368
+ <TablePagination
369
+ table={table}
370
+ options={paginationProp?.options}
371
+ optionsLabel={paginationProp?.optionsLabel}
372
+ pageCount={pageCount}
373
+ />
374
+ )}
375
+ </div>
376
+ </>
377
+ );
378
+ }
379
+
380
+ Table.columnPinPositions = ColumnPinPosition;
381
+ Table.columnAligns = ColumnAlign;
382
+ Table.getStatusColumnDef = getStatusColumnDef;
383
+ Table.statusAppearances = StatusAppearance;
384
+ Table.getRowActionsColumnDef = getRowActionsColumnDef;
385
+ Table.emptyStateAppearances = IconPredefined.appearances;
@@ -0,0 +1,18 @@
1
+ import { IconPredefined } from '@snack-uikit/icon-predefined';
2
+ import { CrossSVG, SearchSVG } from '@snack-uikit/icons';
3
+
4
+ import { TableEmptyStateProps } from '../TableEmptyState';
5
+
6
+ export const DEFAULT_NO_DATA_TABLE_STATE: TableEmptyStateProps = {
7
+ icon: CrossSVG,
8
+ appearance: IconPredefined.appearances.Red,
9
+ title: 'Data collection error',
10
+ description: 'Try refreshing the page',
11
+ } as const;
12
+
13
+ export const DEFAULT_NO_RESULTS_TABLE_STATE: TableEmptyStateProps = {
14
+ icon: SearchSVG,
15
+ appearance: IconPredefined.appearances.Neutral,
16
+ title: 'Not found',
17
+ description: 'Try entering another query',
18
+ } as const;
@@ -0,0 +1,68 @@
1
+ import { ColumnPinningState, getCoreRowModel, useReactTable } from '@tanstack/react-table';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { useUncontrolledProp } from 'uncontrollable';
4
+
5
+ import { SkeletonText } from '@snack-uikit/skeleton';
6
+
7
+ import { ColumnDefinition } from '../../types';
8
+ import styles from './styles.module.scss';
9
+
10
+ export function useStateControl<TState>(
11
+ control: { initialState?: TState; state?: TState; onChange?(state: TState): void } | undefined,
12
+ defaultState: TState,
13
+ ) {
14
+ const onControlState = useCallback(
15
+ (controlState: TState) => {
16
+ if (control?.state) {
17
+ const newState = typeof controlState === 'function' ? controlState(control.state) : controlState;
18
+
19
+ control?.onChange?.(newState);
20
+ } else {
21
+ control?.onChange?.(controlState);
22
+ }
23
+ },
24
+ [control],
25
+ );
26
+
27
+ const [state, onStateChange] = useUncontrolledProp<TState>(
28
+ control?.state,
29
+ control?.state ?? control?.initialState ?? defaultState,
30
+ onControlState,
31
+ );
32
+
33
+ return {
34
+ state,
35
+ onStateChange,
36
+ };
37
+ }
38
+
39
+ type UseLoadingTableProps<TData> = {
40
+ columnDefinitions: ColumnDefinition<TData>[];
41
+ pageSize: number;
42
+ columnPinning: ColumnPinningState;
43
+ };
44
+
45
+ export function useLoadingTable<TData>({ pageSize, columnDefinitions, columnPinning }: UseLoadingTableProps<TData>) {
46
+ const data = useMemo(() => (Array.from({ length: pageSize }).map(() => '') || []) as TData[], [pageSize]);
47
+ const columns = useMemo(
48
+ () =>
49
+ columnDefinitions.map(column => ({
50
+ ...column,
51
+ cell: () => <SkeletonText className={styles.skeleton} lines={1} width={'100%'} />,
52
+ })),
53
+ [columnDefinitions],
54
+ );
55
+
56
+ const loadingTable = useReactTable<TData>({
57
+ data,
58
+ columns,
59
+
60
+ state: {
61
+ columnPinning,
62
+ },
63
+
64
+ getCoreRowModel: getCoreRowModel(),
65
+ });
66
+
67
+ return { loadingTable };
68
+ }
@@ -0,0 +1 @@
1
+ export * from './Table';
@@ -0,0 +1,55 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-table';
2
+
3
+ .table {
4
+ @include composite-var($table-table-container);
5
+
6
+ position: relative;
7
+ z-index: 0;
8
+
9
+ /* stylelint-disable-next-line declaration-no-important */
10
+ overflow: hidden !important;
11
+
12
+ box-sizing: border-box;
13
+ width: 100%;
14
+ height: auto;
15
+ max-height: calc((var(--page-size, 10) * $size-table-line-height) + $size-table-line-height + $border-width-table * 2);
16
+
17
+ background-color: $sys-neutral-background1-level;
18
+ border-color: $sys-neutral-background1-level;
19
+ border-style: solid;
20
+ }
21
+
22
+ .scrollWrapper {
23
+ &[data-outline] {
24
+ .table {
25
+ border-color: $sys-neutral-decor-default;
26
+ }
27
+ }
28
+ }
29
+
30
+ .tableContent {
31
+ min-width: max-content;
32
+ }
33
+
34
+ .header {
35
+ @include composite-var($table-header);
36
+
37
+ display: flex;
38
+ flex-direction: column;
39
+ }
40
+
41
+ .toolbar {
42
+ flex: 1 0 auto;
43
+ }
44
+
45
+ .filtersWrapper {
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ gap: $dimension-1m;
49
+ align-items: center;
50
+ justify-content: flex-start;
51
+ }
52
+
53
+ .skeleton {
54
+ padding: 0 $dimension-1m;
55
+ }
@@ -0,0 +1,25 @@
1
+ import cn from 'classnames';
2
+ import { ReactNode } from 'react';
3
+
4
+ import { IconPredefined, IconPredefinedProps } from '@snack-uikit/icon-predefined';
5
+ import { Typography } from '@snack-uikit/typography';
6
+
7
+ import styles from './styles.module.scss';
8
+
9
+ export type TableEmptyStateProps = {
10
+ title: string;
11
+ description?: ReactNode;
12
+ className?: string;
13
+ } & Pick<IconPredefinedProps, 'icon' | 'appearance'>;
14
+
15
+ export function TableEmptyState({ title, description, className, icon, appearance }: TableEmptyStateProps) {
16
+ return (
17
+ <div className={cn(styles.tableEmptyStateWrapper, className)}>
18
+ <IconPredefined icon={icon} size={IconPredefined.sizes.L} appearance={appearance} />
19
+ <div className={styles.textWrapper}>
20
+ <Typography.SansTitleM>{title}</Typography.SansTitleM>
21
+ {description && <Typography.SansBodyM>{description}</Typography.SansBodyM>}
22
+ </div>
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1 @@
1
+ export * from './TableEmptyState';
@@ -0,0 +1,18 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-table';
2
+
3
+ .tableEmptyStateWrapper {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: $dimension-3m;
7
+ align-items: center;
8
+ justify-content: center;
9
+
10
+ height: calc((var(--page-size, 10) * $size-table-line-height) + $size-table-line-height);
11
+ }
12
+
13
+ .textWrapper {
14
+ display: flex;
15
+ flex-direction: column;
16
+ align-items: center;
17
+ justify-content: center;
18
+ }