@snack-uikit/table 0.28.4 → 0.28.6-preview-e840ab81.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 (37) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/components/ServerTable/ServerTable.d.ts +1 -1
  4. package/dist/cjs/components/Table/Table.d.ts +1 -1
  5. package/dist/cjs/components/Table/Table.js +24 -3
  6. package/dist/cjs/components/Table/hooks/useSaveTableSettings/index.d.ts +1 -0
  7. package/dist/cjs/components/Table/hooks/useSaveTableSettings/index.js +25 -0
  8. package/dist/cjs/components/Table/hooks/useSaveTableSettings/useSaveTableSettings.d.ts +21 -0
  9. package/dist/cjs/components/Table/hooks/useSaveTableSettings/useSaveTableSettings.js +81 -0
  10. package/dist/cjs/components/Table/hooks/useSaveTableSettings/vallidators.d.ts +3 -0
  11. package/dist/cjs/components/Table/hooks/useSaveTableSettings/vallidators.js +10 -0
  12. package/dist/cjs/components/types.d.ts +5 -3
  13. package/dist/cjs/helperComponents/TablePagination/TablePagination.js +2 -0
  14. package/dist/cjs/utils.d.ts +1 -0
  15. package/dist/cjs/utils.js +14 -2
  16. package/dist/esm/components/ServerTable/ServerTable.d.ts +1 -1
  17. package/dist/esm/components/Table/Table.d.ts +1 -1
  18. package/dist/esm/components/Table/Table.js +11 -3
  19. package/dist/esm/components/Table/hooks/useSaveTableSettings/index.d.ts +1 -0
  20. package/dist/esm/components/Table/hooks/useSaveTableSettings/index.js +1 -0
  21. package/dist/esm/components/Table/hooks/useSaveTableSettings/useSaveTableSettings.d.ts +21 -0
  22. package/dist/esm/components/Table/hooks/useSaveTableSettings/useSaveTableSettings.js +53 -0
  23. package/dist/esm/components/Table/hooks/useSaveTableSettings/vallidators.d.ts +3 -0
  24. package/dist/esm/components/Table/hooks/useSaveTableSettings/vallidators.js +2 -0
  25. package/dist/esm/components/types.d.ts +5 -3
  26. package/dist/esm/helperComponents/TablePagination/TablePagination.js +2 -1
  27. package/dist/esm/utils.d.ts +1 -0
  28. package/dist/esm/utils.js +10 -0
  29. package/package.json +16 -16
  30. package/src/components/ServerTable/ServerTable.tsx +1 -1
  31. package/src/components/Table/Table.tsx +13 -3
  32. package/src/components/Table/hooks/useSaveTableSettings/index.ts +1 -0
  33. package/src/components/Table/hooks/useSaveTableSettings/useSaveTableSettings.ts +108 -0
  34. package/src/components/Table/hooks/useSaveTableSettings/vallidators.ts +7 -0
  35. package/src/components/types.ts +9 -3
  36. package/src/helperComponents/TablePagination/TablePagination.tsx +2 -0
  37. package/src/utils.ts +14 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
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.28.5 (2025-02-10)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **PDS-1081:** make type for filters in generic non required ([136ea97](https://github.com/cloud-ru-tech/snack-uikit/commit/136ea971801290030eb6ad68bfcb8c06b2b3e732))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.28.4 (2025-02-10)
7
18
 
8
19
  ### Only dependencies have been changed
package/README.md CHANGED
@@ -109,6 +109,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
109
109
  ### Props
110
110
  | name | type | default value | description |
111
111
  |------|------|---------------|-------------|
112
+ | saveStateSettings* | `FilterStateOptions<TFilters>` | - | Настройки для сохранения состояний таблицы в query params и local storage |
112
113
  | columnDefinitions* | `ColumnDefinition<TData>[]` | - | Определение внешнего вида и функционала колонок |
113
114
  | data* | `TData[]` | - | Данные для отрисовки |
114
115
  | keepPinnedRows | `boolean` | - | Параметр отвечает за отображение закрепленных строк на всех страницах таблицы |
@@ -175,6 +176,7 @@ const columnDefinitions: ColumnDefinition<TableData>[] = [
175
176
  | name | type | default value | description |
176
177
  |------|------|---------------|-------------|
177
178
  | onChangePage* | `(offset: number, limit: number) => void` | - | |
179
+ | saveStateSettings* | `FilterStateOptions<TFilters>` | - | Настройки для сохранения состояний таблицы в query params и local storage |
178
180
  | columnDefinitions* | `ColumnDefinition<TData>[]` | - | Определение внешнего вида и функционала колонок |
179
181
  | keepPinnedRows | `boolean` | false | Параметр отвечает за отображение закрепленных строк на всех страницах таблицы |
180
182
  | copyPinnedRows | `boolean` | false | Параметр отвечает за сохранение закрепленных строк в теле таблицы |
@@ -1,6 +1,6 @@
1
1
  import { FiltersState } from '@snack-uikit/chips';
2
2
  import { ServerTableProps } from '../types';
3
- export declare function ServerTable<TData extends object, TFilters extends FiltersState>({ items, total, limit, offset, onChangePage, search: searchProp, pagination, columnFilters, manualSorting, manualPagination, manualFiltering, ...rest }: ServerTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
3
+ export declare function ServerTable<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ items, total, limit, offset, onChangePage, search: searchProp, pagination, columnFilters, manualSorting, manualPagination, manualFiltering, ...rest }: ServerTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
4
4
  export declare namespace ServerTable {
5
5
  var getRowActionsColumnDef: typeof import("../../helperComponents").getRowActionsColumnDef;
6
6
  var statusAppearances: Record<string, string>;
@@ -1,7 +1,7 @@
1
1
  import { FiltersState } from '@snack-uikit/chips';
2
2
  import { TableProps } from '../types';
3
3
  /** Компонент таблицы */
4
- export declare function Table<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, suppressSearch, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, bulkActions: bulkActionsProp, ...rest }: TableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
4
+ export declare function Table<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, suppressSearch, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, saveStateSettings, bulkActions: bulkActionsProp, ...rest }: TableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
5
5
  export declare namespace Table {
6
6
  var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
7
7
  var statusAppearances: Record<string, string>;
@@ -34,6 +34,7 @@ const TreeCell_1 = require("../../helperComponents/Cells/TreeCell");
34
34
  const utils_2 = require("../../utils");
35
35
  const hooks_1 = require("./hooks");
36
36
  const usePageReset_1 = require("./hooks/usePageReset");
37
+ const useSaveTableSettings_1 = require("./hooks/useSaveTableSettings/useSaveTableSettings");
37
38
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
38
39
  const utils_3 = require("./utils");
39
40
  /** Компонент таблицы */
@@ -80,9 +81,10 @@ function Table(_a) {
80
81
  enableFuzzySearch,
81
82
  savedState,
82
83
  expanding,
84
+ saveStateSettings,
83
85
  bulkActions: bulkActionsProp
84
86
  } = _a,
85
- rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "suppressSearch", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "expanding", "bulkActions"]);
87
+ rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "suppressSearch", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "expanding", "saveStateSettings", "bulkActions"]);
86
88
  const {
87
89
  state: globalFilter,
88
90
  onStateChange: onGlobalFilterChange
@@ -103,6 +105,25 @@ function Table(_a) {
103
105
  state: pagination,
104
106
  onStateChange: onPaginationChange
105
107
  } = (0, hooks_1.useStateControl)(paginationProp, defaultPaginationState);
108
+ const {
109
+ patchedFilter,
110
+ isSettingsLoaded
111
+ } = (0, useSaveTableSettings_1.useSaveTableSettings)({
112
+ pagination: {
113
+ state: pagination,
114
+ handler: onPaginationChange
115
+ },
116
+ search: {
117
+ state: globalFilter,
118
+ handler: onGlobalFilterChange
119
+ },
120
+ sorting: {
121
+ state: sorting,
122
+ handler: onSortingChange
123
+ },
124
+ filters: columnFilters,
125
+ options: saveStateSettings
126
+ });
106
127
  const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
107
128
  const tableColumns = (0, react_1.useMemo)(() => {
108
129
  let cols = columnDefinitions;
@@ -334,7 +355,7 @@ function Table(_a) {
334
355
  },
335
356
  className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className)
336
357
  }, (0, utils_1.extractSupportProps)(rest), {
337
- children: [showToolbar && (0, jsx_runtime_1.jsx)("div", {
358
+ children: [showToolbar && isSettingsLoaded && (0, jsx_runtime_1.jsx)("div", {
338
359
  className: styles_module_scss_1.default.header,
339
360
  children: (0, jsx_runtime_1.jsx)(toolbar_1.Toolbar, {
340
361
  search: suppressSearch ? undefined : {
@@ -361,7 +382,7 @@ function Table(_a) {
361
382
  })]
362
383
  }) : undefined,
363
384
  moreActions: moreActions,
364
- filterRow: columnFilters,
385
+ filterRow: patchedFilter,
365
386
  "data-test-id": constants_1.TEST_IDS.toolbar
366
387
  })
367
388
  }), (0, jsx_runtime_1.jsx)("div", {
@@ -0,0 +1 @@
1
+ export * from './useSaveTableSettings';
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = {
8
+ enumerable: true,
9
+ get: function () {
10
+ return m[k];
11
+ }
12
+ };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ } : function (o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ });
19
+ var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", {
23
+ value: true
24
+ });
25
+ __exportStar(require("./useSaveTableSettings"), exports);
@@ -0,0 +1,21 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+ import { Handler } from 'uncontrollable';
3
+ import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
4
+ import { FilterRow } from '@snack-uikit/toolbar/src/components/Toolbar/types';
5
+ import { FilterStateOptions } from '@snack-uikit/utils';
6
+ type State<T> = {
7
+ state: T;
8
+ handler: Handler;
9
+ };
10
+ type TableSettings<TFilter extends FiltersState = Record<string, unknown>> = {
11
+ options?: FilterStateOptions<TFilter>;
12
+ filters?: ChipChoiceRowProps<TFilter>;
13
+ pagination: State<PaginationState>;
14
+ search: State<string>;
15
+ sorting: State<SortingState>;
16
+ };
17
+ export declare const useSaveTableSettings: <TFilter extends FiltersState = Record<string, unknown>>({ options, filters, pagination: { state: paginationState, handler: paginationHandler }, search: { state: searchState, handler: searchHandler }, sorting: { state: sortingState, handler: sortingHandler }, }: TableSettings<TFilter>) => {
18
+ patchedFilter: FilterRow<TFilter> | undefined;
19
+ isSettingsLoaded: boolean;
20
+ };
21
+ export {};
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useSaveTableSettings = void 0;
7
+ const react_1 = require("react");
8
+ const utils_1 = require("@snack-uikit/utils");
9
+ const utils_2 = require("../../../../utils");
10
+ const vallidators_1 = require("./vallidators");
11
+ const useSaveTableSettings = _ref => {
12
+ let {
13
+ options,
14
+ filters,
15
+ pagination: {
16
+ state: paginationState,
17
+ handler: paginationHandler
18
+ },
19
+ search: {
20
+ state: searchState,
21
+ handler: searchHandler
22
+ },
23
+ sorting: {
24
+ state: sortingState,
25
+ handler: sortingHandler
26
+ }
27
+ } = _ref;
28
+ const [filter, setFilter] = (0, react_1.useState)(filters === null || filters === void 0 ? void 0 : filters.value);
29
+ const [isSettingsLoaded, setIsSettingsLoaded] = (0, react_1.useState)(false);
30
+ (0, react_1.useEffect)(() => {
31
+ setFilter(filters === null || filters === void 0 ? void 0 : filters.value);
32
+ }, [filters === null || filters === void 0 ? void 0 : filters.value]);
33
+ const settings = (0, react_1.useMemo)(() => ({
34
+ pagination: paginationState,
35
+ filter,
36
+ search: searchState,
37
+ sorting: sortingState
38
+ }), [paginationState, filter, searchState, sortingState]);
39
+ const onFilterChange = (0, react_1.useCallback)(value => {
40
+ (filters === null || filters === void 0 ? void 0 : filters.onChange) && filters.onChange(value);
41
+ setFilter(value);
42
+ },
43
+ // eslint-disable-next-line
44
+ [filters === null || filters === void 0 ? void 0 : filters.onChange]);
45
+ const validate = (0, react_1.useCallback)(data => {
46
+ const isPaginationValid = (0, vallidators_1.validatePaging)(data === null || data === void 0 ? void 0 : data.pagination);
47
+ const isSortingValid = (0, vallidators_1.validateSorting)(data === null || data === void 0 ? void 0 : data.sorting);
48
+ const isSearchValid = typeof (data === null || data === void 0 ? void 0 : data.search) === 'string';
49
+ const isFilterValid = Boolean(options === null || options === void 0 ? void 0 : options.validateData(data === null || data === void 0 ? void 0 : data.filter));
50
+ return isPaginationValid && isSortingValid && isSearchValid && isFilterValid;
51
+ }, [options]);
52
+ const onInitPage = (0, react_1.useCallback)(data => {
53
+ if (!data) {
54
+ setIsSettingsLoaded(true);
55
+ return;
56
+ }
57
+ data.pagination && paginationHandler(data.pagination);
58
+ data.search && searchHandler(data.search);
59
+ data.sorting && sortingHandler(data.sorting);
60
+ data.filter && onFilterChange((0, utils_2.customDateParser)(data.filter));
61
+ setIsSettingsLoaded(true);
62
+ // eslint-disable-next-line
63
+ }, []);
64
+ const filterStateOptions = (0, react_1.useMemo)(() => options ? Object.assign(Object.assign({}, options), {
65
+ validateData: validate
66
+ }) : undefined, [options, validate]);
67
+ (0, utils_1.useSaveFilterState)({
68
+ filter: settings,
69
+ options: filterStateOptions,
70
+ onLoadFilter: onInitPage
71
+ });
72
+ const patchedFilter = (0, react_1.useMemo)(() => filters ? Object.assign(Object.assign({}, filters), {
73
+ value: filter,
74
+ onChange: onFilterChange
75
+ }) : undefined, [filters, onFilterChange, filter]);
76
+ return {
77
+ patchedFilter,
78
+ isSettingsLoaded
79
+ };
80
+ };
81
+ exports.useSaveTableSettings = useSaveTableSettings;
@@ -0,0 +1,3 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+ export declare const validatePaging: (value: unknown) => value is PaginationState;
3
+ export declare const validateSorting: (value: unknown) => value is SortingState;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.validateSorting = exports.validatePaging = void 0;
7
+ const validatePaging = value => typeof (value === null || value === void 0 ? void 0 : value.pageSize) === 'number' && typeof (value === null || value === void 0 ? void 0 : value.pageIndex) === 'number';
8
+ exports.validatePaging = validatePaging;
9
+ const validateSorting = value => value === null || value === void 0 ? void 0 : value.every(column => typeof (column === null || column === void 0 ? void 0 : column.id) === 'string' && typeof (column === null || column === void 0 ? void 0 : column.desc) === 'boolean');
10
+ exports.validateSorting = validateSorting;
@@ -2,14 +2,14 @@ import { PaginationState, Row, RowPinningState, RowSelectionOptions, RowSelectio
2
2
  import { ReactNode, RefObject } from 'react';
3
3
  import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
4
4
  import { ToolbarProps } from '@snack-uikit/toolbar';
5
- import { WithSupportProps } from '@snack-uikit/utils';
5
+ import { FilterStateOptions, WithSupportProps } from '@snack-uikit/utils';
6
6
  import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
7
7
  import { TreeColumnDefinitionProps } from '../helperComponents/Cells/TreeCell';
8
8
  import { ColumnDefinition } from '../types';
9
9
  type BulkAction = Omit<NonNullable<ToolbarProps<Record<string, string>>['bulkActions']>[number], 'onClick'> & {
10
10
  onClick?(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void): void;
11
11
  };
12
- export type TableProps<TData extends object, TFilters extends FiltersState> = WithSupportProps<{
12
+ export type TableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = WithSupportProps<{
13
13
  /** Данные для отрисовки */
14
14
  data: TData[];
15
15
  /** Определение внешнего вида и функционала колонок */
@@ -64,6 +64,8 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
64
64
  loading?: boolean;
65
65
  onChange?(value: string): void;
66
66
  };
67
+ /** Настройки для сохранения состояний таблицы в query params и local storage*/
68
+ saveStateSettings: FilterStateOptions<TFilters>;
67
69
  /** Включить нечеткий поиск */
68
70
  enableFuzzySearch?: boolean;
69
71
  /** Максимальное кол-во строк на страницу @default 10 */
@@ -143,7 +145,7 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
143
145
  resize?: boolean;
144
146
  };
145
147
  }>;
146
- export type ServerTableProps<TData extends object, TFilters extends FiltersState> = Omit<TableProps<TData, TFilters>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
148
+ export type ServerTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = Omit<TableProps<TData, TFilters>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
147
149
  /** Данные для отрисовки */
148
150
  items?: TData[];
149
151
  /**
@@ -14,6 +14,7 @@ const react_1 = require("react");
14
14
  const chips_1 = require("@snack-uikit/chips");
15
15
  const locale_1 = require("@snack-uikit/locale");
16
16
  const pagination_1 = require("@snack-uikit/pagination");
17
+ const utils_1 = require("@snack-uikit/utils");
17
18
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
18
19
  function TablePagination(_ref) {
19
20
  let {
@@ -52,6 +53,7 @@ function TablePagination(_ref) {
52
53
  value: String(tablePaginationState.pageSize),
53
54
  onChange: handleRowsVolumeOnChange,
54
55
  placement: 'top-end',
56
+ onClick: () => (0, utils_1.isBrowser)() && window.location.reload(),
55
57
  options: options,
56
58
  label: optionsLabel,
57
59
  widthStrategy: 'auto',
@@ -1,3 +1,4 @@
1
1
  import { FilterFn } from '@tanstack/react-table';
2
2
  export declare const fuzzyFilter: FilterFn<any>;
3
3
  export declare const preciseFilter: FilterFn<any>;
4
+ export declare const customDateParser: <T>(value: Record<string, unknown>) => T;
package/dist/cjs/utils.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.preciseFilter = exports.fuzzyFilter = void 0;
6
+ exports.customDateParser = exports.preciseFilter = exports.fuzzyFilter = void 0;
7
7
  const match_sorter_utils_1 = require("@tanstack/match-sorter-utils");
8
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
9
  const fuzzyFilter = (row, columnId, value, addMeta) => {
@@ -22,4 +22,16 @@ const preciseFilter = (row, columnId, value, addMeta) => {
22
22
  });
23
23
  return itemRank.passed;
24
24
  };
25
- exports.preciseFilter = preciseFilter;
25
+ exports.preciseFilter = preciseFilter;
26
+ const isDateString = value => typeof value === 'string' && !isNaN(Number(new Date(value)));
27
+ const customDateParser = value => Object.fromEntries(Object.entries(value).map(_ref => {
28
+ let [key, value] = _ref;
29
+ if (isDateString(value)) {
30
+ return [key, new Date(value)];
31
+ }
32
+ if (Array.isArray(value) && value.some(isDateString)) {
33
+ return [key, value.map(element => isDateString(element) ? new Date(element) : element)];
34
+ }
35
+ return [key, value];
36
+ }));
37
+ exports.customDateParser = customDateParser;
@@ -1,6 +1,6 @@
1
1
  import { FiltersState } from '@snack-uikit/chips';
2
2
  import { ServerTableProps } from '../types';
3
- export declare function ServerTable<TData extends object, TFilters extends FiltersState>({ items, total, limit, offset, onChangePage, search: searchProp, pagination, columnFilters, manualSorting, manualPagination, manualFiltering, ...rest }: ServerTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
3
+ export declare function ServerTable<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ items, total, limit, offset, onChangePage, search: searchProp, pagination, columnFilters, manualSorting, manualPagination, manualFiltering, ...rest }: ServerTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
4
4
  export declare namespace ServerTable {
5
5
  var getRowActionsColumnDef: typeof import("../../helperComponents").getRowActionsColumnDef;
6
6
  var statusAppearances: Record<string, string>;
@@ -1,7 +1,7 @@
1
1
  import { FiltersState } from '@snack-uikit/chips';
2
2
  import { TableProps } from '../types';
3
3
  /** Компонент таблицы */
4
- export declare function Table<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, suppressSearch, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, bulkActions: bulkActionsProp, ...rest }: TableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
4
+ export declare function Table<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, rowPinning, columnDefinitions, keepPinnedRows, copyPinnedRows, enableSelectPinned, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize, pageCount, loading, outline, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar, suppressSearch, toolbarAfter, suppressPagination, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, saveStateSettings, bulkActions: bulkActionsProp, ...rest }: TableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
5
5
  export declare namespace Table {
6
6
  var getStatusColumnDef: typeof import("../../helperComponents").getStatusColumnDef;
7
7
  var statusAppearances: Record<string, string>;
@@ -26,13 +26,14 @@ import { getTreeColumnDef } from '../../helperComponents/Cells/TreeCell';
26
26
  import { fuzzyFilter } from '../../utils';
27
27
  import { useLoadingTable, useStateControl } from './hooks';
28
28
  import { usePageReset } from './hooks/usePageReset';
29
+ import { useSaveTableSettings } from './hooks/useSaveTableSettings/useSaveTableSettings';
29
30
  import styles from './styles.module.css';
30
31
  import { getColumnStyleVars, getCurrentlyConfiguredHeaderWidth, getInitColumnSizeFromLocalStorage, saveStateToLocalStorage, } from './utils';
31
32
  /** Компонент таблицы */
32
33
  export function Table(_a) {
33
34
  var { data, rowPinning = {
34
35
  top: [],
35
- }, columnDefinitions, keepPinnedRows = false, copyPinnedRows = false, enableSelectPinned = false, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, suppressSearch = false, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, autoResetPageIndex = false, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, bulkActions: bulkActionsProp } = _a, rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "suppressSearch", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "expanding", "bulkActions"]);
36
+ }, columnDefinitions, keepPinnedRows = false, copyPinnedRows = false, enableSelectPinned = false, rowSelection: rowSelectionProp, search, sorting: sortingProp, columnFilters, pagination: paginationProp, className, onRowClick, onRefresh, pageSize = DEFAULT_PAGE_SIZE, pageCount, loading = false, outline = false, moreActions, exportSettings, dataFiltered, dataError, noDataState, noResultsState, errorDataState, suppressToolbar = false, suppressSearch = false, toolbarAfter, suppressPagination = false, manualSorting = false, manualPagination = false, manualFiltering = false, autoResetPageIndex = false, scrollRef, scrollContainerRef, getRowId, enableFuzzySearch, savedState, expanding, saveStateSettings, bulkActions: bulkActionsProp } = _a, rest = __rest(_a, ["data", "rowPinning", "columnDefinitions", "keepPinnedRows", "copyPinnedRows", "enableSelectPinned", "rowSelection", "search", "sorting", "columnFilters", "pagination", "className", "onRowClick", "onRefresh", "pageSize", "pageCount", "loading", "outline", "moreActions", "exportSettings", "dataFiltered", "dataError", "noDataState", "noResultsState", "errorDataState", "suppressToolbar", "suppressSearch", "toolbarAfter", "suppressPagination", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "scrollRef", "scrollContainerRef", "getRowId", "enableFuzzySearch", "savedState", "expanding", "saveStateSettings", "bulkActions"]);
36
37
  const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl(search, '');
