@cloud-ru/uikit-product-mobile-table 0.14.0 → 0.15.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 (53) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/components/MobileTable.d.ts +2 -2
  3. package/dist/cjs/components/MobileTable.js +82 -6
  4. package/dist/cjs/components/index.d.ts +1 -0
  5. package/dist/cjs/components/index.js +1 -0
  6. package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.d.ts +7 -0
  7. package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.js +18 -0
  8. package/dist/cjs/helperComponents/ColumnsSettings/index.d.ts +1 -0
  9. package/dist/cjs/helperComponents/ColumnsSettings/index.js +17 -0
  10. package/dist/cjs/helperComponents/ColumnsSettings/styles.module.css +3 -0
  11. package/dist/cjs/helperComponents/TableSorting/TableSorting.d.ts +10 -0
  12. package/dist/cjs/helperComponents/TableSorting/TableSorting.js +29 -0
  13. package/dist/cjs/helperComponents/TableSorting/index.d.ts +1 -0
  14. package/dist/cjs/helperComponents/TableSorting/index.js +17 -0
  15. package/dist/cjs/helperComponents/TableSorting/styles.module.css +3 -0
  16. package/dist/cjs/helperComponents/TableSorting/useTableSorting.d.ts +23 -0
  17. package/dist/cjs/helperComponents/TableSorting/useTableSorting.js +189 -0
  18. package/dist/cjs/helperComponents/TableSorting/utils.d.ts +9 -0
  19. package/dist/cjs/helperComponents/TableSorting/utils.js +75 -0
  20. package/dist/cjs/helperComponents/index.d.ts +3 -1
  21. package/dist/cjs/helperComponents/index.js +3 -1
  22. package/dist/esm/components/MobileTable.d.ts +2 -2
  23. package/dist/esm/components/MobileTable.js +84 -8
  24. package/dist/esm/components/index.d.ts +1 -0
  25. package/dist/esm/components/index.js +1 -0
  26. package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.d.ts +7 -0
  27. package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.js +12 -0
  28. package/dist/esm/helperComponents/ColumnsSettings/index.d.ts +1 -0
  29. package/dist/esm/helperComponents/ColumnsSettings/index.js +1 -0
  30. package/dist/esm/helperComponents/ColumnsSettings/styles.module.css +3 -0
  31. package/dist/esm/helperComponents/TableSorting/TableSorting.d.ts +10 -0
  32. package/dist/esm/helperComponents/TableSorting/TableSorting.js +26 -0
  33. package/dist/esm/helperComponents/TableSorting/index.d.ts +1 -0
  34. package/dist/esm/helperComponents/TableSorting/index.js +1 -0
  35. package/dist/esm/helperComponents/TableSorting/styles.module.css +3 -0
  36. package/dist/esm/helperComponents/TableSorting/useTableSorting.d.ts +23 -0
  37. package/dist/esm/helperComponents/TableSorting/useTableSorting.js +183 -0
  38. package/dist/esm/helperComponents/TableSorting/utils.d.ts +9 -0
  39. package/dist/esm/helperComponents/TableSorting/utils.js +70 -0
  40. package/dist/esm/helperComponents/index.d.ts +3 -1
  41. package/dist/esm/helperComponents/index.js +3 -1
  42. package/package.json +6 -3
  43. package/src/components/MobileTable.tsx +143 -9
  44. package/src/components/index.ts +1 -0
  45. package/src/helperComponents/ColumnsSettings/ColumnsSettings.tsx +28 -0
  46. package/src/helperComponents/ColumnsSettings/index.ts +1 -0
  47. package/src/helperComponents/ColumnsSettings/styles.module.scss +5 -0
  48. package/src/helperComponents/TableSorting/TableSorting.tsx +60 -0
  49. package/src/helperComponents/TableSorting/index.ts +1 -0
  50. package/src/helperComponents/TableSorting/styles.module.scss +9 -0
  51. package/src/helperComponents/TableSorting/useTableSorting.tsx +248 -0
  52. package/src/helperComponents/TableSorting/utils.ts +89 -0
  53. package/src/helperComponents/index.ts +3 -1
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ColumnsSettings"), exports);
17
18
  __exportStar(require("./RowActionsCell"), exports);
18
19
  __exportStar(require("./StatusColumnDef"), exports);
19
20
  __exportStar(require("./TableCard"), exports);
20
- __exportStar(require("./TablePagination"), exports);
21
21
  __exportStar(require("./TableEmptyState"), exports);
22
+ __exportStar(require("./TablePagination"), exports);
23
+ __exportStar(require("./TableSorting"), exports);
@@ -1,12 +1,12 @@
1
1
  import { FiltersState, MobileChipChoiceRowProps } from '@cloud-ru/uikit-product-mobile-chips';
2
2
  import { WithSupportProps } from '@cloud-ru/uikit-product-utils';
3
3
  import { TableProps } from '@snack-uikit/table';
