@snack-uikit/table 0.30.0 → 0.30.1-preview-9a03a1c9.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 (61) hide show
  1. package/README.md +2 -0
  2. package/dist/cjs/components/Table/Table.d.ts +1 -1
  3. package/dist/cjs/components/Table/Table.js +116 -89
  4. package/dist/cjs/components/Table/hooks/index.d.ts +1 -0
  5. package/dist/cjs/components/Table/hooks/index.js +2 -1
  6. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.d.ts +9 -0
  7. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.js +76 -0
  8. package/dist/cjs/components/types.d.ts +2 -0
  9. package/dist/cjs/constants.d.ts +1 -0
  10. package/dist/cjs/constants.js +3 -2
  11. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  12. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.js +13 -3
  13. package/dist/cjs/helperComponents/Cells/HeaderCell/DragHandle.d.ts +6 -0
  14. package/dist/cjs/helperComponents/Cells/HeaderCell/DragHandle.js +27 -0
  15. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  16. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.js +22 -2
  17. package/dist/cjs/helperComponents/Cells/HeaderCell/styles.module.css +11 -0
  18. package/dist/cjs/helperComponents/Rows/BodyRow.d.ts +4 -1
  19. package/dist/cjs/helperComponents/Rows/BodyRow.js +30 -12
  20. package/dist/cjs/helperComponents/Rows/HeaderRow.d.ts +7 -1
  21. package/dist/cjs/helperComponents/Rows/HeaderRow.js +33 -13
  22. package/dist/cjs/helperComponents/hooks.d.ts +22 -13
  23. package/dist/cjs/helperComponents/hooks.js +63 -15
  24. package/dist/cjs/types.d.ts +5 -0
  25. package/dist/esm/components/Table/Table.d.ts +1 -1
  26. package/dist/esm/components/Table/Table.js +17 -12
  27. package/dist/esm/components/Table/hooks/index.d.ts +1 -0
  28. package/dist/esm/components/Table/hooks/index.js +1 -0
  29. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.d.ts +9 -0
  30. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.js +66 -0
  31. package/dist/esm/components/types.d.ts +2 -0
  32. package/dist/esm/constants.d.ts +1 -0
  33. package/dist/esm/constants.js +1 -0
  34. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  35. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.js +7 -3
  36. package/dist/esm/helperComponents/Cells/HeaderCell/DragHandle.d.ts +6 -0
  37. package/dist/esm/helperComponents/Cells/HeaderCell/DragHandle.js +6 -0
  38. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  39. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.js +13 -4
  40. package/dist/esm/helperComponents/Cells/HeaderCell/styles.module.css +11 -0
  41. package/dist/esm/helperComponents/Rows/BodyRow.d.ts +4 -1
  42. package/dist/esm/helperComponents/Rows/BodyRow.js +4 -3
  43. package/dist/esm/helperComponents/Rows/HeaderRow.d.ts +7 -1
  44. package/dist/esm/helperComponents/Rows/HeaderRow.js +4 -3
  45. package/dist/esm/helperComponents/hooks.d.ts +22 -13
  46. package/dist/esm/helperComponents/hooks.js +58 -15
  47. package/dist/esm/types.d.ts +5 -0
  48. package/package.json +6 -2
  49. package/src/components/Table/Table.tsx +133 -102
  50. package/src/components/Table/hooks/index.ts +1 -0
  51. package/src/components/Table/hooks/useColumnOrderByDrag.ts +100 -0
  52. package/src/components/types.ts +3 -0
  53. package/src/constants.tsx +2 -0
  54. package/src/helperComponents/Cells/BodyCell/BodyCell.tsx +9 -2
  55. package/src/helperComponents/Cells/HeaderCell/DragHandle.tsx +18 -0
  56. package/src/helperComponents/Cells/HeaderCell/HeaderCell.tsx +19 -4
  57. package/src/helperComponents/Cells/HeaderCell/styles.module.scss +11 -0
  58. package/src/helperComponents/Rows/BodyRow.tsx +36 -9
  59. package/src/helperComponents/Rows/HeaderRow.tsx +51 -18
  60. package/src/helperComponents/hooks.ts +78 -15
  61. package/src/types.ts +6 -0
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useSortable } from '@dnd-kit/sortable';
2
3
  import { flexRender } from '@tanstack/react-table';
3
4
  import cn from 'classnames';
4
5
  import { useRef } from 'react';
5
6
  import { TruncateString } from '@snack-uikit/truncate-string';
6
- import { TEST_IDS } from '../../../constants';
7
+ import { TEST_IDS, UNDRAGGABLE_COLUMNS } from '../../../constants';
7
8
  import { useCellSizes } from '../../hooks';
8
9
  import { Cell } from '../Cell';
