@snack-uikit/table 0.33.4 → 0.34.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 (80) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +5 -3
  3. package/dist/cjs/components/Table/Table.d.ts +1 -1
  4. package/dist/cjs/components/Table/Table.js +107 -48
  5. package/dist/cjs/components/Table/hooks/index.d.ts +1 -0
  6. package/dist/cjs/components/Table/hooks/index.js +2 -1
  7. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.d.ts +8 -0
  8. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.js +49 -0
  9. package/dist/cjs/components/Table/utils.d.ts +13 -0
  10. package/dist/cjs/components/Table/utils.js +71 -0
  11. package/dist/cjs/components/types.d.ts +12 -3
  12. package/dist/cjs/constants.d.ts +1 -0
  13. package/dist/cjs/constants.js +3 -2
  14. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  15. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.js +13 -3
  16. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  17. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.js +41 -17
  18. package/dist/cjs/helperComponents/Cells/HeaderCell/styles.module.css +10 -0
  19. package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.d.ts +8 -0
  20. package/dist/cjs/helperComponents/ColumnsSettings/ColumnsSettings.js +38 -0
  21. package/dist/cjs/helperComponents/ColumnsSettings/index.d.ts +1 -0
  22. package/dist/cjs/helperComponents/ColumnsSettings/index.js +25 -0
  23. package/dist/cjs/helperComponents/ColumnsSettings/styles.module.css +3 -0
  24. package/dist/cjs/helperComponents/Rows/BodyRow.d.ts +4 -1
  25. package/dist/cjs/helperComponents/Rows/BodyRow.js +20 -11
  26. package/dist/cjs/helperComponents/Rows/HeaderRow.d.ts +7 -1
  27. package/dist/cjs/helperComponents/Rows/HeaderRow.js +13 -5
  28. package/dist/cjs/helperComponents/hooks.d.ts +9 -9
  29. package/dist/cjs/helperComponents/hooks.js +39 -11
  30. package/dist/cjs/helperComponents/index.d.ts +1 -0
  31. package/dist/cjs/helperComponents/index.js +2 -1
  32. package/dist/cjs/types.d.ts +11 -3
  33. package/dist/esm/components/Table/Table.d.ts +1 -1
  34. package/dist/esm/components/Table/Table.js +40 -12
  35. package/dist/esm/components/Table/hooks/index.d.ts +1 -0
  36. package/dist/esm/components/Table/hooks/index.js +1 -0
  37. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.d.ts +8 -0
  38. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.js +39 -0
  39. package/dist/esm/components/Table/utils.d.ts +13 -0
  40. package/dist/esm/components/Table/utils.js +70 -0
  41. package/dist/esm/components/types.d.ts +12 -3
  42. package/dist/esm/constants.d.ts +1 -0
  43. package/dist/esm/constants.js +1 -0
  44. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  45. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.js +7 -3
  46. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  47. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.js +14 -4
  48. package/dist/esm/helperComponents/Cells/HeaderCell/styles.module.css +10 -0
  49. package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.d.ts +8 -0
  50. package/dist/esm/helperComponents/ColumnsSettings/ColumnsSettings.js +12 -0
  51. package/dist/esm/helperComponents/ColumnsSettings/index.d.ts +1 -0
  52. package/dist/esm/helperComponents/ColumnsSettings/index.js +1 -0
  53. package/dist/esm/helperComponents/ColumnsSettings/styles.module.css +3 -0
  54. package/dist/esm/helperComponents/Rows/BodyRow.d.ts +4 -1
  55. package/dist/esm/helperComponents/Rows/BodyRow.js +4 -3
  56. package/dist/esm/helperComponents/Rows/HeaderRow.d.ts +7 -1
  57. package/dist/esm/helperComponents/Rows/HeaderRow.js +3 -2
  58. package/dist/esm/helperComponents/hooks.d.ts +9 -9
  59. package/dist/esm/helperComponents/hooks.js +32 -11
  60. package/dist/esm/helperComponents/index.d.ts +1 -0
  61. package/dist/esm/helperComponents/index.js +1 -0
  62. package/dist/esm/types.d.ts +11 -3
  63. package/package.json +9 -5
  64. package/src/components/Table/Table.tsx +147 -61
  65. package/src/components/Table/hooks/index.ts +1 -0
  66. package/src/components/Table/hooks/useColumnOrderByDrag.ts +67 -0
  67. package/src/components/Table/utils.ts +118 -0
  68. package/src/components/types.ts +13 -3
  69. package/src/constants.tsx +1 -0
  70. package/src/helperComponents/Cells/BodyCell/BodyCell.tsx +9 -2
  71. package/src/helperComponents/Cells/HeaderCell/HeaderCell.tsx +50 -23
  72. package/src/helperComponents/Cells/HeaderCell/styles.module.scss +15 -0
  73. package/src/helperComponents/ColumnsSettings/ColumnsSettings.tsx +28 -0
  74. package/src/helperComponents/ColumnsSettings/index.ts +1 -0
  75. package/src/helperComponents/ColumnsSettings/styles.module.scss +5 -0
  76. package/src/helperComponents/Rows/BodyRow.tsx +30 -8
  77. package/src/helperComponents/Rows/HeaderRow.tsx +21 -4
  78. package/src/helperComponents/hooks.ts +41 -11
  79. package/src/helperComponents/index.ts +1 -0
  80. package/src/types.ts +21 -3