37
38
  const { state: rowSelection, onStateChange: onRowSelectionChange } = useStateControl(rowSelectionProp, {});
38
39
  const defaultPaginationState = useMemo(() => ({
@@ -41,6 +42,13 @@ export function Table(_a) {
41
42
  }), [pageSize]);
42
43
  const { state: sorting, onStateChange: onSortingChange } = useStateControl(sortingProp, []);
43
44
  const { state: pagination, onStateChange: onPaginationChange } = useStateControl(paginationProp, defaultPaginationState);
45
+ const { patchedFilter, isSettingsLoaded } = useSaveTableSettings({
46
+ pagination: { state: pagination, handler: onPaginationChange },
47
+ search: { state: globalFilter, handler: onGlobalFilterChange },
48
+ sorting: { state: sorting, handler: onSortingChange },
49
+ filters: columnFilters,
50
+ options: saveStateSettings,
51
+ });
44
52
  const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
45
53
  const tableColumns = useMemo(() => {
46
54
  let cols = columnDefinitions;
@@ -237,14 +245,14 @@ export function Table(_a) {
237
245
  const showToolbar = !suppressToolbar;
238
246
  return (_jsx(CellAutoResizeContext.Provider, { value: { updateCellMap }, children: _jsxs("div", Object.assign({ style: {
239
247
  '--page-size': cssPageSize,
240
- }, className: cn(styles.wrapper, className) }, extractSupportProps(rest), { children: [showToolbar && (_jsx("div", { className: styles.header, children: _jsx(Toolbar, { search: suppressSearch
248
+ }, className: cn(styles.wrapper, className) }, extractSupportProps(rest), { children: [showToolbar && isSettingsLoaded && (_jsx("div", { className: styles.header, children: _jsx(Toolbar, { search: suppressSearch
241
249
  ? undefined
242
250
  : {
243
251
  value: globalFilter,
244
252
  onChange: onGlobalFilterChange,
245
253
  loading: search === null || search === void 0 ? void 0 : search.loading,
246
254
  placeholder: (search === null || search === void 0 ? void 0 : search.placeholder) || t('searchPlaceholder'),
247
- }, className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, bulkActions: bulkActions, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), onCheck: enableSelection ? handleOnCheck : undefined, outline: outline, after: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions, filterRow: columnFilters, "data-test-id": TEST_IDS.toolbar }) })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', className: styles.table, ref: scrollContainerRef, children: [_jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [centerRows.length || filteredTopRows.length ? _jsx(HeaderRow, {}) : null, filteredTopRows.length ? (_jsx("div", { className: styles.topRowWrapper, children: filteredTopRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))) })) : null, centerRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length + filteredTopRows.length })] })) }) }), _jsx("div", { className: styles.scrollStub, ref: scrollRef })] }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount, optionsRender: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsRender }))] })) }));
255
+ }, className: styles.toolbar, onRefresh: onRefresh ? handleOnRefresh : undefined, bulkActions: bulkActions, selectionMode: (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow) ? 'multiple' : 'single', checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected(), onCheck: enableSelection ? handleOnCheck : undefined, outline: outline, after: toolbarAfter || exportSettings ? (_jsxs(_Fragment, { children: [toolbarAfter, exportSettings && (_jsx(ExportButton, { settings: exportSettings, columnDefinitions: columnDefinitions, data: data, topRows: filteredTopRows, centerRows: centerRows }))] })) : undefined, moreActions: moreActions, filterRow: patchedFilter, "data-test-id": TEST_IDS.toolbar }) })), _jsx("div", { className: styles.scrollWrapper, "data-outline": outline || undefined, children: _jsxs(Scroll, { size: 's', className: styles.table, ref: scrollContainerRef, children: [_jsx("div", { className: styles.tableContent, style: columnSizeVars, children: _jsx(TableContext.Provider, { value: { table }, children: loading ? (_jsxs(SkeletonContextProvider, { loading: true, children: [_jsx(HeaderRow, {}), loadingTableRows.map(row => (_jsx(BodyRow, { row: row }, row.id)))] })) : (_jsxs(_Fragment, { children: [centerRows.length || filteredTopRows.length ? _jsx(HeaderRow, {}) : null, filteredTopRows.length ? (_jsx("div", { className: styles.topRowWrapper, children: filteredTopRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))) })) : null, centerRows.map(row => (_jsx(BodyRow, { row: row, onRowClick: onRowClick }, row.id))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length + filteredTopRows.length })] })) }) }), _jsx("div", { className: styles.scrollStub, ref: scrollRef })] }) }), !suppressPagination && (_jsx(TablePagination, { table: table, options: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.options, optionsLabel: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsLabel, pageCount: pageCount, optionsRender: paginationProp === null || paginationProp === void 0 ? void 0 : paginationProp.optionsRender }))] })) }));
248
256
  }
