@snack-uikit/table 0.29.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 (66) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/components/Table/Table.d.ts +1 -1
  4. package/dist/cjs/components/Table/Table.js +141 -104
  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 +9 -0
  8. package/dist/cjs/components/Table/hooks/useColumnOrderByDrag.js +76 -0
  9. package/dist/cjs/components/Table/utils.js +6 -3
  10. package/dist/cjs/components/types.d.ts +2 -0
  11. package/dist/cjs/constants.d.ts +1 -0
  12. package/dist/cjs/constants.js +3 -2
  13. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  14. package/dist/cjs/helperComponents/Cells/BodyCell/BodyCell.js +13 -3
  15. package/dist/cjs/helperComponents/Cells/HeaderCell/DragHandle.d.ts +6 -0
  16. package/dist/cjs/helperComponents/Cells/HeaderCell/DragHandle.js +27 -0
  17. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  18. package/dist/cjs/helperComponents/Cells/HeaderCell/HeaderCell.js +22 -2
  19. package/dist/cjs/helperComponents/Cells/HeaderCell/styles.module.css +11 -0
  20. package/dist/cjs/helperComponents/Rows/BodyRow.d.ts +4 -1
  21. package/dist/cjs/helperComponents/Rows/BodyRow.js +30 -12
  22. package/dist/cjs/helperComponents/Rows/HeaderRow.d.ts +7 -1
  23. package/dist/cjs/helperComponents/Rows/HeaderRow.js +33 -13
  24. package/dist/cjs/helperComponents/hooks.d.ts +22 -13
  25. package/dist/cjs/helperComponents/hooks.js +63 -15
  26. package/dist/cjs/types.d.ts +5 -0
  27. package/dist/esm/components/Table/Table.d.ts +1 -1
  28. package/dist/esm/components/Table/Table.js +34 -25
  29. package/dist/esm/components/Table/hooks/index.d.ts +1 -0
  30. package/dist/esm/components/Table/hooks/index.js +1 -0
  31. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.d.ts +9 -0
  32. package/dist/esm/components/Table/hooks/useColumnOrderByDrag.js +66 -0
  33. package/dist/esm/components/Table/utils.js +6 -3
  34. package/dist/esm/components/types.d.ts +2 -0
  35. package/dist/esm/constants.d.ts +1 -0
  36. package/dist/esm/constants.js +1 -0
  37. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.d.ts +2 -1
  38. package/dist/esm/helperComponents/Cells/BodyCell/BodyCell.js +7 -3
  39. package/dist/esm/helperComponents/Cells/HeaderCell/DragHandle.d.ts +6 -0
  40. package/dist/esm/helperComponents/Cells/HeaderCell/DragHandle.js +6 -0
  41. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.d.ts +2 -1
  42. package/dist/esm/helperComponents/Cells/HeaderCell/HeaderCell.js +13 -4
  43. package/dist/esm/helperComponents/Cells/HeaderCell/styles.module.css +11 -0
  44. package/dist/esm/helperComponents/Rows/BodyRow.d.ts +4 -1
  45. package/dist/esm/helperComponents/Rows/BodyRow.js +4 -3
  46. package/dist/esm/helperComponents/Rows/HeaderRow.d.ts +7 -1
  47. package/dist/esm/helperComponents/Rows/HeaderRow.js +4 -3
  48. package/dist/esm/helperComponents/hooks.d.ts +22 -13
  49. package/dist/esm/helperComponents/hooks.js +58 -15
  50. package/dist/esm/types.d.ts +5 -0
  51. package/package.json +6 -2
  52. package/src/components/Table/Table.tsx +151 -117
  53. package/src/components/Table/hooks/index.ts +1 -0
  54. package/src/components/Table/hooks/useColumnOrderByDrag.ts +100 -0
  55. package/src/components/Table/utils.ts +8 -3
  56. package/src/components/types.ts +3 -0
  57. package/src/constants.tsx +2 -0
  58. package/src/helperComponents/Cells/BodyCell/BodyCell.tsx +9 -2
  59. package/src/helperComponents/Cells/HeaderCell/DragHandle.tsx +18 -0
  60. package/src/helperComponents/Cells/HeaderCell/HeaderCell.tsx +19 -4
  61. package/src/helperComponents/Cells/HeaderCell/styles.module.scss +15 -4
  62. package/src/helperComponents/Rows/BodyRow.tsx +36 -9
  63. package/src/helperComponents/Rows/HeaderRow.tsx +51 -18
  64. package/src/helperComponents/Rows/styles.module.scss +2 -2
  65. package/src/helperComponents/hooks.ts +78 -15
  66. package/src/types.ts +6 -0
