@snack-uikit/table 0.16.8-preview-fa3ea861.0 → 0.16.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +0 -2
- package/dist/components/Table/Table.d.ts +1 -1
- package/dist/components/Table/Table.js +10 -48
- package/dist/components/types.d.ts +0 -2
- package/dist/constants.js +1 -1
- package/dist/helperComponents/Cells/CopyCell/styles.module.css +0 -3
- package/dist/helperComponents/Cells/HeaderCell/HeaderCell.js +1 -1
- package/dist/helperComponents/Cells/HeaderCell/ResizeHandle.js +2 -9
- package/dist/helperComponents/Cells/HeaderCell/styles.module.css +0 -4
- package/dist/helperComponents/Cells/RowActionsCell/RowActionsCell.d.ts +15 -2
- package/dist/helperComponents/Cells/RowActionsCell/RowActionsCell.js +23 -15
- package/dist/helperComponents/Cells/RowActionsCell/styles.module.css +8 -2
- package/dist/helperComponents/Rows/BodyRow.js +2 -2
- package/dist/helperComponents/Rows/styles.module.css +0 -1
- package/dist/helperComponents/contexts.d.ts +2 -2
- package/dist/helperComponents/contexts.js +2 -2
- package/dist/types.d.ts +1 -3
- package/package.json +11 -10
- package/src/components/Table/Table.tsx +12 -59
- package/src/components/types.ts +0 -3
- package/src/constants.ts +1 -1
- package/src/helperComponents/Cells/CopyCell/styles.module.scss +0 -3
- package/src/helperComponents/Cells/HeaderCell/HeaderCell.tsx +0 -2
- package/src/helperComponents/Cells/HeaderCell/ResizeHandle.tsx +4 -14
- package/src/helperComponents/Cells/HeaderCell/styles.module.scss +0 -5
- package/src/helperComponents/Cells/RowActionsCell/RowActionsCell.tsx +51 -28
- package/src/helperComponents/Cells/RowActionsCell/styles.module.scss +11 -2
- package/src/helperComponents/Rows/BodyRow.tsx +3 -3
- package/src/helperComponents/Rows/styles.module.scss +0 -1
- package/src/helperComponents/contexts.ts +4 -4
- package/src/types.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 0.16.8 (2024-05-08)
|
|
7
|
+
|
|
8
|
+
### Only dependencies have been changed
|
|
9
|
+
* [@snack-uikit/chips@0.13.4](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/chips/CHANGELOG.md)
|
|
10
|
+
* [@snack-uikit/icons@0.21.0](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/icons/CHANGELOG.md)
|
|
11
|
+
* [@snack-uikit/info-block@0.3.5](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/info-block/CHANGELOG.md)
|
|
12
|
+
* [@snack-uikit/list@0.11.5](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/list/CHANGELOG.md)
|
|
13
|
+
* [@snack-uikit/pagination@0.6.8](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/pagination/CHANGELOG.md)
|
|
14
|
+
* [@snack-uikit/tag@0.9.2](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/tag/CHANGELOG.md)
|
|
15
|
+
* [@snack-uikit/toggles@0.9.9](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/toggles/CHANGELOG.md)
|
|
16
|
+
* [@snack-uikit/toolbar@0.7.33](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/toolbar/CHANGELOG.md)
|
|
17
|
+
* [@snack-uikit/truncate-string@0.4.14](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/truncate-string/CHANGELOG.md)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
## 0.16.7 (2024-04-27)
|
|
7
24
|
|
|
8
25
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -114,7 +114,6 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
114
114
|
| sorting | `{ initialState?: SortingState; state?: SortingState; onChange?(state: SortingState): void; }` | - | Параметры отвечают за возможность сортировки, их стоит использовать если нужно отслеживать состояние <br> <strong>initialState</strong>: Начальное состояние сортировки <br> <strong>state</strong>: Состояние сортировки, жестко устанавливаемое снаружи <br> <strong>onChange</strong>: Колбэк на изменение сортировки |
|
|
115
115
|
| rowSelection | `{ initialState?: RowSelectionState; state?: RowSelectionState; enable?: boolean \| ((row: Row<TData>) => boolean); multiRow?: boolean; onChange?(state: RowSelectionState): void; }` | - | Параметры отвечают за возможность выбора строк <br> <strong>initialState</strong>: Начальное состояние выбора строк <br> <strong>state</strong>: Состояние выбора строк, жестко устанавливаемое снаружи <br> <strong>enable</strong>: Колбэк определяющий можно ли выбрать строку <br> <strong>multiRow</strong>: Мульти-выбор строк (включен по-умолчанию, когда включается выбор) <br> <strong>onChange</strong>: Колбэк на выбор строк |
|
|
116
116
|
| search | `{ initialValue?: string; state?: string; placeholder?: string; loading?: boolean; onChange?(value: string): void; }` | 'Search'<br> <strong>loading</strong>: Состояние загрузки в строке поиска <br> <strong>onChange</strong>: Колбэк на изменение данных в строке поиска | Параметры отвечают за глобальный поиск в таблице <br> <strong>initialState</strong>: Начальное состояние строки поиска <br> <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br> <strong>placeholder</strong>: Placeholder строки поиска |
|
|
117
|
-
| enableFuzzySearch | `boolean` | - | Включить нечеткий поиск |
|
|
118
117
|
| pageSize | `number` | 10 | Максимальное кол-во строк на страницу |
|
|
119
118
|
| pagination | `{ state?: PaginationState; options?: number[]; optionsLabel?: string; onChange?(state: PaginationState): void; }` | 'Rows volume: ' <br> <strong>onChange</strong>: Колбэк на изменение пагинации | Параметры отвечают за пагинацию в таблице <br> <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br> <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу <br> <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу |
|
|
120
119
|
| pageCount | `number` | - | Кол-во страниц (используется для внешнего управления) |
|
|
@@ -170,7 +169,6 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
170
169
|
| loading | `boolean` | - | Состояние загрузки |
|
|
171
170
|
| sorting | `{ initialState?: SortingState; state?: SortingState; onChange?(state: SortingState): void; }` | - | Параметры отвечают за возможность сортировки, их стоит использовать если нужно отслеживать состояние <br> <strong>initialState</strong>: Начальное состояние сортировки <br> <strong>state</strong>: Состояние сортировки, жестко устанавливаемое снаружи <br> <strong>onChange</strong>: Колбэк на изменение сортировки |
|
|
172
171
|
| rowSelection | `{ initialState?: RowSelectionState; state?: RowSelectionState; enable?: boolean \| ((row: Row<TData>) => boolean); multiRow?: boolean; onChange?(state: RowSelectionState): void; }` | - | Параметры отвечают за возможность выбора строк <br> <strong>initialState</strong>: Начальное состояние выбора строк <br> <strong>state</strong>: Состояние выбора строк, жестко устанавливаемое снаружи <br> <strong>enable</strong>: Колбэк определяющий можно ли выбрать строку <br> <strong>multiRow</strong>: Мульти-выбор строк (включен по-умолчанию, когда включается выбор) <br> <strong>onChange</strong>: Колбэк на выбор строк |
|
|
173
|
-
| enableFuzzySearch | `boolean` | - | Включить нечеткий поиск |
|
|
174
172
|
| onRowClick | `RowClickHandler<TData>` | - | Колбэк клика по строке |
|
|
175
173
|
| className | `string` | - | CSS-класс |
|
|
176
174
|
| onRefresh | `() => void` | - | Колбек обновления данных |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TableProps } from '../types';
|
|
2
2
|
/** Компонент таблицы */
|
|
3
|
-
export declare function Table<TData extends object>({ data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportFileName, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, scrollRef, scrollContainerRef, getRowId,
|
|
3
|
+
export declare function Table<TData extends object>({ data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize, pageCount, loading, outline, moreActions, exportFileName, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, toolbarBefore, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, scrollRef, scrollContainerRef, getRowId, ...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: {
|
|
@@ -12,7 +12,7 @@ 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 { useCallback, useEffect, useMemo
|
|
15
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
16
16
|
import { useLocale } from '@snack-uikit/locale';
|
|
17
17
|
import { Scroll } from '@snack-uikit/scroll';
|
|
18
18
|
import { SkeletonContextProvider } from '@snack-uikit/skeleton';
|
|
@@ -27,7 +27,7 @@ import { useStateControl } from './hooks/useStateControl';
|
|
|
27
27
|
import styles from './styles.module.css';
|
|
28
28
|
/** Компонент таблицы */
|
|
29
29
|
export function Table(_a) {
|
|
30
|
-
var { data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportFileName, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, toolbarBefore, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, scrollRef, scrollContainerRef, getRowId
|
|
30
|
+
var { data, columnDefinitions, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, onDelete, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportFileName, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, toolbarBefore, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, scrollRef, scrollContainerRef, getRowId } = _a, rest = __rest(_a, ["data", "columnDefinitions", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "onDelete", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportFileName", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "toolbarBefore", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "scrollRef", "scrollContainerRef", "getRowId"]);
|
|
31
31
|
const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl(search, '');
|
|
32
32
|
const { state: rowSelection, onStateChange: onRowSelectionChange } = useStateControl(rowSelectionProp, {});
|
|
33
33
|
const defaultPaginationState = useMemo(() => ({
|
|
@@ -37,11 +37,6 @@ export function Table(_a) {
|
|
|
37
37
|
const { state: sorting, onStateChange: onSortingChange } = useStateControl(sortingProp, []);
|
|
38
38
|
const { state: pagination, onStateChange: onPaginationChange } = useStateControl(paginationProp, defaultPaginationState);
|
|
39
39
|
const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (pagination.pageIndex >= data.length / pagination.pageSize) {
|
|
42
|
-
onPaginationChange(Object.assign(Object.assign({}, pagination), { pageIndex: 0 }));
|
|
43
|
-
}
|
|
44
|
-
}, [data.length, onPaginationChange, pagination]);
|
|
45
40
|
const tableColumns = useMemo(() => {
|
|
46
41
|
let cols = columnDefinitions;
|
|
47
42
|
if (enableSelection) {
|
|
@@ -80,7 +75,7 @@ export function Table(_a) {
|
|
|
80
75
|
manualSorting,
|
|
81
76
|
manualPagination,
|
|
82
77
|
manualFiltering,
|
|
83
|
-
globalFilterFn:
|
|
78
|
+
globalFilterFn: fuzzyFilter,
|
|
84
79
|
onGlobalFilterChange,
|
|
85
80
|
getRowId,
|
|
86
81
|
onRowSelectionChange,
|
|
@@ -94,7 +89,6 @@ export function Table(_a) {
|
|
|
94
89
|
onSortingChange,
|
|
95
90
|
getSortedRowModel: getSortedRowModel(),
|
|
96
91
|
onPaginationChange,
|
|
97
|
-
autoResetPageIndex: false,
|
|
98
92
|
getPaginationRowModel: getPaginationRowModel(),
|
|
99
93
|
getCoreRowModel: getCoreRowModel(),
|
|
100
94
|
columnResizeMode: 'onEnd',
|
|
@@ -122,62 +116,30 @@ export function Table(_a) {
|
|
|
122
116
|
return;
|
|
123
117
|
}
|
|
124
118
|
}, [loading, rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow, table]);
|
|
125
|
-
|
|
126
|
-
const headers = table.getFlatHeaders();
|
|
127
|
-
const columnSizesSnapshot = table
|
|
128
|
-
.getAllColumns()
|
|
129
|
-
.map(column => column.getSize())
|
|
130
|
-
.join('_');
|
|
119
|
+
useEffect(() => { }, []);
|
|
131
120
|
const columnSizeVars = useMemo(() => {
|
|
132
|
-
var _a;
|
|
133
|
-
const sizeKey = (id) => `--table-column-${id}-size`;
|
|
134
|
-
const flexKey = (id) => `--table-column-${id}-flex`;
|
|
135
|
-
const getCurrentlyConfiguredHeaderWidth = (id) => {
|
|
136
|
-
const cell = document.querySelector(`[data-header-id="${id}"]`);
|
|
137
|
-
const resizeHandler = cell === null || cell === void 0 ? void 0 : cell.querySelector('[data-test-id="table__header-cell-resize-handle-moving-part"]');
|
|
138
|
-
if (cell && resizeHandler) {
|
|
139
|
-
const { width } = cell.getBoundingClientRect();
|
|
140
|
-
const offset = parseInt(resizeHandler.style.getPropertyValue('--offset'));
|
|
141
|
-
return width + offset;
|
|
142
|
-
}
|
|
143
|
-
return 0;
|
|
144
|
-
};
|
|
145
121
|
const originalColumnDefs = table._getColumnDefs();
|
|
122
|
+
const headers = table.getFlatHeaders();
|
|
146
123
|
const colSizes = {};
|
|
147
124
|
for (let i = 0; i < headers.length; i++) {
|
|
148
125
|
const header = headers[i];
|
|
149
126
|
const originalColDef = originalColumnDefs.find(col => getColumnId(header) === col.id);
|
|
150
127
|
const originalColumnDefSize = originalColDef === null || originalColDef === void 0 ? void 0 : originalColDef.size;
|
|
151
128
|
const initSize = originalColumnDefSize ? `${originalColumnDefSize}px` : '100%';
|
|
152
|
-
const prevSize = (_a = columnSizeVarsRef.current) === null || _a === void 0 ? void 0 : _a[sizeKey(header.id)];
|
|
153
129
|
let size = initSize;
|
|
154
130
|
if (header.column.getCanResize()) {
|
|
155
131
|
const currentSize = header.getSize();
|
|
156
132
|
const colDefSize = header.column.columnDef.size;
|
|
157
133
|
size = currentSize === colDefSize ? initSize : `${currentSize}px`;
|
|
158
|
-
if (prevSize === '100%' && currentSize !== colDefSize) {
|
|
159
|
-
const realSize = getCurrentlyConfiguredHeaderWidth(header.id);
|
|
160
|
-
table.setColumnSizing(old => (Object.assign(Object.assign({}, old), { [header.id]: realSize })));
|
|
161
|
-
size = `${realSize}px`;
|
|
162
|
-
}
|
|
163
134
|
}
|
|
164
|
-
colSizes[
|
|
165
|
-
colSizes[
|
|
135
|
+
colSizes[`--table-column-${header.id}-size`] = size;
|
|
136
|
+
colSizes[`--table-column-${header.id}-flex`] = size === '100%' ? 'unset' : '0';
|
|
166
137
|
}
|
|
167
138
|
return colSizes;
|
|
168
|
-
/*
|
|
169
|
-
|
|
170
|
-
to reduce unnecessary recalculations
|
|
171
|
-
|
|
172
|
-
headers ids can also change, so they also should present here
|
|
173
|
-
|
|
174
|
-
columnSizesSnapshot will trigger re-render after double-click size reset
|
|
175
|
-
*/
|
|
139
|
+
/* effect must be called only on columnSizingInfo.isResizingColumn changes
|
|
140
|
+
to reduce unnecessary recalculations */
|
|
176
141
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
177
|
-
}, [table.getState().columnSizingInfo.isResizingColumn
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
columnSizeVarsRef.current = columnSizeVars;
|
|
180
|
-
}, [columnSizeVars]);
|
|
142
|
+
}, [table.getState().columnSizingInfo.isResizingColumn]);
|
|
181
143
|
const tableRows = table.getRowModel().rows;
|
|
182
144
|
const loadingTableRows = loadingTable.getRowModel().rows;
|
|
183
145
|
const tablePagination = table.getState().pagination;
|
|
@@ -48,8 +48,6 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
48
48
|
loading?: boolean;
|
|
49
49
|
onChange?(value: string): void;
|
|
50
50
|
};
|
|
51
|
-
/** Включить нечеткий поиск */
|
|
52
|
-
enableFuzzySearch?: boolean;
|
|
53
51
|
/** Максимальное кол-во строк на страницу @default 10 */
|
|
54
52
|
pageSize?: number;
|
|
55
53
|
/** Параметры отвечают за пагинацию в таблице <br>
|
package/dist/constants.js
CHANGED
|
@@ -17,7 +17,7 @@ export const TEST_IDS = {
|
|
|
17
17
|
rowActions: {
|
|
18
18
|
droplistTrigger: 'table__body-row__droplistTrigger',
|
|
19
19
|
droplist: 'table__body-row__actions-droplist',
|
|
20
|
-
option: '
|
|
20
|
+
option: 'table__body-row__action-option',
|
|
21
21
|
},
|
|
22
22
|
statusIndicator: 'table__status-indicator',
|
|
23
23
|
statusLabel: 'table__status-label',
|
|
@@ -26,5 +26,5 @@ export function HeaderCell({ header, className }) {
|
|
|
26
26
|
return;
|
|
27
27
|
return (_a = header.column.getToggleSortingHandler()) === null || _a === void 0 ? void 0 : _a(e);
|
|
28
28
|
};
|
|
29
|
-
return (_jsxs(Cell, { style: style, onMouseUp: sortingHandler, "data-sortable": isSortable || undefined, "data-no-padding": columnDef.noHeaderCellPadding || undefined, "data-no-offset": columnDef.noHeaderCellBorderOffset || undefined, "data-test-id": TEST_IDS.headerCell, "data-
|
|
29
|
+
return (_jsxs(Cell, { style: style, onMouseUp: sortingHandler, "data-sortable": isSortable || undefined, "data-no-padding": columnDef.noHeaderCellPadding || undefined, "data-no-offset": columnDef.noHeaderCellBorderOffset || undefined, "data-test-id": TEST_IDS.headerCell, "data-resizing": isResizing || undefined, role: 'columnheader', className: cn(styles.tableHeaderCell, className, columnDef.headerClassName), ref: cellRef, children: [_jsxs("div", { className: styles.tableHeaderCellMain, children: [columnDef.header && (_jsx("div", { className: styles.tableHeaderCellName, children: _jsx(TruncateString, { text: flexRender(columnDef.header, header.getContext()) }) })), Boolean(sortIcon) && (_jsx("div", { className: styles.tableHeaderIcon, "data-sort-direction": sortDirection, "data-test-id": TEST_IDS.headerSortIndicator, children: sortIcon }))] }), Boolean(isResizable) && _jsx(ResizeHandle, { header: header, cellRef: cellRef })] }));
|
|
30
30
|
}
|
|
@@ -22,15 +22,8 @@ function getResizeIndicatorOffset({ header, cellRef }) {
|
|
|
22
22
|
export function ResizeHandle({ header, cellRef }) {
|
|
23
23
|
const isResizing = header.column.getIsResizing();
|
|
24
24
|
const resizeHandler = header.getResizeHandler();
|
|
25
|
-
const handleMouseDown = event => {
|
|
26
|
-
if (event.detail === 2) {
|
|
27
|
-
header.column.resetSize();
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
resizeHandler(event);
|
|
31
|
-
};
|
|
32
25
|
const offset = isResizing ? getResizeIndicatorOffset({ header, cellRef }) : 0;
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", {
|
|
34
|
-
|
|
26
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn(styles.tableHeaderIcon, styles.tableHeaderResizeHandle), "data-resizing": isResizing || undefined, onMouseDown: resizeHandler, onTouchStart: resizeHandler }), isResizing && (_jsx("div", { className: styles.tableHeaderResizeIndicator, style: {
|
|
27
|
+
transform: `translateX(${offset}px)`,
|
|
35
28
|
} }))] }));
|
|
36
29
|
}
|
|
@@ -69,9 +69,6 @@
|
|
|
69
69
|
-moz-user-select:none;
|
|
70
70
|
user-select:none;
|
|
71
71
|
}
|
|
72
|
-
.tableHeaderCell[data-align=right]{
|
|
73
|
-
justify-content:flex-end;
|
|
74
|
-
}
|
|
75
72
|
|
|
76
73
|
.tableHeaderCellMain{
|
|
77
74
|
overflow:auto;
|
|
@@ -106,7 +103,6 @@
|
|
|
106
103
|
z-index:2;
|
|
107
104
|
top:0;
|
|
108
105
|
right:0;
|
|
109
|
-
transform:translateX(var(--offset));
|
|
110
106
|
width:1px;
|
|
111
107
|
height:100%;
|
|
112
108
|
background-color:var(--sys-neutral-decor-activated, #bfc2cc);
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { CellContext } from '@tanstack/react-table';
|
|
2
|
-
import {
|
|
2
|
+
import { MouseEvent, ReactElement } from 'react';
|
|
3
|
+
import { BaseItemProps } from '@snack-uikit/list';
|
|
3
4
|
import { ColumnDefinition } from '../../../types';
|
|
4
|
-
export type
|
|
5
|
+
export type RowActionInfo<TData> = {
|
|
6
|
+
rowId: string;
|
|
7
|
+
data: TData;
|
|
8
|
+
itemId?: string;
|
|
9
|
+
};
|
|
10
|
+
export type RowActionProps<TData> = Pick<BaseItemProps, 'content' | 'disabled' | 'data-test-id'> & {
|
|
11
|
+
icon?: ReactElement;
|
|
12
|
+
tagLabel?: string;
|
|
13
|
+
id?: string;
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
onClick(row: RowActionInfo<TData>, e: MouseEvent<HTMLElement>): void;
|
|
16
|
+
};
|
|
17
|
+
export type ActionsGenerator<TData> = (cell: CellContext<TData, unknown>) => RowActionProps<TData>[];
|
|
5
18
|
export type RowActionsColumnDefProps<TData> = {
|
|
6
19
|
/** Действия для строки */
|
|
7
20
|
actionsGenerator: ActionsGenerator<TData>;
|
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo, useRef } from 'react';
|
|
3
3
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
4
4
|
import { MoreSVG } from '@snack-uikit/icons';
|
|
5
|
-
import { Droplist
|
|
5
|
+
import { Droplist } from '@snack-uikit/list';
|
|
6
|
+
import { Tag } from '@snack-uikit/tag';
|
|
6
7
|
import { COLUMN_PIN_POSITION, TEST_IDS } from '../../../constants';
|
|
7
8
|
import { useRowContext } from '../../contexts';
|
|
8
9
|
import styles from './styles.module.css';
|
|
9
10
|
function RowActionsCell({ row, actions }) {
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
(_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, e);
|
|
16
|
-
cb(e);
|
|
17
|
-
} });
|
|
18
|
-
}
|
|
19
|
-
return Object.assign(Object.assign({}, item), { items: item.items.map(i => patchItem(i, cb)) });
|
|
20
|
-
}, []);
|
|
11
|
+
const { droplistOpened, setDroplistOpen } = useRowContext();
|
|
12
|
+
const triggerRef = useRef(null);
|
|
13
|
+
const handleItemClick = (item) => (e) => {
|
|
14
|
+
item.onClick({ rowId: row.id, itemId: item.id, data: row.original }, e);
|
|
15
|
+
};
|
|
21
16
|
const disabled = !row.getCanSelect();
|
|
22
17
|
const stopPropagationClick = (e) => {
|
|
23
18
|
e.stopPropagation();
|
|
24
19
|
};
|
|
25
|
-
const
|
|
20
|
+
const visibleActions = useMemo(() => actions.filter(item => !(item === null || item === void 0 ? void 0 : item.hidden)), [actions]);
|
|
26
21
|
return (
|
|
27
22
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
28
|
-
_jsx("div", { onClick: stopPropagationClick, className: styles.rowActionsCellWrap, "data-open":
|
|
23
|
+
_jsx("div", { onClick: stopPropagationClick, className: styles.rowActionsCellWrap, "data-open": droplistOpened || undefined, children: !disabled && Boolean(visibleActions.length) && (_jsx(Droplist, { trigger: 'clickAndFocusVisible', open: droplistOpened, onOpenChange: setDroplistOpen, placement: 'bottom-end', size: 'm', "data-test-id": TEST_IDS.rowActions.droplist, triggerElemRef: triggerRef, items: actions
|
|
24
|
+
.filter(item => !(item === null || item === void 0 ? void 0 : item.hidden))
|
|
25
|
+
.map(item => ({
|
|
26
|
+
id: item.id,
|
|
27
|
+
onClick: e => {
|
|
28
|
+
handleItemClick(item)(e);
|
|
29
|
+
setDroplistOpen(false);
|
|
30
|
+
},
|
|
31
|
+
disabled: item.disabled,
|
|
32
|
+
content: item.content,
|
|
33
|
+
beforeContent: item.icon,
|
|
34
|
+
afterContent: item.tagLabel ? _jsx(Tag, { label: item.tagLabel }) : undefined,
|
|
35
|
+
'data-test-id': item['data-test-id'] || TEST_IDS.rowActions.option,
|
|
36
|
+
})), children: _jsx("span", { className: styles.rowActionsCellTrigger, children: _jsx(ButtonFunction, { icon: _jsx(MoreSVG, { size: 24 }), "data-test-id": TEST_IDS.rowActions.droplistTrigger, ref: triggerRef }) }) })) }));
|
|
29
37
|
}
|
|
30
38
|
/** Вспомогательная функция для создания ячейки с дополнительными действиями у строки */
|
|
31
39
|
export function getRowActionsColumnDef({ actionsGenerator, pinned, }) {
|
|
@@ -16,14 +16,20 @@
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.rowActionsCellWrap{
|
|
19
|
-
--offset:0px;
|
|
20
|
-
cursor:pointer;
|
|
21
19
|
box-sizing:border-box;
|
|
22
20
|
width:100%;
|
|
23
21
|
height:100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.rowActionsCellTrigger{
|
|
25
|
+
--offset:0px;
|
|
24
26
|
padding-left:var(--space-table-cell-action-padding, 4px);
|
|
25
27
|
padding-right:var(--space-table-cell-action-padding, 4px);
|
|
28
|
+
cursor:pointer;
|
|
26
29
|
display:flex;
|
|
27
30
|
align-items:center;
|
|
28
31
|
justify-content:center;
|
|
32
|
+
box-sizing:border-box;
|
|
33
|
+
width:100%;
|
|
34
|
+
height:100%;
|
|
29
35
|
}
|
|
@@ -9,7 +9,7 @@ import { Row } from './Row';
|
|
|
9
9
|
import styles from './styles.module.css';
|
|
10
10
|
export function BodyRow({ row, onRowClick }) {
|
|
11
11
|
const { pinnedLeft, pinnedRight, unpinned } = useRowCells(row);
|
|
12
|
-
const [
|
|
12
|
+
const [droplistOpened, setDroplistOpen] = useState(false);
|
|
13
13
|
const disabled = !row.getCanSelect();
|
|
14
14
|
const handleRowClick = (e) => {
|
|
15
15
|
if (disabled)
|
|
@@ -21,5 +21,5 @@ export function BodyRow({ row, onRowClick }) {
|
|
|
21
21
|
toggleSelected: row.toggleSelected,
|
|
22
22
|
});
|
|
23
23
|
};
|
|
24
|
-
return (_jsx(RowContext.Provider, { value: {
|
|
24
|
+
return (_jsx(RowContext.Provider, { value: { droplistOpened, setDroplistOpen }, children: _jsxs(Row, { onClick: handleRowClick, "data-clickable": Boolean(onRowClick) || undefined, "data-disabled": disabled || undefined, "data-selected": row.getIsSelected() || undefined, "data-actions-opened": droplistOpened || undefined, "data-test-id": TEST_IDS.bodyRow, "data-row-id": row.id, className: styles.bodyRow, children: [pinnedLeft && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Left, children: pinnedLeft.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))) })), unpinned.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))), pinnedRight && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Right, children: pinnedRight.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))) }))] }) }));
|
|
25
25
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Table } from '@tanstack/react-table';
|
|
2
2
|
import { Dispatch, SetStateAction } from 'react';
|
|
3
3
|
type RowContext = {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
droplistOpened: boolean;
|
|
5
|
+
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
6
6
|
};
|
|
7
7
|
export declare const RowContext: import("react").Context<RowContext>;
|
|
8
8
|
export declare const useRowContext: () => RowContext;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
export const RowContext = createContext({
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
droplistOpened: false,
|
|
4
|
+
setDroplistOpen() { },
|
|
5
5
|
});
|
|
6
6
|
export const useRowContext = () => useContext(RowContext);
|
|
7
7
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/dist/types.d.ts
CHANGED
|
@@ -9,8 +9,6 @@ type ColumnPinPosition = ValueOf<typeof COLUMN_PIN_POSITION>;
|
|
|
9
9
|
type BaseColumnDefinition<TData> = Except<ColumnDef<TData>, 'footer' | 'enablePinning' | 'enableGrouping' | 'enableColumnFilter' | 'filterFn' | 'enableGlobalFilter' | 'enableMultiSort' | 'enableHiding'> & {
|
|
10
10
|
/** Заголовок колонки */
|
|
11
11
|
header?: string | ((ctx: HeaderContext<TData, unknown>) => string);
|
|
12
|
-
/** Позиционирование заголовка колонки */
|
|
13
|
-
headerAlign?: ColumnAlign;
|
|
14
12
|
/** Позиционирование контента ячейки в теле таблицы */
|
|
15
13
|
align?: ColumnAlign;
|
|
16
14
|
/** Отключить паддинг у ячейки в теле таблицы */
|
|
@@ -39,5 +37,5 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
39
37
|
size: number;
|
|
40
38
|
};
|
|
41
39
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
42
|
-
export type { RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
|
|
40
|
+
export type { RowActionInfo, RowActionProps, RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
|
|
43
41
|
export type { 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.16.8
|
|
7
|
+
"version": "0.16.8",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -33,17 +33,18 @@
|
|
|
33
33
|
"scripts": {},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@snack-uikit/button": "0.17.1",
|
|
36
|
-
"@snack-uikit/chips": "0.13.4
|
|
36
|
+
"@snack-uikit/chips": "0.13.4",
|
|
37
37
|
"@snack-uikit/icon-predefined": "0.5.1",
|
|
38
|
-
"@snack-uikit/icons": "0.
|
|
39
|
-
"@snack-uikit/info-block": "0.3.
|
|
40
|
-
"@snack-uikit/list": "0.11.5
|
|
41
|
-
"@snack-uikit/pagination": "0.6.
|
|
38
|
+
"@snack-uikit/icons": "0.21.0",
|
|
39
|
+
"@snack-uikit/info-block": "0.3.5",
|
|
40
|
+
"@snack-uikit/list": "0.11.5",
|
|
41
|
+
"@snack-uikit/pagination": "0.6.8",
|
|
42
42
|
"@snack-uikit/scroll": "0.5.3",
|
|
43
43
|
"@snack-uikit/skeleton": "0.3.4",
|
|
44
|
-
"@snack-uikit/
|
|
45
|
-
"@snack-uikit/
|
|
46
|
-
"@snack-uikit/
|
|
44
|
+
"@snack-uikit/tag": "0.9.2",
|
|
45
|
+
"@snack-uikit/toggles": "0.9.9",
|
|
46
|
+
"@snack-uikit/toolbar": "0.7.33",
|
|
47
|
+
"@snack-uikit/truncate-string": "0.4.14",
|
|
47
48
|
"@snack-uikit/typography": "0.6.2",
|
|
48
49
|
"@snack-uikit/utils": "3.3.0",
|
|
49
50
|
"@tanstack/match-sorter-utils": "8.11.8",
|
|
@@ -57,5 +58,5 @@
|
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@snack-uikit/locale": "*"
|
|
59
60
|
},
|
|
60
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "59bd2ac46a341357d543ada864a34e09745b3e42"
|
|
61
62
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useReactTable,
|
|
12
12
|
} from '@tanstack/react-table';
|
|
13
13
|
import cn from 'classnames';
|
|
14
|
-
import { RefObject, useCallback, useEffect, useMemo
|
|
14
|
+
import { RefObject, useCallback, useEffect, useMemo } from 'react';
|
|
15
15
|
|
|
16
16
|
import { useLocale } from '@snack-uikit/locale';
|
|
17
17
|
import { Scroll } from '@snack-uikit/scroll';
|
|
@@ -76,7 +76,7 @@ export function Table<TData extends object>({
|
|
|
76
76
|
scrollRef,
|
|
77
77
|
scrollContainerRef,
|
|
78
78
|
getRowId,
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
...rest
|
|
81
81
|
}: TableProps<TData>) {
|
|
82
82
|
const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl<string>(search, '');
|
|
@@ -99,12 +99,6 @@ export function Table<TData extends object>({
|
|
|
99
99
|
);
|
|
100
100
|
const enableSelection = Boolean(rowSelectionProp?.enable);
|
|
101
101
|
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
if (pagination.pageIndex >= data.length / pagination.pageSize) {
|
|
104
|
-
onPaginationChange({ ...pagination, pageIndex: 0 });
|
|
105
|
-
}
|
|
106
|
-
}, [data.length, onPaginationChange, pagination]);
|
|
107
|
-
|
|
108
102
|
const tableColumns: ColumnDefinition<TData>[] = useMemo(() => {
|
|
109
103
|
let cols: ColumnDefinition<TData>[] = columnDefinitions;
|
|
110
104
|
if (enableSelection) {
|
|
@@ -112,7 +106,6 @@ export function Table<TData extends object>({
|
|
|
112
106
|
}
|
|
113
107
|
return cols;
|
|
114
108
|
}, [columnDefinitions, enableSelection]);
|
|
115
|
-
|
|
116
109
|
const columnPinning = useMemo(() => {
|
|
117
110
|
const pinningState: Required<ColumnPinningState> = { left: [], right: [] };
|
|
118
111
|
for (const col of tableColumns) {
|
|
@@ -146,7 +139,7 @@ export function Table<TData extends object>({
|
|
|
146
139
|
manualPagination,
|
|
147
140
|
manualFiltering,
|
|
148
141
|
|
|
149
|
-
globalFilterFn:
|
|
142
|
+
globalFilterFn: fuzzyFilter,
|
|
150
143
|
onGlobalFilterChange,
|
|
151
144
|
|
|
152
145
|
getRowId,
|
|
@@ -161,9 +154,9 @@ export function Table<TData extends object>({
|
|
|
161
154
|
onSortingChange,
|
|
162
155
|
getSortedRowModel: getSortedRowModel(),
|
|
163
156
|
onPaginationChange,
|
|
164
|
-
autoResetPageIndex: false,
|
|
165
157
|
getPaginationRowModel: getPaginationRowModel(),
|
|
166
158
|
getCoreRowModel: getCoreRowModel(),
|
|
159
|
+
|
|
167
160
|
columnResizeMode: 'onEnd',
|
|
168
161
|
});
|
|
169
162
|
|
|
@@ -196,41 +189,18 @@ export function Table<TData extends object>({
|
|
|
196
189
|
}
|
|
197
190
|
}, [loading, rowSelectionProp?.multiRow, table]);
|
|
198
191
|
|
|
199
|
-
|
|
200
|
-
const headers = table.getFlatHeaders();
|
|
201
|
-
const columnSizesSnapshot = table
|
|
202
|
-
.getAllColumns()
|
|
203
|
-
.map(column => column.getSize())
|
|
204
|
-
.join('_');
|
|
192
|
+
useEffect(() => {}, []);
|
|
205
193
|
|
|
206
194
|
const columnSizeVars = useMemo(() => {
|
|
207
|
-
const sizeKey = (id: string) => `--table-column-${id}-size`;
|
|
208
|
-
const flexKey = (id: string) => `--table-column-${id}-flex`;
|
|
209
|
-
|
|
210
|
-
const getCurrentlyConfiguredHeaderWidth = (id: string) => {
|
|
211
|
-
const cell = document.querySelector<HTMLDivElement>(`[data-header-id="${id}"]`);
|
|
212
|
-
const resizeHandler = cell?.querySelector<HTMLDivElement>(
|
|
213
|
-
'[data-test-id="table__header-cell-resize-handle-moving-part"]',
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
if (cell && resizeHandler) {
|
|
217
|
-
const { width } = cell.getBoundingClientRect();
|
|
218
|
-
const offset = parseInt(resizeHandler.style.getPropertyValue('--offset'));
|
|
219
|
-
return width + offset;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return 0;
|
|
223
|
-
};
|
|
224
|
-
|
|
225
195
|
const originalColumnDefs = table._getColumnDefs();
|
|
226
|
-
const
|
|
196
|
+
const headers = table.getFlatHeaders();
|
|
197
|
+
const colSizes: { [key: string]: string } = {};
|
|
227
198
|
|
|
228
199
|
for (let i = 0; i < headers.length; i++) {
|
|
229
200
|
const header = headers[i];
|
|
230
201
|
const originalColDef = originalColumnDefs.find(col => getColumnId(header) === col.id);
|
|
231
202
|
const originalColumnDefSize = originalColDef?.size;
|
|
232
203
|
const initSize = originalColumnDefSize ? `${originalColumnDefSize}px` : '100%';
|
|
233
|
-
const prevSize = columnSizeVarsRef.current?.[sizeKey(header.id)];
|
|
234
204
|
|
|
235
205
|
let size = initSize;
|
|
236
206
|
|
|
@@ -239,34 +209,17 @@ export function Table<TData extends object>({
|
|
|
239
209
|
const colDefSize = header.column.columnDef.size;
|
|
240
210
|
|
|
241
211
|
size = currentSize === colDefSize ? initSize : `${currentSize}px`;
|
|
242
|
-
|
|
243
|
-
if (prevSize === '100%' && currentSize !== colDefSize) {
|
|
244
|
-
const realSize = getCurrentlyConfiguredHeaderWidth(header.id);
|
|
245
|
-
table.setColumnSizing(old => ({ ...old, [header.id]: realSize }));
|
|
246
|
-
|
|
247
|
-
size = `${realSize}px`;
|
|
248
|
-
}
|
|
249
212
|
}
|
|
250
213
|
|
|
251
|
-
colSizes[
|
|
252
|
-
colSizes[
|
|
214
|
+
colSizes[`--table-column-${header.id}-size`] = size;
|
|
215
|
+
colSizes[`--table-column-${header.id}-flex`] = size === '100%' ? 'unset' : '0';
|
|
253
216
|
}
|
|
254
217
|
|
|
255
218
|
return colSizes;
|
|
256
|
-
/*
|
|
257
|
-
|
|
258
|
-
to reduce unnecessary recalculations
|
|
259
|
-
|
|
260
|
-
headers ids can also change, so they also should present here
|
|
261
|
-
|
|
262
|
-
columnSizesSnapshot will trigger re-render after double-click size reset
|
|
263
|
-
*/
|
|
219
|
+
/* effect must be called only on columnSizingInfo.isResizingColumn changes
|
|
220
|
+
to reduce unnecessary recalculations */
|
|
264
221
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
265
|
-
}, [table.getState().columnSizingInfo.isResizingColumn
|
|
266
|
-
|
|
267
|
-
useEffect(() => {
|
|
268
|
-
columnSizeVarsRef.current = columnSizeVars;
|
|
269
|
-
}, [columnSizeVars]);
|
|
222
|
+
}, [table.getState().columnSizingInfo.isResizingColumn]);
|
|
270
223
|
|
|
271
224
|
const tableRows = table.getRowModel().rows;
|
|
272
225
|
const loadingTableRows = loadingTable.getRowModel().rows;
|
package/src/components/types.ts
CHANGED
|
@@ -52,9 +52,6 @@ export type TableProps<TData extends object> = WithSupportProps<{
|
|
|
52
52
|
onChange?(value: string): void;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
/** Включить нечеткий поиск */
|
|
56
|
-
enableFuzzySearch?: boolean;
|
|
57
|
-
|
|
58
55
|
/** Максимальное кол-во строк на страницу @default 10 */
|
|
59
56
|
pageSize?: number;
|
|
60
57
|
|
package/src/constants.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const TEST_IDS = {
|
|
|
19
19
|
rowActions: {
|
|
20
20
|
droplistTrigger: 'table__body-row__droplistTrigger',
|
|
21
21
|
droplist: 'table__body-row__actions-droplist',
|
|
22
|
-
option: '
|
|
22
|
+
option: 'table__body-row__action-option',
|
|
23
23
|
},
|
|
24
24
|
statusIndicator: 'table__status-indicator',
|
|
25
25
|
statusLabel: 'table__status-label',
|
|
@@ -46,8 +46,6 @@ export function HeaderCell<TData>({ header, className }: HeaderCellProps<TData>)
|
|
|
46
46
|
data-no-padding={columnDef.noHeaderCellPadding || undefined}
|
|
47
47
|
data-no-offset={columnDef.noHeaderCellBorderOffset || undefined}
|
|
48
48
|
data-test-id={TEST_IDS.headerCell}
|
|
49
|
-
data-align={columnDef.headerAlign || undefined}
|
|
50
|
-
data-header-id={header.id}
|
|
51
49
|
data-resizing={isResizing || undefined}
|
|
52
50
|
role='columnheader'
|
|
53
51
|
className={cn(styles.tableHeaderCell, className, columnDef.headerClassName)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Header } from '@tanstack/react-table';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
-
import {
|
|
3
|
+
import { RefObject } from 'react';
|
|
4
4
|
|
|
5
5
|
import styles from './styles.module.scss';
|
|
6
6
|
|
|
@@ -37,34 +37,24 @@ function getResizeIndicatorOffset<TData>({ header, cellRef }: ResizeHandleProps<
|
|
|
37
37
|
export function ResizeHandle<TData>({ header, cellRef }: ResizeHandleProps<TData>) {
|
|
38
38
|
const isResizing = header.column.getIsResizing();
|
|
39
39
|
const resizeHandler = header.getResizeHandler();
|
|
40
|
-
const handleMouseDown: MouseEventHandler = event => {
|
|
41
|
-
if (event.detail === 2) {
|
|
42
|
-
header.column.resetSize();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
resizeHandler(event);
|
|
47
|
-
};
|
|
48
40
|
|
|
49
41
|
const offset = isResizing ? getResizeIndicatorOffset({ header, cellRef }) : 0;
|
|
50
42
|
|
|
51
43
|
return (
|
|
52
44
|
<>
|
|
45
|
+
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
|
53
46
|
<div
|
|
54
|
-
role='button'
|
|
55
|
-
tabIndex={0}
|
|
56
47
|
className={cn(styles.tableHeaderIcon, styles.tableHeaderResizeHandle)}
|
|
57
48
|
data-resizing={isResizing || undefined}
|
|
58
|
-
onMouseDown={
|
|
49
|
+
onMouseDown={resizeHandler}
|
|
59
50
|
onTouchStart={resizeHandler}
|
|
60
51
|
/>
|
|
61
52
|
|
|
62
53
|
{isResizing && (
|
|
63
54
|
<div
|
|
64
|
-
data-test-id='table__header-cell-resize-handle-moving-part'
|
|
65
55
|
className={styles.tableHeaderResizeIndicator}
|
|
66
56
|
style={{
|
|
67
|
-
|
|
57
|
+
transform: `translateX(${offset}px)`,
|
|
68
58
|
}}
|
|
69
59
|
/>
|
|
70
60
|
)}
|
|
@@ -92,10 +92,6 @@
|
|
|
92
92
|
&[data-resizing] {
|
|
93
93
|
user-select: none;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
&[data-align='right'] {
|
|
97
|
-
justify-content: flex-end;
|
|
98
|
-
}
|
|
99
95
|
}
|
|
100
96
|
|
|
101
97
|
.tableHeaderCellMain {
|
|
@@ -137,7 +133,6 @@
|
|
|
137
133
|
z-index: 2;
|
|
138
134
|
top: 0;
|
|
139
135
|
right: 0;
|
|
140
|
-
transform: translateX(var(--offset));
|
|
141
136
|
|
|
142
137
|
width: 1px;
|
|
143
138
|
height: 100%;
|
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
import { CellContext, Row } from '@tanstack/react-table';
|
|
2
|
-
import { MouseEvent,
|
|
2
|
+
import { MouseEvent, ReactElement, useMemo, useRef } from 'react';
|
|
3
3
|
|
|
4
4
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
5
5
|
import { MoreSVG } from '@snack-uikit/icons';
|
|
6
|
-
import {
|
|
6
|
+
import { BaseItemProps, Droplist } from '@snack-uikit/list';
|
|
7
|
+
import { Tag } from '@snack-uikit/tag';
|
|
7
8
|
|
|
8
9
|
import { COLUMN_PIN_POSITION, TEST_IDS } from '../../../constants';
|
|
9
10
|
import { ColumnDefinition } from '../../../types';
|
|
10
11
|
import { useRowContext } from '../../contexts';
|
|
11
12
|
import styles from './styles.module.scss';
|
|
12
13
|
|
|
14
|
+
export type RowActionInfo<TData> = {
|
|
15
|
+
rowId: string;
|
|
16
|
+
data: TData;
|
|
17
|
+
itemId?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type RowActionProps<TData> = Pick<BaseItemProps, 'content' | 'disabled' | 'data-test-id'> & {
|
|
21
|
+
icon?: ReactElement;
|
|
22
|
+
tagLabel?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
hidden?: boolean;
|
|
25
|
+
onClick(row: RowActionInfo<TData>, e: MouseEvent<HTMLElement>): void;
|
|
26
|
+
};
|
|
27
|
+
|
|
13
28
|
type RowActionsCellProps<TData> = {
|
|
14
|
-
actions:
|
|
29
|
+
actions: RowActionProps<TData>[];
|
|
15
30
|
row: Row<TData>;
|
|
16
31
|
};
|
|
17
32
|
|
|
18
33
|
function RowActionsCell<TData>({ row, actions }: RowActionsCellProps<TData>) {
|
|
19
|
-
const {
|
|
34
|
+
const { droplistOpened, setDroplistOpen } = useRowContext();
|
|
35
|
+
const triggerRef = useRef(null);
|
|
20
36
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
...item,
|
|
25
|
-
onClick(e) {
|
|
26
|
-
item.onClick?.(e);
|
|
27
|
-
cb(e);
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return { ...item, items: item.items.map(i => patchItem(i, cb)) };
|
|
33
|
-
}, []);
|
|
37
|
+
const handleItemClick = (item: RowActionProps<TData>) => (e: MouseEvent<HTMLElement>) => {
|
|
38
|
+
item.onClick({ rowId: row.id, itemId: item.id, data: row.original }, e);
|
|
39
|
+
};
|
|
34
40
|
|
|
35
41
|
const disabled = !row.getCanSelect();
|
|
36
42
|
|
|
@@ -38,32 +44,49 @@ function RowActionsCell<TData>({ row, actions }: RowActionsCellProps<TData>) {
|
|
|
38
44
|
e.stopPropagation();
|
|
39
45
|
};
|
|
40
46
|
|
|
41
|
-
const
|
|
42
|
-
() => actions.map(item => patchItem(item, () => setDropListOpen(false))),
|
|
43
|
-
[actions, patchItem, setDropListOpen],
|
|
44
|
-
);
|
|
47
|
+
const visibleActions: RowActionProps<TData>[] = useMemo(() => actions.filter(item => !item?.hidden), [actions]);
|
|
45
48
|
|
|
46
49
|
return (
|
|
47
50
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
48
|
-
<div onClick={stopPropagationClick} className={styles.rowActionsCellWrap} data-open={
|
|
49
|
-
{!disabled && Boolean(
|
|
51
|
+
<div onClick={stopPropagationClick} className={styles.rowActionsCellWrap} data-open={droplistOpened || undefined}>
|
|
52
|
+
{!disabled && Boolean(visibleActions.length) && (
|
|
50
53
|
<Droplist
|
|
51
54
|
trigger='clickAndFocusVisible'
|
|
52
|
-
open={
|
|
53
|
-
onOpenChange={
|
|
55
|
+
open={droplistOpened}
|
|
56
|
+
onOpenChange={setDroplistOpen}
|
|
54
57
|
placement='bottom-end'
|
|
55
58
|
size='m'
|
|
56
59
|
data-test-id={TEST_IDS.rowActions.droplist}
|
|
57
|
-
|
|
60
|
+
triggerElemRef={triggerRef}
|
|
61
|
+
items={actions
|
|
62
|
+
.filter(item => !item?.hidden)
|
|
63
|
+
.map(item => ({
|
|
64
|
+
id: item.id,
|
|
65
|
+
onClick: e => {
|
|
66
|
+
handleItemClick(item)(e);
|
|
67
|
+
setDroplistOpen(false);
|
|
68
|
+
},
|
|
69
|
+
disabled: item.disabled,
|
|
70
|
+
content: item.content,
|
|
71
|
+
beforeContent: item.icon,
|
|
72
|
+
afterContent: item.tagLabel ? <Tag label={item.tagLabel} /> : undefined,
|
|
73
|
+
'data-test-id': item['data-test-id'] || TEST_IDS.rowActions.option,
|
|
74
|
+
}))}
|
|
58
75
|
>
|
|
59
|
-
<
|
|
76
|
+
<span className={styles.rowActionsCellTrigger}>
|
|
77
|
+
<ButtonFunction
|
|
78
|
+
icon={<MoreSVG size={24} />}
|
|
79
|
+
data-test-id={TEST_IDS.rowActions.droplistTrigger}
|
|
80
|
+
ref={triggerRef}
|
|
81
|
+
/>
|
|
82
|
+
</span>
|
|
60
83
|
</Droplist>
|
|
61
84
|
)}
|
|
62
85
|
</div>
|
|
63
86
|
);
|
|
64
87
|
}
|
|
65
88
|
|
|
66
|
-
export type ActionsGenerator<TData> = (cell: CellContext<TData, unknown>) =>
|
|
89
|
+
export type ActionsGenerator<TData> = (cell: CellContext<TData, unknown>) => RowActionProps<TData>[];
|
|
67
90
|
|
|
68
91
|
export type RowActionsColumnDefProps<TData> = {
|
|
69
92
|
/** Действия для строки */
|
|
@@ -24,15 +24,24 @@
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.rowActionsCellWrap {
|
|
27
|
-
--offset: 0px;
|
|
28
|
-
cursor: pointer;
|
|
29
27
|
box-sizing: border-box;
|
|
30
28
|
width: 100%;
|
|
31
29
|
height: 100%;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.rowActionsCellTrigger {
|
|
33
|
+
--offset: 0px;
|
|
32
34
|
|
|
33
35
|
@include composite-var($table-cells-action);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
cursor: pointer;
|
|
34
39
|
|
|
35
40
|
display: flex;
|
|
36
41
|
align-items: center;
|
|
37
42
|
justify-content: center;
|
|
43
|
+
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 100%;
|
|
38
47
|
}
|
|
@@ -26,7 +26,7 @@ type BodyRowProps<TData> = {
|
|
|
26
26
|
export function BodyRow<TData>({ row, onRowClick }: BodyRowProps<TData>) {
|
|
27
27
|
const { pinnedLeft, pinnedRight, unpinned } = useRowCells(row);
|
|
28
28
|
|
|
29
|
-
const [
|
|
29
|
+
const [droplistOpened, setDroplistOpen] = useState(false);
|
|
30
30
|
|
|
31
31
|
const disabled = !row.getCanSelect();
|
|
32
32
|
|
|
@@ -42,13 +42,13 @@ export function BodyRow<TData>({ row, onRowClick }: BodyRowProps<TData>) {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
|
-
<RowContext.Provider value={{
|
|
45
|
+
<RowContext.Provider value={{ droplistOpened, setDroplistOpen }}>
|
|
46
46
|
<Row
|
|
47
47
|
onClick={handleRowClick}
|
|
48
48
|
data-clickable={Boolean(onRowClick) || undefined}
|
|
49
49
|
data-disabled={disabled || undefined}
|
|
50
50
|
data-selected={row.getIsSelected() || undefined}
|
|
51
|
-
data-actions-opened={
|
|
51
|
+
data-actions-opened={droplistOpened || undefined}
|
|
52
52
|
data-test-id={TEST_IDS.bodyRow}
|
|
53
53
|
data-row-id={row.id}
|
|
54
54
|
className={styles.bodyRow}
|
|
@@ -2,13 +2,13 @@ import { Table } from '@tanstack/react-table';
|
|
|
2
2
|
import { createContext, Dispatch, SetStateAction, useContext } from 'react';
|
|
3
3
|
|
|
4
4
|
type RowContext = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
droplistOpened: boolean;
|
|
6
|
+
setDroplistOpen: Dispatch<SetStateAction<boolean>>;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export const RowContext = createContext<RowContext>({
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
droplistOpened: false,
|
|
11
|
+
setDroplistOpen() {},
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
export const useRowContext = () => useContext(RowContext);
|
package/src/types.ts
CHANGED
|
@@ -33,8 +33,6 @@ type BaseColumnDefinition<TData> = Except<
|
|
|
33
33
|
> & {
|
|
34
34
|
/** Заголовок колонки */
|
|
35
35
|
header?: string | ((ctx: HeaderContext<TData, unknown>) => string);
|
|
36
|
-
/** Позиционирование заголовка колонки */
|
|
37
|
-
headerAlign?: ColumnAlign;
|
|
38
36
|
/** Позиционирование контента ячейки в теле таблицы */
|
|
39
37
|
align?: ColumnAlign;
|
|
40
38
|
/** Отключить паддинг у ячейки в теле таблицы */
|
|
@@ -66,6 +64,8 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
66
64
|
export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
|
|
67
65
|
|
|
68
66
|
export type {
|
|
67
|
+
RowActionInfo,
|
|
68
|
+
RowActionProps,
|
|
69
69
|
RowActionsColumnDefProps,
|
|
70
70
|
StatusColumnDefinitionProps,
|
|
71
71
|
RowInfo,
|