4
- export type MobileTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = Pick<TableProps<TData, TFilters>, 'data' | 'columnDefinitions' | 'suppressPagination' | 'suppressToolbar' | 'suppressSearch' | 'search' | 'onRefresh' | 'toolbarAfter' | 'moreActions' | 'className' | 'enableFuzzySearch' | 'loading' | 'dataError' | 'dataFiltered' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'sorting' | 'pagination' | 'pageCount' | 'manualFiltering' | 'manualPagination' | 'manualSorting' | 'getRowId' | 'rowSelection' | 'bulkActions'> & WithSupportProps<{
4
+ export type MobileTableProps<TData extends object, TFilters extends FiltersState = Record<string, unknown>> = Pick<TableProps<TData, TFilters>, 'data' | 'columnDefinitions' | 'suppressPagination' | 'suppressToolbar' | 'suppressSearch' | 'search' | 'onRefresh' | 'moreActions' | 'className' | 'enableFuzzySearch' | 'loading' | 'dataError' | 'dataFiltered' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'sorting' | 'pagination' | 'pageCount' | 'manualFiltering' | 'manualPagination' | 'manualSorting' | 'getRowId' | 'rowSelection' | 'bulkActions' | 'columnsSettings' | 'savedState' | 'autoResetPageIndex' | 'toolbarAfter'> & WithSupportProps<{
5
5
  headlineId?: string;
6
6
  headerBackground?: 'default' | '1-level' | '2-level';
7
7
  columnFilters?: MobileChipChoiceRowProps<FiltersState>;
8
8
  }>;
9
- export declare function MobileTable<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, columnDefinitions, headlineId, suppressPagination, suppressToolbar, suppressSearch, enableFuzzySearch, search, onRefresh, toolbarAfter, moreActions, columnFilters, className, headerBackground, noDataState, noResultsState, errorDataState, loading, dataError, dataFiltered, pagination: paginationProp, pageCount, sorting: sortingProp, manualSorting, manualPagination, manualFiltering, getRowId, rowSelection: rowSelectionProp, bulkActions: bulkActionsProp, ...rest }: MobileTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
9
+ export declare function MobileTable<TData extends object, TFilters extends FiltersState = Record<string, unknown>>({ data, columnDefinitions, headlineId, suppressPagination, suppressToolbar, suppressSearch, enableFuzzySearch, search, onRefresh, toolbarAfter, moreActions, columnFilters, className, headerBackground, noDataState, noResultsState, errorDataState, loading, dataError, dataFiltered, pagination: paginationProp, pageCount, sorting: sortingProp, manualSorting, manualPagination, manualFiltering, autoResetPageIndex, getRowId, rowSelection: rowSelectionProp, bulkActions: bulkActionsProp, columnsSettings: columnsSettingsProp, savedState, ...rest }: MobileTableProps<TData, TFilters>): import("react/jsx-runtime").JSX.Element;
10
10
  export declare namespace MobileTable {
11
11
  var getRowActionsColumnDef: typeof import("../helperComponents").getRowActionsColumnDef;
12
12
  var getStatusColumnDef: typeof import("../helperComponents").getStatusColumnDef;
@@ -12,18 +12,20 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
14
14
  import cn from 'classnames';
15
- import { useCallback, useMemo } from 'react';
15
+ import { useCallback, useEffect, useMemo } from 'react';
16
16
  import { useLocale } from '@cloud-ru/uikit-product-locale';
17
17
  import { MobileToolbar } from '@cloud-ru/uikit-product-mobile-toolbar';
18
18
  import { extractSupportProps } from '@cloud-ru/uikit-product-utils';
19
19
  import { SkeletonContextProvider } from '@snack-uikit/skeleton';
20
- import { getRowActionsColumnDef, getStatusColumnDef, TableCard, TableEmptyState, TablePagination, useEmptyState, } from '../helperComponents';
20
+ import { getPinnedGroups, useColumnOrderByDrag, useColumnSettings, usePageReset, } from '@snack-uikit/table';
21
+ import { getRowActionsColumnDef, getStatusColumnDef, TableCard, TableEmptyState, TablePagination, TableSorting, useEmptyState, } from '../helperComponents';
22
+ import { ColumnsSettings } from '../helperComponents/ColumnsSettings';
21
23
  import { DEFAULT_PAGE_SIZE } from './constants';
22
24
  import { useLoadingTable, useStateControl } from './hooks';
23
25
  import styles from './styles.module.css';
24
26
  import { fuzzyFilter } from './utils';
25
27
  export function MobileTable(_a) {
26
- var { data, columnDefinitions, headlineId, suppressPagination = false, suppressToolbar = false, suppressSearch = false, enableFuzzySearch = false, search, onRefresh, toolbarAfter, moreActions, columnFilters, className, headerBackground = 'default', noDataState, noResultsState, errorDataState, loading, dataError, dataFiltered, pagination: paginationProp, pageCount, sorting: sortingProp, manualSorting = false, manualPagination = false, manualFiltering = false, getRowId, rowSelection: rowSelectionProp, bulkActions: bulkActionsProp } = _a, rest = __rest(_a, ["data", "columnDefinitions", "headlineId", "suppressPagination", "suppressToolbar", "suppressSearch", "enableFuzzySearch", "search", "onRefresh", "toolbarAfter", "moreActions", "columnFilters", "className", "headerBackground", "noDataState", "noResultsState", "errorDataState", "loading", "dataError", "dataFiltered", "pagination", "pageCount", "sorting", "manualSorting", "manualPagination", "manualFiltering", "getRowId", "rowSelection", "bulkActions"]);
28
+ var { data, columnDefinitions, headlineId, suppressPagination = false, suppressToolbar = false, suppressSearch = false, enableFuzzySearch = false, search, onRefresh, toolbarAfter, moreActions, columnFilters, className, headerBackground = 'default', noDataState, noResultsState, errorDataState, loading, dataError, dataFiltered, pagination: paginationProp, pageCount, sorting: sortingProp, manualSorting = false, manualPagination = false, manualFiltering = false, autoResetPageIndex = false, getRowId, rowSelection: rowSelectionProp, bulkActions: bulkActionsProp, columnsSettings: columnsSettingsProp, savedState } = _a, rest = __rest(_a, ["data", "columnDefinitions", "headlineId", "suppressPagination", "suppressToolbar", "suppressSearch", "enableFuzzySearch", "search", "onRefresh", "toolbarAfter", "moreActions", "columnFilters", "className", "headerBackground", "noDataState", "noResultsState", "errorDataState", "loading", "dataError", "dataFiltered", "pagination", "pageCount", "sorting", "manualSorting", "manualPagination", "manualFiltering", "autoResetPageIndex", "getRowId", "rowSelection", "bulkActions", "columnsSettings", "savedState"]);
27
29
  const defaultPaginationState = useMemo(() => ({ pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE }), []);
28
30
  const { state: sorting, onStateChange: onSortingChange } = useStateControl(sortingProp, []);
29
31
  const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl(search, '');
@@ -39,23 +41,88 @@ export function MobileTable(_a) {
39
41
  }
40
42
  return isParentSelected && isCurrentRowSelected;
41
43
  }, [rowSelectionProp]);