10
+ import { DragHandle } from './DragHandle';
9
11
  import { getSortingIcon } from './helpers';
10
12
  import { ResizeHandle } from './ResizeHandle';
11
13
  import styles from './styles.module.css';
12
- export function HeaderCell({ header, pinPosition, className }) {
14
+ export function HeaderCell({ header, pinPosition, isDraggable, className }) {
13
15
  const cellRef = useRef(null);
14
16
  const isSortable = header.column.getCanSort();
15
17
  const isResizable = header.column.getCanResize();
@@ -19,12 +21,19 @@ export function HeaderCell({ header, pinPosition, className }) {
19
21
  const columnSizingInfo = header.getContext().table.getState().columnSizingInfo;
20
22
  const isSomeColumnResizing = columnSizingInfo.isResizingColumn;
21
23
  const columnDef = header.column.columnDef;
22
- const style = useCellSizes(header);
24
+ const style = useCellSizes(header, { isDraggable });
25
+ const { attributes, listeners, setNodeRef } = useSortable({
26
+ id: header.column.id,
27
+ });
23
28
  const sortingHandler = (e) => {
24
29
  var _a;
25
30
  if (isSomeColumnResizing)
26
31
  return;
27
32
  return (_a = header.column.getToggleSortingHandler()) === null || _a === void 0 ? void 0 : _a(e);
28
33
  };
29
- return (_jsxs(Cell, { style: style, onClick: sortingHandler, "data-sortable": isSortable || undefined, "data-no-padding": columnDef.noHeaderCellPadding || undefined, "data-no-offset": columnDef.noHeaderCellBorderOffset || undefined, "data-test-id": TEST_IDS.headerCell, "data-align": columnDef.headerAlign || undefined, "data-header-id": header.id, "data-resizing": isResizing || undefined, "data-pin-position": pinPosition || undefined, role: 'columnheader', className: cn(styles.tableHeaderCell, className, columnDef.headerClassName), ref: cellRef, children: [_jsxs("div", { className: styles.tableHeaderCellMain, children: [columnDef.header && (_jsx("div", { className: styles.tableHeaderCellName, children: _jsx(TruncateString, { text: flexRender(columnDef.header, header.getContext()) }) })), Boolean(sortIcon) && (_jsx("div", { className: styles.tableHeaderIcon, "data-sort-direction": sortDirection, "data-test-id": TEST_IDS.headerSortIndicator, children: sortIcon }))] }), Boolean(isResizable) && _jsx(ResizeHandle, { header: header, cellRef: cellRef })] }));
34
+ const isAvailableForDrag = !UNDRAGGABLE_COLUMNS.includes(header.column.id);
35
+ return (_jsxs(Cell, { style: style, onClick: sortingHandler, "data-sortable": isSortable || undefined, "data-draggable": isDraggable || undefined, "data-no-padding": columnDef.noHeaderCellPadding || undefined, "data-no-offset": columnDef.noHeaderCellBorderOffset || undefined, "data-test-id": TEST_IDS.headerCell, "data-align": columnDef.headerAlign || undefined, "data-header-id": header.id, "data-resizing": isResizing || undefined, "data-pin-position": pinPosition || undefined, role: 'columnheader', className: cn(styles.tableHeaderCell, className, columnDef.headerClassName), ref: element => {
36
+ setNodeRef(element);
37
+ return cellRef;
38
+ }, children: [_jsxs("div", { className: styles.tableHeaderCellMain, children: [columnDef.header && (_jsx("div", { className: styles.tableHeaderCellName, children: _jsx(TruncateString, { text: flexRender(columnDef.header, header.getContext()) }) })), Boolean(sortIcon) && (_jsx("div", { className: styles.tableHeaderIcon, "data-sort-direction": sortDirection, "data-test-id": TEST_IDS.headerSortIndicator, children: sortIcon }))] }), isAvailableForDrag && Boolean(isDraggable) && _jsx(DragHandle, { attributes: attributes, listeners: listeners }), Boolean(isResizable) && _jsx(ResizeHandle, { header: header, cellRef: cellRef })] }));
30
39
  }
@@ -129,4 +129,15 @@
129
129
  .tableHeaderIcon svg{
130
130
  width:var(--size-icon-container-xs, 16px) !important;
131
131
  height:var(--size-icon-container-xs, 16px) !important;
132
+ }
133
+
134
+ .dragHandle{
135
+ cursor:pointer;
136
+ display:flex;
137
+ flex-direction:row;
138
+ gap:2px;
139
+ }
140
+
141
+ .dragIcon{
142
+ color:var(--sys-neutral-text-light, #8b8e9b);
132
143
  }
@@ -1,5 +1,6 @@
1
1
  import { Row as TableRow } from '@tanstack/react-table';
2
2
  import { MouseEvent } from 'react';
3
+ import { GroupedColumnOrderState } from '../../types';
3
4
  export type RowInfo<TData> = {
4
5
  id: string;
5
6
  data: TData;
@@ -10,5 +11,7 @@ export type RowClickHandler<TData> = (e: MouseEvent<HTMLDivElement>, row: RowInf
10
11
  export type BodyRowProps<TData> = {
11
12
  row: TableRow<TData>;
12
13
  onRowClick?: RowClickHandler<TData>;
14
+ groupedColumnOrderState: GroupedColumnOrderState;
15
+ enableColumnsOrderSortByDrag?: boolean;
13
16
  };
14
- export declare function BodyRow<TData>({ row, onRowClick }: BodyRowProps<TData>): import("react/jsx-runtime").JSX.Element;
17
+ export declare function BodyRow<TData>({ row, onRowClick, groupedColumnOrderState, enableColumnsOrderSortByDrag, }: BodyRowProps<TData>): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';
2
3
  import { useState } from 'react';
3
4
  import { COLUMN_PIN_POSITION, TEST_IDS } from '../../constants';
4
5
  import { BodyCell } from '../Cells';
@@ -7,8 +8,8 @@ import { useRowCells } from '../hooks';
7
8
  import { PinnedCells } from './PinnedCells';
8
9
  import { Row } from './Row';
9
10
  import styles from './styles.module.css';
10
- export function BodyRow({ row, onRowClick }) {
11
- const { pinnedLeft, pinnedRight, unpinned } = useRowCells(row);
11
+ export function BodyRow({ row, onRowClick, groupedColumnOrderState, enableColumnsOrderSortByDrag, }) {
12
+ const { leftPinned, rightPinned, unpinned } = useRowCells(row, groupedColumnOrderState);
12
13
  const [dropListOpened, setDropListOpen] = useState(false);
13
14
  const disabled = !row.getCanSelect();
14
15
  const handleRowClick = (e) => {
@@ -23,5 +24,5 @@ export function BodyRow({ row, onRowClick }) {
23
24
  };
24
25
  return (_jsx(RowContext.Provider, { value: { dropListOpened, setDropListOpen }, children: _jsxs(Row, { onClick: handleRowClick, "data-clickable": Boolean(onRowClick) || undefined, "data-disabled": disabled || undefined, "data-selected": row.getIsSelected() ||
25
26
  (row.getIsSomeSelected() && !row.getCanMultiSelect() && !row.getIsExpanded()) ||
26
- undefined, "data-actions-opened": dropListOpened || undefined, "data-test-id": TEST_IDS.bodyRow, "data-row-id": row.id, className: styles.bodyRow, children: [pinnedLeft && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Left, children: pinnedLeft.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))) })), unpinned.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))), pinnedRight && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Right, children: pinnedRight.map(cell => (_jsx(BodyCell, { cell: cell }, cell.id))) }))] }) }));
27
+ undefined, "data-actions-opened": dropListOpened || undefined, "data-test-id": TEST_IDS.bodyRow, "data-row-id": row.id, className: styles.bodyRow, children: [leftPinned && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Left, children: leftPinned.map(cell => (_jsx(SortableContext, { items: groupedColumnOrderState.leftPinned, strategy: horizontalListSortingStrategy, children: _jsx(BodyCell, { cell: cell, isDraggable: enableColumnsOrderSortByDrag }, cell.id) }, cell.id))) })), unpinned.map(cell => (_jsx(SortableContext, { items: groupedColumnOrderState.unpinned, strategy: horizontalListSortingStrategy, children: _jsx(BodyCell, { cell: cell, isDraggable: enableColumnsOrderSortByDrag }, cell.id) }, cell.id))), rightPinned && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Right, children: rightPinned.map(cell => (_jsx(SortableContext, { items: groupedColumnOrderState.rightPinned, strategy: horizontalListSortingStrategy, children: _jsx(BodyCell, { cell: cell, isDraggable: enableColumnsOrderSortByDrag }, cell.id) }, cell.id))) }))] }) }));
27
28
  }
