@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/components/Table/Table.js +5 -2
  4. package/dist/cjs/components/Table/hooks/index.d.ts +1 -0
  5. package/dist/cjs/components/Table/hooks/index.js +2 -1
  6. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.d.ts +4 -2
  7. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.js +28 -5
  8. package/dist/cjs/components/Table/hooks/useColumnSettings.d.ts +6 -0
  9. package/dist/cjs/components/Table/hooks/useColumnSettings.js +41 -0
  10. package/dist/cjs/components/Table/utils.js +10 -5
  11. package/dist/cjs/components/types.d.ts +1 -0
  12. package/dist/cjs/constants.d.ts +5 -0
  13. package/dist/cjs/constants.js +7 -1
  14. package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.js +1 -0
  15. package/dist/cjs/types.d.ts +9 -6
  16. package/dist/esm/components/Table/Table.js +4 -4
  17. package/dist/esm/components/Table/hooks/index.d.ts +1 -0
  18. package/dist/esm/components/Table/hooks/index.js +1 -0
  19. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.d.ts +4 -2
  20. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.js +31 -5
  21. package/dist/esm/components/Table/hooks/useColumnSettings.d.ts +6 -0
  22. package/dist/esm/components/Table/hooks/useColumnSettings.js +38 -0
  23. package/dist/esm/components/Table/utils.js +11 -5
  24. package/dist/esm/components/types.d.ts +1 -0
  25. package/dist/esm/constants.d.ts +5 -0
  26. package/dist/esm/constants.js +6 -0
  27. package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.js +1 -1
  28. package/dist/esm/types.d.ts +9 -6
  29. package/package.json +2 -2
  30. package/src/components/Table/Table.tsx +3 -4
  31. package/src/components/Table/hooks/index.ts +1 -0
  32. package/src/components/Table/hooks/useColumnOrderByDrag.ts +52 -6
  33. package/src/components/Table/hooks/useColumnSettings.ts +53 -0
  34. package/src/components/Table/utils.ts +14 -8
  35. package/src/components/types.ts +1 -0
  36. package/src/constants.tsx +7 -0
  37. package/src/helperComponents/ColumnsSettings/ColumnsSettings.tsx +1 -0
  38. 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 [enabledColumns, setEnabledColumns] = (0, react_1.useState)(() => (0, utils_3.prepareColumnsSettingsMap)(columnDefinitions));
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 {};
@@ -1,3 +1,4 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
3
  export * from './useColumnOrderByDrag';
4
+ export * from './useColumnSettings';
@@ -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
- export declare function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnDefinition<TData>[]): {
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: import("react").Dispatch<import("react").SetStateAction<string[]>>;
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
- function prepareInitialState(tableColumns) {
12
- return tableColumns.filter(column => column.pinned !== 'left' && column.pinned !== 'right').map(utils_1.getColumnIdentifier);
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, setColumnOrder] = (0, react_1.useState)(() => prepareInitialState(tableColumns));
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
- return ('id' in colDef || 'accessorKey' in colDef) && 'headerConfigLabel' in colDef;
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.headerConfigLabel
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> & {
@@ -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;
@@ -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',
@@ -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, prepareColumnsSettingsMap, saveStateToLocalStorage, } from './utils';
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 [enabledColumns, setEnabledColumns] = useState(() => prepareColumnsSettingsMap(columnDefinitions));
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,3 +1,4 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
3
  export * from './useColumnOrderByDrag';
4
+ export * from './useColumnSettings';
@@ -1,3 +1,4 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
3
  export * from './useColumnOrderByDrag';
4
+ export * from './useColumnSettings';
@@ -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
- export declare function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnDefinition<TData>[]): {
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: import("react").Dispatch<import("react").SetStateAction<string[]>>;
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
- function prepareInitialState(tableColumns) {
6
- return tableColumns.filter(column => column.pinned !== 'left' && column.pinned !== 'right').map(getColumnIdentifier);
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, setColumnOrder] = useState(() => prepareInitialState(tableColumns));
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
- return ('id' in colDef || 'accessorKey' in colDef) && 'headerConfigLabel' in colDef;
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.headerConfigLabel,
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> & {
@@ -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;
@@ -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
  }
@@ -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.34.9",
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": "e14c6ec6fbc59b086b7ed348403eb1bac740b9c3"
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 [enabledColumns, setEnabledColumns] = useState<string[]>(() => prepareColumnsSettingsMap(columnDefinitions));
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 {};
@@ -1,3 +1,4 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
3
  export * from './useColumnOrderByDrag';
4
+ export * from './useColumnSettings';
@@ -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
- function prepareInitialState<TData extends object>(tableColumns: ColumnDefinition<TData>[]) {
17
- return tableColumns.filter(column => column.pinned !== 'left' && column.pinned !== 'right').map(getColumnIdentifier);
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>(tableColumns: ColumnDefinition<TData>[]) {
27
- const [columnOrder, setColumnOrder] = useState<string[]>(() => prepareInitialState(tableColumns));
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 ('id' in colDef || 'accessorKey' in colDef) && 'headerConfigLabel' in colDef;
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.headerConfigLabel as string,
118
+ option: columnDefinition.columnSettings?.label as string,
113
119
  },
114
120
  switch: true,
115
121
  showSwitchIcon: true,
@@ -161,6 +161,7 @@ type BaseTableProps<TData extends object, TFilters extends FiltersState = Record
161
161
  id: string;
162
162
  filterQueryKey?: string;
163
163
  resize?: boolean;
164
+ columnSettings?: boolean;
164
165
  };
165
166
  }>;
166
167
 
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
- headerConfigLabel?: string;
76
- };
80
+ }
81
+ );
77
82
 
78
83
  type FilterableNormalColumnDefinition<TData> = NormalColumnDefinition<TData> & FilterableProps;
79
84