249
257
  Table.getStatusColumnDef = getStatusColumnDef;
250
258
  Table.statusAppearances = STATUS_APPEARANCE;
@@ -0,0 +1 @@
1
+ export * from './useSaveTableSettings';
@@ -0,0 +1 @@
1
+ export * from './useSaveTableSettings';
@@ -0,0 +1,21 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+ import { Handler } from 'uncontrollable';
3
+ import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
4
+ import { FilterRow } from '@snack-uikit/toolbar/src/components/Toolbar/types';
5
+ import { FilterStateOptions } from '@snack-uikit/utils';
6
+ type State<T> = {
7
+ state: T;
8
+ handler: Handler;
9
+ };
10
+ type TableSettings<TFilter extends FiltersState = Record<string, unknown>> = {
11
+ options?: FilterStateOptions<TFilter>;
12
+ filters?: ChipChoiceRowProps<TFilter>;
13
+ pagination: State<PaginationState>;
14
+ search: State<string>;
15
+ sorting: State<SortingState>;
16
+ };
17
+ export declare const useSaveTableSettings: <TFilter extends FiltersState = Record<string, unknown>>({ options, filters, pagination: { state: paginationState, handler: paginationHandler }, search: { state: searchState, handler: searchHandler }, sorting: { state: sortingState, handler: sortingHandler }, }: TableSettings<TFilter>) => {
18
+ patchedFilter: FilterRow<TFilter> | undefined;
19
+ isSettingsLoaded: boolean;
20
+ };
21
+ export {};
@@ -0,0 +1,53 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { useSaveFilterState } from '@snack-uikit/utils';
3
+ import { customDateParser } from '../../../../utils';
4
+ import { validatePaging, validateSorting } from './vallidators';
5
+ export const useSaveTableSettings = ({ options, filters, pagination: { state: paginationState, handler: paginationHandler }, search: { state: searchState, handler: searchHandler }, sorting: { state: sortingState, handler: sortingHandler }, }) => {
6
+ const [filter, setFilter] = useState(filters === null || filters === void 0 ? void 0 : filters.value);
7
+ const [isSettingsLoaded, setIsSettingsLoaded] = useState(false);
8
+ useEffect(() => {
9
+ setFilter(filters === null || filters === void 0 ? void 0 : filters.value);
10
+ }, [filters === null || filters === void 0 ? void 0 : filters.value]);
11
+ const settings = useMemo(() => ({
12
+ pagination: paginationState,
13
+ filter,
14
+ search: searchState,
15
+ sorting: sortingState,
16
+ }), [paginationState, filter, searchState, sortingState]);
17
+ const onFilterChange = useCallback((value) => {
18
+ (filters === null || filters === void 0 ? void 0 : filters.onChange) && filters.onChange(value);
19
+ setFilter(value);
20
+ },
21
+ // eslint-disable-next-line
22
+ [filters === null || filters === void 0 ? void 0 : filters.onChange]);
23
+ const validate = useCallback((data) => {
24
+ const isPaginationValid = validatePaging(data === null || data === void 0 ? void 0 : data.pagination);
25
+ const isSortingValid = validateSorting(data === null || data === void 0 ? void 0 : data.sorting);
26
+ const isSearchValid = typeof (data === null || data === void 0 ? void 0 : data.search) === 'string';
27
+ const isFilterValid = Boolean(options === null || options === void 0 ? void 0 : options.validateData(data === null || data === void 0 ? void 0 : data.filter));
28
+ return isPaginationValid && isSortingValid && isSearchValid && isFilterValid;
29
+ }, [options]);
30
+ const onInitPage = useCallback((data) => {
31
+ if (!data) {
32
+ setIsSettingsLoaded(true);
33
+ return;
34
+ }
35
+ data.pagination && paginationHandler(data.pagination);
36
+ data.search && searchHandler(data.search);
37
+ data.sorting && sortingHandler(data.sorting);
38
+ data.filter && onFilterChange(customDateParser(data.filter));
39
+ setIsSettingsLoaded(true);
40
+ // eslint-disable-next-line
41
+ }, []);
42
+ const filterStateOptions = useMemo(() => (options ? Object.assign(Object.assign({}, options), { validateData: validate }) : undefined), [options, validate]);
43
+ useSaveFilterState({
44
+ filter: settings,
45
+ options: filterStateOptions,
46
+ onLoadFilter: onInitPage,
47
+ });
48
+ const patchedFilter = useMemo(() => (filters ? Object.assign(Object.assign({}, filters), { value: filter, onChange: onFilterChange }) : undefined), [filters, onFilterChange, filter]);
49
+ return {
50
+ patchedFilter,
51
+ isSettingsLoaded,
52
+ };
53
+ };
@@ -0,0 +1,3 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+ export declare const validatePaging: (value: unknown) => value is PaginationState;
3
+ export declare const validateSorting: (value: unknown) => value is SortingState;
@@ -0,0 +1,2 @@
1
+ export const validatePaging = (value) => typeof (value === null || value === void 0 ? void 0 : value.pageSize) === 'number' && typeof (value === null || value === void 0 ? void 0 : value.pageIndex) === 'number';
2
+ export const validateSorting = (value) => value === null || value === void 0 ? void 0 : value.every(column => typeof (column === null || column === void 0 ? void 0 : column.id) === 'string' && typeof (column === null || column === void 0 ? void 0 : column.desc) === 'boolean');
@@ -2,14 +2,14 @@ import { PaginationState, Row, RowPinningState, RowSelectionOptions, RowSelectio
2
2
  import { ReactNode, RefObject } from 'react';
3
3
  import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
4
4
  import { ToolbarProps } from '@snack-uikit/toolbar';
5
- import { WithSupportProps } from '@snack-uikit/utils';
5
+ import { FilterStateOptions, WithSupportProps } from '@snack-uikit/utils';
6
6
  import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
7
7
  import { TreeColumnDefinitionProps } from '../helperComponents/Cells/TreeCell';
8
8
  import { ColumnDefinition } from '../types';
9
9
  type BulkAction = Omit<NonNullable<ToolbarProps<Record<string, string>>['bulkActions']>[number], 'onClick'> & {
10
10
  onClick?(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void): void;
11
11
  };
12
- export type TableProps<TData extends object, TFilters extends FiltersState> = WithSupportProps<{
12
+ export type TableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = WithSupportProps<{
13
13
  /** Данные для отрисовки */
14
14
  data: TData[];
15
15
  /** Определение внешнего вида и функционала колонок */
@@ -64,6 +64,8 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
64
64
  loading?: boolean;
65
65
  onChange?(value: string): void;
66
66
  };
67
+ /** Настройки для сохранения состояний таблицы в query params и local storage*/
68
+ saveStateSettings: FilterStateOptions<TFilters>;
67
69
  /** Включить нечеткий поиск */
68
70
  enableFuzzySearch?: boolean;
69
71
  /** Максимальное кол-во строк на страницу @default 10 */
@@ -143,7 +145,7 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
143
145
  resize?: boolean;
144
146
  };
145
147
  }>;
