@snack-uikit/table 0.34.9 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/dist/cjs/components/Table/Table.js +5 -2
- package/dist/cjs/components/Table/hooks/index.d.ts +1 -0
- package/dist/cjs/components/Table/hooks/index.js +2 -1
- package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.d.ts +4 -2
- package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.js +28 -5
- package/dist/cjs/components/Table/hooks/useColumnSettings.d.ts +6 -0
- package/dist/cjs/components/Table/hooks/useColumnSettings.js +41 -0
- package/dist/cjs/components/Table/utils.js +10 -5
- package/dist/cjs/components/types.d.ts +1 -0
- package/dist/cjs/constants.d.ts +5 -0
- package/dist/cjs/constants.js +7 -1
- package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.js +1 -0
- package/dist/cjs/types.d.ts +9 -6
- package/dist/esm/components/Table/Table.js +4 -4
- package/dist/esm/components/Table/hooks/index.d.ts +1 -0
- package/dist/esm/components/Table/hooks/index.js +1 -0
- package/dist/esm/components/Table/hooks/useColumnOrderByDrag.d.ts +4 -2
- package/dist/esm/components/Table/hooks/useColumnOrderByDrag.js +31 -5
- package/dist/esm/components/Table/hooks/useColumnSettings.d.ts +6 -0
- package/dist/esm/components/Table/hooks/useColumnSettings.js +38 -0
- package/dist/esm/components/Table/utils.js +11 -5
- package/dist/esm/components/types.d.ts +1 -0
- package/dist/esm/constants.d.ts +5 -0
- package/dist/esm/constants.js +6 -0
- package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.js +1 -1
- package/dist/esm/types.d.ts +9 -6
- package/package.json +2 -2
- package/src/components/Table/Table.tsx +3 -4
- package/src/components/Table/hooks/index.ts +1 -0
- package/src/components/Table/hooks/useColumnOrderByDrag.ts +52 -6
- package/src/components/Table/hooks/useColumnSettings.ts +53 -0
- package/src/components/Table/utils.ts +14 -8
- package/src/components/types.ts +1 -0
- package/src/constants.tsx +7 -0
- package/src/helperComponents/ColumnsSettings/ColumnsSettings.tsx +1 -0
- package/src/types.ts +12 -7
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.35.0 (2025-03-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **PDS-1971:** newValue renaming ([37b4eb9](https://github.com/cloud-ru-tech/snack-uikit/commit/37b4eb9b11bb13db122edf3d8e2fea9b4b8e8b25))
|
|
12
|
+
* **PDS-1971:** useColumnSettings return compose as object ([61b3799](https://github.com/cloud-ru-tech/snack-uikit/commit/61b3799acd59e31546e6da44641d5a01738d55e5))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **PDS-1971:** add columnSettings to savedState and mode of initvalue ([72863cf](https://github.com/cloud-ru-tech/snack-uikit/commit/72863cfdc7a2ae81b709755792615067b425efa2))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
## 0.34.9 (2025-03-19)
|
|
7
24
|
|
|
8
25
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
145
145
|
| scrollRef | `RefObject<HTMLElement>` | - | Ссылка на элемент, обозначающий самый конец прокручиваемого списка |
|
|
146
146
|
| scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
|
|
147
147
|
| rowPinning | `Pick<RowPinningState, "top">` | { top: [], } | Определение какие строки должны быть закреплены в таблице |
|
|
148
|
-
| savedState | `{ id: string; filterQueryKey?: string; resize?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
148
|
+
| savedState | `{ id: string; filterQueryKey?: string; resize?: boolean; columnSettings?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
149
149
|
| pagination | `{ state?: PaginationState; options?: number[]; optionsLabel?: string; onChange?(state: PaginationState): void; optionsRender?(value: string \| number, idx: number): string \| number; }` | 'Rows volume: ' <br> <strong>onChange</strong>: Колбэк на изменение пагинации | Параметры отвечают за пагинацию в таблице <br> <strong>state</strong>: Состояние строки поиска, жестко устанавливаемое снаружи <br> <strong>options</strong>: Варианты в выпадающем селекторе для установки кол-ва строк на страницу <br> <strong>optionsLabel</strong>: Текст для селектора кол-ва строк на страницу |
|
|
150
150
|
| autoResetPageIndex | `boolean` | - | Автоматический сброс пагинации к первой странице при изменении данных или состояния (e.g фильтры, сортировки, и т.д) |
|
|
151
151
|
| pageCount | `number` | - | Кол-во страниц (используется для внешнего управления) |
|
|
@@ -211,7 +211,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
|
|
|
211
211
|
| scrollRef | `RefObject<HTMLElement>` | - | Ссылка на элемент, обозначающий самый конец прокручиваемого списка |
|
|
212
212
|
| scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
|
|
213
213
|
| rowPinning | `Pick<RowPinningState, "top">` | - | Определение какие строки должны быть закреплены в таблице |
|
|
214
|
-
| savedState | `{ id: string; filterQueryKey?: string; resize?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
214
|
+
| savedState | `{ id: string; filterQueryKey?: string; resize?: boolean; columnSettings?: boolean; }` | - | Конфиг для сохранения состояния в localStorage. <br> Поле id должно быть уникальным для разных таблиц в рамках приложения. <br> Для корректной работы необходимо наличие id в конфиге columnDefinitions |
|
|
215
215
|
| autoResetPageIndex | `boolean` | - | Автоматический сброс пагинации к первой странице при изменении данных или состояния (e.g фильтры, сортировки, и т.д) |
|
|
216
216
|
| suppressPagination | `boolean` | - | Отключение пагинации |
|
|
217
217
|
| manualPagination | `boolean` | true | |
|
|
@@ -150,7 +150,10 @@ function Table(_a) {
|
|
|
150
150
|
}, [columnFilters, areColumnFiltersOpen, filter, setFilter, filterVisibility, setFilterVisibility]);
|
|
151
151
|
const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
|
|
152
152
|
const manualPagination = infiniteLoading || manualPaginationProp;
|
|
153
|
-
const
|
|
153
|
+
const {
|
|
154
|
+
enabledColumns,
|
|
155
|
+
setEnabledColumns
|
|
156
|
+
} = (0, hooks_1.useColumnSettings)(columnDefinitions, savedState);
|
|
154
157
|
const areColumnsSettingsEnabled = Boolean(columnsSettingsProp === null || columnsSettingsProp === void 0 ? void 0 : columnsSettingsProp.enableSettingsMenu);
|
|
155
158
|
const filteredColumnDefinitions = (0, react_1.useMemo)(() => {
|
|
156
159
|
if (!areColumnsSettingsEnabled) {
|
|
@@ -179,7 +182,7 @@ function Table(_a) {
|
|
|
179
182
|
setColumnOrder,
|
|
180
183
|
sensors,
|
|
181
184
|
handleDragEnd
|
|
182
|
-
} = (0, hooks_1.useColumnOrderByDrag)(tableColumns);
|
|
185
|
+
} = (0, hooks_1.useColumnOrderByDrag)(tableColumns, savedState);
|
|
183
186
|
const dndContextProps = (0, react_1.useMemo)(() => {
|
|
184
187
|
if (!enableColumnsOrderSortByDrag) {
|
|
185
188
|
return {};
|
|
@@ -24,4 +24,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
24
24
|
});
|
|
25
25
|
__exportStar(require("./useLoadingTable"), exports);
|
|
26
26
|
__exportStar(require("./useStateControl"), exports);
|
|
27
|
-
__exportStar(require("./useColumnOrderByDrag"), exports);
|
|
27
|
+
__exportStar(require("./useColumnOrderByDrag"), exports);
|
|
28
|
+
__exportStar(require("./useColumnSettings"), exports);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { DragEndEvent, SensorOptions } from '@dnd-kit/core';
|
|
2
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
3
|
import { ColumnDefinition } from '../../../types';
|
|
3
|
-
|
|
4
|
+
import { TableProps } from '../../types';
|
|
5
|
+
export declare function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnDefinition<TData>[], savedState: TableProps<TData>['savedState']): {
|
|
4
6
|
columnOrder: string[];
|
|
5
|
-
setColumnOrder:
|
|
7
|
+
setColumnOrder: Dispatch<SetStateAction<string[]>>;
|
|
6
8
|
handleDragEnd: ({ active, over }: DragEndEvent) => void;
|
|
7
9
|
sensors: import("@dnd-kit/core").SensorDescriptor<SensorOptions>[];
|
|
8
10
|
};
|
|
@@ -8,16 +8,39 @@ const core_1 = require("@dnd-kit/core");
|
|
|
8
8
|
const sortable_1 = require("@dnd-kit/sortable");
|
|
9
9
|
const react_1 = require("react");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const validateColumnOrderLocalStorageValue = value => Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
12
|
+
const getLocalStorageColumnOrderKey = id => `${id}_columnOrder`;
|
|
13
|
+
function prepareInitialState(tableColumns, savedState) {
|
|
14
|
+
const columnOrder = tableColumns.filter(column => column.pinned !== 'left' && column.pinned !== 'right').map(utils_1.getColumnIdentifier);
|
|
15
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
16
|
+
const persistState = JSON.parse(localStorage.getItem(getLocalStorageColumnOrderKey(savedState.id)) || 'null');
|
|
17
|
+
const persistValue = validateColumnOrderLocalStorageValue(persistState) ? persistState : null;
|
|
18
|
+
if (persistValue !== null) {
|
|
19
|
+
return [...persistValue, ...columnOrder.filter(column => !(persistValue === null || persistValue === void 0 ? void 0 : persistValue.includes(column)))];
|
|
20
|
+
}
|
|
21
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(columnOrder));
|
|
22
|
+
}
|
|
23
|
+
return columnOrder;
|
|
13
24
|
}
|
|
14
25
|
const draggingOptions = {
|
|
15
26
|
activationConstraint: {
|
|
16
27
|
distance: 5 // Is required to differ click (sort) from drag
|
|
17
28
|
}
|
|
18
29
|
};
|
|
19
|
-
function useColumnOrderByDrag(tableColumns) {
|
|
20
|
-
const [columnOrder,
|
|
30
|
+
function useColumnOrderByDrag(tableColumns, savedState) {
|
|
31
|
+
const [columnOrder, setColumnOrderState] = (0, react_1.useState)(() => prepareInitialState(tableColumns, savedState));
|
|
32
|
+
const setColumnOrder = (0, react_1.useCallback)(value => {
|
|
33
|
+
let updatedOrder;
|
|
34
|
+
if (value instanceof Function) {
|
|
35
|
+
updatedOrder = value(columnOrder);
|
|
36
|
+
} else {
|
|
37
|
+
updatedOrder = value;
|
|
38
|
+
}
|
|
39
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
40
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(updatedOrder));
|
|
41
|
+
}
|
|
42
|
+
setColumnOrderState(updatedOrder);
|
|
43
|
+
}, [columnOrder, savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings, savedState === null || savedState === void 0 ? void 0 : savedState.id]);
|
|
21
44
|
const handleDragEnd = (0, react_1.useCallback)(_ref => {
|
|
22
45
|
let {
|
|
23
46
|
active,
|
|
@@ -39,7 +62,7 @@ function useColumnOrderByDrag(tableColumns) {
|
|
|
39
62
|
const newIndex = columnOrder.indexOf(overId);
|
|
40
63
|
return (0, sortable_1.arrayMove)(columnOrder, oldIndex, newIndex);
|
|
41
64
|
});
|
|
42
|
-
}, [columnOrder]);
|
|
65
|
+
}, [columnOrder, setColumnOrder]);
|
|
43
66
|
const sensors = (0, core_1.useSensors)((0, core_1.useSensor)(core_1.MouseSensor, draggingOptions), (0, core_1.useSensor)(core_1.TouchSensor, {}), (0, core_1.useSensor)(core_1.KeyboardSensor, {}));
|
|
44
67
|
return {
|
|
45
68
|
columnOrder,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ColumnDefinition } from '../../../types';
|
|
2
|
+
import { TableProps } from '../../types';
|
|
3
|
+
export declare function useColumnSettings<TData extends object, TFilter extends Record<string, unknown>>(columnDefinitions: ColumnDefinition<TData>[], savedState: TableProps<TFilter>['savedState']): {
|
|
4
|
+
enabledColumns: string[];
|
|
5
|
+
setEnabledColumns: (value: string[]) => void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useColumnSettings = useColumnSettings;
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
const validateSettingsLocalStorageValue = value => Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
10
|
+
function useColumnSettings(columnDefinitions, savedState) {
|
|
11
|
+
const localStorageKey = `${savedState === null || savedState === void 0 ? void 0 : savedState.id}_columnSettings`;
|
|
12
|
+
const [enabledColumns, setEnabledColumns] = (0, react_1.useState)(() => {
|
|
13
|
+
const persistState = JSON.parse(localStorage.getItem(localStorageKey) || 'null');
|
|
14
|
+
const persistValue = validateSettingsLocalStorageValue(persistState) ? persistState : null;
|
|
15
|
+
return columnDefinitions.filter(colDef => {
|
|
16
|
+
var _a, _b;
|
|
17
|
+
const columnIdentifier = (0, utils_1.getColumnIdentifier)(colDef);
|
|
18
|
+
if ('columnSettings' in colDef) {
|
|
19
|
+
if (((_a = colDef.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) === 'hidden') {
|
|
20
|
+
return true;
|
|
21
|
+
} else if (persistValue !== null && (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings)) {
|
|
22
|
+
if (persistValue.find(el => el === columnIdentifier)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return ((_b = colDef.columnSettings) === null || _b === void 0 ? void 0 : _b.mode) !== 'defaultFalse';
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}).map(utils_1.getColumnIdentifier);
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
enabledColumns,
|
|
34
|
+
setEnabledColumns: value => {
|
|
35
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
36
|
+
localStorage.setItem(localStorageKey, JSON.stringify(value));
|
|
37
|
+
}
|
|
38
|
+
setEnabledColumns(value);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -63,7 +63,8 @@ function saveStateToLocalStorage(_ref2) {
|
|
|
63
63
|
})));
|
|
64
64
|
}
|
|
65
65
|
function isFilterableColumn(colDef) {
|
|
66
|
-
|
|
66
|
+
var _a;
|
|
67
|
+
return ('id' in colDef || 'accessorKey' in colDef) && 'columnSettings' in colDef && ((_a = colDef.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) !== 'hidden';
|
|
67
68
|
}
|
|
68
69
|
function getColumnIdentifier(colDef) {
|
|
69
70
|
if ('id' in colDef && colDef.id) {
|
|
@@ -74,19 +75,23 @@ function getColumnIdentifier(colDef) {
|
|
|
74
75
|
// @ts-ignore
|
|
75
76
|
return colDef.accessorKey;
|
|
76
77
|
}
|
|
77
|
-
function prepareColumnsSettingsMap(columnDefinitions) {
|
|
78
|
-
return columnDefinitions.filter(isFilterableColumn).map(getColumnIdentifier);
|
|
79
|
-
}
|
|
80
78
|
const sortColumnDefinitions = columnOrder => function sortColDefs(colDefA, colDefB) {
|
|
81
79
|
const indexItemA = columnOrder.findIndex(columnIndex => columnIndex === getColumnIdentifier(colDefA));
|
|
82
80
|
const indexItemB = columnOrder.findIndex(columnIndex => columnIndex === getColumnIdentifier(colDefB));
|
|
83
81
|
return indexItemA - indexItemB;
|
|
84
82
|
};
|
|
83
|
+
function prepareColumnsSettingsMap(columnDefinitions) {
|
|
84
|
+
return columnDefinitions.filter(el => {
|
|
85
|
+
var _a;
|
|
86
|
+
return isFilterableColumn(el) && ((_a = el.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) !== 'defaultFalse';
|
|
87
|
+
}).map(getColumnIdentifier);
|
|
88
|
+
}
|
|
85
89
|
function createColumnsSettingsOption(columnDefinition) {
|
|
90
|
+
var _a;
|
|
86
91
|
return {
|
|
87
92
|
id: getColumnIdentifier(columnDefinition),
|
|
88
93
|
content: {
|
|
89
|
-
option: columnDefinition.
|
|
94
|
+
option: (_a = columnDefinition.columnSettings) === null || _a === void 0 ? void 0 : _a.label
|
|
90
95
|
},
|
|
91
96
|
switch: true,
|
|
92
97
|
showSwitchIcon: true
|
|
@@ -131,6 +131,7 @@ type BaseTableProps<TData extends object, TFilters extends FiltersState = Record
|
|
|
131
131
|
id: string;
|
|
132
132
|
filterQueryKey?: string;
|
|
133
133
|
resize?: boolean;
|
|
134
|
+
columnSettings?: boolean;
|
|
134
135
|
};
|
|
135
136
|
}>;
|
|
136
137
|
export type InfiniteTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = BaseTableProps<TData, TFilters> & {
|
package/dist/cjs/constants.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare const COLUMN_ALIGN: {
|
|
|
6
6
|
readonly Left: "left";
|
|
7
7
|
readonly Right: "right";
|
|
8
8
|
};
|
|
9
|
+
export declare const COLUMN_SETTINGS_MODE: {
|
|
10
|
+
readonly Hidden: "hidden";
|
|
11
|
+
readonly DefaultTrue: "defaultTrue";
|
|
12
|
+
readonly DefaultFalse: "defaultFalse";
|
|
13
|
+
};
|
|
9
14
|
export declare const TEST_IDS: {
|
|
10
15
|
headerSortIndicator: string;
|
|
11
16
|
headerRow: string;
|
package/dist/cjs/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.DEFAULT_COLUMNS = exports.DEFAULT_ROW_SELECTION = exports.DEFAULT_FILTER_VISIBILITY = exports.DEFAULT_SORTING = exports.DEFAULT_PAGE_SIZE = exports.SORT_FN = exports.TEST_IDS = exports.COLUMN_ALIGN = exports.COLUMN_PIN_POSITION = void 0;
|
|
6
|
+
exports.DEFAULT_COLUMNS = exports.DEFAULT_ROW_SELECTION = exports.DEFAULT_FILTER_VISIBILITY = exports.DEFAULT_SORTING = exports.DEFAULT_PAGE_SIZE = exports.SORT_FN = exports.TEST_IDS = exports.COLUMN_SETTINGS_MODE = exports.COLUMN_ALIGN = exports.COLUMN_PIN_POSITION = void 0;
|
|
7
7
|
exports.COLUMN_PIN_POSITION = {
|
|
8
8
|
Left: 'left',
|
|
9
9
|
Right: 'right'
|
|
@@ -12,6 +12,12 @@ exports.COLUMN_ALIGN = {
|
|
|
12
12
|
Left: 'left',
|
|
13
13
|
Right: 'right'
|
|
14
14
|
};
|
|
15
|
+
exports.COLUMN_SETTINGS_MODE = {
|
|
16
|
+
/* Скрыто в настройках, значение всегда True */
|
|
17
|
+
Hidden: 'hidden',
|
|
18
|
+
DefaultTrue: 'defaultTrue',
|
|
19
|
+
DefaultFalse: 'defaultFalse'
|
|
20
|
+
};
|
|
15
21
|
exports.TEST_IDS = {
|
|
16
22
|
headerSortIndicator: 'table__header__sort-indicator',
|
|
17
23
|
headerRow: 'table__header-row',
|
|
@@ -29,6 +29,7 @@ function ColumnsSettings(_ref) {
|
|
|
29
29
|
mode: 'multiple'
|
|
30
30
|
},
|
|
31
31
|
placement: 'bottom-end',
|
|
32
|
+
"data-test-id": 'table__column-settings-droplist',
|
|
32
33
|
children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
|
|
33
34
|
size: 'm',
|
|
34
35
|
"data-test-id": 'table__column-settings',
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { CellContext, ColumnDef, ColumnMeta, HeaderContext, PaginationState, Row
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
4
4
|
import { ValueOf } from '@snack-uikit/utils';
|
|
5
|
-
import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
|
|
5
|
+
import { COLUMN_ALIGN, COLUMN_PIN_POSITION, COLUMN_SETTINGS_MODE } from './constants';
|
|
6
6
|
import { EmptyStateProps, Except } from './helperComponents';
|
|
7
7
|
type ColumnAlign = ValueOf<typeof COLUMN_ALIGN>;
|
|
8
8
|
type ColumnPinPosition = ValueOf<typeof COLUMN_PIN_POSITION>;
|
|
9
|
+
type ColumnSettingsMode = ValueOf<typeof COLUMN_SETTINGS_MODE>;
|
|
9
10
|
type BaseColumnDefinition<TData> = Except<ColumnDef<TData>, 'footer' | 'enablePinning' | 'enableGrouping' | 'enableColumnFilter' | 'filterFn' | 'enableGlobalFilter' | 'enableMultiSort' | 'enableHiding'> & {
|
|
10
11
|
/** Заголовок колонки */
|
|
11
12
|
header?: ReactNode | ((ctx: HeaderContext<TData, unknown>) => ReactNode);
|
|
@@ -39,14 +40,16 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
39
40
|
size: number;
|
|
40
41
|
};
|
|
41
42
|
type FilterableProps = {
|
|
43
|
+
columnSettings?: {
|
|
44
|
+
/** Название колонки в настройках таблицы */
|
|
45
|
+
label?: string;
|
|
46
|
+
mode?: ColumnSettingsMode;
|
|
47
|
+
};
|
|
48
|
+
} & ({
|
|
42
49
|
id: string;
|
|
43
|
-
/** Название колонки в настройках таблицы */
|
|
44
|
-
headerConfigLabel?: string;
|
|
45
50
|
} | {
|
|
46
51
|
accessorKey: string;
|
|
47
|
-
|
|
48
|
-
headerConfigLabel?: string;
|
|
49
|
-
};
|
|
52
|
+
});
|
|
50
53
|
type FilterableNormalColumnDefinition<TData> = NormalColumnDefinition<TData> & FilterableProps;
|
|
51
54
|
type FilterablePinnedColumnDefinition<TData> = PinnedColumnDefinition<TData> & FilterableProps;
|
|
52
55
|
export type FilterableColumnDefinition<TData> = FilterableNormalColumnDefinition<TData> | FilterablePinnedColumnDefinition<TData>;
|
|
@@ -26,11 +26,11 @@ import { CellAutoResizeContext, useCellAutoResizeController } from '../../contex
|
|
|
26
26
|
import { BodyRow, ColumnsSettings, ExportButton, getColumnId, getRowActionsColumnDef, getSelectionCellColumnDef, getStatusColumnDef, HeaderRow, STATUS_APPEARANCE, TableContext, TableEmptyState, TablePagination, useEmptyState, } from '../../helperComponents';
|
|
27
27
|
import { getTreeColumnDef } from '../../helperComponents/Cells/TreeCell';
|
|
28
28
|
import { customDateParser, fuzzyFilter } from '../../utils';
|
|
29
|
-
import { useColumnOrderByDrag, useLoadingTable, useStateControl } from './hooks';
|
|
29
|
+
import { useColumnOrderByDrag, useColumnSettings, useLoadingTable, useStateControl } from './hooks';
|
|
30
30
|
import { usePageReset } from './hooks/usePageReset';
|
|
31
31
|
import { useSaveTableSettings } from './hooks/useSaveTableSettings';
|
|
32
32
|
import styles from './styles.module.css';
|
|
33
|
-
import { getColumnIdentifier, getColumnStyleVars, getCurrentlyConfiguredHeaderWidth, getInitColumnSizeFromLocalStorage, getInitialColumnsOpenValue, isFilterableColumn, prepareColumnsSettings,
|
|
33
|
+
import { getColumnIdentifier, getColumnStyleVars, getCurrentlyConfiguredHeaderWidth, getInitColumnSizeFromLocalStorage, getInitialColumnsOpenValue, isFilterableColumn, prepareColumnsSettings, saveStateToLocalStorage, } from './utils';
|
|
34
34
|
/** Компонент таблицы */
|
|
35
35
|
export function Table(_a) {
|
|
36
36
|
var { data, rowPinning = {
|
|
@@ -81,7 +81,7 @@ export function Table(_a) {
|
|
|
81
81
|
}, [columnFilters, areColumnFiltersOpen, filter, setFilter, filterVisibility, setFilterVisibility]);
|
|
82
82
|
const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
|
|
83
83
|
const manualPagination = infiniteLoading || manualPaginationProp;
|
|
84
|
-
const
|
|
84
|
+
const { enabledColumns, setEnabledColumns } = useColumnSettings(columnDefinitions, savedState);
|
|
85
85
|
const areColumnsSettingsEnabled = Boolean(columnsSettingsProp === null || columnsSettingsProp === void 0 ? void 0 : columnsSettingsProp.enableSettingsMenu);
|
|
86
86
|
const filteredColumnDefinitions = useMemo(() => {
|
|
87
87
|
if (!areColumnsSettingsEnabled) {
|
|
@@ -105,7 +105,7 @@ export function Table(_a) {
|
|
|
105
105
|
return cols;
|
|
106
106
|
}, [filteredColumnDefinitions, enableSelection, enableSelectPinned, expanding]);
|
|
107
107
|
const enableColumnsOrderSortByDrag = Boolean(columnsSettingsProp === null || columnsSettingsProp === void 0 ? void 0 : columnsSettingsProp.enableDrag);
|
|
108
|
-
const { columnOrder, setColumnOrder, sensors, handleDragEnd } = useColumnOrderByDrag(tableColumns);
|
|
108
|
+
const { columnOrder, setColumnOrder, sensors, handleDragEnd } = useColumnOrderByDrag(tableColumns, savedState);
|
|
109
109
|
const dndContextProps = useMemo(() => {
|
|
110
110
|
if (!enableColumnsOrderSortByDrag) {
|
|
111
111
|
return {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { DragEndEvent, SensorOptions } from '@dnd-kit/core';
|
|
2
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
3
|
import { ColumnDefinition } from '../../../types';
|
|
3
|
-
|
|
4
|
+
import { TableProps } from '../../types';
|
|
5
|
+
export declare function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnDefinition<TData>[], savedState: TableProps<TData>['savedState']): {
|
|
4
6
|
columnOrder: string[];
|
|
5
|
-
setColumnOrder:
|
|
7
|
+
setColumnOrder: Dispatch<SetStateAction<string[]>>;
|
|
6
8
|
handleDragEnd: ({ active, over }: DragEndEvent) => void;
|
|
7
9
|
sensors: import("@dnd-kit/core").SensorDescriptor<SensorOptions>[];
|
|
8
10
|
};
|
|
@@ -2,16 +2,42 @@ import { KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors, } from
|
|
|
2
2
|
import { arrayMove } from '@dnd-kit/sortable';
|
|
3
3
|
import { useCallback, useState } from 'react';
|
|
4
4
|
import { getColumnIdentifier } from '../utils';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const validateColumnOrderLocalStorageValue = (value) => Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
6
|
+
const getLocalStorageColumnOrderKey = (id) => `${id}_columnOrder`;
|
|
7
|
+
function prepareInitialState(tableColumns, savedState) {
|
|
8
|
+
const columnOrder = tableColumns
|
|
9
|
+
.filter(column => column.pinned !== 'left' && column.pinned !== 'right')
|
|
10
|
+
.map(getColumnIdentifier);
|
|
11
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
12
|
+
const persistState = JSON.parse(localStorage.getItem(getLocalStorageColumnOrderKey(savedState.id)) || 'null');
|
|
13
|
+
const persistValue = validateColumnOrderLocalStorageValue(persistState) ? persistState : null;
|
|
14
|
+
if (persistValue !== null) {
|
|
15
|
+
return [...persistValue, ...columnOrder.filter(column => !(persistValue === null || persistValue === void 0 ? void 0 : persistValue.includes(column)))];
|
|
16
|
+
}
|
|
17
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(columnOrder));
|
|
18
|
+
}
|
|
19
|
+
return columnOrder;
|
|
7
20
|
}
|
|
8
21
|
const draggingOptions = {
|
|
9
22
|
activationConstraint: {
|
|
10
23
|
distance: 5, // Is required to differ click (sort) from drag
|
|
11
24
|
},
|
|
12
25
|
};
|
|
13
|
-
export function useColumnOrderByDrag(tableColumns) {
|
|
14
|
-
const [columnOrder,
|
|
26
|
+
export function useColumnOrderByDrag(tableColumns, savedState) {
|
|
27
|
+
const [columnOrder, setColumnOrderState] = useState(() => prepareInitialState(tableColumns, savedState));
|
|
28
|
+
const setColumnOrder = useCallback(value => {
|
|
29
|
+
let updatedOrder;
|
|
30
|
+
if (value instanceof Function) {
|
|
31
|
+
updatedOrder = value(columnOrder);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
updatedOrder = value;
|
|
35
|
+
}
|
|
36
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
37
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(updatedOrder));
|
|
38
|
+
}
|
|
39
|
+
setColumnOrderState(updatedOrder);
|
|
40
|
+
}, [columnOrder, savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings, savedState === null || savedState === void 0 ? void 0 : savedState.id]);
|
|
15
41
|
const handleDragEnd = useCallback(({ active, over }) => {
|
|
16
42
|
if (!active || !over) {
|
|
17
43
|
return;
|
|
@@ -29,7 +55,7 @@ export function useColumnOrderByDrag(tableColumns) {
|
|
|
29
55
|
const newIndex = columnOrder.indexOf(overId);
|
|
30
56
|
return arrayMove(columnOrder, oldIndex, newIndex);
|
|
31
57
|
});
|
|
32
|
-
}, [columnOrder]);
|
|
58
|
+
}, [columnOrder, setColumnOrder]);
|
|
33
59
|
const sensors = useSensors(useSensor(MouseSensor, draggingOptions), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {}));
|
|
34
60
|
return {
|
|
35
61
|
columnOrder,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ColumnDefinition } from '../../../types';
|
|
2
|
+
import { TableProps } from '../../types';
|
|
3
|
+
export declare function useColumnSettings<TData extends object, TFilter extends Record<string, unknown>>(columnDefinitions: ColumnDefinition<TData>[], savedState: TableProps<TFilter>['savedState']): {
|
|
4
|
+
enabledColumns: string[];
|
|
5
|
+
setEnabledColumns: (value: string[]) => void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { getColumnIdentifier } from '../utils';
|
|
3
|
+
const validateSettingsLocalStorageValue = (value) => Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
4
|
+
export function useColumnSettings(columnDefinitions, savedState) {
|
|
5
|
+
const localStorageKey = `${savedState === null || savedState === void 0 ? void 0 : savedState.id}_columnSettings`;
|
|
6
|
+
const [enabledColumns, setEnabledColumns] = useState(() => {
|
|
7
|
+
const persistState = JSON.parse(localStorage.getItem(localStorageKey) || 'null');
|
|
8
|
+
const persistValue = validateSettingsLocalStorageValue(persistState) ? persistState : null;
|
|
9
|
+
return columnDefinitions
|
|
10
|
+
.filter(colDef => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
const columnIdentifier = getColumnIdentifier(colDef);
|
|
13
|
+
if ('columnSettings' in colDef) {
|
|
14
|
+
if (((_a = colDef.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) === 'hidden') {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
else if (persistValue !== null && (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings)) {
|
|
18
|
+
if (persistValue.find(el => el === columnIdentifier)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return ((_b = colDef.columnSettings) === null || _b === void 0 ? void 0 : _b.mode) !== 'defaultFalse';
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
})
|
|
27
|
+
.map(getColumnIdentifier);
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
enabledColumns,
|
|
31
|
+
setEnabledColumns: (value) => {
|
|
32
|
+
if (savedState === null || savedState === void 0 ? void 0 : savedState.columnSettings) {
|
|
33
|
+
localStorage.setItem(localStorageKey, JSON.stringify(value));
|
|
34
|
+
}
|
|
35
|
+
setEnabledColumns(value);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -36,7 +36,10 @@ export function saveStateToLocalStorage({ id, columnId, size }) {
|
|
|
36
36
|
localStorage.setItem(id, JSON.stringify(Object.assign(Object.assign({}, (savedStateFromStorage || {})), { resizeState: newResizeState })));
|
|
37
37
|
}
|
|
38
38
|
export function isFilterableColumn(colDef) {
|
|
39
|
-
|
|
39
|
+
var _a;
|
|
40
|
+
return (('id' in colDef || 'accessorKey' in colDef) &&
|
|
41
|
+
'columnSettings' in colDef &&
|
|
42
|
+
((_a = colDef.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) !== 'hidden');
|
|
40
43
|
}
|
|
41
44
|
export function getColumnIdentifier(colDef) {
|
|
42
45
|
if ('id' in colDef && colDef.id) {
|
|
@@ -47,19 +50,22 @@ export function getColumnIdentifier(colDef) {
|
|
|
47
50
|
// @ts-ignore
|
|
48
51
|
return colDef.accessorKey;
|
|
49
52
|
}
|
|
50
|
-
export function prepareColumnsSettingsMap(columnDefinitions) {
|
|
51
|
-
return columnDefinitions.filter(isFilterableColumn).map(getColumnIdentifier);
|
|
52
|
-
}
|
|
53
53
|
const sortColumnDefinitions = (columnOrder) => function sortColDefs(colDefA, colDefB) {
|
|
54
54
|
const indexItemA = columnOrder.findIndex(columnIndex => columnIndex === getColumnIdentifier(colDefA));
|
|
55
55
|
const indexItemB = columnOrder.findIndex(columnIndex => columnIndex === getColumnIdentifier(colDefB));
|
|
56
56
|
return indexItemA - indexItemB;
|
|
57
57
|
};
|
|
58
|
+
export function prepareColumnsSettingsMap(columnDefinitions) {
|
|
59
|
+
return columnDefinitions
|
|
60
|
+
.filter(el => { var _a; return isFilterableColumn(el) && ((_a = el.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) !== 'defaultFalse'; })
|
|
61
|
+
.map(getColumnIdentifier);
|
|
62
|
+
}
|
|
58
63
|
function createColumnsSettingsOption(columnDefinition) {
|
|
64
|
+
var _a;
|
|
59
65
|
return {
|
|
60
66
|
id: getColumnIdentifier(columnDefinition),
|
|
61
67
|
content: {
|
|
62
|
-
option: columnDefinition.
|
|
68
|
+
option: (_a = columnDefinition.columnSettings) === null || _a === void 0 ? void 0 : _a.label,
|
|
63
69
|
},
|
|
64
70
|
switch: true,
|
|
65
71
|
showSwitchIcon: true,
|
|
@@ -131,6 +131,7 @@ type BaseTableProps<TData extends object, TFilters extends FiltersState = Record
|
|
|
131
131
|
id: string;
|
|
132
132
|
filterQueryKey?: string;
|
|
133
133
|
resize?: boolean;
|
|
134
|
+
columnSettings?: boolean;
|
|
134
135
|
};
|
|
135
136
|
}>;
|
|
136
137
|
export type InfiniteTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = BaseTableProps<TData, TFilters> & {
|
package/dist/esm/constants.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare const COLUMN_ALIGN: {
|
|
|
6
6
|
readonly Left: "left";
|
|
7
7
|
readonly Right: "right";
|
|
8
8
|
};
|
|
9
|
+
export declare const COLUMN_SETTINGS_MODE: {
|
|
10
|
+
readonly Hidden: "hidden";
|
|
11
|
+
readonly DefaultTrue: "defaultTrue";
|
|
12
|
+
readonly DefaultFalse: "defaultFalse";
|
|
13
|
+
};
|
|
9
14
|
export declare const TEST_IDS: {
|
|
10
15
|
headerSortIndicator: string;
|
|
11
16
|
headerRow: string;
|
package/dist/esm/constants.js
CHANGED
|
@@ -6,6 +6,12 @@ export const COLUMN_ALIGN = {
|
|
|
6
6
|
Left: 'left',
|
|
7
7
|
Right: 'right',
|
|
8
8
|
};
|
|
9
|
+
export const COLUMN_SETTINGS_MODE = {
|
|
10
|
+
/* Скрыто в настройках, значение всегда True */
|
|
11
|
+
Hidden: 'hidden',
|
|
12
|
+
DefaultTrue: 'defaultTrue',
|
|
13
|
+
DefaultFalse: 'defaultFalse',
|
|
14
|
+
};
|
|
9
15
|
export const TEST_IDS = {
|
|
10
16
|
headerSortIndicator: 'table__header__sort-indicator',
|
|
11
17
|
headerRow: 'table__header-row',
|
|
@@ -8,5 +8,5 @@ export function ColumnsSettings({ columnsSettings, enabledColumns, setEnabledCol
|
|
|
8
8
|
value: enabledColumns,
|
|
9
9
|
onChange: setEnabledColumns,
|
|
10
10
|
mode: 'multiple',
|
|
11
|
-
}, placement: 'bottom-end', children: _jsx(ButtonFunction, { size: 'm', "data-test-id": 'table__column-settings', icon: _jsx(FunctionSettingsSVG, {}) }) }));
|
|
11
|
+
}, placement: 'bottom-end', "data-test-id": 'table__column-settings-droplist', children: _jsx(ButtonFunction, { size: 'm', "data-test-id": 'table__column-settings', icon: _jsx(FunctionSettingsSVG, {}) }) }));
|
|
12
12
|
}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { CellContext, ColumnDef, ColumnMeta, HeaderContext, PaginationState, Row
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
4
4
|
import { ValueOf } from '@snack-uikit/utils';
|
|
5
|
-
import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
|
|
5
|
+
import { COLUMN_ALIGN, COLUMN_PIN_POSITION, COLUMN_SETTINGS_MODE } from './constants';
|
|
6
6
|
import { EmptyStateProps, Except } from './helperComponents';
|
|
7
7
|
type ColumnAlign = ValueOf<typeof COLUMN_ALIGN>;
|
|
8
8
|
type ColumnPinPosition = ValueOf<typeof COLUMN_PIN_POSITION>;
|
|
9
|
+
type ColumnSettingsMode = ValueOf<typeof COLUMN_SETTINGS_MODE>;
|
|
9
10
|
type BaseColumnDefinition<TData> = Except<ColumnDef<TData>, 'footer' | 'enablePinning' | 'enableGrouping' | 'enableColumnFilter' | 'filterFn' | 'enableGlobalFilter' | 'enableMultiSort' | 'enableHiding'> & {
|
|
10
11
|
/** Заголовок колонки */
|
|
11
12
|
header?: ReactNode | ((ctx: HeaderContext<TData, unknown>) => ReactNode);
|
|
@@ -39,14 +40,16 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
39
40
|
size: number;
|
|
40
41
|
};
|
|
41
42
|
type FilterableProps = {
|
|
43
|
+
columnSettings?: {
|
|
44
|
+
/** Название колонки в настройках таблицы */
|
|
45
|
+
label?: string;
|
|
46
|
+
mode?: ColumnSettingsMode;
|
|
47
|
+
};
|
|
48
|
+
} & ({
|
|
42
49
|
id: string;
|
|
43
|
-
/** Название колонки в настройках таблицы */
|
|
44
|
-
headerConfigLabel?: string;
|
|
45
50
|
} | {
|
|
46
51
|
accessorKey: string;
|
|
47
|
-
|
|
48
|
-
headerConfigLabel?: string;
|
|
49
|
-
};
|
|
52
|
+
});
|
|
50
53
|
type FilterableNormalColumnDefinition<TData> = NormalColumnDefinition<TData> & FilterableProps;
|
|
51
54
|
type FilterablePinnedColumnDefinition<TData> = PinnedColumnDefinition<TData> & FilterableProps;
|
|
52
55
|
export type FilterableColumnDefinition<TData> = FilterableNormalColumnDefinition<TData> | FilterablePinnedColumnDefinition<TData>;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Table",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.35.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"@snack-uikit/locale": "*"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "fe98ae1edb6c3d6ccd3a4fc67bf4a4ba55b13e35"
|
|
70
70
|
}
|
|
@@ -52,7 +52,7 @@ import { getTreeColumnDef } from '../../helperComponents/Cells/TreeCell';
|
|
|
52
52
|
import { ColumnDefinition } from '../../types';
|
|
53
53
|
import { customDateParser, fuzzyFilter } from '../../utils';
|
|
54
54
|
import { TableProps } from '../types';
|
|
55
|
-
import { useColumnOrderByDrag, useLoadingTable, useStateControl } from './hooks';
|
|
55
|
+
import { useColumnOrderByDrag, useColumnSettings, useLoadingTable, useStateControl } from './hooks';
|
|
56
56
|
import { usePageReset } from './hooks/usePageReset';
|
|
57
57
|
import { useSaveTableSettings } from './hooks/useSaveTableSettings';
|
|
58
58
|
import styles from './styles.module.scss';
|
|
@@ -64,7 +64,6 @@ import {
|
|
|
64
64
|
getInitialColumnsOpenValue,
|
|
65
65
|
isFilterableColumn,
|
|
66
66
|
prepareColumnsSettings,
|
|
67
|
-
prepareColumnsSettingsMap,
|
|
68
67
|
saveStateToLocalStorage,
|
|
69
68
|
} from './utils';
|
|
70
69
|
|
|
@@ -199,7 +198,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
|
|
|
199
198
|
|
|
200
199
|
const manualPagination = infiniteLoading || manualPaginationProp;
|
|
201
200
|
|
|
202
|
-
const
|
|
201
|
+
const { enabledColumns, setEnabledColumns } = useColumnSettings(columnDefinitions, savedState);
|
|
203
202
|
const areColumnsSettingsEnabled = Boolean(columnsSettingsProp?.enableSettingsMenu);
|
|
204
203
|
|
|
205
204
|
const filteredColumnDefinitions = useMemo(() => {
|
|
@@ -228,7 +227,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
|
|
|
228
227
|
}, [filteredColumnDefinitions, enableSelection, enableSelectPinned, expanding]);
|
|
229
228
|
|
|
230
229
|
const enableColumnsOrderSortByDrag = Boolean(columnsSettingsProp?.enableDrag);
|
|
231
|
-
const { columnOrder, setColumnOrder, sensors, handleDragEnd } = useColumnOrderByDrag(tableColumns);
|
|
230
|
+
const { columnOrder, setColumnOrder, sensors, handleDragEnd } = useColumnOrderByDrag(tableColumns, savedState);
|
|
232
231
|
const dndContextProps: DndContextProps = useMemo(() => {
|
|
233
232
|
if (!enableColumnsOrderSortByDrag) {
|
|
234
233
|
return {};
|
|
@@ -8,13 +8,37 @@ import {
|
|
|
8
8
|
useSensors,
|
|
9
9
|
} from '@dnd-kit/core';
|
|
10
10
|
import { arrayMove } from '@dnd-kit/sortable';
|
|
11
|
-
import { useCallback, useState } from 'react';
|
|
11
|
+
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
|
12
12
|
|
|
13
13
|
import { ColumnDefinition } from '../../../types';
|
|
14
|
+
import { TableProps } from '../../types';
|
|
14
15
|
import { getColumnIdentifier } from '../utils';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const validateColumnOrderLocalStorageValue = (value: unknown): value is string[] =>
|
|
18
|
+
Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
19
|
+
|
|
20
|
+
const getLocalStorageColumnOrderKey = (id: string) => `${id}_columnOrder`;
|
|
21
|
+
|
|
22
|
+
function prepareInitialState<TData extends object>(
|
|
23
|
+
tableColumns: ColumnDefinition<TData>[],
|
|
24
|
+
savedState: TableProps<TData>['savedState'],
|
|
25
|
+
) {
|
|
26
|
+
const columnOrder = tableColumns
|
|
27
|
+
.filter(column => column.pinned !== 'left' && column.pinned !== 'right')
|
|
28
|
+
.map(getColumnIdentifier);
|
|
29
|
+
|
|
30
|
+
if (savedState?.columnSettings) {
|
|
31
|
+
const persistState = JSON.parse(localStorage.getItem(getLocalStorageColumnOrderKey(savedState.id)) || 'null');
|
|
32
|
+
const persistValue: string[] | null = validateColumnOrderLocalStorageValue(persistState) ? persistState : null;
|
|
33
|
+
|
|
34
|
+
if (persistValue !== null) {
|
|
35
|
+
return [...persistValue, ...columnOrder.filter(column => !persistValue?.includes(column))];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(columnOrder));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return columnOrder;
|
|
18
42
|
}
|
|
19
43
|
|
|
20
44
|
const draggingOptions: SensorOptions = {
|
|
@@ -23,8 +47,30 @@ const draggingOptions: SensorOptions = {
|
|
|
23
47
|
},
|
|
24
48
|
};
|
|
25
49
|
|
|
26
|
-
export function useColumnOrderByDrag<TData extends object>(
|
|
27
|
-
|
|
50
|
+
export function useColumnOrderByDrag<TData extends object>(
|
|
51
|
+
tableColumns: ColumnDefinition<TData>[],
|
|
52
|
+
savedState: TableProps<TData>['savedState'],
|
|
53
|
+
) {
|
|
54
|
+
const [columnOrder, setColumnOrderState] = useState<string[]>(() => prepareInitialState(tableColumns, savedState));
|
|
55
|
+
|
|
56
|
+
const setColumnOrder: Dispatch<SetStateAction<string[]>> = useCallback(
|
|
57
|
+
value => {
|
|
58
|
+
let updatedOrder: string[];
|
|
59
|
+
|
|
60
|
+
if (value instanceof Function) {
|
|
61
|
+
updatedOrder = value(columnOrder);
|
|
62
|
+
} else {
|
|
63
|
+
updatedOrder = value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (savedState?.columnSettings) {
|
|
67
|
+
localStorage.setItem(getLocalStorageColumnOrderKey(savedState.id), JSON.stringify(updatedOrder));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setColumnOrderState(updatedOrder);
|
|
71
|
+
},
|
|
72
|
+
[columnOrder, savedState?.columnSettings, savedState?.id],
|
|
73
|
+
);
|
|
28
74
|
|
|
29
75
|
const handleDragEnd = useCallback(
|
|
30
76
|
({ active, over }: DragEndEvent) => {
|
|
@@ -49,7 +95,7 @@ export function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnD
|
|
|
49
95
|
return arrayMove(columnOrder, oldIndex, newIndex);
|
|
50
96
|
});
|
|
51
97
|
},
|
|
52
|
-
[columnOrder],
|
|
98
|
+
[columnOrder, setColumnOrder],
|
|
53
99
|
);
|
|
54
100
|
|
|
55
101
|
const sensors = useSensors(
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ColumnDefinition } from '../../../types';
|
|
4
|
+
import { TableProps } from '../../types';
|
|
5
|
+
import { getColumnIdentifier } from '../utils';
|
|
6
|
+
|
|
7
|
+
const validateSettingsLocalStorageValue = (value: unknown): value is string[] =>
|
|
8
|
+
Array.isArray(value) && value.every(setting => typeof setting === 'string');
|
|
9
|
+
|
|
10
|
+
export function useColumnSettings<TData extends object, TFilter extends Record<string, unknown>>(
|
|
11
|
+
columnDefinitions: ColumnDefinition<TData>[],
|
|
12
|
+
savedState: TableProps<TFilter>['savedState'],
|
|
13
|
+
) {
|
|
14
|
+
const localStorageKey = `${savedState?.id}_columnSettings`;
|
|
15
|
+
|
|
16
|
+
const [enabledColumns, setEnabledColumns] = useState<string[]>(() => {
|
|
17
|
+
const persistState = JSON.parse(localStorage.getItem(localStorageKey) || 'null');
|
|
18
|
+
const persistValue: string[] | null = validateSettingsLocalStorageValue(persistState) ? persistState : null;
|
|
19
|
+
|
|
20
|
+
return columnDefinitions
|
|
21
|
+
.filter(colDef => {
|
|
22
|
+
const columnIdentifier = getColumnIdentifier(colDef);
|
|
23
|
+
|
|
24
|
+
if ('columnSettings' in colDef) {
|
|
25
|
+
if (colDef.columnSettings?.mode === 'hidden') {
|
|
26
|
+
return true;
|
|
27
|
+
} else if (persistValue !== null && savedState?.columnSettings) {
|
|
28
|
+
if (persistValue.find(el => el === columnIdentifier)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return colDef.columnSettings?.mode !== 'defaultFalse';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return true;
|
|
39
|
+
})
|
|
40
|
+
.map(getColumnIdentifier);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
enabledColumns,
|
|
45
|
+
setEnabledColumns: (value: string[]) => {
|
|
46
|
+
if (savedState?.columnSettings) {
|
|
47
|
+
localStorage.setItem(localStorageKey, JSON.stringify(value));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setEnabledColumns(value);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -72,7 +72,11 @@ export function saveStateToLocalStorage({ id, columnId, size }: SaveStateToLocal
|
|
|
72
72
|
export function isFilterableColumn<TData extends object>(
|
|
73
73
|
colDef: ColumnDefinition<TData>,
|
|
74
74
|
): colDef is FilterableColumnDefinition<TData> {
|
|
75
|
-
return (
|
|
75
|
+
return (
|
|
76
|
+
('id' in colDef || 'accessorKey' in colDef) &&
|
|
77
|
+
'columnSettings' in colDef &&
|
|
78
|
+
colDef.columnSettings?.mode !== 'hidden'
|
|
79
|
+
);
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export function getColumnIdentifier<TData extends object>(colDef: ColumnDefinition<TData>): string {
|
|
@@ -86,12 +90,6 @@ export function getColumnIdentifier<TData extends object>(colDef: ColumnDefiniti
|
|
|
86
90
|
return colDef.accessorKey as unknown as string;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
export function prepareColumnsSettingsMap<TData extends object>(
|
|
90
|
-
columnDefinitions: ColumnDefinition<TData>[],
|
|
91
|
-
): string[] {
|
|
92
|
-
return columnDefinitions.filter(isFilterableColumn).map(getColumnIdentifier);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
93
|
const sortColumnDefinitions = (columnOrder: string[]) =>
|
|
96
94
|
function sortColDefs<TData extends object>(
|
|
97
95
|
colDefA: FilterableColumnDefinition<TData>,
|
|
@@ -103,13 +101,21 @@ const sortColumnDefinitions = (columnOrder: string[]) =>
|
|
|
103
101
|
return indexItemA - indexItemB;
|
|
104
102
|
};
|
|
105
103
|
|
|
104
|
+
export function prepareColumnsSettingsMap<TData extends object>(
|
|
105
|
+
columnDefinitions: ColumnDefinition<TData>[],
|
|
106
|
+
): string[] {
|
|
107
|
+
return columnDefinitions
|
|
108
|
+
.filter(el => isFilterableColumn(el) && el.columnSettings?.mode !== 'defaultFalse')
|
|
109
|
+
.map(getColumnIdentifier);
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
function createColumnsSettingsOption<TData extends object>(
|
|
107
113
|
columnDefinition: FilterableColumnDefinition<TData>,
|
|
108
114
|
): BaseItemProps {
|
|
109
115
|
return {
|
|
110
116
|
id: getColumnIdentifier(columnDefinition),
|
|
111
117
|
content: {
|
|
112
|
-
option: columnDefinition.
|
|
118
|
+
option: columnDefinition.columnSettings?.label as string,
|
|
113
119
|
},
|
|
114
120
|
switch: true,
|
|
115
121
|
showSwitchIcon: true,
|
package/src/components/types.ts
CHANGED
package/src/constants.tsx
CHANGED
|
@@ -8,6 +8,13 @@ export const COLUMN_ALIGN = {
|
|
|
8
8
|
Right: 'right',
|
|
9
9
|
} as const;
|
|
10
10
|
|
|
11
|
+
export const COLUMN_SETTINGS_MODE = {
|
|
12
|
+
/* Скрыто в настройках, значение всегда True */
|
|
13
|
+
Hidden: 'hidden',
|
|
14
|
+
DefaultTrue: 'defaultTrue',
|
|
15
|
+
DefaultFalse: 'defaultFalse',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
11
18
|
export const TEST_IDS = {
|
|
12
19
|
headerSortIndicator: 'table__header__sort-indicator',
|
|
13
20
|
headerRow: 'table__header-row',
|
|
@@ -21,6 +21,7 @@ export function ColumnsSettings({ columnsSettings, enabledColumns, setEnabledCol
|
|
|
21
21
|
mode: 'multiple',
|
|
22
22
|
}}
|
|
23
23
|
placement='bottom-end'
|
|
24
|
+
data-test-id='table__column-settings-droplist'
|
|
24
25
|
>
|
|
25
26
|
<ButtonFunction size='m' data-test-id='table__column-settings' icon={<FunctionSettingsSVG />} />
|
|
26
27
|
</Droplist>
|
package/src/types.ts
CHANGED
|
@@ -13,13 +13,15 @@ import { ReactNode } from 'react';
|
|
|
13
13
|
import { ToolbarProps } from '@snack-uikit/toolbar';
|
|
14
14
|
import { ValueOf } from '@snack-uikit/utils';
|
|
15
15
|
|
|
16
|
-
import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
|
|
16
|
+
import { COLUMN_ALIGN, COLUMN_PIN_POSITION, COLUMN_SETTINGS_MODE } from './constants';
|
|
17
17
|
import { EmptyStateProps, Except } from './helperComponents';
|
|
18
18
|
|
|
19
19
|
type ColumnAlign = ValueOf<typeof COLUMN_ALIGN>;
|
|
20
20
|
|
|
21
21
|
type ColumnPinPosition = ValueOf<typeof COLUMN_PIN_POSITION>;
|
|
22
22
|
|
|
23
|
+
type ColumnSettingsMode = ValueOf<typeof COLUMN_SETTINGS_MODE>;
|
|
24
|
+
|
|
23
25
|
type BaseColumnDefinition<TData> = Except<
|
|
24
26
|
ColumnDef<TData>,
|
|
25
27
|
| 'footer'
|
|
@@ -63,17 +65,20 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
|
|
|
63
65
|
size: number;
|
|
64
66
|
};
|
|
65
67
|
|
|
66
|
-
type FilterableProps =
|
|
68
|
+
type FilterableProps = {
|
|
69
|
+
columnSettings?: {
|
|
70
|
+
/** Название колонки в настройках таблицы */
|
|
71
|
+
label?: string;
|
|
72
|
+
mode?: ColumnSettingsMode;
|
|
73
|
+
};
|
|
74
|
+
} & (
|
|
67
75
|
| {
|
|
68
76
|
id: string;
|
|
69
|
-
/** Название колонки в настройках таблицы */
|
|
70
|
-
headerConfigLabel?: string;
|
|
71
77
|
}
|
|
72
78
|
| {
|
|
73
79
|
accessorKey: string;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
80
|
+
}
|
|
81
|
+
);
|
|
77
82
|
|
|
78
83
|
type FilterableNormalColumnDefinition<TData> = NormalColumnDefinition<TData> & FilterableProps;
|
|
79
84
|
|