44
+ const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
45
+ const pinnedGroups = useMemo(() => getPinnedGroups(columnDefinitions), [columnDefinitions]);
46
+ const { enabledColumns, setEnabledColumns, getColumnsSettings, enabledTableColumns, enabledColumnsDefinitions, areColumnsSettingsEnabled, } = useColumnSettings({
47
+ columnDefinitions,
48
+ pinnedGroups,
49
+ savedState,
50
+ columnsSettings: columnsSettingsProp,
51
+ rowSelection: undefined,
52
+ enableSelectPinned: false,
53
+ expanding: undefined,
54
+ });
55
+ const { columnOrder, setColumnOrder, enableColumnsOrderSortByDrag } = useColumnOrderByDrag({
56
+ tableColumns: columnDefinitions,
57
+ savedState,
58
+ columnSettings: columnsSettingsProp,
59
+ });
60
+ const columnsSettings = useMemo(() => getColumnsSettings(columnOrder), [columnOrder, getColumnsSettings]);
61
+ // Получаем список колонок с mode: 'hidden', которые всегда доступны для сортировки
62
+ const hiddenColumnsBySettings = useMemo(() => {
63
+ if (!areColumnsSettingsEnabled)
64
+ return new Set();
65
+ const hidden = new Set();
66
+ columnDefinitions.forEach(colDef => {
67
+ var _a;
68
+ let columnId;
69
+ if ('id' in colDef && colDef.id) {
70
+ columnId = colDef.id;
71
+ }
72
+ else if ('accessorKey' in colDef && colDef.accessorKey) {
73
+ columnId = String(colDef.accessorKey);
74
+ }
75
+ if (columnId) {
76
+ const colDefWithSettings = colDef;
77
+ if (((_a = colDefWithSettings.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) === 'hidden') {
78
+ hidden.add(columnId);
79
+ }
80
+ }
81
+ });
82
+ return hidden;
83
+ }, [areColumnsSettingsEnabled, columnDefinitions]);
84
+ // Сбрасываем сортировку, если колонка с активной сортировкой была скрыта
85
+ // Колонки с mode: 'hidden' всегда доступны для сортировки
86
+ useEffect(() => {
87
+ var _a;
88
+ if (areColumnsSettingsEnabled && enabledColumns && sorting.length > 0) {
89
+ const activeSortColumnId = (_a = sorting[0]) === null || _a === void 0 ? void 0 : _a.id;
90
+ if (activeSortColumnId) {
91
+ const isHiddenColumn = hiddenColumnsBySettings.has(activeSortColumnId);
92
+ const isEnabledColumn = enabledColumns.includes(activeSortColumnId);
93
+ // Сбрасываем сортировку только если колонка не скрыта через mode: 'hidden' и не включена
94
+ if (!isHiddenColumn && !isEnabledColumn) {
95
+ // Колонка с активной сортировкой скрыта - сбрасываем сортировку
96
+ onSortingChange([]);
97
+ }
98
+ }
99
+ }
100
+ }, [areColumnsSettingsEnabled, enabledColumns, sorting, onSortingChange, hiddenColumnsBySettings]);
42
101
  const table = useReactTable({
43
102
  data,
44
- columns: columnDefinitions,
103
+ columns: enabledTableColumns,
45
104
  getCoreRowModel: getCoreRowModel(),
46
105
  getPaginationRowModel: getPaginationRowModel(),
47
106
  getFilteredRowModel: getFilteredRowModel(),
48
107
  getSortedRowModel: getSortedRowModel(),
49
- state: { pagination, globalFilter, sorting, rowSelection },
108
+ state: {
109
+ pagination,
110
+ globalFilter,
111
+ sorting,
112
+ rowSelection,
113
+ columnOrder: enableColumnsOrderSortByDrag ? columnOrder : undefined,
114
+ },
50
115
  pageCount,
51
116
  onPaginationChange,
52
117
  onSortingChange,
118
+ onColumnOrderChange: enableColumnsOrderSortByDrag ? setColumnOrder : undefined,
53
119
  globalFilterFn: enableFuzzySearch ? fuzzyFilter : 'includesString',
54
120
  enableFilters: true,
55
121
  manualSorting,
56
122
  manualPagination,
57
123
  manualFiltering,
58
124
  getRowId,
125
+ autoResetPageIndex,
59
126
  onRowSelectionChange,
60
127
  enableGrouping: true,
61
128
  enableRowSelection,
@@ -64,7 +131,7 @@ export function MobileTable(_a) {
64
131
  });
65
132
  const { loadingTable } = useLoadingTable({
66
133
  pageSize: DEFAULT_PAGE_SIZE,
67
- columnDefinitions,
134
+ columnDefinitions: enabledColumnsDefinitions,
68
135
  });
69
136
  const tableRows = table.getRowModel().rows;
70
137
  const loadingTableRows = loadingTable.getRowModel().rows;
@@ -74,7 +141,6 @@ export function MobileTable(_a) {
74
141
  table.resetRowSelection();
75
142
  onRefresh === null || onRefresh === void 0 ? void 0 : onRefresh();
76
143
  }, [onRefresh, table]);
77
- const enableSelection = Boolean(rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.enable);
78
144
  const bulkActions = useMemo(() => enableSelection
79
145
  ? bulkActionsProp === null || bulkActionsProp === void 0 ? void 0 : bulkActionsProp.map(action => (Object.assign(Object.assign({}, action), { onClick: () => { var _a; return (_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action, table.getState().rowSelection, table.resetRowSelection); } })))
80
146
  : undefined, [bulkActionsProp, enableSelection, table]);
@@ -98,6 +164,16 @@ export function MobileTable(_a) {
98
164
  const selectionMode = (rowSelectionProp === null || rowSelectionProp === void 0 ? void 0 : rowSelectionProp.multiRow)
99
165
  ? 'multiple'
100
166
  : 'single';
167
+ const hasSortableColumns = useMemo(() => columnDefinitions.some(column => column.enableSorting !== false), [columnDefinitions]);
168
+ const shouldShowSorting = useMemo(() => Boolean(sortingProp) || hasSortableColumns, [sortingProp, hasSortableColumns]);
169
+ const tableFilteredRows = table.getFilteredRowModel().rows;
170
+ usePageReset({
171
+ manualPagination,
172
+ maximumAvailablePage: pageCount || tableFilteredRows.length / pagination.pageSize,
173
+ pagination,
174
+ onPaginationChange,
175
+ autoResetPageIndex,
176
+ });
101
177
  return (_jsxs("div", Object.assign({ className: cn(styles.tableWrapper, className) }, extractSupportProps(rest), { children: [(!suppressToolbar || columnFilters) && (_jsx("div", { className: styles.header, "data-background": headerBackground, children: _jsx(MobileToolbar, { search: suppressSearch
102
178
  ? undefined
103
179
  : {
@@ -105,7 +181,7 @@ export function MobileTable(_a) {
105
181
  onChange: onGlobalFilterChange,
106
182
  loading: search === null || search === void 0 ? void 0 : search.loading,
107
183
  placeholder: (search === null || search === void 0 ? void 0 : search.placeholder) || t('searchPlaceholder'),
108
- }, onRefresh: onRefresh ? handleOnRefresh : undefined, outline: true, filterRow: columnFilters, after: toolbarAfter, moreActions: moreActions, bulkActions: bulkActions, selectedCount: table.getSelectedRowModel().rows.length, selectionMode: selectionMode, onCheck: enableSelection ? handleOnToolbarCheck : undefined, checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected() }) })), _jsx("div", { className: styles.table, children: loading ? (_jsx(SkeletonContextProvider, { loading: true, children: loadingTableRows.map((row, index) => (_jsx(TableCard, { headlineId: headlineId, row: row, table: loadingTable, selection: 'none' }, index))) })) : (_jsxs(_Fragment, { children: [tableRows.map((row, index) => (_jsx(TableCard, { headlineId: headlineId, row: row, table: table, selection: enableSelection ? selectionMode : 'none' }, index))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length })] })) }), !suppressPagination && _jsx(TablePagination, { table: table })] })));
184
+ }, onRefresh: onRefresh ? handleOnRefresh : undefined, outline: true, filterRow: columnFilters, after: toolbarAfter || shouldShowSorting || (areColumnsSettingsEnabled && columnsSettings) ? (_jsxs(_Fragment, { children: [toolbarAfter, shouldShowSorting && (_jsx(TableSorting, { table: table, columnDefinitions: columnDefinitions, enabledColumns: areColumnsSettingsEnabled ? enabledColumns : undefined, areColumnsSettingsEnabled: areColumnsSettingsEnabled })), areColumnsSettingsEnabled && columnsSettings && (_jsx(ColumnsSettings, { columnsSettings: columnsSettings, enabledColumns: enabledColumns, setEnabledColumns: setEnabledColumns }))] })) : undefined, moreActions: moreActions, bulkActions: bulkActions, selectedCount: table.getSelectedRowModel().rows.length, selectionMode: selectionMode, onCheck: enableSelection ? handleOnToolbarCheck : undefined, checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected() }) })), _jsx("div", { className: styles.table, children: loading ? (_jsx(SkeletonContextProvider, { loading: true, children: loadingTableRows.map((row, index) => (_jsx(TableCard, { headlineId: headlineId, row: row, table: loadingTable, selection: 'none' }, index))) })) : (_jsxs(_Fragment, { children: [tableRows.map((row, index) => (_jsx(TableCard, { headlineId: headlineId, row: row, table: table, selection: enableSelection ? selectionMode : 'none' }, index))), _jsx(TableEmptyState, { emptyStates: emptyStates, dataError: dataError, dataFiltered: dataFiltered || Boolean(table.getState().globalFilter), tableRowsLength: tableRows.length })] })) }), !suppressPagination && _jsx(TablePagination, { table: table })] })));
109
185
  }