146
- export type ServerTableProps<TData extends object, TFilters extends FiltersState> = Omit<TableProps<TData, TFilters>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
148
+ export type ServerTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = Omit<TableProps<TData, TFilters>, 'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'> & {
147
149
  /** Данные для отрисовки */
148
150
  items?: TData[];
149
151
  /**
@@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react';
3
3
  import { ChipChoice } from '@snack-uikit/chips';
4
4
  import { useLocale } from '@snack-uikit/locale';
5
5
  import { Pagination } from '@snack-uikit/pagination';
6
+ import { isBrowser } from '@snack-uikit/utils';
6
7
  import styles from './styles.module.css';
7
8
  export function TablePagination({ table, options: optionsProp, optionsLabel: optionsLabelProp, optionsRender, }) {
8
9
  const { t } = useLocale('Table');
@@ -21,5 +22,5 @@ export function TablePagination({ table, options: optionsProp, optionsLabel: opt
21
22
  if (table.getPageCount() <= 1 && !options) {
22
23
  return null;
23
24
  }
24
- return (_jsxs("div", { className: styles.footer, children: [table.getPageCount() > 1 && (_jsx(Pagination, { total: table.getPageCount(), page: tablePaginationState.pageIndex + 1, onChange: handlePaginationOnChange, size: 'xs', className: styles.pagination })), options && table.getRowModel().rows.length >= Number(options[0].value) && (_jsx(ChipChoice.Single, { value: String(tablePaginationState.pageSize), onChange: handleRowsVolumeOnChange, placement: 'top-end', options: options, label: optionsLabel, widthStrategy: 'auto', size: 'xs' }))] }));
25
+ return (_jsxs("div", { className: styles.footer, children: [table.getPageCount() > 1 && (_jsx(Pagination, { total: table.getPageCount(), page: tablePaginationState.pageIndex + 1, onChange: handlePaginationOnChange, size: 'xs', className: styles.pagination })), options && table.getRowModel().rows.length >= Number(options[0].value) && (_jsx(ChipChoice.Single, { value: String(tablePaginationState.pageSize), onChange: handleRowsVolumeOnChange, placement: 'top-end', onClick: () => isBrowser() && window.location.reload(), options: options, label: optionsLabel, widthStrategy: 'auto', size: 'xs' }))] }));
25
26
  }
@@ -1,3 +1,4 @@
1
1
  import { FilterFn } from '@tanstack/react-table';
2
2
  export declare const fuzzyFilter: FilterFn<any>;
3
3
  export declare const preciseFilter: FilterFn<any>;
4
+ export declare const customDateParser: <T>(value: Record<string, unknown>) => T;
package/dist/esm/utils.js CHANGED
@@ -15,3 +15,13 @@ export const preciseFilter = (row, columnId, value, addMeta) => {
15
15
  });
16
16
  return itemRank.passed;
17
17
  };
18
+ const isDateString = (value) => typeof value === 'string' && !isNaN(Number(new Date(value)));
19
+ export const customDateParser = (value) => Object.fromEntries(Object.entries(value).map(([key, value]) => {
20
+ if (isDateString(value)) {
21
+ return [key, new Date(value)];
22
+ }
23
+ if (Array.isArray(value) && value.some(isDateString)) {
24
+ return [key, value.map(element => (isDateString(element) ? new Date(element) : element))];
25
+ }
26
+ return [key, value];
27
+ }));
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Table",
7
- "version": "0.28.4",
7
+ "version": "0.28.6-preview-e840ab81.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -36,20 +36,20 @@
36
36
  "license": "Apache-2.0",
37
37
  "scripts": {},
38
38
  "dependencies": {
39
- "@snack-uikit/button": "0.19.7",
40
- "@snack-uikit/chips": "0.25.2",
41
- "@snack-uikit/icon-predefined": "0.7.3",
42
- "@snack-uikit/icons": "0.24.2",
43
- "@snack-uikit/info-block": "0.6.13",
44
- "@snack-uikit/list": "0.25.0",
45
- "@snack-uikit/pagination": "0.10.2",
46
- "@snack-uikit/scroll": "0.9.3",
47
- "@snack-uikit/skeleton": "0.6.2",
48
- "@snack-uikit/toggles": "0.13.5",
49
- "@snack-uikit/toolbar": "0.11.4",
50
- "@snack-uikit/truncate-string": "0.6.9",
51
- "@snack-uikit/typography": "0.8.4",
52
- "@snack-uikit/utils": "3.7.0",
39
+ "@snack-uikit/button": "0.19.8-preview-e840ab81.0",
40
+ "@snack-uikit/chips": "0.25.3-preview-e840ab81.0",
41
+ "@snack-uikit/icon-predefined": "0.7.4-preview-e840ab81.0",
42
+ "@snack-uikit/icons": "0.24.3-preview-e840ab81.0",
43
+ "@snack-uikit/info-block": "0.6.14-preview-e840ab81.0",
44
+ "@snack-uikit/list": "0.25.1-preview-e840ab81.0",
45
+ "@snack-uikit/pagination": "0.10.3-preview-e840ab81.0",
46
+ "@snack-uikit/scroll": "0.9.4-preview-e840ab81.0",
47
+ "@snack-uikit/skeleton": "0.6.3-preview-e840ab81.0",
48
+ "@snack-uikit/toggles": "0.13.6-preview-e840ab81.0",
49
+ "@snack-uikit/toolbar": "0.11.6-preview-e840ab81.0",
50
+ "@snack-uikit/truncate-string": "0.6.10-preview-e840ab81.0",
51
+ "@snack-uikit/typography": "0.8.5-preview-e840ab81.0",
52
+ "@snack-uikit/utils": "3.7.1-preview-e840ab81.0",
53
53
  "@tanstack/match-sorter-utils": "8.11.8",
54
54
  "@tanstack/react-table": "8.12.0",
55
55
  "classnames": "2.5.1",
@@ -61,5 +61,5 @@
61
61
  "peerDependencies": {
62
62
  "@snack-uikit/locale": "*"
63
63
  },
64
- "gitHead": "44591f943a79171320e8db3523ec38953ff101d8"
64
+ "gitHead": "1dc61e115ed2debadc06541b10d10df416d1d5e5"
65
65
  }
@@ -10,7 +10,7 @@ import { ServerTableProps } from '../types';
10
10
  import { DEFAULT_PAGINATION_LIMIT } from './constants';
11
11
  import { onSearchDebounced } from './utils';
12
12
 
13
- export function ServerTable<TData extends object, TFilters extends FiltersState>({
13
+ export function ServerTable<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({
14
14
  items,
15
15
  total = DEFAULT_PAGINATION_LIMIT,
16
16
  limit = DEFAULT_PAGINATION_LIMIT,
@@ -45,6 +45,7 @@ import { fuzzyFilter } from '../../utils';
45
45
  import { TableProps } from '../types';
46
46
  import { useLoadingTable, useStateControl } from './hooks';
47
47
  import { usePageReset } from './hooks/usePageReset';
48
+ import { useSaveTableSettings } from './hooks/useSaveTableSettings/useSaveTableSettings';
48
49
  import styles from './styles.module.scss';
49
50
  import {
50
51
  getColumnStyleVars,
@@ -96,6 +97,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
96
97
  enableFuzzySearch,
97
98
  savedState,
98
99
  expanding,
100
+ saveStateSettings,
99
101
  bulkActions: bulkActionsProp,
100
102
  ...rest
101
103
  }: TableProps<TData, TFilters>) {
@@ -104,7 +106,6 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
104
106
  rowSelectionProp,
105
107
  {},
106
108
  );
107
-
108
109
  const defaultPaginationState = useMemo(
109
110
  () => ({
110
111
  pageIndex: 0,
@@ -117,6 +118,15 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
117
118
  paginationProp,
118
119
  defaultPaginationState,
119
120
  );
121
+
122
+ const { patchedFilter, isSettingsLoaded } = useSaveTableSettings({
123
+ pagination: { state: pagination, handler: onPaginationChange },
124
+ search: { state: globalFilter, handler: onGlobalFilterChange },
125
+ sorting: { state: sorting, handler: onSortingChange },
126
+ filters: columnFilters,
127
+ options: saveStateSettings,
128
+ });
129
+
120
130
  const enableSelection = Boolean(rowSelectionProp?.enable);
121
131
 
122
132
  const tableColumns: ColumnDefinition<TData>[] = useMemo(() => {
@@ -366,7 +376,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
366
376
  className={cn(styles.wrapper, className)}
367
377
  {...extractSupportProps(rest)}
368
378
  >
369
- {showToolbar && (
379
+ {showToolbar && isSettingsLoaded && (
370
380
  <div className={styles.header}>
371
381
  <Toolbar
372
382
  search={
@@ -404,7 +414,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
404
414
  ) : undefined
405
415
  }
406
416
  moreActions={moreActions}
407
- filterRow={columnFilters}
417
+ filterRow={patchedFilter}
408
418
  data-test-id={TEST_IDS.toolbar}
409
419
  />
410
420
  </div>
@@ -0,0 +1 @@
1
+ export * from './useSaveTableSettings';
@@ -0,0 +1,108 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { Handler } from 'uncontrollable';
4
+
5
+ import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
6
+ import { FilterRow } from '@snack-uikit/toolbar/src/components/Toolbar/types';
7
+ import { FilterStateOptions, useSaveFilterState } from '@snack-uikit/utils';
8
+
9
+ import { customDateParser } from '../../../../utils';
10
+ import { validatePaging, validateSorting } from './vallidators';
11
+
12
+ type State<T> = {
13
+ state: T;
14
+ handler: Handler;
15
+ };
16
+
17
+ type Settings<TFilter> = {
18
+ filter?: TFilter;
19
+ pagination: PaginationState;
20
+ search: string;
21
+ sorting: SortingState;
22
+ };
23
+
24
+ type ValidationFn<TFilter> = (value: unknown) => value is Settings<TFilter>;
25
+
26
+ type TableSettings<TFilter extends FiltersState = Record<string, unknown>> = {
27
+ options?: FilterStateOptions<TFilter>;
28
+ filters?: ChipChoiceRowProps<TFilter>;
29
+ pagination: State<PaginationState>;
30
+ search: State<string>;
31
+ sorting: State<SortingState>;
32
+ };
33
+
34
+ export const useSaveTableSettings = <TFilter extends FiltersState = Record<string, unknown>>({
35
+ options,
36
+ filters,
37
+ pagination: { state: paginationState, handler: paginationHandler },
38
+ search: { state: searchState, handler: searchHandler },
39
+ sorting: { state: sortingState, handler: sortingHandler },
40
+ }: TableSettings<TFilter>) => {
41
+ const [filter, setFilter] = useState<TFilter | undefined>(filters?.value);
42
+ const [isSettingsLoaded, setIsSettingsLoaded] = useState<boolean>(false);
43
+ useEffect(() => {
44
+ setFilter(filters?.value);
45
+ }, [filters?.value]);
46
+
47
+ const settings = useMemo<Settings<TFilter>>(
48
+ () => ({
49
+ pagination: paginationState,
50
+ filter,
51
+ search: searchState,
52
+ sorting: sortingState,
53
+ }),
54
+ [paginationState, filter, searchState, sortingState],
55
+ );
56
+
57
+ const onFilterChange = useCallback(
58
+ (value: TFilter) => {
59
+ filters?.onChange && filters.onChange(value);
60
+ setFilter(value);
61
+ },
62
+ // eslint-disable-next-line
63
+ [filters?.onChange],
64
+ );
65
+
66
+ const validate = useCallback<ValidationFn<TFilter>>(
67
+ (data: unknown): data is Settings<TFilter> => {
68
+ const isPaginationValid = validatePaging((data as Settings<TFilter>)?.pagination);
69
+ const isSortingValid = validateSorting((data as Settings<TFilter>)?.sorting);
70
+ const isSearchValid = typeof (data as Settings<TFilter>)?.search === 'string';
71
+ const isFilterValid = Boolean(options?.validateData((data as Settings<TFilter>)?.filter));
72
+ return isPaginationValid && isSortingValid && isSearchValid && isFilterValid;
73
+ },
74
+ [options],
75
+ );
76
+
77
+ const onInitPage = useCallback((data: Settings<TFilter> | null) => {
78
+ if (!data) {
79
+ setIsSettingsLoaded(true);
80
+ return;
81
+ }
82
+ data.pagination && paginationHandler(data.pagination);
83
+ data.search && searchHandler(data.search);
84
+ data.sorting && sortingHandler(data.sorting);
85
+ data.filter && onFilterChange(customDateParser(data.filter));
86
+ setIsSettingsLoaded(true);
87
+ // eslint-disable-next-line
88
+ }, []);
89
+
90
+ const filterStateOptions = useMemo<FilterStateOptions<Settings<TFilter>> | undefined>(
91
+ () => (options ? { ...options, validateData: validate } : undefined),
92
+ [options, validate],
93
+ );
94
+ useSaveFilterState<Settings<TFilter>>({
95
+ filter: settings,
96
+ options: filterStateOptions,
97
+ onLoadFilter: onInitPage,
98
+ });
99
+
100
+ const patchedFilter = useMemo<FilterRow<TFilter> | undefined>(
101
+ () => (filters ? { ...filters, value: filter, onChange: onFilterChange } : undefined),
102
+ [filters, onFilterChange, filter],
103
+ );
104
+ return {
105
+ patchedFilter,
106
+ isSettingsLoaded,
107
+ };
108
+ };
@@ -0,0 +1,7 @@
1
+ import { PaginationState, SortingState } from '@tanstack/react-table';
2
+
3
+ export const validatePaging = (value: unknown): value is PaginationState =>
4
+ typeof (value as PaginationState)?.pageSize === 'number' && typeof (value as PaginationState)?.pageIndex === 'number';
5
+
6
+ export const validateSorting = (value: unknown): value is SortingState =>
7
+ (value as SortingState)?.every(column => typeof column?.id === 'string' && typeof column?.desc === 'boolean');
@@ -10,7 +10,7 @@ import { ReactNode, RefObject } from 'react';
10
10
 
11
11
  import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
12
12
  import { ToolbarProps } from '@snack-uikit/toolbar';
13
- import { WithSupportProps } from '@snack-uikit/utils';
13
+ import { FilterStateOptions, WithSupportProps } from '@snack-uikit/utils';
14
14
 
15
15
  import { EmptyStateProps, ExportButtonProps, RowClickHandler } from '../helperComponents';
16
16
  import { TreeColumnDefinitionProps } from '../helperComponents/Cells/TreeCell';
@@ -20,7 +20,10 @@ type BulkAction = Omit<NonNullable<ToolbarProps<Record<string, string>>['bulkAct
20
20
  onClick?(selectionState: RowSelectionState, resetRowSelection: (defaultState?: boolean) => void): void;
21
21
  };
22
22
 
23
- export type TableProps<TData extends object, TFilters extends FiltersState> = WithSupportProps<{
23
+ export type TableProps<
24
+ TData extends object,
25
+ TFilters extends FiltersState = Record<string, unknown>,
26
+ > = WithSupportProps<{
24
27
  /** Данные для отрисовки */
25
28
  data: TData[];
26
29
  /** Определение внешнего вида и функционала колонок */
@@ -77,6 +80,9 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
77
80
  onChange?(value: string): void;
78
81
  };