@@ -1,7 +1,9 @@
1
+ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';
1
2
  import { Row as TableRow } from '@tanstack/react-table';
2
3
  import { MouseEvent, useState } from 'react';
3
4
 
4
5
  import { COLUMN_PIN_POSITION, TEST_IDS } from '../../constants';
6
+ import { GroupedColumnOrderState } from '../../types';
5
7
  import { BodyCell } from '../Cells';
6
8
  import { RowContext } from '../contexts';
7
9
  import { useRowCells } from '../hooks';
@@ -21,10 +23,17 @@ export type RowClickHandler<TData> = (e: MouseEvent<HTMLDivElement>, row: RowInf
21
23
  export type BodyRowProps<TData> = {
22
24
  row: TableRow<TData>;
23
25
  onRowClick?: RowClickHandler<TData>;
26
+ groupedColumnOrderState: GroupedColumnOrderState;
27
+ enableColumnsOrderSortByDrag?: boolean;
24
28
  };
25
29
 
26
- export function BodyRow<TData>({ row, onRowClick }: BodyRowProps<TData>) {
27
- const { pinnedLeft, pinnedRight, unpinned } = useRowCells(row);
30
+ export function BodyRow<TData>({
31
+ row,
32
+ onRowClick,
33
+ groupedColumnOrderState,
34
+ enableColumnsOrderSortByDrag,
35
+ }: BodyRowProps<TData>) {
36
+ const { leftPinned, rightPinned, unpinned } = useRowCells(row, groupedColumnOrderState);
28
37
 
29
38
  const [dropListOpened, setDropListOpen] = useState(false);
30
39
 
@@ -57,22 +66,40 @@ export function BodyRow<TData>({ row, onRowClick }: BodyRowProps<TData>) {
57
66
  data-row-id={row.id}
58
67
  className={styles.bodyRow}
59
68
  >
60
- {pinnedLeft && (
69
+ {leftPinned && (
61
70
  <PinnedCells position={COLUMN_PIN_POSITION.Left}>
62
- {pinnedLeft.map(cell => (
63
- <BodyCell key={cell.id} cell={cell} />
71
+ {leftPinned.map(cell => (
72
+ <SortableContext
73
+ key={cell.id}
74
+ items={groupedColumnOrderState.leftPinned}
75
+ strategy={horizontalListSortingStrategy}
76
+ >
77
+ <BodyCell key={cell.id} cell={cell} isDraggable={enableColumnsOrderSortByDrag} />
78
+ </SortableContext>
64
79
  ))}
65
80
  </PinnedCells>
66
81
  )}
67
82
 
68
83
  {unpinned.map(cell => (
69
- <BodyCell key={cell.id} cell={cell} />
84
+ <SortableContext
85
+ key={cell.id}
86
+ items={groupedColumnOrderState.unpinned}
87
+ strategy={horizontalListSortingStrategy}
88
+ >
89
+ <BodyCell key={cell.id} cell={cell} isDraggable={enableColumnsOrderSortByDrag} />
90
+ </SortableContext>
70
91
  ))}
71
92
 
72
- {pinnedRight && (
93
+ {rightPinned && (
73
94
  <PinnedCells position={COLUMN_PIN_POSITION.Right}>
74
- {pinnedRight.map(cell => (
75
- <BodyCell key={cell.id} cell={cell} />
95
+ {rightPinned.map(cell => (
96
+ <SortableContext
97
+ key={cell.id}
98
+ items={groupedColumnOrderState.rightPinned}
99
+ strategy={horizontalListSortingStrategy}
100
+ >
101
+ <BodyCell key={cell.id} cell={cell} isDraggable={enableColumnsOrderSortByDrag} />
102
+ </SortableContext>
76
103
  ))}
77
104
  </PinnedCells>
78
105
  )}
@@ -1,37 +1,70 @@
1
+ import { horizontalListSortingStrategy, SortableContext } from '@dnd-kit/sortable';
2
+
1
3
  import { COLUMN_PIN_POSITION, TEST_IDS } from '../../constants';
4
+ import { GroupedColumnOrderState } from '../../types';
2
5
  import { HeaderCell } from '../Cells';
3
6
  import { useHeaderGroups } from '../hooks';
4
7
  import { PinnedCells } from './PinnedCells';
5
8
  import { Row } from './Row';
6
9
  import styles from './styles.module.scss';
7
10
 
8
- export function HeaderRow() {
9
- const { leftPinned, unpinned, rightPinned } = useHeaderGroups();
11
+ type Props = {
12
+ groupedColumnOrderState: GroupedColumnOrderState;
13
+ enableColumnsOrderSortByDrag?: boolean;
14
+ };
15
+
16
+ export function HeaderRow({ groupedColumnOrderState, enableColumnsOrderSortByDrag }: Props) {
17
+ const { leftPinned, unpinned, rightPinned } = useHeaderGroups(groupedColumnOrderState);
10
18
 
11
19
  return (
12
20
  <Row className={styles.tableHeader} data-test-id={TEST_IDS.headerRow}>
13
21
  {leftPinned && (
14
- <PinnedCells position={COLUMN_PIN_POSITION.Left}>
15
- {leftPinned.map(headerGroup =>
16
- headerGroup.headers.map(header =>
17
- header.isPlaceholder ? null : <HeaderCell key={header.id} header={header} />,
18
- ),
19
- )}
20
- </PinnedCells>
22
+ <SortableContext items={groupedColumnOrderState.leftPinned} strategy={horizontalListSortingStrategy}>
23
+ <PinnedCells position={COLUMN_PIN_POSITION.Left}>
24
+ {leftPinned.map(headerGroup =>
25
+ headerGroup.headers.map(header =>
26
+ header.isPlaceholder ? null : (
27
+ <HeaderCell
28
+ key={header.id}
29
+ header={header}
30
+ isDraggable={enableColumnsOrderSortByDrag && groupedColumnOrderState.leftPinned.length > 1}
31
+ />
32
+ ),
33
+ ),
34
+ )}
35
+ </PinnedCells>
36
+ </SortableContext>
21
37
  )}
22
38
 
23
- {unpinned.map(headerGroup => headerGroup.headers.map(header => <HeaderCell key={header.id} header={header} />))}
39
+ <SortableContext items={groupedColumnOrderState.unpinned} strategy={horizontalListSortingStrategy}>
40
+ {unpinned.map(headerGroup =>
41
+ headerGroup.headers.map(header => (
42
+ <HeaderCell
43
+ key={header.id}
44
+ header={header}
45
+ isDraggable={enableColumnsOrderSortByDrag && groupedColumnOrderState.unpinned.length > 1}
46
+ />
47
+ )),
48
+ )}
49
+ </SortableContext>
24
50
 
25
51
  {rightPinned && (
26
- <PinnedCells position={COLUMN_PIN_POSITION.Right}>
27
- {rightPinned.map(headerGroup =>
28
- headerGroup.headers.map(header =>
29
- header.isPlaceholder ? null : (
30
- <HeaderCell key={header.id} header={header} pinPosition={COLUMN_PIN_POSITION.Right} />
52
+ <SortableContext items={groupedColumnOrderState.rightPinned} strategy={horizontalListSortingStrategy}>
53
+ <PinnedCells position={COLUMN_PIN_POSITION.Right}>
54
+ {rightPinned.map(headerGroup =>
55
+ headerGroup.headers.map(header =>
56
+ header.isPlaceholder ? null : (
57
+ <HeaderCell
58
+ key={header.id}
59
+ header={header}
60
+ pinPosition={COLUMN_PIN_POSITION.Right}
61
+ isDraggable={enableColumnsOrderSortByDrag && groupedColumnOrderState.rightPinned.length > 1}
62
+ />
63
+ ),
31
64
  ),
32
- ),
33
- )}
34
- </PinnedCells>
65
+ )}
66
+ </PinnedCells>
67
+ </SortableContext>
35
68
  )}
36
69
  </Row>
37
70
  );
@@ -42,8 +42,8 @@ $snack-ui-table-row-background: var(--snack-ui-table-row-background);
42
42
  content: '';
43
43
 
44
44
  position: absolute;
45
- top: calc(0px - styles-tokens-table.$border-width-table);
46
- bottom: calc(0px - styles-tokens-table.$border-width-table);
45
+ top: calc(0px - #{styles-tokens-table.$border-width-table});
46
+ bottom: calc(0px - #{styles-tokens-table.$border-width-table});
47
47
 
48
48
  box-sizing: border-box;
49
49
  width: styles-tokens-table.$border-width-table;
@@ -1,17 +1,36 @@
1
+ import { useSortable } from '@dnd-kit/sortable';
2
+ import { CSS } from '@dnd-kit/utilities';
1
3
  import { Cell, Header, HeaderGroup, Row } from '@tanstack/react-table';
2
- import { useMemo } from 'react';
4
+ import { CSSProperties, useMemo } from 'react';
3
5
 
6
+ import { GroupedColumnOrderState } from '../types';
4
7
  import { useTableContext } from './contexts';
5
8
 
6
9
  function hasHeaders<TData>(groups: HeaderGroup<TData>[]) {
7
10
  return groups.some(group => group.headers.length);
8
11
  }
9
12
 
10
- export function useHeaderGroups() {
13
+ const sortHeaderGroup = (groups: HeaderGroup<unknown>[], groupOrder: string[]) =>
14
+ groups.map(group => ({
15
+ ...group,
16
+ headers: group.headers.sort((a, b) => {
17
+ const indexA = groupOrder.findIndex(columnId => columnId === a.id);
18
+ const indexB = groupOrder.findIndex(columnId => columnId === b.id);
19
+
20
+ if (indexA > -1 && indexB > -1) {
21
+ return indexA - indexB;
22
+ }
23
+
24
+ return 0;
25
+ }),
26
+ }));
27
+
28
+ export function useHeaderGroups(groupedColumnOrderState: GroupedColumnOrderState) {
11
29
  const { table } = useTableContext();
12
30
 
13
31
  const columnDefs = table._getColumnDefs();
14
32
  const pinEnabled = table.getIsSomeColumnsPinned();
33
+ const { columnOrder } = table.getState();
15
34
 
16
35
  return useMemo(() => {
17
36
  if (!pinEnabled) {
@@ -24,20 +43,34 @@ export function useHeaderGroups() {
24
43
  const right = table.getRightHeaderGroups();
25
44
 
26
45
  return {
27
- leftPinned: hasHeaders(left) ? left : undefined,
28
- rightPinned: hasHeaders(right) ? right : undefined,
46
+ leftPinned: hasHeaders(left) ? sortHeaderGroup(left, groupedColumnOrderState.leftPinned) : undefined,
47
+ rightPinned: hasHeaders(right) ? sortHeaderGroup(right, groupedColumnOrderState.rightPinned) : undefined,
29
48
  unpinned: table.getCenterHeaderGroups(),
30
49
  };
31
50
  // need to rebuild if columnDefinitions has changed
32
51
  // eslint-disable-next-line react-hooks/exhaustive-deps
33
- }, [table, pinEnabled, columnDefs]);
52
+ }, [table, pinEnabled, columnDefs, columnOrder, groupedColumnOrderState]);
53
+ }
54
+
55
+ function sortPinnedColumnsByOrderState<TData>(groupOrder: string[]) {
56
+ return (a: Cell<TData, unknown>, b: Cell<TData, unknown>) => {
57
+ const indexA = groupOrder.findIndex(columnId => columnId === a.column.id);
58
+ const indexB = groupOrder.findIndex(columnId => columnId === b.column.id);
59
+
60
+ if (indexA > -1 && indexB > -1) {
61
+ return indexA - indexB;
62
+ }
63
+
64
+ return 0;
65
+ };
34
66
  }
35
67
 
36
- export function useRowCells<TData>(row: Row<TData>) {
68
+ export function useRowCells<TData>(row: Row<TData>, groupedColumnOrderState: GroupedColumnOrderState) {
37
69
  const { table } = useTableContext();
38
70
 
39
71
  const pinEnabled = table.getIsSomeColumnsPinned();
40
72
  const columnDefs = table._getColumnDefs();
73
+ const { columnOrder } = table.getState();
41
74
 
42
75
  return useMemo(() => {
43
76
  if (!pinEnabled) {
@@ -50,30 +83,60 @@ export function useRowCells<TData>(row: Row<TData>) {
50
83
  const right = row.getRightVisibleCells();
51
84
 
52
85
  return {
53
- pinnedLeft: left.length ? left : undefined,
54
- pinnedRight: right.length ? right : undefined,
86
+ leftPinned: left.length
87
+ ? left.slice().sort(sortPinnedColumnsByOrderState(groupedColumnOrderState.leftPinned))
88
+ : undefined,
89
+ rightPinned: right.length
90
+ ? right.slice().sort(sortPinnedColumnsByOrderState(groupedColumnOrderState.rightPinned))
91
+ : undefined,
55
92
  unpinned: row.getCenterVisibleCells(),
56
93
  };
57
94
  // need to rebuild if columnDefinitions has changed
58
95
  // eslint-disable-next-line react-hooks/exhaustive-deps
59
- }, [row, pinEnabled, columnDefs]);
96
+ }, [row, pinEnabled, columnDefs, columnOrder, groupedColumnOrderState]);
60
97
  }
61
98
 
62
- export function useCellSizes<TData>(element: Cell<TData, unknown> | Header<TData, unknown>) {
99
+ type CellSizesOptions = {
100
+ isDraggable?: boolean;
101
+ };
102
+
103
+ export function useCellSizes<TData>(
104
+ element: Cell<TData, unknown> | Header<TData, unknown>,
105
+ options?: CellSizesOptions,
106
+ ) {
63
107
  const column = element.column;
64
108
 
109
+ const { isDragging, transform } = useSortable({
110
+ id: column.id,
111
+ });
112
+
65
113
  const minWidth = column.columnDef.minSize;
66
114
  const maxWidth = column.columnDef.maxSize;
67
115
  const width = `var(--table-column-${column.id}-size)`;
68
116
  const flexShrink = `var(--table-column-${column.id}-flex)`;
69
117
 
70
- return useMemo(
71
- () => ({
118
+ const isHeaderCell = 'headerGroup' in element;
119
+
120
+ return useMemo(() => {
121
+ const styles: CSSProperties = {
72
122
  minWidth,
73
123
  width,
74
124
  maxWidth,
75
125
  flexShrink,
76
- }),
77
- [flexShrink, maxWidth, minWidth, width],
78
- );
126
+ };
127
+
128
+ if (options?.isDraggable) {
129
+ styles.opacity = isDragging ? 0.8 : 1;
130
+ styles.position = 'relative';
131
+ styles.transform = CSS.Translate.toString(transform);
132
+ styles.transition = 'width transform 0.2s ease-in-out';
133
+ styles.zIndex = isDragging ? 1 : 0;
134
+
135
+ if (isHeaderCell) {
136
+ styles.whiteSpace = 'nowrap';
137
+ }
138
+ }
139
+
140
+ return styles;
141
+ }, [options?.isDraggable, flexShrink, isDragging, isHeaderCell, maxWidth, minWidth, transform, width]);
79
142
  }
package/src/types.ts CHANGED
@@ -66,6 +66,12 @@ type PinnedColumnDefinition<TData> = BaseColumnDefinition<TData> & {
66
66
 
67
67
  export type ColumnDefinition<TData> = NormalColumnDefinition<TData> | PinnedColumnDefinition<TData>;
68
68
 
69
+ export type GroupedColumnOrderState = {
70
+ leftPinned: string[];
71
+ rightPinned: string[];
72
+ unpinned: string[];
73
+ };
74
+
69
75
  export type {
70
76
  RowActionsColumnDefProps,
71
77
  StatusColumnDefinitionProps,