@snack-uikit/table 0.25.17 → 0.25.18-preview-cfe0e263.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/README.md +8 -0
- package/dist/cjs/components/Table/Table.d.ts +1 -1
- package/dist/cjs/components/Table/Table.js +59 -29
- package/dist/cjs/components/Table/hooks/useRowVirtualizer.d.ts +2 -0
- package/dist/cjs/components/Table/hooks/useRowVirtualizer.js +34 -0
- package/dist/cjs/components/Table/styles.module.css +4 -0
- package/dist/cjs/components/Table/utils.d.ts +10 -0
- package/dist/cjs/components/Table/utils.js +19 -0
- package/dist/cjs/components/types.d.ts +7 -2
- package/dist/cjs/helperComponents/Rows/VirtualRow.d.ts +7 -0
- package/dist/cjs/helperComponents/Rows/VirtualRow.js +43 -0
- package/dist/cjs/helperComponents/Rows/styles.module.css +8 -0
- package/dist/cjs/types.d.ts +2 -0
- package/dist/esm/components/Table/Table.d.ts +1 -1
- package/dist/esm/components/Table/Table.js +38 -18
- package/dist/esm/components/Table/hooks/useRowVirtualizer.d.ts +2 -0
- package/dist/esm/components/Table/hooks/useRowVirtualizer.js +13 -0
- package/dist/esm/components/Table/styles.module.css +4 -0
- package/dist/esm/components/Table/utils.d.ts +10 -0
- package/dist/esm/components/Table/utils.js +14 -0
- package/dist/esm/components/types.d.ts +7 -2
- package/dist/esm/helperComponents/Rows/VirtualRow.d.ts +7 -0
- package/dist/esm/helperComponents/Rows/VirtualRow.js +21 -0
- package/dist/esm/helperComponents/Rows/styles.module.css +8 -0
- package/dist/esm/types.d.ts +2 -0
- package/package.json +4 -2
- package/src/components/Table/Table.tsx +67 -23
- package/src/components/Table/hooks/useRowVirtualizer.ts +43 -0
- package/src/components/Table/styles.module.scss +6 -0
- package/src/components/Table/{utils.ts → utils.tsx} +18 -0
- package/src/components/types.ts +11 -2
- package/src/helperComponents/Rows/VirtualRow.tsx +24 -0
- package/src/helperComponents/Rows/styles.module.scss +8 -0
- package/src/types.ts +3 -0
package/README.md
CHANGED
|
@@ -148,6 +148,10 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
148
148
|
| scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
|
|
149
149
|
| rowPinning | `Pick<RowPinningState, "top">` | { top: [], } | Определение какие строки должны быть закреплены в таблице |
|
|
150
150
|
| savedState | `{ id: string; resize?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
151
|
+
| onScroll | `(event?: Event) => void` | - | |
|
|
152
|
+
| enableRowVirtualization | `boolean` | - | |
|
|
153
|
+
| rowVirtualizerOptions | `PartialKeys<VirtualizerOptions<Element, Element>, "observeElementRect" \| "observeElementOffset" \| "scrollToFn">` | - | |
|
|
154
|
+
| rowVirtualizerInstanceRef | `MutableRefObject<Virtualizer<Element, Element>>` | - | |
|
|
151
155
|
## Table.getStatusColumnDef
|
|
152
156
|
Вспомогательная функция для создания ячейки со статусом
|
|
153
157
|
### Props
|
|
@@ -208,6 +212,10 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
208
212
|
| scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
|
|
209
213
|
| rowPinning | `Pick<RowPinningState, "top">` | - | Определение какие строки должны быть закреплены в таблице |
|
|
210
214
|
| savedState | `{ id: string; resize?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
215
|
+
| onScroll | `(event?: Event) => void` | - | |
|
|
216
|
+
| enableRowVirtualization | `boolean` | - | |
|
|
217
|
+
| rowVirtualizerOptions | `PartialKeys<VirtualizerOptions<Element, Element>, "observeElementRect" \| "observeElementOffset" \| "scrollToFn">` | - | |
|
|
218
|
+
| rowVirtualizerInstanceRef | `MutableRefObject<Virtualizer<Element, Element>>` | - | |
|
|
211
219
|
| items | `TData[]` | - | Данные для отрисовки |
|
|
212
220
|
| total | `number` | 10 | Общее кол-во строк |
|
|
213
221
|
| limit | `number` | 10 | Кол-во строк на страницу |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TableProps } from '../types';
|
|
2
2
|
/** Компонент таблицы */
|
|
3
|
-
export declare function Table<TData extends object>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function Table<TData extends object>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, onScroll, enableRowVirtualization, rowVirtualizerOptions, rowVirtualizerInstanceRef, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export declare namespace Table {
|
|
5
5
|
var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
|
|
6
6
|
var statusAppearances: Record<string, string>;
|
|
@@ -20,22 +20,25 @@ exports.Table = Table;
|
|
|
20
20
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
21
21
|
const react_table_1 = require("@tanstack/react-table");
|
|
22
22
|
const classnames_1 = __importDefault(require("classnames"));
|
|
23
|
+
const merge_refs_1 = __importDefault(require("merge-refs"));
|
|
23
24
|
const react_1 = require("react");
|
|
24
25
|
const locale_1 = require("@snack-uikit/locale");
|
|
25
26
|
const scroll_1 = require("@snack-uikit/scroll");
|
|
26
27
|
const skeleton_1 = require("@snack-uikit/skeleton");
|
|
27
28
|
const toolbar_1 = require("@snack-uikit/toolbar");
|
|
28
|
-
const truncate_string_1 = require("@snack-uikit/truncate-string");
|
|
29
29
|
const utils_1 = require("@snack-uikit/utils");
|
|
30
30
|
const constants_1 = require("../../constants");
|
|
31
31
|
const helperComponents_1 = require("../../helperComponents");
|
|
32
|
+
const VirtualRow_1 = require("../../helperComponents/Rows/VirtualRow");
|
|
32
33
|
const utils_2 = require("../../utils");
|
|
33
34
|
const hooks_1 = require("./hooks");
|
|
34
35
|
const usePageReset_1 = require("./hooks/usePageReset");
|
|
36
|
+
const useRowVirtualizer_1 = require("./hooks/useRowVirtualizer");
|
|
35
37
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
36
38
|
const utils_3 = require("./utils");
|
|
37
39
|
/** Компонент таблицы */
|
|
38
40
|
function Table(_a) {
|
|
41
|
+
var _b, _c;
|
|
39
42
|
var {
|
|
40
43
|
data,
|
|
41
44
|
rowPinning = {
|
|
@@ -77,9 +80,13 @@ function Table(_a) {
|
|
|
77
80
|
scrollContainerRef,
|
|
78
81
|
getRowId,
|
|
79
82
|
enableFuzzySearch,
|
|
80
|
-
savedState
|
|
83
|
+
savedState,
|
|
84
|
+
onScroll,
|
|
85
|
+
enableRowVirtualization,
|
|
86
|
+
rowVirtualizerOptions,
|
|
87
|
+
rowVirtualizerInstanceRef
|
|
81
88
|
} = _a,
|
|
82
|
-
rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "toolbarBefore", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState"]);
|
|
89
|
+
rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "toolbarBefore", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "onScroll", "enableRowVirtualization", "rowVirtualizerOptions", "rowVirtualizerInstanceRef"]);
|
|
83
90
|
const {
|
|
84
91
|
state: globalFilter,
|
|
85
92
|
onStateChange: onGlobalFilterChange
|
|
@@ -101,6 +108,7 @@ function Table(_a) {
|
|
|
101
108
|
onStateChange: onPaginationChange
|
|
102
109
|
} = (0, hooks_1.useStateControl)(paginationProp, defaultPaginationState);
|
|
103
110
|
const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
|
|
111
|
+
const tableContainerRef = (0, react_1.useRef)(null);
|
|
104
112
|
const tableColumns = (0, react_1.useMemo)(() => {
|
|
105
113
|
let cols = columnDefinitions;
|
|
106
114
|
if (enableSelection) {
|
|
@@ -138,10 +146,7 @@ function Table(_a) {
|
|
|
138
146
|
enableSorting: false,
|
|
139
147
|
enableResizing: false,
|
|
140
148
|
minSize: 40,
|
|
141
|
-
cell:
|
|
142
|
-
text: String(cell.getValue()),
|
|
143
|
-
maxLines: 1
|
|
144
|
-
})
|
|
149
|
+
cell: utils_3.truncateCell
|
|
145
150
|
},
|
|
146
151
|
manualSorting,
|
|
147
152
|
manualPagination,
|
|
@@ -164,7 +169,13 @@ function Table(_a) {
|
|
|
164
169
|
getPaginationRowModel: (0, react_table_1.getPaginationRowModel)(),
|
|
165
170
|
getCoreRowModel: (0, react_table_1.getCoreRowModel)(),
|
|
166
171
|
columnResizeMode: 'onEnd',
|
|
167
|
-
keepPinnedRows
|
|
172
|
+
keepPinnedRows,
|
|
173
|
+
meta: {
|
|
174
|
+
tableContainerRef,
|
|
175
|
+
rowVirtualizerInstanceRef,
|
|
176
|
+
rowVirtualizerOptions,
|
|
177
|
+
enableRowVirtualization
|
|
178
|
+
}
|
|
168
179
|
});
|
|
169
180
|
const {
|
|
170
181
|
loadingTable
|
|
@@ -204,10 +215,22 @@ function Table(_a) {
|
|
|
204
215
|
}, [loading, rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow, table, enableSelectPinned]);
|
|
205
216
|
const columnSizeVarsRef = (0, react_1.useRef)();
|
|
206
217
|
const headers = table.getFlatHeaders();
|
|
207
|
-
const
|
|
218
|
+
const tableRows = table.getRowModel().rows;
|
|
219
|
+
const tableCenterRows = table.getCenterRows();
|
|
220
|
+
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
221
|
+
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
222
|
+
const topRows = table.getTopRows();
|
|
223
|
+
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
224
|
+
const tablePagination = table.getState().pagination;
|
|
225
|
+
const [isScrollBeenInitialized, setScrollInitialization] = (0, react_1.useState)(false);
|
|
226
|
+
const rowVirtualizer = (0, useRowVirtualizer_1.useRowVirtualizer)(table, isScrollBeenInitialized);
|
|
227
|
+
const overallVirtualHeight = (_b = rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.getTotalSize()) !== null && _b !== void 0 ? _b : 0;
|
|
228
|
+
const cssSizeVars = (0, react_1.useMemo)(() => {
|
|
208
229
|
var _a;
|
|
209
230
|
const originalColumnDefs = table._getColumnDefs();
|
|
210
|
-
const colSizes = {
|
|
231
|
+
const colSizes = overallVirtualHeight > 0 ? {
|
|
232
|
+
height: `${overallVirtualHeight}px`
|
|
233
|
+
} : {};
|
|
211
234
|
for (let i = 0; i < headers.length; i++) {
|
|
212
235
|
const header = headers[i];
|
|
213
236
|
const {
|
|
@@ -265,17 +288,10 @@ function Table(_a) {
|
|
|
265
288
|
table.getTotalSize() will trigger re-render after double-click size reset
|
|
266
289
|
*/
|
|
267
290
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
268
|
-
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize()]);
|
|
291
|
+
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize(), overallVirtualHeight]);
|
|
269
292
|
(0, react_1.useEffect)(() => {
|
|
270
|
-
columnSizeVarsRef.current =
|
|
271
|
-
}, [
|
|
272
|
-
const tableRows = table.getRowModel().rows;
|
|
273
|
-
const tableCenterRows = table.getCenterRows();
|
|
274
|
-
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
275
|
-
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
276
|
-
const topRows = table.getTopRows();
|
|
277
|
-
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
278
|
-
const tablePagination = table.getState().pagination;
|
|
293
|
+
columnSizeVarsRef.current = cssSizeVars;
|
|
294
|
+
}, [cssSizeVars]);
|
|
279
295
|
const filteredTopRows = table.getState().globalFilter ? topRows.filter(tr => tableFilteredRowsIds.includes(tr.id)) : topRows;
|
|
280
296
|
const centerRows = copyPinnedRows ? tableRows : tableCenterRows;
|
|
281
297
|
const {
|
|
@@ -290,6 +306,12 @@ function Table(_a) {
|
|
|
290
306
|
const tempPageSize = (!suppressPagination ? tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize : pageSize) + filteredTopRows.length;
|
|
291
307
|
return !tableRows.length ? Math.min(Math.max(tempPageSize, 5), constants_1.DEFAULT_PAGE_SIZE) : tempPageSize;
|
|
292
308
|
}, [filteredTopRows.length, pageSize, suppressPagination, tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize, tableRows.length]);
|
|
309
|
+
const tableCtx = (0, react_1.useMemo)(() => ({
|
|
310
|
+
table
|
|
311
|
+
}), [table]);
|
|
312
|
+
const onInitializeScroll = (0, react_1.useCallback)(() => {
|
|
313
|
+
setScrollInitialization(true);
|
|
314
|
+
}, []);
|
|
293
315
|
(0, usePageReset_1.usePageReset)({
|
|
294
316
|
manualPagination,
|
|
295
317
|
maximumAvailablePage: pageCount || data.length / pagination.pageSize,
|
|
@@ -341,15 +363,15 @@ function Table(_a) {
|
|
|
341
363
|
"data-outline": outline || undefined,
|
|
342
364
|
children: (0, jsx_runtime_1.jsxs)(scroll_1.Scroll, {
|
|
343
365
|
size: 's',
|
|
366
|
+
ref: (0, merge_refs_1.default)(tableContainerRef, scrollContainerRef),
|
|
344
367
|
className: styles_module_scss_1.default.table,
|
|
345
|
-
|
|
368
|
+
onScroll: onScroll,
|
|
369
|
+
onInitialized: onInitializeScroll,
|
|
346
370
|
children: [(0, jsx_runtime_1.jsx)("div", {
|
|
347
371
|
className: styles_module_scss_1.default.tableContent,
|
|
348
|
-
style:
|
|
372
|
+
style: cssSizeVars,
|
|
349
373
|
children: (0, jsx_runtime_1.jsx)(helperComponents_1.TableContext.Provider, {
|
|
350
|
-
value:
|
|
351
|
-
table
|
|
352
|
-
},
|
|
374
|
+
value: tableCtx,
|
|
353
375
|
children: loading ? (0, jsx_runtime_1.jsxs)(skeleton_1.SkeletonContextProvider, {
|
|
354
376
|
loading: true,
|
|
355
377
|
children: [(0, jsx_runtime_1.jsx)(helperComponents_1.HeaderRow, {}), loadingTableRows.map(row => (0, jsx_runtime_1.jsx)(helperComponents_1.BodyRow, {
|
|
@@ -362,10 +384,18 @@ function Table(_a) {
|
|
|
362
384
|
row: row,
|
|
363
385
|
onRowClick: onRowClick
|
|
364
386
|
}, row.id))
|
|
365
|
-
}) : null,
|
|
366
|
-
row:
|
|
367
|
-
|
|
368
|
-
|
|
387
|
+
}) : null, ((_c = rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.getVirtualItems()) !== null && _c !== void 0 ? _c : centerRows).map((rowOrVirtualRow, index) => {
|
|
388
|
+
const row = (0, utils_3.isVirtualRow)(rowOrVirtualRow) ? centerRows[rowOrVirtualRow.index] : centerRows[index];
|
|
389
|
+
return (0, jsx_runtime_1.jsx)(VirtualRow_1.VirtualRow, {
|
|
390
|
+
ref: rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.measureElement,
|
|
391
|
+
"data-index": row.index,
|
|
392
|
+
virtualRow: (0, utils_3.isVirtualRow)(rowOrVirtualRow) ? rowOrVirtualRow : undefined,
|
|
393
|
+
children: (0, jsx_runtime_1.jsx)(helperComponents_1.BodyRow, {
|
|
394
|
+
row: row,
|
|
395
|
+
onRowClick: onRowClick
|
|
396
|
+
})
|
|
397
|
+
}, row.id);
|
|
398
|
+
}), (0, jsx_runtime_1.jsx)(helperComponents_1.TableEmptyState, {
|
|
369
399
|
emptyStates: emptyStates,
|
|
370
400
|
dataError: dataError,
|
|
371
401
|
dataFiltered: dataFiltered || Boolean(table.getState().globalFilter),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useRowVirtualizer = useRowVirtualizer;
|
|
7
|
+
const react_virtual_1 = require("@tanstack/react-virtual");
|
|
8
|
+
function useRowVirtualizer(table, isScrollInitialized) {
|
|
9
|
+
const {
|
|
10
|
+
getRowModel,
|
|
11
|
+
options: {
|
|
12
|
+
meta = {}
|
|
13
|
+
}
|
|
14
|
+
} = table;
|
|
15
|
+
const {
|
|
16
|
+
rowVirtualizerInstanceRef,
|
|
17
|
+
rowVirtualizerOptions,
|
|
18
|
+
tableContainerRef,
|
|
19
|
+
enableRowVirtualization
|
|
20
|
+
} = meta;
|
|
21
|
+
const normalRowHeight = 40;
|
|
22
|
+
const rowVirtualizer = (0, react_virtual_1.useVirtualizer)(Object.assign({
|
|
23
|
+
count: getRowModel().rows.length,
|
|
24
|
+
estimateSize: () => normalRowHeight,
|
|
25
|
+
getScrollElement: () => isScrollInitialized ? tableContainerRef.current : null,
|
|
26
|
+
measureElement: typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') === -1 ? element => element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height : undefined,
|
|
27
|
+
overscan: 28,
|
|
28
|
+
enabled: enableRowVirtualization
|
|
29
|
+
}, rowVirtualizerOptions));
|
|
30
|
+
if (rowVirtualizerInstanceRef && enableRowVirtualization) {
|
|
31
|
+
rowVirtualizerInstanceRef.current = rowVirtualizer;
|
|
32
|
+
}
|
|
33
|
+
return enableRowVirtualization ? rowVirtualizer : undefined;
|
|
34
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
.table{
|
|
2
2
|
border-width:var(--border-width-table, 1px);
|
|
3
3
|
border-radius:var(--radius-table-container, 14px);
|
|
4
|
+
will-change:transform;
|
|
5
|
+
overflow-anchor:none;
|
|
4
6
|
position:relative;
|
|
5
7
|
z-index:0;
|
|
8
|
+
contain:paint;
|
|
6
9
|
overflow:hidden !important;
|
|
7
10
|
display:flex;
|
|
8
11
|
box-sizing:border-box;
|
|
@@ -26,6 +29,7 @@
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
.tableContent{
|
|
32
|
+
content-visibility:auto;
|
|
29
33
|
min-width:-moz-max-content;
|
|
30
34
|
min-width:max-content;
|
|
31
35
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CellContext } from '@tanstack/react-table';
|
|
2
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
1
3
|
export declare function getCurrentlyConfiguredHeaderWidth(id: string): number;
|
|
2
4
|
export declare function getColumnStyleVars(id: string): {
|
|
3
5
|
sizeKey: string;
|
|
@@ -14,4 +16,12 @@ type SaveStateToLocalStorageProps = {
|
|
|
14
16
|
size: string;
|
|
15
17
|
};
|
|
16
18
|
export declare function saveStateToLocalStorage({ id, columnId, size }: SaveStateToLocalStorageProps): void;
|
|
19
|
+
/**
|
|
20
|
+
* check the key, because index is contained in a common table row
|
|
21
|
+
*/
|
|
22
|
+
export declare function isVirtualRow(row: unknown): row is VirtualItem;
|
|
23
|
+
/**
|
|
24
|
+
* @description prevent permanent recreation of the function on rerender
|
|
25
|
+
*/
|
|
26
|
+
export declare function truncateCell<TData>(cell: CellContext<TData, unknown>): import("react/jsx-runtime").JSX.Element;
|
|
17
27
|
export {};
|
|
@@ -7,6 +7,10 @@ exports.getCurrentlyConfiguredHeaderWidth = getCurrentlyConfiguredHeaderWidth;
|
|
|
7
7
|
exports.getColumnStyleVars = getColumnStyleVars;
|
|
8
8
|
exports.getInitColumnSizeFromLocalStorage = getInitColumnSizeFromLocalStorage;
|
|
9
9
|
exports.saveStateToLocalStorage = saveStateToLocalStorage;
|
|
10
|
+
exports.isVirtualRow = isVirtualRow;
|
|
11
|
+
exports.truncateCell = truncateCell;
|
|
12
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
13
|
+
const truncate_string_1 = require("@snack-uikit/truncate-string");
|
|
10
14
|
const utils_1 = require("@snack-uikit/utils");
|
|
11
15
|
function getCurrentlyConfiguredHeaderWidth(id) {
|
|
12
16
|
if ((0, utils_1.isBrowser)()) {
|
|
@@ -53,4 +57,19 @@ function saveStateToLocalStorage(_ref2) {
|
|
|
53
57
|
localStorage.setItem(id, JSON.stringify(Object.assign(Object.assign({}, savedStateFromStorage || {}), {
|
|
54
58
|
resizeState: newResizeState
|
|
55
59
|
})));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* check the key, because index is contained in a common table row
|
|
63
|
+
*/
|
|
64
|
+
function isVirtualRow(row) {
|
|
65
|
+
return typeof row === 'object' && row != null && 'key' in row;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* @description prevent permanent recreation of the function on rerender
|
|
69
|
+
*/
|
|
70
|
+
function truncateCell(cell) {
|
|
71
|
+
return (0, jsx_runtime_1.jsx)(truncate_string_1.TruncateString, {
|
|
72
|
+
text: String(cell.getValue()),
|
|
73
|
+
maxLines: 1
|
|
74
|
+
});
|
|
56
75
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { PaginationState, Row, RowPinningState, RowSelectionOptions, RowSelectionState, SortingState } from '@tanstack/react-table';
|
|
2
|
-
import {
|
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
+
import { MutableRefObject, ReactNode, RefObject } from 'react';
|
|
3
4
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
4
5
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
5
6
|
import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
|
|
6
|
-
import { ColumnDefinition } from '../types';
|
|
7
|
+
import { ColumnDefinition, RowVirtualizer } from '../types';
|
|
7
8
|
export type TableProps<TData extends object> = WithSupportProps<{
|
|
8
9
|
/** Данные для отрисовки */
|
|
9
10
|
data: TData[];
|
|
@@ -129,6 +130,10 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
129
130
|
id: string;
|
|
130
131
|
resize?: boolean;
|
|
131
132
|
};
|
|
133
|
+
onScroll?: (event?: Event) => void;
|
|
134
|
+
enableRowVirtualization?: boolean;
|
|
135
|
+
rowVirtualizerOptions?: Parameters<typeof useVirtualizer>[0];
|
|
136
|
+
rowVirtualizerInstanceRef?: MutableRefObject<RowVirtualizer>;
|
|
132
137
|
}>;
|
|
133
138
|
export type ServerTableProps<TData extends object> = Omit<TableProps<TData>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
|
|
134
139
|
/** Данные для отрисовки */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
export declare const VirtualRow: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
virtualRow?: VirtualItem;
|
|
5
|
+
} & {
|
|
6
|
+
children?: import("react").ReactNode | undefined;
|
|
7
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __rest = void 0 && (void 0).__rest || function (s, e) {
|
|
4
|
+
var t = {};
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function") 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])) t[p[i]] = s[p[i]];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
12
|
+
return mod && mod.__esModule ? mod : {
|
|
13
|
+
"default": mod
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
19
|
+
exports.VirtualRow = void 0;
|
|
20
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
23
|
+
exports.VirtualRow = (0, react_1.forwardRef)((_a, ref) => {
|
|
24
|
+
var {
|
|
25
|
+
virtualRow,
|
|
26
|
+
children
|
|
27
|
+
} = _a,
|
|
28
|
+
attributes = __rest(_a, ["virtualRow", "children"]);
|
|
29
|
+
if (!virtualRow) {
|
|
30
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {
|
|
31
|
+
children: children
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return (0, jsx_runtime_1.jsx)("div", Object.assign({
|
|
35
|
+
ref: ref,
|
|
36
|
+
className: styles_module_scss_1.default.virtualRow,
|
|
37
|
+
style: {
|
|
38
|
+
'--virtual-row-deviation': `${virtualRow.start}px`
|
|
39
|
+
}
|
|
40
|
+
}, attributes, {
|
|
41
|
+
children: children
|
|
42
|
+
}));
|
|
43
|
+
});
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CellContext, ColumnDef, ColumnMeta, HeaderContext, PaginationState, RowSelectionOptions, RowSelectionState, SortingState } from '@tanstack/react-table';
|
|
2
|
+
import { Virtualizer } from '@tanstack/react-virtual';
|
|
2
3
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
3
4
|
import { ValueOf } from '@snack-uikit/utils';
|
|
4
5
|
import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
|
|
@@ -39,5 +40,6 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
39
40
|
size: number;
|
|
40
41
|
};
|
|
41
42
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
43
|
+
export type RowVirtualizer = Virtualizer<Element, Element> | null;
|
|
42
44
|
export type { RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
|
|
43
45
|
export type { ColumnPinPosition, PaginationState, SortingState, RowSelectionState, RowSelectionOptions, EmptyStateProps, ToolbarProps, HeaderContext, CellContext, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TableProps } from '../types';
|
|
2
2
|
/** Компонент таблицы */
|
|
3
|
-
export declare function Table<TData extends object>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function Table<TData extends object>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, onScroll, enableRowVirtualization, rowVirtualizerOptions, rowVirtualizerInstanceRef, ...rest }: TableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export declare namespace Table {
|
|
5
5
|
var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
|
|
6
6
|
var statusAppearances: Record<string, string>;
|
|
@@ -12,25 +12,28 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
|
|
14
14
|
import cn from 'classnames';
|
|
15
|
-
import
|
|
15
|
+
import mergeRefs from 'merge-refs';
|
|
16
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
17
|
import { useLocale } from '@snack-uikit/locale';
|
|
17
18
|
import { Scroll } from '@snack-uikit/scroll';
|
|
18
19
|
import { SkeletonContextProvider } from '@snack-uikit/skeleton';
|
|
19
20
|
import { Toolbar } from '@snack-uikit/toolbar';
|
|
20
|
-
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
21
21
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
22
22
|
import { DEFAULT_PAGE_SIZE } from '../../constants';
|
|
23
23
|
import { BodyRow, ExportButton, getColumnId, getRowActionsColumnDef, getSelectionCellColumnDef, getStatusColumnDef, HeaderRow, STATUS_APPEARANCE, TableContext, TableEmptyState, TablePagination, useEmptyState, } from '../../helperComponents';
|
|
24
|
+
import { VirtualRow } from '../../helperComponents/Rows/VirtualRow';
|
|
24
25
|
import { fuzzyFilter } from '../../utils';
|
|
25
26
|
import { useLoadingTable, useStateControl } from './hooks';
|
|
26
27
|
import { usePageReset } from './hooks/usePageReset';
|
|
28
|
+
import { useRowVirtualizer } from './hooks/useRowVirtualizer';
|
|
27
29
|
import styles from './styles.module.css';
|
|
28
|
-
import { getColumnStyleVars, getCurrentlyConfiguredHeaderWidth, getInitColumnSizeFromLocalStorage, saveStateToLocalStorage, } from './utils';
|
|
30
|
+
import { getColumnStyleVars, getCurrentlyConfiguredHeaderWidth, getInitColumnSizeFromLocalStorage, isVirtualRow, saveStateToLocalStorage, truncateCell, } from './utils';
|
|
29
31
|
/** Компонент таблицы */
|
|
30
32
|
export function Table(_a) {
|
|
33
|
+
var _b, _c;
|
|
31
34
|
var { data, rowPinning = {
|
|
32
35
|
top: [],
|
|
33
|
-
}, columnDefinitions, keepPinnedRows = false, copyPinnedRows = false, enableSelectPinned = false, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, toolbarBefore, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, autoResetPageIndex = false, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState } = _a, rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "toolbarBefore", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState"]);
|
|
36
|
+
}, columnDefinitions, keepPinnedRows = false, copyPinnedRows = false, enableSelectPinned = false, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, toolbarBefore, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, autoResetPageIndex = false, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, onScroll, enableRowVirtualization, rowVirtualizerOptions, rowVirtualizerInstanceRef } = _a, rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "toolbarBefore", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "onScroll", "enableRowVirtualization", "rowVirtualizerOptions", "rowVirtualizerInstanceRef"]);
|
|
34
37
|
const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl(search, '');
|
|
35
38
|
const { state: rowSelection, onStateChange: onRowSelectionChange } = useStateControl(rowSelectionProp, {});
|
|
36
39
|
const defaultPaginationState = useMemo(() => ({
|
|
@@ -40,6 +43,7 @@ export function Table(_a) {
|
|
|
40
43
|
const { state: sorting, onStateChange: onSortingChange } = useStateControl(sortingProp, []);
|
|
41
44
|
const { state: pagination, onStateChange: onPaginationChange } = useStateControl(paginationProp, defaultPaginationState);
|
|
42
45
|
const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
|
|
46
|
+
const tableContainerRef = useRef(null);
|
|
43
47
|
const tableColumns = useMemo(() => {
|
|
44
48
|
let cols = columnDefinitions;
|
|
45
49
|
if (enableSelection) {
|
|
@@ -74,7 +78,7 @@ export function Table(_a) {
|
|
|
74
78
|
enableSorting: false,
|
|
75
79
|
enableResizing: false,
|
|
76
80
|
minSize: 40,
|
|
77
|
-
cell:
|
|
81
|
+
cell: truncateCell,
|
|
78
82
|
},
|
|
79
83
|
manualSorting,
|
|
80
84
|
manualPagination,
|
|
@@ -98,6 +102,12 @@ export function Table(_a) {
|
|
|
98
102
|
getCoreRowModel: getCoreRowModel(),
|
|
99
103
|
columnResizeMode: 'onEnd',
|
|
100
104
|
keepPinnedRows,
|
|
105
|
+
meta: {
|
|
106
|
+
tableContainerRef,
|
|
107
|
+
rowVirtualizerInstanceRef,
|
|
108
|
+
rowVirtualizerOptions,
|
|
109
|
+
enableRowVirtualization,
|
|
110
|
+
},
|
|
101
111
|
});
|
|
102
112
|
const { loadingTable } = useLoadingTable({
|
|
103
113
|
pageSize: Math.min(Math.max(pageSize, 5), DEFAULT_PAGE_SIZE),
|
|
@@ -135,10 +145,20 @@ export function Table(_a) {
|
|
|
135
145
|
}, [loading, rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow, table, enableSelectPinned]);
|
|
136
146
|
const columnSizeVarsRef = useRef();
|
|
137
147
|
const headers = table.getFlatHeaders();
|
|
138
|
-
const
|
|
148
|
+
const tableRows = table.getRowModel().rows;
|
|
149
|
+
const tableCenterRows = table.getCenterRows();
|
|
150
|
+
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
151
|
+
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
152
|
+
const topRows = table.getTopRows();
|
|
153
|
+
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
154
|
+
const tablePagination = table.getState().pagination;
|
|
155
|
+
const [isScrollBeenInitialized, setScrollInitialization] = useState(false);
|
|
156
|
+
const rowVirtualizer = useRowVirtualizer(table, isScrollBeenInitialized);
|
|
157
|
+
const overallVirtualHeight = (_b = rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.getTotalSize()) !== null && _b !== void 0 ? _b : 0;
|
|
158
|
+
const cssSizeVars = useMemo(() => {
|
|
139
159
|
var _a;
|
|
140
160
|
const originalColumnDefs = table._getColumnDefs();
|
|
141
|
-
const colSizes = {};
|
|
161
|
+
const colSizes = overallVirtualHeight > 0 ? { height: `${overallVirtualHeight}px` } : {};
|
|
142
162
|
for (let i = 0; i < headers.length; i++) {
|
|
143
163
|
const header = headers[i];
|
|
144
164
|
const { sizeKey, flexKey } = getColumnStyleVars(header.id);
|
|
@@ -187,17 +207,10 @@ export function Table(_a) {
|
|
|
187
207
|
table.getTotalSize() will trigger re-render after double-click size reset
|
|
188
208
|
*/
|
|
189
209
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
190
|
-
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize()]);
|
|
210
|
+
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize(), overallVirtualHeight]);
|
|
191
211
|
useEffect(() => {
|
|
192
|
-
columnSizeVarsRef.current =
|
|
193
|
-
}, [
|
|
194
|
-
const tableRows = table.getRowModel().rows;
|
|
195
|
-
const tableCenterRows = table.getCenterRows();
|
|
196
|
-
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
197
|
-
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
198
|
-
const topRows = table.getTopRows();
|
|
199
|
-
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
200
|
-
const tablePagination = table.getState().pagination;
|
|
212
|
+
columnSizeVarsRef.current = cssSizeVars;
|
|
213
|
+
}, [cssSizeVars]);
|
|
201
214
|
const filteredTopRows = table.getState().globalFilter
|
|
202
215
|
? topRows.filter(tr => tableFilteredRowsIds.includes(tr.id))
|
|
203
216
|
: topRows;
|
|
@@ -208,6 +221,10 @@ export function Table(_a) {
|
|
|
208
221
|
const tempPageSize = (!suppressPagination ? tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize : pageSize) + filteredTopRows.length;
|
|
209
222
|
return !tableRows.length ? Math.min(Math.max(tempPageSize, 5), DEFAULT_PAGE_SIZE) : tempPageSize;
|
|
210
223
|
}, [filteredTopRows.length, pageSize, suppressPagination, tablePagination === null || tablePagination === void 0 ? void 0 : tablePagination.pageSize, tableRows.length]);
|
|
224
|
+
const tableCtx = useMemo(() => ({ table }), [table]);
|
|
225
|
+
const onInitializeScroll = useCallback(() => {
|
|
226
|
+
setScrollInitialization(true);
|
|
227
|
+
}, []);
|
|
211
228
|
usePageReset({
|
|
212
229
|
manualPagination,
|
|
213
230
|
maximumAvailablePage: pageCount || data.length / pagination.pageSize,
|
|
@@ -222,7 +239,10 @@ export function Table(_a) {
|
|
|
222
239
|
onChange: onGlobalFilterChange,
|
|
223
240
|
loading: search === null || search === void 0 ? void 0 : search.loading,
|
|
224
241
|
placeholder: (search === null || search === void 0 ? void 0 : search.placeholder) || t('searchPlaceholder'),
|
|
225
|
-
}, 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: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions }), columnFilters && _jsxs("div", { className: styles.filtersWrapper, children: [" ", columnFilters, " "] })] })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', className: styles.table,
|
|
242
|
+
}, 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: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions }), columnFilters && _jsxs("div", { className: styles.filtersWrapper, children: [" ", columnFilters, " "] })] })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', ref: mergeRefs(tableContainerRef, scrollContainerRef), className: styles.table, onScroll: onScroll, onInitialized: onInitializeScroll, children: [_jsx("div", { className: styles.tableContent, style: cssSizeVars, children: _jsx(TableContext.Provider, { value: tableCtx, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [centerRows.length || filteredTopRows.length ? _jsx(HeaderRow, {}) : null, filteredTopRows.length ? (_jsx("div", { className: styles.topRowWrapper, children: filteredTopRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))) })) : null, ((_c = rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.getVirtualItems()) !== null && _c !== void 0 ? _c : centerRows).map((rowOrVirtualRow, index) => {
|
|
243
|
+
const row = isVirtualRow(rowOrVirtualRow) ? centerRows[rowOrVirtualRow.index] : centerRows[index];
|
|
244
|
+
return (_jsx(VirtualRow, { ref: rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.measureElement, "data-index": row.index, virtualRow: isVirtualRow(rowOrVirtualRow) ? rowOrVirtualRow : undefined, children: _jsx(BodyRow, { row: row, onRowClick: onRowClick }) }, row.id));
|
|
245
|
+
}), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length + filteredTopRows.length })] })) }) }), _jsx("div", { className: styles.scrollStub, ref: scrollRef })] }) }), !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, optionsRender: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsRender }))] })) }));
|
|
226
246
|
}
|
|
227
247
|
Table.getStatusColumnDef = getStatusColumnDef;
|
|
228
248
|
Table.statusAppearances = STATUS_APPEARANCE;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
2
|
+
export function useRowVirtualizer(table, isScrollInitialized) {
|
|
3
|
+
const { getRowModel, options: { meta = {} }, } = table;
|
|
4
|
+
const { rowVirtualizerInstanceRef, rowVirtualizerOptions, tableContainerRef, enableRowVirtualization } = meta;
|
|
5
|
+
const normalRowHeight = 40;
|
|
6
|
+
const rowVirtualizer = useVirtualizer(Object.assign({ count: getRowModel().rows.length, estimateSize: () => normalRowHeight, getScrollElement: () => (isScrollInitialized ? tableContainerRef.current : null), measureElement: typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') === -1
|
|
7
|
+
? element => element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height
|
|
8
|
+
: undefined, overscan: 28, enabled: enableRowVirtualization }, rowVirtualizerOptions));
|
|
9
|
+
if (rowVirtualizerInstanceRef && enableRowVirtualization) {
|
|
10
|
+
rowVirtualizerInstanceRef.current = rowVirtualizer;
|
|
11
|
+
}
|
|
12
|
+
return enableRowVirtualization ? rowVirtualizer : undefined;
|
|
13
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
.table{
|
|
2
2
|
border-width:var(--border-width-table, 1px);
|
|
3
3
|
border-radius:var(--radius-table-container, 14px);
|
|
4
|
+
will-change:transform;
|
|
5
|
+
overflow-anchor:none;
|
|
4
6
|
position:relative;
|
|
5
7
|
z-index:0;
|
|
8
|
+
contain:paint;
|
|
6
9
|
overflow:hidden !important;
|
|
7
10
|
display:flex;
|
|
8
11
|
box-sizing:border-box;
|
|
@@ -26,6 +29,7 @@
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
.tableContent{
|
|
32
|
+
content-visibility:auto;
|
|
29
33
|
min-width:-moz-max-content;
|
|
30
34
|
min-width:max-content;
|
|
31
35
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CellContext } from '@tanstack/react-table';
|
|
2
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
1
3
|
export declare function getCurrentlyConfiguredHeaderWidth(id: string): number;
|
|
2
4
|
export declare function getColumnStyleVars(id: string): {
|
|
3
5
|
sizeKey: string;
|
|
@@ -14,4 +16,12 @@ type SaveStateToLocalStorageProps = {
|
|
|
14
16
|
size: string;
|
|
15
17
|
};
|
|
16
18
|
export declare function saveStateToLocalStorage({ id, columnId, size }: SaveStateToLocalStorageProps): void;
|
|
19
|
+
/**
|
|
20
|
+
* check the key, because index is contained in a common table row
|
|
21
|
+
*/
|
|
22
|
+
export declare function isVirtualRow(row: unknown): row is VirtualItem;
|
|
23
|
+
/**
|
|
24
|
+
* @description prevent permanent recreation of the function on rerender
|
|
25
|
+
*/
|
|
26
|
+
export declare function truncateCell<TData>(cell: CellContext<TData, unknown>): import("react/jsx-runtime").JSX.Element;
|
|
17
27
|
export {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
1
3
|
import { isBrowser } from '@snack-uikit/utils';
|
|
2
4
|
export function getCurrentlyConfiguredHeaderWidth(id) {
|
|
3
5
|
if (isBrowser()) {
|
|
@@ -32,3 +34,15 @@ export function saveStateToLocalStorage({ id, columnId, size }) {
|
|
|
32
34
|
newResizeState[`${RESIZED_KEY}-${columnId}`] = size;
|
|
33
35
|
localStorage.setItem(id, JSON.stringify(Object.assign(Object.assign({}, (savedStateFromStorage || {})), { resizeState: newResizeState })));
|
|
34
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* check the key, because index is contained in a common table row
|
|
39
|
+
*/
|
|
40
|
+
export function isVirtualRow(row) {
|
|
41
|
+
return typeof row === 'object' && row != null && 'key' in row;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @description prevent permanent recreation of the function on rerender
|
|
45
|
+
*/
|
|
46
|
+
export function truncateCell(cell) {
|
|
47
|
+
return _jsx(TruncateString, { text: String(cell.getValue()), maxLines: 1 });
|
|
48
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { PaginationState, Row, RowPinningState, RowSelectionOptions, RowSelectionState, SortingState } from '@tanstack/react-table';
|
|
2
|
-
import {
|
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
+
import { MutableRefObject, ReactNode, RefObject } from 'react';
|
|
3
4
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
4
5
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
5
6
|
import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
|
|
6
|
-
import { ColumnDefinition } from '../types';
|
|
7
|
+
import { ColumnDefinition, RowVirtualizer } from '../types';
|
|
7
8
|
export type TableProps<TData extends object> = WithSupportProps<{
|
|
8
9
|
/** Данные для отрисовки */
|
|
9
10
|
data: TData[];
|
|
@@ -129,6 +130,10 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
129
130
|
id: string;
|
|
130
131
|
resize?: boolean;
|
|
131
132
|
};
|
|
133
|
+
onScroll?: (event?: Event) => void;
|
|
134
|
+
enableRowVirtualization?: boolean;
|
|
135
|
+
rowVirtualizerOptions?: Parameters<typeof useVirtualizer>[0];
|
|
136
|
+
rowVirtualizerInstanceRef?: MutableRefObject<RowVirtualizer>;
|
|
132
137
|
}>;
|
|
133
138
|
export type ServerTableProps<TData extends object> = Omit<TableProps<TData>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
|
|
134
139
|
/** Данные для отрисовки */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
export declare const VirtualRow: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
virtualRow?: VirtualItem;
|
|
5
|
+
} & {
|
|
6
|
+
children?: import("react").ReactNode | undefined;
|
|
7
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
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 { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { forwardRef } from 'react';
|
|
14
|
+
import styles from './styles.module.css';
|
|
15
|
+
export const VirtualRow = forwardRef((_a, ref) => {
|
|
16
|
+
var { virtualRow, children } = _a, attributes = __rest(_a, ["virtualRow", "children"]);
|
|
17
|
+
if (!virtualRow) {
|
|
18
|
+
return _jsx(_Fragment, { children: children });
|
|
19
|
+
}
|
|
20
|
+
return (_jsx("div", Object.assign({ ref: ref, className: styles.virtualRow, style: { '--virtual-row-deviation': `${virtualRow.start}px` } }, attributes, { children: children })));
|
|
21
|
+
});
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CellContext, ColumnDef, ColumnMeta, HeaderContext, PaginationState, RowSelectionOptions, RowSelectionState, SortingState } from '@tanstack/react-table';
|
|
2
|
+
import { Virtualizer } from '@tanstack/react-virtual';
|
|
2
3
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
3
4
|
import { ValueOf } from '@snack-uikit/utils';
|
|
4
5
|
import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
|
|
@@ -39,5 +40,6 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
39
40
|
size: number;
|
|
40
41
|
};
|
|
41
42
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
43
|
+
export type RowVirtualizer = Virtualizer<Element, Element> | null;
|
|
42
44
|
export type { RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
|
|
43
45
|
export type { ColumnPinPosition, PaginationState, SortingState, RowSelectionState, RowSelectionOptions, EmptyStateProps, ToolbarProps, HeaderContext, CellContext, };
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Table",
|
|
7
|
-
"version": "0.25.
|
|
7
|
+
"version": "0.25.18-preview-cfe0e263.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -52,14 +52,16 @@
|
|
|
52
52
|
"@snack-uikit/utils": "3.6.0",
|
|
53
53
|
"@tanstack/match-sorter-utils": "8.11.8",
|
|
54
54
|
"@tanstack/react-table": "8.12.0",
|
|
55
|
+
"@tanstack/react-virtual": "3.10.9",
|
|
55
56
|
"classnames": "2.5.1",
|
|
56
57
|
"copy-to-clipboard": "3.3.3",
|
|
57
58
|
"lodash.debounce": "4.0.8",
|
|
59
|
+
"merge-refs": "1.3.0",
|
|
58
60
|
"uncontrollable": "8.0.4",
|
|
59
61
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
60
62
|
},
|
|
61
63
|
"peerDependencies": {
|
|
62
64
|
"@snack-uikit/locale": "*"
|
|
63
65
|
},
|
|
64
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "d40c24c19cce11b5126931bc317bf88deb0c216a"
|
|
65
67
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CellContext,
|
|
3
2
|
ColumnPinningState,
|
|
4
3
|
getCoreRowModel,
|
|
5
4
|
getFilteredRowModel,
|
|
@@ -11,13 +10,13 @@ import {
|
|
|
11
10
|
useReactTable,
|
|
12
11
|
} from '@tanstack/react-table';
|
|
13
12
|
import cn from 'classnames';
|
|
14
|
-
import
|
|
13
|
+
import mergeRefs from 'merge-refs';
|
|
14
|
+
import { RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
|
|
16
16
|
import { useLocale } from '@snack-uikit/locale';
|
|
17
17
|
import { Scroll } from '@snack-uikit/scroll';
|
|
18
18
|
import { SkeletonContextProvider } from '@snack-uikit/skeleton';
|
|
19
19
|
import { Toolbar } from '@snack-uikit/toolbar';
|
|
20
|
-
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
21
20
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
22
21
|
|
|
23
22
|
import { DEFAULT_PAGE_SIZE } from '../../constants';
|
|
@@ -35,17 +34,21 @@ import {
|
|
|
35
34
|
TablePagination,
|
|
36
35
|
useEmptyState,
|
|
37
36
|
} from '../../helperComponents';
|
|
37
|
+
import { VirtualRow } from '../../helperComponents/Rows/VirtualRow';
|
|
38
38
|
import { ColumnDefinition } from '../../types';
|
|
39
39
|
import { fuzzyFilter } from '../../utils';
|
|
40
40
|
import { TableProps } from '../types';
|
|
41
41
|
import { useLoadingTable, useStateControl } from './hooks';
|
|
42
42
|
import { usePageReset } from './hooks/usePageReset';
|
|
43
|
+
import { useRowVirtualizer } from './hooks/useRowVirtualizer';
|
|
43
44
|
import styles from './styles.module.scss';
|
|
44
45
|
import {
|
|
45
46
|
getColumnStyleVars,
|
|
46
47
|
getCurrentlyConfiguredHeaderWidth,
|
|
47
48
|
getInitColumnSizeFromLocalStorage,
|
|
49
|
+
isVirtualRow,
|
|
48
50
|
saveStateToLocalStorage,
|
|
51
|
+
truncateCell,
|
|
49
52
|
} from './utils';
|
|
50
53
|
|
|
51
54
|
/** Компонент таблицы */
|
|
@@ -92,6 +95,11 @@ export function Table<TData extends object>({
|
|
|
92
95
|
enableFuzzySearch,
|
|
93
96
|
savedState,
|
|
94
97
|
|
|
98
|
+
onScroll,
|
|
99
|
+
enableRowVirtualization,
|
|
100
|
+
rowVirtualizerOptions,
|
|
101
|
+
rowVirtualizerInstanceRef,
|
|
102
|
+
|
|
95
103
|
...rest
|
|
96
104
|
}: TableProps<TData>) {
|
|
97
105
|
const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl<string>(search, '');
|
|
@@ -114,6 +122,7 @@ export function Table<TData extends object>({
|
|
|
114
122
|
defaultPaginationState,
|
|
115
123
|
);
|
|
116
124
|
const enableSelection = Boolean(rowSelectionProp?.enable);
|
|
125
|
+
const tableContainerRef = useRef<HTMLElement>(null);
|
|
117
126
|
|
|
118
127
|
const tableColumns: ColumnDefinition<TData>[] = useMemo(() => {
|
|
119
128
|
let cols: ColumnDefinition<TData>[] = columnDefinitions;
|
|
@@ -137,6 +146,7 @@ export function Table<TData extends object>({
|
|
|
137
146
|
const table = useReactTable<TData>({
|
|
138
147
|
data,
|
|
139
148
|
columns: tableColumns,
|
|
149
|
+
|
|
140
150
|
state: {
|
|
141
151
|
columnPinning,
|
|
142
152
|
globalFilter,
|
|
@@ -150,7 +160,7 @@ export function Table<TData extends object>({
|
|
|
150
160
|
enableSorting: false,
|
|
151
161
|
enableResizing: false,
|
|
152
162
|
minSize: 40,
|
|
153
|
-
cell:
|
|
163
|
+
cell: truncateCell,
|
|
154
164
|
},
|
|
155
165
|
|
|
156
166
|
manualSorting,
|
|
@@ -177,6 +187,13 @@ export function Table<TData extends object>({
|
|
|
177
187
|
getCoreRowModel: getCoreRowModel(),
|
|
178
188
|
columnResizeMode: 'onEnd',
|
|
179
189
|
keepPinnedRows,
|
|
190
|
+
|
|
191
|
+
meta: {
|
|
192
|
+
tableContainerRef,
|
|
193
|
+
rowVirtualizerInstanceRef,
|
|
194
|
+
rowVirtualizerOptions,
|
|
195
|
+
enableRowVirtualization,
|
|
196
|
+
},
|
|
180
197
|
});
|
|
181
198
|
|
|
182
199
|
const { loadingTable } = useLoadingTable({
|
|
@@ -224,9 +241,21 @@ export function Table<TData extends object>({
|
|
|
224
241
|
const columnSizeVarsRef = useRef<Record<string, string>>();
|
|
225
242
|
const headers = table.getFlatHeaders();
|
|
226
243
|
|
|
227
|
-
const
|
|
244
|
+
const tableRows = table.getRowModel().rows;
|
|
245
|
+
const tableCenterRows = table.getCenterRows();
|
|
246
|
+
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
247
|
+
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
248
|
+
const topRows = table.getTopRows();
|
|
249
|
+
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
250
|
+
const tablePagination = table.getState().pagination;
|
|
251
|
+
|
|
252
|
+
const [isScrollBeenInitialized, setScrollInitialization] = useState(false);
|
|
253
|
+
const rowVirtualizer = useRowVirtualizer(table, isScrollBeenInitialized);
|
|
254
|
+
const overallVirtualHeight = rowVirtualizer?.getTotalSize() ?? 0;
|
|
255
|
+
|
|
256
|
+
const cssSizeVars = useMemo(() => {
|
|
228
257
|
const originalColumnDefs = table._getColumnDefs();
|
|
229
|
-
const colSizes: Record<string, string> = {};
|
|
258
|
+
const colSizes: Record<string, string> = overallVirtualHeight > 0 ? { height: `${overallVirtualHeight}px` } : {};
|
|
230
259
|
|
|
231
260
|
for (let i = 0; i < headers.length; i++) {
|
|
232
261
|
const header = headers[i];
|
|
@@ -288,19 +317,11 @@ export function Table<TData extends object>({
|
|
|
288
317
|
table.getTotalSize() will trigger re-render after double-click size reset
|
|
289
318
|
*/
|
|
290
319
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
291
|
-
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize()]);
|
|
320
|
+
}, [table.getState().columnSizingInfo.isResizingColumn, headers, table.getTotalSize(), overallVirtualHeight]);
|
|
292
321
|
|
|
293
322
|
useEffect(() => {
|
|
294
|
-
columnSizeVarsRef.current =
|
|
295
|
-
}, [
|
|
296
|
-
|
|
297
|
-
const tableRows = table.getRowModel().rows;
|
|
298
|
-
const tableCenterRows = table.getCenterRows();
|
|
299
|
-
const tableFilteredRows = table.getFilteredRowModel().rows;
|
|
300
|
-
const tableFilteredRowsIds = tableFilteredRows.map(row => row.id);
|
|
301
|
-
const topRows = table.getTopRows();
|
|
302
|
-
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
303
|
-
const tablePagination = table.getState().pagination;
|
|
323
|
+
columnSizeVarsRef.current = cssSizeVars;
|
|
324
|
+
}, [cssSizeVars]);
|
|
304
325
|
|
|
305
326
|
const filteredTopRows = table.getState().globalFilter
|
|
306
327
|
? topRows.filter(tr => tableFilteredRowsIds.includes(tr.id))
|
|
@@ -316,6 +337,12 @@ export function Table<TData extends object>({
|
|
|
316
337
|
return !tableRows.length ? Math.min(Math.max(tempPageSize, 5), DEFAULT_PAGE_SIZE) : tempPageSize;
|
|
317
338
|
}, [filteredTopRows.length, pageSize, suppressPagination, tablePagination?.pageSize, tableRows.length]);
|
|
318
339
|
|
|
340
|
+
const tableCtx = useMemo(() => ({ table }), [table]);
|
|
341
|
+
|
|
342
|
+
const onInitializeScroll = useCallback(() => {
|
|
343
|
+
setScrollInitialization(true);
|
|
344
|
+
}, []);
|
|
345
|
+
|
|
319
346
|
usePageReset({
|
|
320
347
|
manualPagination,
|
|
321
348
|
maximumAvailablePage: pageCount || data.length / pagination.pageSize,
|
|
@@ -375,9 +402,15 @@ export function Table<TData extends object>({
|
|
|
375
402
|
)}
|
|
376
403
|
|
|
377
404
|
<div className={styles.scrollWrapper} data-outline={outline || undefined}>
|
|
378
|
-
<Scroll
|
|
379
|
-
|
|
380
|
-
|
|
405
|
+
<Scroll
|
|
406
|
+
size='s'
|
|
407
|
+
ref={mergeRefs(tableContainerRef, scrollContainerRef)}
|
|
408
|
+
className={styles.table}
|
|
409
|
+
onScroll={onScroll}
|
|
410
|
+
onInitialized={onInitializeScroll}
|
|
411
|
+
>
|
|
412
|
+
<div className={styles.tableContent} style={cssSizeVars}>
|
|
413
|
+
<TableContext.Provider value={tableCtx}>
|
|
381
414
|
{loading ? (
|
|
382
415
|
<SkeletonContextProvider loading>
|
|
383
416
|
<HeaderRow />
|
|
@@ -396,9 +429,20 @@ export function Table<TData extends object>({
|
|
|
396
429
|
</div>
|
|
397
430
|
) : null}
|
|
398
431
|
|
|
399
|
-
{centerRows.map(
|
|
400
|
-
|
|
401
|
-
|
|
432
|
+
{(rowVirtualizer?.getVirtualItems() ?? centerRows).map((rowOrVirtualRow, index) => {
|
|
433
|
+
const row = isVirtualRow(rowOrVirtualRow) ? centerRows[rowOrVirtualRow.index] : centerRows[index];
|
|
434
|
+
|
|
435
|
+
return (
|
|
436
|
+
<VirtualRow
|
|
437
|
+
key={row.id}
|
|
438
|
+
ref={rowVirtualizer?.measureElement}
|
|
439
|
+
data-index={row.index}
|
|
440
|
+
virtualRow={isVirtualRow(rowOrVirtualRow) ? rowOrVirtualRow : undefined}
|
|
441
|
+
>
|
|
442
|
+
<BodyRow row={row} onRowClick={onRowClick} />
|
|
443
|
+
</VirtualRow>
|
|
444
|
+
);
|
|
445
|
+
})}
|
|
402
446
|
|
|
403
447
|
<TableEmptyState
|
|
404
448
|
emptyStates={emptyStates}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Table } from '@tanstack/react-table';
|
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
+
import { MutableRefObject } from 'react';
|
|
4
|
+
|
|
5
|
+
import { TableProps } from '../../types';
|
|
6
|
+
|
|
7
|
+
type MetaParams = Pick<
|
|
8
|
+
TableProps<never>,
|
|
9
|
+
'rowVirtualizerInstanceRef' | 'rowVirtualizerOptions' | 'enableRowVirtualization'
|
|
10
|
+
> & {
|
|
11
|
+
tableContainerRef: MutableRefObject<HTMLElement | null>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function useRowVirtualizer<T extends object>(table: Table<T>, isScrollInitialized: boolean) {
|
|
15
|
+
const {
|
|
16
|
+
getRowModel,
|
|
17
|
+
options: { meta = {} },
|
|
18
|
+
} = table;
|
|
19
|
+
|
|
20
|
+
const { rowVirtualizerInstanceRef, rowVirtualizerOptions, tableContainerRef, enableRowVirtualization } =
|
|
21
|
+
meta as MetaParams;
|
|
22
|
+
|
|
23
|
+
const normalRowHeight = 40;
|
|
24
|
+
|
|
25
|
+
const rowVirtualizer = useVirtualizer({
|
|
26
|
+
count: getRowModel().rows.length,
|
|
27
|
+
estimateSize: () => normalRowHeight,
|
|
28
|
+
getScrollElement: () => (isScrollInitialized ? tableContainerRef.current : null),
|
|
29
|
+
measureElement:
|
|
30
|
+
typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') === -1
|
|
31
|
+
? element => element?.getBoundingClientRect().height
|
|
32
|
+
: undefined,
|
|
33
|
+
overscan: 28,
|
|
34
|
+
enabled: enableRowVirtualization,
|
|
35
|
+
...rowVirtualizerOptions,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (rowVirtualizerInstanceRef && enableRowVirtualization) {
|
|
39
|
+
rowVirtualizerInstanceRef.current = rowVirtualizer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return enableRowVirtualization ? rowVirtualizer : undefined;
|
|
43
|
+
}
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
.table {
|
|
4
4
|
@include styles-tokens-table.composite-var(styles-tokens-table.$table-table-container);
|
|
5
5
|
|
|
6
|
+
will-change: transform;
|
|
7
|
+
overflow-anchor: none;
|
|
8
|
+
|
|
6
9
|
position: relative;
|
|
7
10
|
/* stylelint-disable-next-line declaration-property-value-allowed-list */
|
|
8
11
|
z-index: 0;
|
|
9
12
|
|
|
13
|
+
contain: paint;
|
|
14
|
+
|
|
10
15
|
/* stylelint-disable-next-line declaration-no-important */
|
|
11
16
|
overflow: hidden !important;
|
|
12
17
|
display: flex;
|
|
@@ -39,6 +44,7 @@
|
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
.tableContent {
|
|
47
|
+
content-visibility: auto;
|
|
42
48
|
min-width: max-content;
|
|
43
49
|
}
|
|
44
50
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { CellContext } from '@tanstack/react-table';
|
|
2
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
3
|
+
|
|
4
|
+
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
1
5
|
import { isBrowser } from '@snack-uikit/utils';
|
|
2
6
|
|
|
3
7
|
export function getCurrentlyConfiguredHeaderWidth(id: string): number {
|
|
@@ -58,3 +62,17 @@ export function saveStateToLocalStorage({ id, columnId, size }: SaveStateToLocal
|
|
|
58
62
|
|
|
59
63
|
localStorage.setItem(id, JSON.stringify({ ...(savedStateFromStorage || {}), resizeState: newResizeState }));
|
|
60
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* check the key, because index is contained in a common table row
|
|
68
|
+
*/
|
|
69
|
+
export function isVirtualRow(row: unknown): row is VirtualItem {
|
|
70
|
+
return typeof row === 'object' && row != null && 'key' in row;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @description prevent permanent recreation of the function on rerender
|
|
75
|
+
*/
|
|
76
|
+
export function truncateCell<TData>(cell: CellContext<TData, unknown>) {
|
|
77
|
+
return <TruncateString text={String(cell.getValue())} maxLines={1} />;
|
|
78
|
+
}
|
package/src/components/types.ts
CHANGED
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
RowSelectionState,
|
|
7
7
|
SortingState,
|
|
8
8
|
} from '@tanstack/react-table';
|
|
9
|
-
import {
|
|
9
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
10
|
+
import { MutableRefObject, ReactNode, RefObject } from 'react';
|
|
10
11
|
|
|
11
12
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
12
13
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
13
14
|
|
|
14
15
|
import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
|
|
15
|
-
import { ColumnDefinition } from '../types';
|
|
16
|
+
import { ColumnDefinition, RowVirtualizer } from '../types';
|
|
16
17
|
|
|
17
18
|
export type TableProps<TData extends object> = WithSupportProps<{
|
|
18
19
|
/** Данные для отрисовки */
|
|
@@ -161,6 +162,14 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
161
162
|
id: string;
|
|
162
163
|
resize?: boolean;
|
|
163
164
|
};
|
|
165
|
+
|
|
166
|
+
onScroll?: (event?: Event) => void;
|
|
167
|
+
// Включение виртуализации для строк
|
|
168
|
+
enableRowVirtualization?: boolean;
|
|
169
|
+
// Параметры для переопределения настроек виртуализации строк
|
|
170
|
+
rowVirtualizerOptions?: Parameters<typeof useVirtualizer>[0];
|
|
171
|
+
// Ссылка на экземпляр useRowVirtualizer
|
|
172
|
+
rowVirtualizerInstanceRef?: MutableRefObject<RowVirtualizer>;
|
|
164
173
|
}>;
|
|
165
174
|
|
|
166
175
|
export type ServerTableProps<TData extends object> = Omit<
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
2
|
+
import { forwardRef, HTMLAttributes, PropsWithChildren } from 'react';
|
|
3
|
+
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
|
|
6
|
+
export const VirtualRow = forwardRef<
|
|
7
|
+
HTMLDivElement,
|
|
8
|
+
PropsWithChildren<HTMLAttributes<HTMLDivElement> & { virtualRow?: VirtualItem }>
|
|
9
|
+
>(({ virtualRow, children, ...attributes }, ref) => {
|
|
10
|
+
if (!virtualRow) {
|
|
11
|
+
return <>{children}</>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={styles.virtualRow}
|
|
18
|
+
style={{ '--virtual-row-deviation': `${virtualRow.start}px` }}
|
|
19
|
+
{...attributes}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
@@ -127,3 +127,11 @@ $snack-ui-table-row-background: var(--snack-ui-table-row-background);
|
|
|
127
127
|
top: 0;
|
|
128
128
|
border: none;
|
|
129
129
|
}
|
|
130
|
+
|
|
131
|
+
.virtualRow {
|
|
132
|
+
position: absolute;
|
|
133
|
+
top: 0;
|
|
134
|
+
left: 0;
|
|
135
|
+
width: 100%;
|
|
136
|
+
transform: translateY(var(--virtual-row-deviation));
|
|
137
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
RowSelectionState,
|
|
9
9
|
SortingState,
|
|
10
10
|
} from '@tanstack/react-table';
|
|
11
|
+
import { Virtualizer } from '@tanstack/react-virtual';
|
|
11
12
|
|
|
12
13
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
13
14
|
import { ValueOf } from '@snack-uikit/utils';
|
|
@@ -65,6 +66,8 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
65
66
|
|
|
66
67
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
67
68
|
|
|
69
|
+
export type RowVirtualizer = Virtualizer<Element, Element> | null;
|
|
70
|
+
|
|
68
71
|
export type {
|
|
69
72
|
RowActionsColumnDefProps,
|
|
70
73
|
StatusColumnDefinitionProps,
|