@@ -1 +1,7 @@
1
- export declare function HeaderRow(): import("react/jsx-runtime").JSX.Element;
1
+ import { GroupedColumnOrderState } from '../../types';
2
+ type Props = {
3
+ groupedColumnOrderState: GroupedColumnOrderState;
4
+ enableColumnsOrderSortByDrag?: boolean;
5
+ };
6
+ export declare function HeaderRow({ groupedColumnOrderState, enableColumnsOrderSortByDrag }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';
2
3
  import { COLUMN_PIN_POSITION, TEST_IDS } from '../../constants';
3
4
  import { HeaderCell } from '../Cells';
4
5
  import { useHeaderGroups } from '../hooks';
5
6
  import { PinnedCells } from './PinnedCells';
6
7
  import { Row } from './Row';
7
8
  import styles from './styles.module.css';
8
- export function HeaderRow() {
9
- const { leftPinned, unpinned, rightPinned } = useHeaderGroups();
10
- return (_jsxs(Row, { className: styles.tableHeader, "data-test-id": TEST_IDS.headerRow, children: [leftPinned && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Left, children: leftPinned.map(headerGroup => headerGroup.headers.map(header => header.isPlaceholder ? null : _jsx(HeaderCell, { header: header }, header.id))) })), unpinned.map(headerGroup => headerGroup.headers.map(header => _jsx(HeaderCell, { header: header }, header.id))), rightPinned && (_jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Right, children: rightPinned.map(headerGroup => headerGroup.headers.map(header => header.isPlaceholder ? null : (_jsx(HeaderCell, { header: header, pinPosition: COLUMN_PIN_POSITION.Right }, header.id)))) }))] }));
9
+ export function HeaderRow({ groupedColumnOrderState, enableColumnsOrderSortByDrag }) {
10
+ const { leftPinned, unpinned, rightPinned } = useHeaderGroups(groupedColumnOrderState);
11
+ return (_jsxs(Row, { className: styles.tableHeader, "data-test-id": TEST_IDS.headerRow, children: [leftPinned && (_jsx(SortableContext, { items: groupedColumnOrderState.leftPinned, strategy: horizontalListSortingStrategy, children: _jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Left, children: leftPinned.map(headerGroup => headerGroup.headers.map(header => header.isPlaceholder ? null : (_jsx(HeaderCell, { header: header, isDraggable: enableColumnsOrderSortByDrag && groupedColumnOrderState.leftPinned.length > 1 }, header.id)))) }) })), _jsx(SortableContext, { items: groupedColumnOrderState.unpinned, strategy: horizontalListSortingStrategy, children: unpinned.map(headerGroup => headerGroup.headers.map(header => (_jsx(HeaderCell, { header: header, isDraggable: enableColumnsOrderSortByDrag && groupedColumnOrderState.unpinned.length > 1 }, header.id)))) }), rightPinned && (_jsx(SortableContext, { items: groupedColumnOrderState.rightPinned, strategy: horizontalListSortingStrategy, children: _jsx(PinnedCells, { position: COLUMN_PIN_POSITION.Right, children: rightPinned.map(headerGroup => headerGroup.headers.map(header => header.isPlaceholder ? null : (_jsx(HeaderCell, { header: header, pinPosition: COLUMN_PIN_POSITION.Right, isDraggable: enableColumnsOrderSortByDrag && groupedColumnOrderState.rightPinned.length > 1 }, header.id)))) }) }))] }));
11
12
  }