110
186
  MobileTable.getRowActionsColumnDef = getRowActionsColumnDef;
111
187
  MobileTable.getStatusColumnDef = getStatusColumnDef;
@@ -1,3 +1,4 @@
1
1
  export * from './AdaptiveTable';
2
2
  export * from './AdaptiveServerTable';
3
3
  export * from './MobileTable';
4
+ export * from '../helperComponents';
@@ -1,3 +1,4 @@
1
1
  export * from './AdaptiveTable';
2
2
  export * from './AdaptiveServerTable';
3
3
  export * from './MobileTable';
4
+ export * from '../helperComponents';
@@ -0,0 +1,7 @@
1
+ import { GroupSelectItemProps } from '@snack-uikit/list';
2
+ export type ColumnsSettingsProps = {
3
+ enabledColumns: string[];
4
+ setEnabledColumns(enabledColumns: string[]): void;
5
+ columnsSettings: [GroupSelectItemProps];
6
+ };
7
+ export declare function ColumnsSettings({ columnsSettings, enabledColumns, setEnabledColumns }: ColumnsSettingsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MobileDroplist } from '@cloud-ru/uikit-product-mobile-dropdown';
3
+ import { ButtonFunction } from '@snack-uikit/button';
4
+ import { FunctionSettingsSVG } from '@snack-uikit/icons';
5
+ import styles from './styles.module.css';
6
+ export function ColumnsSettings({ columnsSettings, enabledColumns, setEnabledColumns }) {
7
+ return (_jsx(MobileDroplist, { className: styles.columnsSettings, items: columnsSettings, selection: {
8
+ value: enabledColumns,
9
+ onChange: setEnabledColumns,
10
+ mode: 'multiple',
11
+ }, "data-test-id": 'table__column-settings-droplist', children: _jsx(ButtonFunction, { size: 'm', "data-test-id": 'table__column-settings', icon: _jsx(FunctionSettingsSVG, {}) }) }));
12
+ }
@@ -0,0 +1 @@
1
+ export * from './ColumnsSettings';
@@ -0,0 +1 @@
1
+ export * from './ColumnsSettings';
@@ -0,0 +1,3 @@
1
+ .columnsSettings{
2
+ min-width:256px;
3
+ }
@@ -0,0 +1,10 @@
1
+ import { SortingState, Table } from '@tanstack/react-table';
2
+ import { ColumnDefinition } from '@snack-uikit/table';
3
+ export type TableSortingProps<TData extends object> = {
4
+ table: Table<TData>;
5
+ sorting?: SortingState;
6
+ columnDefinitions: ColumnDefinition<TData>[];
7
+ enabledColumns?: string[];
8
+ areColumnsSettingsEnabled?: boolean;
9
+ };
10
+ export declare function TableSorting<TData extends object>({ table, sorting, columnDefinitions, enabledColumns, areColumnsSettingsEnabled, }: TableSortingProps<TData>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { ArrowDownSVG, ArrowUpSVG, SortSVG } from '@cloud-ru/uikit-product-icons';
4
+ import { MobileDroplist } from '@cloud-ru/uikit-product-mobile-dropdown';
5
+ import { ButtonFunction } from '@snack-uikit/button';
6
+ import { useTableSorting } from './useTableSorting';
7
+ export function TableSorting({ table, sorting, columnDefinitions, enabledColumns, areColumnsSettingsEnabled = false, }) {
8
+ const [open, setOpen] = useState(false);
9
+ const { items, pinBottom, selection, currentSort, selectedSortId, handleClearSort } = useTableSorting({
10
+ table,
11
+ sorting,
12
+ columnDefinitions,
13
+ enabledColumns,
14
+ areColumnsSettingsEnabled,
15
+ });
16
+ const handleClear = () => {
17
+ handleClearSort();
18
+ setOpen(false);
19
+ };
20
+ const clearItem = (pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom[0]) ? [Object.assign(Object.assign({}, pinBottom[0]), { onClick: handleClear })] : undefined;
21
+ let SortIcon = SortSVG;
22
+ if (currentSort) {
23
+ SortIcon = currentSort.desc ? ArrowDownSVG : ArrowUpSVG;
24
+ }
25
+ return (_jsx(MobileDroplist, { items: items, selection: selection, virtualized: items.length > 10, pinBottom: clearItem, open: open, onOpenChange: setOpen, children: _jsx(ButtonFunction, { size: 'm', icon: _jsx(SortIcon, {}), appearance: selectedSortId ? 'primary' : 'neutral' }) }));
26
+ }
@@ -0,0 +1 @@
1
+ export * from './TableSorting';
@@ -0,0 +1 @@
1
+ export * from './TableSorting';
@@ -0,0 +1,3 @@
1
+ .clearSortItem[data-disabled=true] svg{
2
+ color:var(--sys-neutral-text-disabled, #aaaebd);
3
+ }
@@ -0,0 +1,23 @@
1
+ import { SortingState, Table } from '@tanstack/react-table';
2
+ import { MobileDroplistProps, SelectionSingleState } from '@cloud-ru/uikit-product-mobile-dropdown';
3
+ import { ColumnDefinition } from '@snack-uikit/table';
4
+ type UseTableSortingReturn = {
5
+ items: MobileDroplistProps['items'];
6
+ pinBottom: MobileDroplistProps['items'];
7
+ selection?: SelectionSingleState;
8
+ currentSort: {
9
+ id: string;
10
+ label: string;
11
+ desc: boolean;
12
+ } | null;
13
+ selectedSortId?: string;
14
+ handleClearSort(): void;
15
+ };
16
+ export declare function useTableSorting<TData extends object>({ table, sorting: sortingProp, columnDefinitions, enabledColumns, areColumnsSettingsEnabled, }: {
17
+ table: Table<TData>;
18
+ sorting?: SortingState;
19
+ columnDefinitions: ColumnDefinition<TData>[];
20
+ enabledColumns?: string[];
21
+ areColumnsSettingsEnabled?: boolean;
22
+ }): UseTableSortingReturn;
23
+ export {};
@@ -0,0 +1,183 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useMemo } from 'react';
3
+ import { ArrowDownSVG, ArrowUpSVG, UpdateSVG } from '@cloud-ru/uikit-product-icons';
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import styles from './styles.module.css';
6
+ import { createColumnDefMap, getHeaderLabel, groupHeadersByPinned } from './utils';
7
+ export function useTableSorting({ table, sorting: sortingProp, columnDefinitions, enabledColumns, areColumnsSettingsEnabled = false, }) {
8
+ const { t } = useLocale('Table');
9
+ const sorting = sortingProp !== null && sortingProp !== void 0 ? sortingProp : table.getState().sorting;
10
+ const columnDefMap = useMemo(() => createColumnDefMap(columnDefinitions), [columnDefinitions]);
11
+ const hiddenColumnsBySettings = useMemo(() => {
12
+ if (!areColumnsSettingsEnabled)
13
+ return new Set();
14
+ const hidden = new Set();
15
+ columnDefMap.forEach((colDef, columnId) => {
16
+ var _a;
17
+ const colDefWithSettings = colDef;
18
+ if (((_a = colDefWithSettings.columnSettings) === null || _a === void 0 ? void 0 : _a.mode) === 'hidden') {
19
+ hidden.add(columnId);
20
+ }
21
+ });
22
+ return hidden;
23
+ }, [areColumnsSettingsEnabled, columnDefMap]);
24
+ const sortableHeaders = useMemo(() => {
25
+ let headers = table
26
+ .getFlatHeaders()
27
+ .filter(header => header.column.getCanSort() && header.id !== 'select' && header.id !== 'actions');
28
+ if (areColumnsSettingsEnabled && enabledColumns) {
29
+ headers = headers.filter(header => hiddenColumnsBySettings.has(header.id) || enabledColumns.includes(header.id));
30
+ }
31
+ return headers;
32
+ }, [table, areColumnsSettingsEnabled, enabledColumns, hiddenColumnsBySettings]);
33
+ const currentSort = useMemo(() => {
34
+ if (sorting.length === 0)
35
+ return null;
36
+ const firstSort = sorting[0];
37
+ if (areColumnsSettingsEnabled && enabledColumns) {
38
+ const isHiddenColumn = hiddenColumnsBySettings.has(firstSort.id);
39
+ const isEnabledColumn = enabledColumns.includes(firstSort.id);
40
+ if (!isHiddenColumn && !isEnabledColumn) {
41
+ return null;
42
+ }
43
+ }
44
+ const header = table.getFlatHeaders().find(h => h.id === firstSort.id);
45
+ if (!header)
46
+ return null;
47
+ const headerLabel = getHeaderLabel(header);
48
+ return {
49
+ id: firstSort.id,
50
+ label: headerLabel || firstSort.id,
51
+ desc: firstSort.desc,
52
+ };
53
+ }, [sorting, table, areColumnsSettingsEnabled, enabledColumns, hiddenColumnsBySettings]);
54
+ const handleColumnSortToggle = useCallback((columnId) => {
55
+ const currentSorting = table.getState().sorting;
56
+ const currentColumnSort = currentSorting.find(s => s.id === columnId);
57
+ let newSorting;
58
+ if (!currentColumnSort) {
59
+ newSorting = [{ id: columnId, desc: false }];
60
+ }
61
+ else if (!currentColumnSort.desc) {
62
+ newSorting = [{ id: columnId, desc: true }];
63
+ }
64
+ else {
65
+ newSorting = [];
66
+ }
67
+ table.setSorting(newSorting);
68
+ }, [table]);
69
+ const handleClearSort = useCallback(() => {
70
+ table.setSorting([]);
71
+ }, [table]);
72
+ const selectedSortId = useMemo(() => {
73
+ if (sorting.length === 0)
74
+ return undefined;
75
+ const firstSort = sorting[0];
76
+ if (areColumnsSettingsEnabled && enabledColumns) {
77
+ const isHiddenColumn = hiddenColumnsBySettings.has(firstSort.id);
78
+ const isEnabledColumn = enabledColumns.includes(firstSort.id);
79
+ if (!isHiddenColumn && !isEnabledColumn) {
80
+ return undefined;
81
+ }
82
+ }
83
+ return `sort-${firstSort.id}`;
84
+ }, [sorting, areColumnsSettingsEnabled, enabledColumns, hiddenColumnsBySettings]);
85
+ const createSortItem = useCallback((header) => {
86
+ const columnId = header.id;
87
+ const currentColumnSort = sorting.find(s => s.id === columnId);
88
+ const isAsc = currentColumnSort && !currentColumnSort.desc;
89
+ const isDesc = currentColumnSort && currentColumnSort.desc;
90
+ const headerLabel = getHeaderLabel(header) || columnId;
91
+ let SortIcon = undefined;
92
+ if (isAsc) {
93
+ SortIcon = _jsx(ArrowUpSVG, {});
94
+ }
95
+ else if (isDesc) {
96
+ SortIcon = _jsx(ArrowDownSVG, {});
97
+ }
98
+ return {
99
+ id: `sort-${columnId}`,
100
+ content: {
101
+ option: headerLabel,
102
+ },
103
+ afterContent: SortIcon,
104
+ onClick: () => handleColumnSortToggle(columnId),
105
+ };
106
+ }, [sorting, handleColumnSortToggle]);
107
+ const groupSortableHeadersByPinned = useCallback(() => groupHeadersByPinned(sortableHeaders, columnDefMap), [sortableHeaders, columnDefMap]);
108
+ const { items, pinBottom } = useMemo(() => {
109
+ const { leftHeaders, unpinnedHeaders, rightHeaders } = groupSortableHeadersByPinned();
110
+ const groups = [];
111
+ if (leftHeaders.length > 0) {
112
+ groups.push({
113
+ type: 'group',
114
+ divider: false,
115
+ items: leftHeaders.map(createSortItem),
116
+ });
117
+ }
118
+ if (unpinnedHeaders.length > 0) {
119
+ groups.push({
120
+ type: 'group',
121
+ divider: leftHeaders.length > 0 || rightHeaders.length > 0,
122
+ items: unpinnedHeaders.map(createSortItem),
123
+ });
124
+ }
125
+ if (rightHeaders.length > 0) {
126
+ groups.push({
127
+ type: 'group',
128
+ divider: leftHeaders.length > 0 || unpinnedHeaders.length > 0,
129
+ items: rightHeaders.map(createSortItem),
130
+ });
131
+ }
132
+ const clearItem = [
133
+ {
134
+ id: 'snack-internal-clear-id',
135
+ content: {
136
+ option: t('clearSort'),
137
+ },
138
+ afterContent: _jsx(UpdateSVG, {}),
139
+ onClick: handleClearSort,
140
+ disabled: sorting.length === 0,
141
+ className: styles.clearSortItem,
142
+ },
143
+ ];
144
+ const mainGroup = {
145
+ type: 'group',
146
+ label: t('sort'),
147
+ items: groups,
148
+ mode: 'primary',
149
+ };
150
+ return {
151
+ items: [mainGroup],
152
+ pinBottom: clearItem,
153
+ };
154
+ }, [groupSortableHeadersByPinned, createSortItem, sorting, handleClearSort, t]);
155
+ const handleSelectionChange = useCallback((selectedId) => {
156
+ const id = String(selectedId);
157
+ if (id === 'sort-clear') {
158
+ return;
159
+ }
160
+ const match = id.match(/^sort-(.+)$/);
161
+ if (match) {
162
+ const [, columnId] = match;
163
+ handleColumnSortToggle(columnId);
164
+ }
165
+ }, [handleColumnSortToggle]);
166
+ const selection = useMemo(() => {
167
+ if (!selectedSortId)
168
+ return undefined;
169
+ return {
170
+ mode: 'single',
171
+ value: selectedSortId,
172
+ onChange: handleSelectionChange,
173
+ };
174
+ }, [selectedSortId, handleSelectionChange]);
175
+ return {
176
+ items,
177
+ pinBottom,
178
+ selection,
179
+ currentSort,
180
+ selectedSortId,
181
+ handleClearSort,
182
+ };
183
+ }
@@ -0,0 +1,9 @@
1
+ import { Header } from '@tanstack/react-table';
2
+ import { ColumnDefinition } from '@snack-uikit/table';
3
+ export declare function getHeaderLabel<TData extends object>(header: Header<TData, unknown>): string;
4
+ export declare function createColumnDefMap<TData extends object>(columnDefinitions: ColumnDefinition<TData>[]): Map<string, ColumnDefinition<TData>>;
5
+ export declare function groupHeadersByPinned<TData extends object>(headers: Header<TData, unknown>[], columnDefMap: Map<string, ColumnDefinition<TData>>): {
6
+ leftHeaders: Header<TData, unknown>[];
7
+ unpinnedHeaders: Header<TData, unknown>[];
8
+ rightHeaders: Header<TData, unknown>[];
9
+ };
@@ -0,0 +1,70 @@
1
+ export function getHeaderLabel(header) {
2
+ const headerDef = header.column.columnDef.header;
3
+ if (typeof headerDef === 'string') {
4
+ return headerDef;
5
+ }
6
+ if (typeof headerDef === 'function') {
7
+ try {
8
+ const context = header.getContext();
9
+ const result = headerDef(context);
10
+ if (typeof result === 'string' || typeof result === 'number') {
11
+ return String(result);
12
+ }
13
+ if (result != null && typeof result === 'object' && 'props' in result && result.props) {
14
+ const children = result.props.children;
15
+ if (typeof children === 'string' || typeof children === 'number') {
16
+ return String(children);
17
+ }
18
+ if (Array.isArray(children)) {
19
+ const textChild = children.find(child => typeof child === 'string' || typeof child === 'number');
20
+ if (textChild != null) {
21
+ return String(textChild);
22
+ }
23
+ }
24
+ }
25
+ }
26
+ catch (error) {
27
+ console.error('Error getting header label:', error);
28
+ }
29
+ }
30
+ return header.id || header.column.id || '';
31
+ }
32
+ export function createColumnDefMap(columnDefinitions) {
33
+ const map = new Map();
34
+ columnDefinitions.forEach(colDef => {
35
+ let id;
36
+ if ('id' in colDef && colDef.id) {
37
+ id = colDef.id;
38
+ }
39
+ else if ('accessorKey' in colDef && colDef.accessorKey) {
40
+ id = String(colDef.accessorKey);
41
+ }
42
+ if (id) {
43
+ map.set(id, colDef);
44
+ }
45
+ });
46
+ return map;
47
+ }
48
+ export function groupHeadersByPinned(headers, columnDefMap) {
49
+ const leftHeaders = [];
50
+ const unpinnedHeaders = [];
51
+ const rightHeaders = [];
52
+ headers.forEach(header => {
53
+ const columnDef = columnDefMap.get(header.id);
54
+ if (!columnDef) {
55
+ unpinnedHeaders.push(header);
56
+ return;
57
+ }
58
+ switch (columnDef.pinned) {
59
+ case 'left':
60
+ leftHeaders.push(header);
61
+ break;
62
+ case 'right':
63
+ rightHeaders.push(header);
64
+ break;
65
+ default:
66
+ unpinnedHeaders.push(header);
67
+ }
68
+ });
69
+ return { leftHeaders, unpinnedHeaders, rightHeaders };
70
+ }
@@ -1,5 +1,7 @@
1
+ export * from './ColumnsSettings';
1
2
  export * from './RowActionsCell';
2
3
  export * from './StatusColumnDef';
3
4
  export * from './TableCard';
4
- export * from './TablePagination';
5
5
  export * from './TableEmptyState';
6
+ export * from './TablePagination';
7
+ export * from './TableSorting';
@@ -1,5 +1,7 @@
1
+ export * from './ColumnsSettings';
1
2
  export * from './RowActionsCell';
2
3
  export * from './StatusColumnDef';
3
4
  export * from './TableCard';
4
- export * from './TablePagination';
5
5
  export * from './TableEmptyState';
6
+ export * from './TablePagination';
7
+ export * from './TableSorting';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloud-ru/uikit-product-mobile-table",
3
3
  "title": "Mobile Table",
4
- "version": "0.14.0",
4
+ "version": "0.15.0",
5
5
  "sideEffects": [
6
6
  "*.css",
7
7
  "*.woff",
@@ -42,11 +42,14 @@
42
42
  "@cloud-ru/uikit-product-mobile-toolbar": "0.4.11",
43
43
  "@cloud-ru/uikit-product-utils": "8.0.1",
44
44
  "@snack-uikit/button": "0.19.16",
45
+ "@snack-uikit/figma-tokens": "18.0.1",
46
+ "@snack-uikit/icons": "0.27.4",
45
47
  "@snack-uikit/info-block": "0.6.35",
48
+ "@snack-uikit/list": "0.32.10",
46
49
  "@snack-uikit/pagination": "0.10.21",
47
50
  "@snack-uikit/skeleton": "0.6.9",
48
51
  "@snack-uikit/status": "0.10.7",
49
- "@snack-uikit/table": "0.37.23",
52
+ "@snack-uikit/table": "0.37.25",
50
53
  "@snack-uikit/toggles": "0.13.23",
51
54
  "@tanstack/match-sorter-utils": "8.19.4",
52
55
  "@tanstack/react-table": "8.12.0",
@@ -60,5 +63,5 @@
60
63
  "devDependencies": {
61
64
  "@types/lodash.debounce": "4.0.9"
62
65
  },
63
- "gitHead": "6366d4a4912c09f95af6ba794df7b5a246893762"
66
+ "gitHead": "765bcc332ea9c28ea21abac518944d95e4bfc6fe"
64
67
  }