@@ -1,3 +1,5 @@
1
+ import { useSortable } from '@dnd-kit/sortable';
2
+ import { CSS } from '@dnd-kit/utilities';
1
3
  import { useMemo } from 'react';
2
4
  import { useTableContext } from './contexts';
3
5
  function hasHeaders(groups) {
@@ -7,6 +9,7 @@ export function useHeaderGroups() {
7
9
  const { table } = useTableContext();
8
10
  const columnDefs = table._getColumnDefs();
9
11
  const pinEnabled = table.getIsSomeColumnsPinned();
12
+ const { columnOrder } = table.getState();
10
13
  return useMemo(() => {
11
14
  if (!pinEnabled) {
12
15
  return {
@@ -22,12 +25,13 @@ export function useHeaderGroups() {
22
25
  };
23
26
  // need to rebuild if columnDefinitions has changed
24
27
  // eslint-disable-next-line react-hooks/exhaustive-deps
25
- }, [table, pinEnabled, columnDefs]);
28
+ }, [table, pinEnabled, columnDefs, columnOrder]);
26
29
  }
27
30
  export function useRowCells(row) {
28
31
  const { table } = useTableContext();
29
32
  const pinEnabled = table.getIsSomeColumnsPinned();
30
33
  const columnDefs = table._getColumnDefs();
34
+ const { columnOrder } = table.getState();
31
35
  return useMemo(() => {
32
36
  if (!pinEnabled) {
33
37
  return {
@@ -37,24 +41,41 @@ export function useRowCells(row) {
37
41
  const left = row.getLeftVisibleCells();
38
42
  const right = row.getRightVisibleCells();
39
43
  return {
40
- pinnedLeft: left.length ? left : undefined,
41
- pinnedRight: right.length ? right : undefined,
44
+ leftPinned: left.length ? left : undefined,
45
+ rightPinned: right.length ? right : undefined,
42
46
  unpinned: row.getCenterVisibleCells(),
43
47
  };
44
48
  // need to rebuild if columnDefinitions has changed
45
49
  // eslint-disable-next-line react-hooks/exhaustive-deps
46
- }, [row, pinEnabled, columnDefs]);
50
+ }, [row, pinEnabled, columnDefs, columnOrder]);
47
51
  }
48
- export function useCellSizes(element) {
52
+ export function useCellSizes(element, options) {
49
53
  const column = element.column;
54
+ const { isDragging, transform } = useSortable({
55
+ id: column.id,
56
+ });
50
57
  const minWidth = column.columnDef.minSize;
51
58
  const maxWidth = column.columnDef.maxSize;
52
59
  const width = `var(--table-column-${column.id}-size)`;
53
60
  const flexShrink = `var(--table-column-${column.id}-flex)`;
54
- return useMemo(() => ({
55
- minWidth,
56
- width,
57
- maxWidth,
58
- flexShrink,
59
- }), [flexShrink, maxWidth, minWidth, width]);
61
+ const isHeaderCell = 'headerGroup' in element;
62
+ return useMemo(() => {
63
+ const styles = {
64
+ minWidth,
65
+ width,
66
+ maxWidth,
67
+ flexShrink,
68
+ };
69
+ if (options === null || options === void 0 ? void 0 : options.isDraggable) {
70
+ styles.opacity = isDragging ? 0.8 : 1;
71
+ styles.position = 'relative';
72
+ styles.transform = CSS.Translate.toString(transform);
73
+ styles.transition = 'width transform 0.2s ease-in-out';
74
+ styles.zIndex = isDragging ? 1 : undefined;
75
+ if (isHeaderCell) {
76
+ styles.whiteSpace = 'nowrap';
77
+ }
78
+ }
79
+ return styles;
80
+ }, [options === null || options === void 0 ? void 0 : options.isDraggable, flexShrink, isDragging, isHeaderCell, maxWidth, minWidth, transform, width]);
60
81
  }
@@ -7,3 +7,4 @@ export * from './hooks';
7
7
  export * from './types';
8
8
  export * from './TableEmptyState';
9
9
  export * from './TablePagination';
10
+ export * from './ColumnsSettings';
@@ -7,3 +7,4 @@ export * from './hooks';
7
7
  export * from './types';
8
8
  export * from './TableEmptyState';
9
9
  export * from './TablePagination';
10
+ export * from './ColumnsSettings';
@@ -3,8 +3,7 @@ import { ReactNode } from 'react';
3
3
  import { ToolbarProps } from '@snack-uikit/toolbar';
4
4
  import { ValueOf } from '@snack-uikit/utils';
5
5
  import { COLUMN_ALIGN, COLUMN_PIN_POSITION } from './constants';
6
- import { Except } from './helperComponents';
7
- import { EmptyStateProps } from './helperComponents/TableEmptyState';
6
+ import { EmptyStateProps, Except } from './helperComponents';
8
7
  type ColumnAlign = ValueOf<typeof COLUMN_ALIGN>;
9
8
  type ColumnPinPosition = ValueOf<typeof COLUMN_PIN_POSITION>;
10
9
  type BaseColumnDefinition<TData> = Except<ColumnDef<TData>, 'footer' | 'enablePinning' | 'enableGrouping' | 'enableColumnFilter' | 'filterFn' | 'enableGlobalFilter' | 'enableMultiSort' | 'enableHiding'> & {
@@ -39,6 +38,15 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
39
38
  /** Размер ячейки */
40
39
  size: number;
41
40
  };
42
- export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
41
+ type FilterableProps = {
42
+ id: string;
43
+ /** Название колонки в настройках таблицы */
44
+ headerConfigLabel?: string;
45
+ };
46
+ type FilterableNormalColumnDefinition<TData> = NormalColumnDefinition<TData> & FilterableProps;
47
+ type FilterablePinnedColumnDefinition<TData> = PinnedColumnDefinition<TData> & FilterableProps;
48
+ export type FilterableColumnDefinition<TData> = FilterableNormalColumnDefinition<TData> | FilterablePinnedColumnDefinition<TData>;
49
+ export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData> | FilterableColumnDefinition<TData>;
50
+ export type ColumnOrder = string[];
43
51
  export type { RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
44
52
  export type { ColumnPinPosition, PaginationState, SortingState, RowSelectionState, RowSelectionOptions, EmptyStateProps, ToolbarProps, HeaderContext, CellContext, };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Table",
7
- "version": "0.33.4",
7
+ "version": "0.34.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -37,17 +37,21 @@
37
37
  "scripts": {},
38
38
  "dependencies": {
39
39
  "@cloud-ru/ft-request-payload-transform": "0.1.0",
40
+ "@dnd-kit/core": "6.3.1",
41
+ "@dnd-kit/modifiers": "9.0.0",
42
+ "@dnd-kit/sortable": "10.0.0",
43
+ "@dnd-kit/utilities": "3.2.2",
40
44
  "@snack-uikit/button": "0.19.8",
41
- "@snack-uikit/chips": "0.26.1",
45
+ "@snack-uikit/chips": "0.26.2",
42
46
  "@snack-uikit/icon-predefined": "0.7.4",
43
47
  "@snack-uikit/icons": "0.25.1",
44
48
  "@snack-uikit/info-block": "0.6.16",
45
- "@snack-uikit/list": "0.27.0",
49
+ "@snack-uikit/list": "0.27.1",
46
50
  "@snack-uikit/pagination": "0.10.5",
47
51
  "@snack-uikit/scroll": "0.9.4",
48
52
  "@snack-uikit/skeleton": "0.6.3",
49
53
  "@snack-uikit/toggles": "0.13.8",
50
- "@snack-uikit/toolbar": "0.12.4",
54
+ "@snack-uikit/toolbar": "0.12.5",
51
55
  "@snack-uikit/truncate-string": "0.6.13",
52
56
  "@snack-uikit/typography": "0.8.5",
53
57
  "@snack-uikit/utils": "3.8.0",
@@ -62,5 +66,5 @@
62
66
  "peerDependencies": {
63
67
  "@snack-uikit/locale": "*"
64
68
  },
65
- "gitHead": "d8510c4d566cee9fd0e96ff969fdefb7fb11ac49"
69
+ "gitHead": "cafb29b74a6ac4d38594e9d46599617925165935"
66
70
  }
@@ -1,3 +1,5 @@
1
+ import { closestCenter, DndContext } from '@dnd-kit/core';
2
+ import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
1
3
  import {
2
4
  CellContext,
3
5
  ColumnPinningState,
@@ -13,7 +15,7 @@ import {
13
15
  useReactTable,
14
16
  } from '@tanstack/react-table';
15
17
  import cn from 'classnames';
16
- import { RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
18
+ import { RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
17
19
 
18
20
  import { FiltersState } from '@snack-uikit/chips';
19
21
  import { useLocale } from '@snack-uikit/locale';
@@ -33,6 +35,7 @@ import {
33
35
  import { CellAutoResizeContext, useCellAutoResizeController } from '../../contexts';
34
36
  import {
35
37
  BodyRow,
38
+ ColumnsSettings,
36
39
  ExportButton,
37
40
  getColumnId,
38
41
  getRowActionsColumnDef,
@@ -49,7 +52,7 @@ import { getTreeColumnDef } from '../../helperComponents/Cells/TreeCell';
49
52
  import { ColumnDefinition } from '../../types';
50
53
  import { customDateParser, fuzzyFilter } from '../../utils';
51
54
  import { TableProps } from '../types';
52
- import { useLoadingTable, useStateControl } from './hooks';
55
+ import { useColumnOrderByDrag, useLoadingTable, useStateControl } from './hooks';
53
56
  import { usePageReset } from './hooks/usePageReset';
54
57
  import { useSaveTableSettings } from './hooks/useSaveTableSettings';
55
58
  import styles from './styles.module.scss';
@@ -57,6 +60,9 @@ import {
57
60
  getColumnStyleVars,
58
61
  getCurrentlyConfiguredHeaderWidth,
59
62
  getInitColumnSizeFromLocalStorage,
63
+ isFilterableColumn,
64
+ prepareColumnsSettings,
65
+ prepareColumnsSettingsMap,
60
66
  saveStateToLocalStorage,
61
67
  } from './utils';
62
68
 
@@ -106,6 +112,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
106
112
  expanding,
107
113
  bulkActions: bulkActionsProp,
108
114
  rowAutoHeight,
115
+ columnsSettings: columnsSettingsProp,
109
116
  ...rest
110
117
  }: TableProps<TData, TFilters>) {
111
118
  const { setDataToStorages, defaultFilter } = useSaveTableSettings({
@@ -150,6 +157,8 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
150
157
  DEFAULT_FILTER_VISIBILITY,
151
158
  );
152
159
 
160
+ const [areColumnFiltersOpen, setAreColumnFiltersOpen] = useState<boolean>(true);
161
+
153
162
  useEffect(() => {
154
163
  setDataToStorages({ pagination, sorting, filter, search: globalFilter || '' });
155
164
  }, [pagination, sorting, filter, setDataToStorages, globalFilter]);
@@ -166,26 +175,42 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
166
175
  // eslint-disable-next-line
167
176
  }, [defaultFilter]);
168
177
 
169
- const patchedFilter = useMemo(
170
- () =>
171
- columnFilters
172
- ? {
173
- ...columnFilters,
174
- value: filter,
175
- onChange: setFilter,
176
- visibleFilters: filterVisibility,
177
- onVisibleFiltersChange: setFilterVisibility,
178
- }
179
- : undefined,
180
- [columnFilters, filter, setFilter, filterVisibility, setFilterVisibility],
181
- );
178
+ const patchedFilter = useMemo(() => {
179
+ if (!columnFilters) {
180
+ return undefined;
181
+ }
182
+
183
+ return {
184
+ ...columnFilters,
185
+ open: areColumnFiltersOpen,
186
+ onOpenChange: setAreColumnFiltersOpen,
187
+ value: filter,
188
+ onChange: setFilter,
189
+ visibleFilters: filterVisibility,
190
+ onVisibleFiltersChange: setFilterVisibility,
191
+ };
192
+ }, [columnFilters, areColumnFiltersOpen, filter, setFilter, filterVisibility, setFilterVisibility]);
182
193
 
183
194
  const enableSelection = Boolean(rowSelectionProp?.enable);
184
195
 
185
196
  const manualPagination = infiniteLoading || manualPaginationProp;
186
197
 
198
+ const [enabledColumns, setEnabledColumns] = useState<string[]>(() => prepareColumnsSettingsMap(columnDefinitions));
199
+
200
+ const filteredColumnDefinitions = useMemo(
201
+ () =>
202
+ columnDefinitions.filter(colDef => {
203
+ if (isFilterableColumn(colDef)) {
204
+ return enabledColumns.includes(colDef.id);
205
+ }
206
+
207
+ return true;
208
+ }),
209
+ [columnDefinitions, enabledColumns],
210
+ );
211
+
187
212
  const tableColumns: ColumnDefinition<TData>[] = useMemo(() => {
188
- let cols: ColumnDefinition<TData>[] = columnDefinitions;
213
+ let cols: ColumnDefinition<TData>[] = filteredColumnDefinitions;
189
214
  if (enableSelection && !expanding) {
190
215
  cols = [getSelectionCellColumnDef(enableSelectPinned), ...cols];
191
216
  }
@@ -193,7 +218,26 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
193
218
  cols = [getTreeColumnDef(expanding.expandingColumnDefinition), ...cols];
194
219
  }
195
220
  return cols;
196
- }, [columnDefinitions, enableSelection, enableSelectPinned, expanding]);
221
+ }, [filteredColumnDefinitions, enableSelection, enableSelectPinned, expanding]);
222
+
223
+ const { columnOrder, setColumnOrder, sensors, handleDragEnd } = useColumnOrderByDrag(tableColumns);
224
+
225
+ const filterableColumns = useMemo(() => columnDefinitions.filter(isFilterableColumn), [columnDefinitions]);
226
+ const areAllColumnsEnabled = filterableColumns.length === enabledColumns.length;
227
+
228
+ const { t } = useLocale('Table');
229
+
230
+ const columnsSettings = useMemo(
231
+ () =>
232
+ prepareColumnsSettings({
233
+ columnDefinitions,
234
+ columnOrder,
235
+ areAllColumnsEnabled,
236
+ columnsSettingsHeader: columnsSettingsProp?.headerLabel,
237
+ t,
238
+ }),
239
+ [areAllColumnsEnabled, columnDefinitions, columnOrder, columnsSettingsProp?.headerLabel, t],
240
+ );
197
241
 
198
242
  const columnPinning = useMemo(() => {
199
243
  const pinningState: Required<ColumnPinningState> = { left: [], right: [] };
@@ -225,6 +269,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
225
269
  columns: tableColumns,
226
270
  state: {
227
271
  columnPinning,
272
+ columnOrder,
228
273
  globalFilter,
229
274
  rowSelection,
230
275
  sorting,
@@ -244,6 +289,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
244
289
  return <TruncateString text={String(cell.getValue())} maxLines={1} />;
245
290
  },
246
291
  },
292
+ onColumnOrderChange: setColumnOrder,
247
293
 
248
294
  manualSorting,
249
295
  manualPagination,
@@ -283,7 +329,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
283
329
 
284
330
  const { loadingTable } = useLoadingTable({
285
331
  pageSize: Math.min(Math.max(pageSize, 5), DEFAULT_PAGE_SIZE),
286
- columnDefinitions: tableColumns,
332
+ columnDefinitions: filteredColumnDefinitions,
287
333
  columnPinning,
288
334
  });
289
335
 
@@ -410,7 +456,6 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
410
456
  : topRows;
411
457
  const centerRows = copyPinnedRows ? tableRows : tableCenterRows;
412
458
 
413
- const { t } = useLocale('Table');
414
459
  const emptyStates = useEmptyState({ noDataState, noResultsState, errorDataState });
415
460
 
416
461
  usePageReset({
@@ -424,6 +469,8 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
424
469
  const { updateCellMap } = useCellAutoResizeController(table);
425
470
 
426
471
  const showToolbar = !suppressToolbar;
472
+ const showColumnsSettings = Boolean(columnsSettingsProp?.headerLabel);
473
+ const enableColumnsOrderSortByDrag = Boolean(columnsSettingsProp?.enableDrag);
427
474
 
428
475
  return (
429
476
  <div className={cn(styles.wrapper, className)} {...extractSupportProps(rest)}>
@@ -449,7 +496,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
449
496
  onCheck={enableSelection ? handleOnToolbarCheck : undefined}
450
497
  outline={outline}
451
498
  after={
452
- toolbarAfter || exportSettings ? (
499
+ toolbarAfter || exportSettings || showColumnsSettings ? (
453
500
  <>
454
501
  {toolbarAfter}
455
502
  {exportSettings && (
@@ -461,6 +508,13 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
461
508
  centerRows={centerRows}
462
509
  />
463
510
  )}
511
+ {showColumnsSettings && (
512
+ <ColumnsSettings
513
+ columnsSettings={columnsSettings}
514
+ enabledColumns={enabledColumns}
515
+ setEnabledColumns={setEnabledColumns}
516
+ />
517
+ )}
464
518
  </>
465
519
  ) : undefined
466
520
  }
@@ -474,47 +528,79 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
474
528
  <Scroll size='s' className={styles.table} ref={scrollContainerRef} data-outline={outline || undefined}>
475
529
  <div className={styles.tableContent} style={columnSizes.vars}>
476
530
  <CellAutoResizeContext.Provider value={{ updateCellMap }}>
477
- <TableContext.Provider value={{ table }}>
478
- {(!infiniteLoading || !data.length) && loading ? (
479
- <SkeletonContextProvider loading>
480
- <HeaderRow rowAutoHeight={rowAutoHeight} />
481
- {loadingTableRows.map(row => (
482
- <BodyRow key={row.id} row={row} rowAutoHeight={rowAutoHeight} />
483
- ))}
484
- </SkeletonContextProvider>
485
- ) : (
486
- <>
487
- {centerRows.length || filteredTopRows.length ? <HeaderRow /> : null}
488
-
489
- {filteredTopRows.length ? (
490
- <div className={styles.topRowWrapper}>
491
- {filteredTopRows.map(row => (
492
- <BodyRow key={row.id} row={row} onRowClick={onRowClick} rowAutoHeight={rowAutoHeight} />
493
- ))}
494
- </div>
495
- ) : null}
496
-
497
- {centerRows.map(row => (
498
- <BodyRow key={row.id} row={row} onRowClick={onRowClick} rowAutoHeight={rowAutoHeight} />
499
- ))}
500
-
501
- {data.length > 0 && infiniteLoading && loading && !dataError && (
502
- <SkeletonContextProvider loading>
503
- {loadingTableRows.slice(0, 3).map(row => (
504
- <BodyRow key={row.id} row={row} />
505
- ))}
506
- </SkeletonContextProvider>
507
- )}
508
-
509
- <TableEmptyState
510
- emptyStates={emptyStates}
511
- dataError={dataError}
512
- dataFiltered={dataFiltered || Boolean(table.getState().globalFilter)}
513
- tableRowsLength={tableRows.length + filteredTopRows.length}
514
- />
515
- </>
516
- )}
517
- </TableContext.Provider>
531
+ <DndContext
532
+ collisionDetection={closestCenter}
533
+ modifiers={[restrictToHorizontalAxis]}
534
+ onDragEnd={handleDragEnd}
535
+ sensors={sensors}
536
+ >
537
+ <TableContext.Provider value={{ table }}>
538
+ {(!infiniteLoading || !data.length) && loading ? (
539
+ <SkeletonContextProvider loading>
540
+ <HeaderRow rowAutoHeight={rowAutoHeight} columnOrder={columnOrder} />
541
+ {loadingTableRows.map(row => (
542
+ <BodyRow key={row.id} row={row} rowAutoHeight={rowAutoHeight} columnOrder={columnOrder} />
543
+ ))}
544
+ </SkeletonContextProvider>
545
+ ) : (
546
+ <>
547
+ {centerRows.length || filteredTopRows.length ? (
548
+ <HeaderRow
549
+ rowAutoHeight={rowAutoHeight}
550
+ columnOrder={columnOrder}
551
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
552
+ />
553
+ ) : null}
554
+
555
+ {filteredTopRows.length ? (
556
+ <div className={styles.topRowWrapper}>
557
+ {filteredTopRows.map(row => (
558
+ <BodyRow
559
+ key={row.id}
560
+ row={row}
561
+ onRowClick={onRowClick}
562
+ rowAutoHeight={rowAutoHeight}
563
+ columnOrder={columnOrder}
564
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
565
+ />
566
+ ))}
567
+ </div>
568
+ ) : null}
569
+
570
+ {centerRows.map(row => (
571
+ <BodyRow
572
+ key={row.id}
573
+ row={row}
574
+ onRowClick={onRowClick}
575
+ rowAutoHeight={rowAutoHeight}
576
+ columnOrder={columnOrder}
577
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
578
+ />
579
+ ))}
580
+
581
+ {data.length > 0 && infiniteLoading && loading && !dataError && (
582
+ <SkeletonContextProvider loading>
583
+ {loadingTableRows.slice(0, 3).map(row => (
584
+ <BodyRow
585
+ key={row.id}
586
+ row={row}
587
+ columnOrder={columnOrder}
588
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
589
+ />
590
+ ))}
591
+ </SkeletonContextProvider>
592
+ )}
593
+
594
+ <TableEmptyState
595
+ emptyStates={emptyStates}
596
+ dataError={dataError}
597
+ dataFiltered={dataFiltered || Boolean(table.getState().globalFilter)}
598
+ tableRowsLength={tableRows.length + filteredTopRows.length}
599
+ />
600
+ </>
601
+ )}
602
+ </TableContext.Provider>
603
+ </DndContext>
518
604
  </CellAutoResizeContext.Provider>
519
605
  </div>
520
606
 
@@ -1,2 +1,3 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
+ export * from './useColumnOrderByDrag';
@@ -0,0 +1,67 @@
1
+ import {
2
+ DragEndEvent,
3
+ KeyboardSensor,
4
+ MouseSensor,
5
+ SensorOptions,
6
+ TouchSensor,
7
+ useSensor,
8
+ useSensors,
9
+ } from '@dnd-kit/core';
10
+ import { arrayMove } from '@dnd-kit/sortable';
11
+ import { useCallback, useState } from 'react';
12
+
13
+ import { ColumnDefinition } from '../../../types';
14
+
15
+ function prepareInitialState<TData extends object>(tableColumns: ColumnDefinition<TData>[]) {
16
+ return tableColumns.filter(column => column.pinned !== 'left' && column.pinned !== 'right').map(c => c.id as string);
17
+ }
18
+
19
+ const draggingOptions: SensorOptions = {
20
+ activationConstraint: {
21
+ distance: 5, // Is required to differ click (sort) from drag
22
+ },
23
+ };
24
+
25
+ export function useColumnOrderByDrag<TData extends object>(tableColumns: ColumnDefinition<TData>[]) {
26
+ const [columnOrder, setColumnOrder] = useState<string[]>(() => prepareInitialState(tableColumns));
27
+
28
+ const handleDragEnd = useCallback(
29
+ ({ active, over }: DragEndEvent) => {
30
+ if (!active || !over) {
31
+ return;
32
+ }
33
+
34
+ const activeId = active.id.toString();
35
+ const overId = over.id.toString();
36
+
37
+ if (activeId === overId) {
38
+ return;
39
+ }
40
+
41
+ if (!columnOrder.includes(overId)) {
42
+ return;
43
+ }
44
+
45
+ setColumnOrder(columnOrder => {
46
+ const oldIndex = columnOrder.indexOf(activeId);
47
+ const newIndex = columnOrder.indexOf(overId);
48
+ return arrayMove(columnOrder, oldIndex, newIndex);
49
+ });
50
+ },
51
+ [columnOrder],
52
+ );
53
+
54
+ const sensors = useSensors(
55
+ useSensor(MouseSensor, draggingOptions),
56
+ useSensor(TouchSensor, {}),
57
+ useSensor(KeyboardSensor, {}),
58
+ );
59
+
60
+ return {
61
+ columnOrder,
62
+ setColumnOrder,
63
+
64
+ handleDragEnd,
65
+ sensors,
66
+ };
67
+ }