@@ -1,25 +1,34 @@
1
1
  import { Cell, Header, HeaderGroup, Row } from '@tanstack/react-table';
2
- export declare function useHeaderGroups(): {
2
+ import { CSSProperties } from 'react';
3
+ import { GroupedColumnOrderState } from '../types';
4
+ export declare function useHeaderGroups(groupedColumnOrderState: GroupedColumnOrderState): {
3
5
  unpinned: HeaderGroup<any>[];
4
6
  leftPinned?: undefined;
5
7
  rightPinned?: undefined;
6
8
  } | {
7
- leftPinned: HeaderGroup<any>[] | undefined;
8
- rightPinned: HeaderGroup<any>[] | undefined;
9
+ leftPinned: {
10
+ headers: Header<unknown, unknown>[];
11
+ depth: number;
12
+ id: string;
13
+ }[] | undefined;
14
+ rightPinned: {
15
+ headers: Header<unknown, unknown>[];
16
+ depth: number;
17
+ id: string;
18
+ }[] | undefined;
9
19
  unpinned: HeaderGroup<any>[];
10
20
  };
11
- export declare function useRowCells<TData>(row: Row<TData>): {
21
+ export declare function useRowCells<TData>(row: Row<TData>, groupedColumnOrderState: GroupedColumnOrderState): {
12
22
  unpinned: Cell<TData, unknown>[];
13
- pinnedLeft?: undefined;
14
- pinnedRight?: undefined;
23
+ leftPinned?: undefined;
24
+ rightPinned?: undefined;
15
25
  } | {
16
- pinnedLeft: Cell<TData, unknown>[] | undefined;
17
- pinnedRight: Cell<TData, unknown>[] | undefined;
26
+ leftPinned: Cell<TData, unknown>[] | undefined;
27
+ rightPinned: Cell<TData, unknown>[] | undefined;
18
28
  unpinned: Cell<TData, unknown>[];
19
29
  };
20
- export declare function useCellSizes<TData>(element: Cell<TData, unknown> | Header<TData, unknown>): {
21
- minWidth: number | undefined;
22
- width: string;
23
- maxWidth: number | undefined;
24
- flexShrink: string;
30
+ type CellSizesOptions = {
31
+ isDraggable?: boolean;
25
32
  };
33
+ export declare function useCellSizes<TData>(element: Cell<TData, unknown> | Header<TData, unknown>, options?: CellSizesOptions): CSSProperties;
34
+ export {};
@@ -1,12 +1,23 @@
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) {
4
6
  return groups.some(group => group.headers.length);
5
7
  }
6
- export function useHeaderGroups() {
8
+ const sortHeaderGroup = (groups, groupOrder) => groups.map(group => (Object.assign(Object.assign({}, group), { headers: group.headers.sort((a, b) => {
9
+ const indexA = groupOrder.findIndex(columnId => columnId === a.id);
10
+ const indexB = groupOrder.findIndex(columnId => columnId === b.id);
11
+ if (indexA > -1 && indexB > -1) {
12
+ return indexA - indexB;
13
+ }
14
+ return 0;
15
+ }) })));
16
+ export function useHeaderGroups(groupedColumnOrderState) {
7
17
  const { table } = useTableContext();
8
18
  const columnDefs = table._getColumnDefs();
9
19
  const pinEnabled = table.getIsSomeColumnsPinned();
20
+ const { columnOrder } = table.getState();
10
21
  return useMemo(() => {
11
22
  if (!pinEnabled) {
12
23
  return {
@@ -16,18 +27,29 @@ export function useHeaderGroups() {
16
27
  const left = table.getLeftHeaderGroups();
17
28
  const right = table.getRightHeaderGroups();
18
29
  return {
19
- leftPinned: hasHeaders(left) ? left : undefined,
20
- rightPinned: hasHeaders(right) ? right : undefined,
30
+ leftPinned: hasHeaders(left) ? sortHeaderGroup(left, groupedColumnOrderState.leftPinned) : undefined,
31
+ rightPinned: hasHeaders(right) ? sortHeaderGroup(right, groupedColumnOrderState.rightPinned) : undefined,
21
32
  unpinned: table.getCenterHeaderGroups(),
22
33
  };
23
34
  // need to rebuild if columnDefinitions has changed
24
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
25
- }, [table, pinEnabled, columnDefs]);
36
+ }, [table, pinEnabled, columnDefs, columnOrder, groupedColumnOrderState]);
37
+ }
38
+ function sortPinnedColumnsByOrderState(groupOrder) {
39
+ return (a, b) => {
40
+ const indexA = groupOrder.findIndex(columnId => columnId === a.column.id);
41
+ const indexB = groupOrder.findIndex(columnId => columnId === b.column.id);
42
+ if (indexA > -1 && indexB > -1) {
43
+ return indexA - indexB;
44
+ }
45
+ return 0;
46
+ };
26
47
  }
27
- export function useRowCells(row) {
48
+ export function useRowCells(row, groupedColumnOrderState) {
28
49
  const { table } = useTableContext();
29
50
  const pinEnabled = table.getIsSomeColumnsPinned();
30
51
  const columnDefs = table._getColumnDefs();
52
+ const { columnOrder } = table.getState();
31
53
  return useMemo(() => {
32
54
  if (!pinEnabled) {
33
55
  return {
@@ -37,24 +59,45 @@ export function useRowCells(row) {
37
59
  const left = row.getLeftVisibleCells();
38
60
  const right = row.getRightVisibleCells();
39
61
  return {
40
- pinnedLeft: left.length ? left : undefined,
41
- pinnedRight: right.length ? right : undefined,
62
+ leftPinned: left.length
63
+ ? left.slice().sort(sortPinnedColumnsByOrderState(groupedColumnOrderState.leftPinned))
64
+ : undefined,
65
+ rightPinned: right.length
66
+ ? right.slice().sort(sortPinnedColumnsByOrderState(groupedColumnOrderState.rightPinned))
67
+ : undefined,
42
68
  unpinned: row.getCenterVisibleCells(),
43
69
  };
44
70
  // need to rebuild if columnDefinitions has changed
45
71
  // eslint-disable-next-line react-hooks/exhaustive-deps
46
- }, [row, pinEnabled, columnDefs]);
72
+ }, [row, pinEnabled, columnDefs, columnOrder, groupedColumnOrderState]);
47
73
  }
48
- export function useCellSizes(element) {
74
+ export function useCellSizes(element, options) {
49
75
  const column = element.column;
76
+ const { isDragging, transform } = useSortable({
77
+ id: column.id,
78
+ });
50
79
  const minWidth = column.columnDef.minSize;
51
80
  const maxWidth = column.columnDef.maxSize;
52
81
  const width = `var(--table-column-${column.id}-size)`;
53
82
  const flexShrink = `var(--table-column-${column.id}-flex)`;
54
- return useMemo(() => ({
55
- minWidth,
56
- width,
57
- maxWidth,
58
- flexShrink,
59
- }), [flexShrink, maxWidth, minWidth, width]);
83
+ const isHeaderCell = 'headerGroup' in element;
84
+ return useMemo(() => {
85
+ const styles = {
86
+ minWidth,
87
+ width,
88
+ maxWidth,
89
+ flexShrink,
90
+ };
91
+ if (options === null || options === void 0 ? void 0 : options.isDraggable) {
92
+ styles.opacity = isDragging ? 0.8 : 1;
93
+ styles.position = 'relative';
94
+ styles.transform = CSS.Translate.toString(transform);
95
+ styles.transition = 'width transform 0.2s ease-in-out';
96
+ styles.zIndex = isDragging ? 1 : 0;
97
+ if (isHeaderCell) {
98
+ styles.whiteSpace = 'nowrap';
99
+ }
100
+ }
101
+ return styles;
102
+ }, [options === null || options === void 0 ? void 0 : options.isDraggable, flexShrink, isDragging, isHeaderCell, maxWidth, minWidth, transform, width]);
60
103
  }
@@ -40,5 +40,10 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
40
40
  size: number;
41
41
  };
42
42
  export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
43
+ export type GroupedColumnOrderState = {
44
+ leftPinned: string[];
45
+ rightPinned: string[];
46
+ unpinned: string[];
47
+ };
43
48
  export type { RowActionsColumnDefProps, StatusColumnDefinitionProps, RowInfo, RowClickHandler, ActionsGenerator, CopyCellProps, MapStatusToAppearanceFnType, } from './helperComponents';
44
49
  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.30.0",
7
+ "version": "0.30.1-preview-9a03a1c9.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -36,6 +36,10 @@
36
36
  "license": "Apache-2.0",
37
37
  "scripts": {},
38
38
  "dependencies": {
39
+ "@dnd-kit/core": "6.3.1",
40
+ "@dnd-kit/modifiers": "9.0.0",
41
+ "@dnd-kit/sortable": "10.0.0",
42
+ "@dnd-kit/utilities": "3.2.2",
39
43
  "@snack-uikit/button": "0.19.7",
40
44
  "@snack-uikit/chips": "0.25.3",
41
45
  "@snack-uikit/icon-predefined": "0.7.3",
@@ -61,5 +65,5 @@
61
65
  "peerDependencies": {
62
66
  "@snack-uikit/locale": "*"
63
67
  },
64
- "gitHead": "afb93f86dbab6039f4c267a9d0e85350b59053c5"
68
+ "gitHead": "a7e76287bbbbb376afb7bc895cfa1e125751608f"
65
69
  }
@@ -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,
@@ -43,7 +45,7 @@ import { getTreeColumnDef } from '../../helperComponents/Cells/TreeCell';
43
45
  import { ColumnDefinition } from '../../types';
44
46
  import { fuzzyFilter } from '../../utils';
45
47
  import { TableProps } from '../types';
46
- import { useLoadingTable, useStateControl } from './hooks';
48
+ import { useColumnOrderByDrag, useLoadingTable, useStateControl } from './hooks';
47
49
  import { usePageReset } from './hooks/usePageReset';
48
50
  import styles from './styles.module.scss';
49
51
  import {
@@ -97,6 +99,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
97
99
  savedState,
98
100
  expanding,
99
101
  bulkActions: bulkActionsProp,
102
+ enableColumnsOrderSortByDrag,
100
103
  ...rest
101
104
  }: TableProps<TData, TFilters>) {
102
105
  const { state: globalFilter, onStateChange: onGlobalFilterChange } = useStateControl<string>(search, '');
@@ -130,6 +133,9 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
130
133
  return cols;
131
134
  }, [columnDefinitions, enableSelection, enableSelectPinned, expanding]);
132
135
 
136
+ const { columnOrder, setColumnOrder, groupedColumnOrderState, sensors, handleDragEnd } =
137
+ useColumnOrderByDrag(tableColumns);
138
+
133
139
  const columnPinning = useMemo(() => {
134
140
  const pinningState: Required<ColumnPinningState> = { left: [], right: [] };
135
141
  for (const col of tableColumns) {
@@ -160,6 +166,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
160
166
  columns: tableColumns,
161
167
  state: {
162
168
  columnPinning,
169
+ columnOrder,
163
170
  globalFilter,
164
171
  rowSelection,
165
172
  sorting,
@@ -173,6 +180,7 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
173
180
  minSize: 40,
174
181
  cell: (cell: CellContext<TData, unknown>) => <TruncateString text={String(cell.getValue())} maxLines={1} />,
175
182
  },
183
+ onColumnOrderChange: setColumnOrder,
176
184
 
177
185
  manualSorting,
178
186
  manualPagination,
@@ -362,110 +370,133 @@ export function Table<TData extends object, TFilters extends FiltersState = Reco
362
370
  const showToolbar = !suppressToolbar;
363
371
 
364
372
  return (
365
- <CellAutoResizeContext.Provider value={{ updateCellMap }}>
366
- <div
367
- style={{
368
- '--page-size': cssPageSize,
369
- }}
370
- className={cn(styles.wrapper, className)}
371
- {...extractSupportProps(rest)}
372
- >
373
- {showToolbar && (
374
- <div className={styles.header}>
375
- <Toolbar
376
- search={
377
- suppressSearch
378
- ? undefined
379
- : {
380
- value: globalFilter,
381
- onChange: onGlobalFilterChange,
382
- loading: search?.loading,
383
- placeholder: search?.placeholder || t('searchPlaceholder'),
384
- }
385
- }
386
- className={styles.toolbar}
387
- onRefresh={onRefresh ? handleOnRefresh : undefined}
388
- bulkActions={bulkActions}
389
- selectionMode={rowSelectionProp?.multiRow ? 'multiple' : 'single'}
390
- checked={table.getIsAllPageRowsSelected()}
391
- indeterminate={table.getIsSomePageRowsSelected()}
392
- onCheck={enableSelection ? handleOnToolbarCheck : undefined}
393
- outline={outline}
394
- after={
395
- toolbarAfter || exportSettings ? (
396
- <>
397
- {toolbarAfter}
398
- {exportSettings && (
399
- <ExportButton
400
- settings={exportSettings}
401
- columnDefinitions={columnDefinitions}
402
- data={data}
403
- topRows={filteredTopRows}
404
- centerRows={centerRows}
373
+ <DndContext
374
+ collisionDetection={closestCenter}
375
+ modifiers={[restrictToHorizontalAxis]}
376
+ onDragEnd={handleDragEnd}
377
+ sensors={sensors}
378
+ >
379
+ <CellAutoResizeContext.Provider value={{ updateCellMap }}>
380
+ <div
381
+ style={{
382
+ '--page-size': cssPageSize,
383
+ }}
384
+ className={cn(styles.wrapper, className)}
385
+ {...extractSupportProps(rest)}
386
+ >
387
+ {showToolbar && (
388
+ <div className={styles.header}>
389
+ <Toolbar
390
+ search={
391
+ suppressSearch
392
+ ? undefined
393
+ : {
394
+ value: globalFilter,
395
+ onChange: onGlobalFilterChange,
396
+ loading: search?.loading,
397
+ placeholder: search?.placeholder || t('searchPlaceholder'),
398
+ }
399
+ }
400
+ className={styles.toolbar}
401
+ onRefresh={onRefresh ? handleOnRefresh : undefined}
402
+ bulkActions={bulkActions}
403
+ selectionMode={rowSelectionProp?.multiRow ? 'multiple' : 'single'}
404
+ checked={table.getIsAllPageRowsSelected()}
405
+ indeterminate={table.getIsSomePageRowsSelected()}
406
+ onCheck={enableSelection ? handleOnToolbarCheck : undefined}
407
+ outline={outline}
408
+ after={
409
+ toolbarAfter || exportSettings ? (
410
+ <>
411
+ {toolbarAfter}
412
+ {exportSettings && (
413
+ <ExportButton
414
+ settings={exportSettings}
415
+ columnDefinitions={columnDefinitions}
416
+ data={data}
417
+ topRows={filteredTopRows}
418
+ centerRows={centerRows}
419
+ />
420
+ )}
421
+ </>
422
+ ) : undefined
423
+ }
424
+ moreActions={moreActions}
425
+ filterRow={columnFilters}
426
+ data-test-id={TEST_IDS.toolbar}
427
+ />
428
+ </div>
429
+ )}
430
+
431
+ <div className={styles.scrollWrapper} data-outline={outline || undefined}>
432
+ <Scroll size='s' className={styles.table} ref={scrollContainerRef}>
433
+ <div className={styles.tableContent} style={columnSizes.vars}>
434
+ <TableContext.Provider value={{ table }}>
435
+ {loading ? (
436
+ <SkeletonContextProvider loading>
437
+ <HeaderRow groupedColumnOrderState={groupedColumnOrderState} />
438
+ {loadingTableRows.map(row => (
439
+ <BodyRow key={row.id} row={row} groupedColumnOrderState={groupedColumnOrderState} />
440
+ ))}
441
+ </SkeletonContextProvider>
442
+ ) : (
443
+ <>
444
+ {centerRows.length || filteredTopRows.length ? (
445
+ <HeaderRow
446
+ groupedColumnOrderState={groupedColumnOrderState}
447
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
448
+ />
449
+ ) : null}
450
+
451
+ {filteredTopRows.length ? (
452
+ <div className={styles.topRowWrapper}>
453
+ {filteredTopRows.map(row => (
454
+ <BodyRow
455
+ key={row.id}
456
+ row={row}
457
+ onRowClick={onRowClick}
458
+ groupedColumnOrderState={groupedColumnOrderState}
459
+ />
460
+ ))}
461
+ </div>
462
+ ) : null}
463
+
464
+ {centerRows.map(row => (
465
+ <BodyRow
466
+ key={row.id}
467
+ row={row}
468
+ onRowClick={onRowClick}
469
+ groupedColumnOrderState={groupedColumnOrderState}
470
+ enableColumnsOrderSortByDrag={enableColumnsOrderSortByDrag}
471
+ />
472
+ ))}
473
+
474
+ <TableEmptyState
475
+ emptyStates={emptyStates}
476
+ dataError={dataError}
477
+ dataFiltered={dataFiltered || Boolean(table.getState().globalFilter)}
478
+ tableRowsLength={tableRows.length + filteredTopRows.length}
405
479
  />
406
- )}
407
- </>
408
- ) : undefined
409
- }
410
- moreActions={moreActions}
411
- filterRow={columnFilters}
412
- data-test-id={TEST_IDS.toolbar}
413
- />
480
+ </>
481
+ )}
482
+ </TableContext.Provider>
483
+ </div>
484
+ <div className={styles.scrollStub} ref={scrollRef as RefObject<HTMLDivElement>} />
485
+ </Scroll>
414
486
  </div>
415
- )}
416
-
417
- <div className={styles.scrollWrapper} data-outline={outline || undefined}>
418
- <Scroll size='s' className={styles.table} ref={scrollContainerRef}>
419
- <div className={styles.tableContent} style={columnSizes.vars}>
420
- <TableContext.Provider value={{ table }}>
421
- {loading ? (
422
- <SkeletonContextProvider loading>
423
- <HeaderRow />
424
- {loadingTableRows.map(row => (
425
- <BodyRow key={row.id} row={row} />
426
- ))}
427
- </SkeletonContextProvider>
428
- ) : (
429
- <>
430
- {centerRows.length || filteredTopRows.length ? <HeaderRow /> : null}
431
-
432
- {filteredTopRows.length ? (
433
- <div className={styles.topRowWrapper}>
434
- {filteredTopRows.map(row => (
435
- <BodyRow key={row.id} row={row} onRowClick={onRowClick} />
436
- ))}
437
- </div>
438
- ) : null}
439
-
440
- {centerRows.map(row => (
441
- <BodyRow key={row.id} row={row} onRowClick={onRowClick} />
442
- ))}
443
-
444
- <TableEmptyState
445
- emptyStates={emptyStates}
446
- dataError={dataError}
447
- dataFiltered={dataFiltered || Boolean(table.getState().globalFilter)}
448
- tableRowsLength={tableRows.length + filteredTopRows.length}
449
- />
450
- </>
451
- )}
452
- </TableContext.Provider>
453
- </div>
454
- <div className={styles.scrollStub} ref={scrollRef as RefObject<HTMLDivElement>} />
455
- </Scroll>
456
- </div>
457
487
 
458
- {!suppressPagination && (
459
- <TablePagination
460
- table={table}
461
- options={paginationProp?.options}
462
- optionsLabel={paginationProp?.optionsLabel}
463
- pageCount={pageCount}
464
- optionsRender={paginationProp?.optionsRender}
465
- />
466
- )}
467
- </div>
468
- </CellAutoResizeContext.Provider>
488
+ {!suppressPagination && (
489
+ <TablePagination
490
+ table={table}
491
+ options={paginationProp?.options}
492
+ optionsLabel={paginationProp?.optionsLabel}
493
+ pageCount={pageCount}
494
+ optionsRender={paginationProp?.optionsRender}
495
+ />
496
+ )}
497
+ </div>
498
+ </CellAutoResizeContext.Provider>
499
+ </DndContext>
469
500
  );
470
501
  }
471
502
 
@@ -1,2 +1,3 @@
1
1
  export * from './useLoadingTable';
2
2
  export * from './useStateControl';
3
+ export * from './useColumnOrderByDrag';