79
82
 
83
+ /** Настройки для сохранения состояний таблицы в query params и local storage*/
84
+ saveStateSettings: FilterStateOptions<TFilters>;
85
+
80
86
  /** Включить нечеткий поиск */
81
87
  enableFuzzySearch?: boolean;
82
88
 
@@ -175,7 +181,7 @@ export type TableProps<TData extends object, TFilters extends FiltersState> = Wi
175
181
  };
176
182
  }>;
177
183
 
178
- export type ServerTableProps<TData extends object, TFilters extends FiltersState> = Omit<
184
+ export type ServerTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = Omit<
179
185
  TableProps<TData, TFilters>,
180
186
  'pageSize' | 'pageCount' | 'pagination' | 'search' | 'data'
181
187
  > & {
@@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react';
4
4
  import { ChipChoice } from '@snack-uikit/chips';
5
5
  import { useLocale } from '@snack-uikit/locale';
6
6
  import { Pagination } from '@snack-uikit/pagination';
7
+ import { isBrowser } from '@snack-uikit/utils';
7
8
 
8
9
  import styles from './styles.module.scss';
9
10
 
@@ -72,6 +73,7 @@ export function TablePagination<TData>({
72
73
  value={String(tablePaginationState.pageSize)}
73
74
  onChange={handleRowsVolumeOnChange}
74
75
  placement='top-end'
76
+ onClick={() => isBrowser() && window.location.reload()}
75
77
  options={options}
76
78
  label={optionsLabel}
77
79
  widthStrategy='auto'
package/src/utils.ts CHANGED
@@ -22,3 +22,17 @@ export const preciseFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
22
22
 
23
23
  return itemRank.passed;
24
24
  };
25
+
26
+ const isDateString = (value: unknown): value is string => typeof value === 'string' && !isNaN(Number(new Date(value)));
27
+ export const customDateParser = <T>(value: Record<string, unknown>): T =>
28
+ Object.fromEntries(
29
+ Object.entries(value).map(([key, value]) => {
30
+ if (isDateString(value)) {
31
+ return [key, new Date(value)];
32
+ }
33
+ if (Array.isArray(value) && value.some(isDateString)) {
34
+ return [key, value.map(element => (isDateString(element) ? new Date(element) : element))];
35
+ }
36
+ return [key, value];
37
+ }),
38
+ ) as T;