@purpurds/table 8.3.1 → 8.4.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.
- package/dist/LICENSE.txt +205 -35
- package/dist/drag-indicator-circle.d.ts +13 -0
- package/dist/drag-indicator-circle.d.ts.map +1 -0
- package/dist/draggable-table.d.ts +23 -0
- package/dist/draggable-table.d.ts.map +1 -0
- package/dist/empty-table.d.ts +14 -0
- package/dist/empty-table.d.ts.map +1 -0
- package/dist/loading-table-rows.d.ts +13 -0
- package/dist/loading-table-rows.d.ts.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/table-body.d.ts +2 -2
- package/dist/table-body.d.ts.map +1 -1
- package/dist/table-column-header-cell.d.ts +15 -2
- package/dist/table-column-header-cell.d.ts.map +1 -1
- package/dist/table-content.d.ts +42 -0
- package/dist/table-content.d.ts.map +1 -0
- package/dist/table-headers.d.ts +28 -0
- package/dist/table-headers.d.ts.map +1 -0
- package/dist/table-row-cell-skeleton.d.ts +1 -1
- package/dist/table-row-cell-skeleton.d.ts.map +1 -1
- package/dist/table-row-cell.d.ts +5 -2
- package/dist/table-row-cell.d.ts.map +1 -1
- package/dist/table-row.d.ts +2 -2
- package/dist/table-row.d.ts.map +1 -1
- package/dist/table-settings-drawer.d.ts +44 -11
- package/dist/table-settings-drawer.d.ts.map +1 -1
- package/dist/table.cjs.js +89 -85
- package/dist/table.cjs.js.map +1 -1
- package/dist/table.d.ts +3 -3
- package/dist/table.d.ts.map +1 -1
- package/dist/table.es.js +14397 -10195
- package/dist/table.es.js.map +1 -1
- package/dist/test-utils/helpers.d.ts +1 -0
- package/dist/test-utils/helpers.d.ts.map +1 -1
- package/dist/types.d.ts +23 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/use-drag-handle.hook.d.ts +15 -0
- package/dist/use-drag-handle.hook.d.ts.map +1 -0
- package/dist/use-drag-indicator-position.hook.d.ts +19 -0
- package/dist/use-drag-indicator-position.hook.d.ts.map +1 -0
- package/dist/use-drop-indicator.hook.d.ts +15 -0
- package/dist/use-drop-indicator.hook.d.ts.map +1 -0
- package/dist/use-element-visibility.hook.d.ts +4 -0
- package/dist/use-element-visibility.hook.d.ts.map +1 -0
- package/dist/use-table-scroll.hook.d.ts +6 -0
- package/dist/use-table-scroll.hook.d.ts.map +1 -0
- package/dist/utils/custom-keyboard-coordinates.d.ts +8 -0
- package/dist/utils/custom-keyboard-coordinates.d.ts.map +1 -0
- package/package.json +27 -23
- package/src/drag-indicator-circle.tsx +36 -0
- package/src/draggable-table.test.tsx +381 -0
- package/src/draggable-table.tsx +191 -0
- package/src/empty-table.tsx +54 -0
- package/src/loading-table-rows.tsx +41 -0
- package/src/table-body.tsx +1 -3
- package/src/table-column-header-cell.tsx +135 -64
- package/src/table-content-drag.test.tsx +505 -0
- package/src/table-content.tsx +165 -0
- package/src/table-dnd-integration.test.tsx +425 -0
- package/src/table-drag-and-drop.test.tsx +276 -0
- package/src/table-headers.tsx +118 -0
- package/src/table-row-cell-skeleton.tsx +1 -1
- package/src/table-row-cell.test.tsx +2 -1
- package/src/table-row-cell.tsx +42 -31
- package/src/table-row.tsx +1 -3
- package/src/table-settings-drawer.module.scss +165 -2
- package/src/table-settings-drawer.test.tsx +0 -99
- package/src/table-settings-drawer.tsx +359 -53
- package/src/table.module.scss +191 -30
- package/src/table.stories.tsx +60 -4
- package/src/table.test.tsx +5 -1
- package/src/table.tsx +255 -213
- package/src/test-utils/helpers.ts +2 -0
- package/src/types.ts +25 -2
- package/src/use-drag-handle.hook.tsx +60 -0
- package/src/use-drag-handle.test.tsx +380 -0
- package/src/use-drag-indicator-position.hook.ts +74 -0
- package/src/use-drop-indicator.hook.ts +46 -0
- package/src/use-element-visibility.hook.ts +28 -0
- package/src/use-table-scroll.hook.tsx +30 -0
- package/src/utils/custom-keyboard-coordinates.ts +83 -0
- package/vitest.setup.ts +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
import { type UniqueIdentifier } from "@dnd-kit/core";
|
|
3
|
+
import { useSortable } from "@dnd-kit/sortable";
|
|
2
4
|
import { Button } from "@purpurds/button";
|
|
3
5
|
import { Checkbox, type CheckedState } from "@purpurds/checkbox";
|
|
4
6
|
import { IconArrowDown } from "@purpurds/icon/arrow-down";
|
|
@@ -18,9 +20,15 @@ import {
|
|
|
18
20
|
} from "@tanstack/react-table";
|
|
19
21
|
import c from "classnames/bind";
|
|
20
22
|
|
|
23
|
+
import { DragIndicatorCircle } from "./drag-indicator-circle";
|
|
21
24
|
import styles from "./table.module.scss";
|
|
25
|
+
import { TableColumnDragHandle, useDragHandle } from "./use-drag-handle.hook";
|
|
26
|
+
import { useDragIndicatorPosition } from "./use-drag-indicator-position.hook";
|
|
27
|
+
import { useDropIndicator } from "./use-drop-indicator.hook";
|
|
28
|
+
import { useElementVisibility } from "./use-element-visibility.hook";
|
|
22
29
|
import useTruncatedTooltip from "./use-truncated-hook";
|
|
23
30
|
import { pxToRemString } from "./utils/unit-conversions";
|
|
31
|
+
|
|
24
32
|
const cx = c.bind(styles);
|
|
25
33
|
|
|
26
34
|
export type SortingEnabledProps = {
|
|
@@ -33,6 +41,16 @@ export type SortingDisabledProps = {
|
|
|
33
41
|
sortingAriaLabels?: never;
|
|
34
42
|
};
|
|
35
43
|
|
|
44
|
+
export type ColumnDragEnabledProps = {
|
|
45
|
+
enableColumnDrag: true;
|
|
46
|
+
columnDragAriaLabel: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ColumnDragDisabledProps = {
|
|
50
|
+
enableColumnDrag?: false;
|
|
51
|
+
columnDragAriaLabel?: never;
|
|
52
|
+
};
|
|
53
|
+
|
|
36
54
|
export type TableColumnHeaderCellProps<TData> = {
|
|
37
55
|
className?: string;
|
|
38
56
|
stickyColumn: boolean;
|
|
@@ -41,7 +59,12 @@ export type TableColumnHeaderCellProps<TData> = {
|
|
|
41
59
|
tanstackTable: Table<TData>;
|
|
42
60
|
isScrolled?: boolean;
|
|
43
61
|
showBorder?: boolean;
|
|
44
|
-
|
|
62
|
+
sortableId?: string;
|
|
63
|
+
activeId?: UniqueIdentifier | null;
|
|
64
|
+
overlayActive?: boolean;
|
|
65
|
+
tableHasFilters: boolean;
|
|
66
|
+
} & (SortingEnabledProps | SortingDisabledProps) &
|
|
67
|
+
(ColumnDragEnabledProps | ColumnDragDisabledProps);
|
|
45
68
|
|
|
46
69
|
export type SortingAriaLabels = {
|
|
47
70
|
desc: string;
|
|
@@ -63,35 +86,35 @@ export const TableColumnHeaderCell = <TData extends RowData>({
|
|
|
63
86
|
tanstackTable,
|
|
64
87
|
isScrolled,
|
|
65
88
|
showBorder,
|
|
89
|
+
enableColumnDrag,
|
|
90
|
+
sortableId,
|
|
91
|
+
activeId,
|
|
92
|
+
overlayActive,
|
|
93
|
+
columnDragAriaLabel,
|
|
94
|
+
tableHasFilters,
|
|
66
95
|
}: TableColumnHeaderCellProps<TData>) => {
|
|
96
|
+
const headerRef = useRef<HTMLTableCellElement | null>(null);
|
|
97
|
+
|
|
98
|
+
const { attributes, listeners, setNodeRef, isSorting } = useSortable({
|
|
99
|
+
id: sortableId || header.id,
|
|
100
|
+
disabled: !enableColumnDrag || activeId !== null,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const { mouseDownActive, handleMouseDown } = useDragHandle();
|
|
104
|
+
const isActiveColumn = activeId === header.id || sortableId === activeId;
|
|
105
|
+
const draggingActive = (overlayActive || isSorting || mouseDownActive) && isActiveColumn;
|
|
106
|
+
const isCheckBox = header.column.id === "row-selection" || header.id === "row-selection";
|
|
107
|
+
const isRadiobutton = header.column.id === "row-radio" || header.id === "row-radio";
|
|
67
108
|
const canSort = enableSorting && header.column.getCanSort();
|
|
68
|
-
const hasFilter = header.column.
|
|
69
|
-
const isCheckBox = header.column.columnDef.meta?.cellType === "rowSelection";
|
|
70
|
-
const isRadiobutton = header.column.columnDef.meta?.cellType === "rowToggle";
|
|
71
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
72
|
-
const elementRef = useRef<HTMLTableCellElement>(null);
|
|
73
|
-
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
const currentElement = elementRef.current;
|
|
76
|
-
if (!currentElement) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
109
|
+
const hasFilter = header.column.columnDef.meta?.filterVariant !== undefined && tableHasFilters;
|
|
79
110
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
observer.observe(currentElement);
|
|
111
|
+
const dropIndicatorPosition = useDropIndicator({
|
|
112
|
+
itemId: header.id,
|
|
113
|
+
sortableId,
|
|
114
|
+
enableDrag: !!enableColumnDrag,
|
|
115
|
+
});
|
|
88
116
|
|
|
89
|
-
|
|
90
|
-
if (currentElement) {
|
|
91
|
-
observer.unobserve(currentElement);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}, []);
|
|
117
|
+
const isVisible = useElementVisibility(headerRef, 1);
|
|
95
118
|
|
|
96
119
|
const getSortingDirection = (sortingDirection: SortDirection | false) => {
|
|
97
120
|
switch (sortingDirection) {
|
|
@@ -116,47 +139,95 @@ export const TableColumnHeaderCell = <TData extends RowData>({
|
|
|
116
139
|
? `${header.column.columnDef.header}`
|
|
117
140
|
: undefined;
|
|
118
141
|
|
|
142
|
+
const setRefs = (node: HTMLTableCellElement | null) => {
|
|
143
|
+
headerRef.current = node;
|
|
144
|
+
if (setNodeRef) {
|
|
145
|
+
setNodeRef(node);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const circleIndicatorPosition = useDragIndicatorPosition({
|
|
150
|
+
elementRef: headerRef,
|
|
151
|
+
dropIndicatorPosition,
|
|
152
|
+
});
|
|
153
|
+
|
|
119
154
|
return (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
<>
|
|
156
|
+
{circleIndicatorPosition && (
|
|
157
|
+
<DragIndicatorCircle
|
|
158
|
+
top={circleIndicatorPosition.top}
|
|
159
|
+
left={circleIndicatorPosition.left}
|
|
160
|
+
container={circleIndicatorPosition.container}
|
|
161
|
+
/>
|
|
162
|
+
)}
|
|
163
|
+
<th
|
|
164
|
+
ref={setRefs}
|
|
165
|
+
className={cx(className, rootClassName, {
|
|
166
|
+
[`${rootClassName}__checkbox`]: isCheckBox,
|
|
167
|
+
[`${rootClassName}__sticky-column`]: stickyColumn,
|
|
168
|
+
[`${rootClassName}__sticky-header`]: stickyHeaders,
|
|
169
|
+
[`${rootClassName}__sticky-column__with-sticky-border`]: isScrolled && showBorder,
|
|
170
|
+
[`${rootClassName}__first-header-visible`]: isFirstColumn && isVisible,
|
|
171
|
+
[`${rootClassName}__last-header-visible`]: isLastColumn && isVisible,
|
|
172
|
+
[`${rootClassName}__border-radius-first-cell`]: isFirstColumn && !overlayActive,
|
|
173
|
+
[`${rootClassName}__border-radius-last-cell`]: isLastColumn && !overlayActive,
|
|
174
|
+
[`${rootClassName}__draggable`]: enableColumnDrag,
|
|
175
|
+
[`${rootClassName}__column-drag-enabled`]: enableColumnDrag,
|
|
176
|
+
[`${rootClassName}__dragging`]: draggingActive && !overlayActive,
|
|
177
|
+
[`${rootClassName}--drop-indicator-before`]: dropIndicatorPosition === "before",
|
|
178
|
+
[`${rootClassName}--drop-indicator-after`]: dropIndicatorPosition === "after",
|
|
179
|
+
})}
|
|
180
|
+
style={{
|
|
181
|
+
width: isCheckBox || isRadiobutton ? widthInRemString : undefined,
|
|
182
|
+
}}
|
|
183
|
+
scope="col"
|
|
184
|
+
aria-sort={getSortingDirection(header.column.getIsSorted())}
|
|
185
|
+
aria-label={ariaLabel}
|
|
186
|
+
{...(enableColumnDrag
|
|
187
|
+
? { ...attributes, ...listeners, id: `header-${sortableId || header.id}` }
|
|
188
|
+
: {})}
|
|
189
|
+
>
|
|
190
|
+
{enableColumnDrag && (
|
|
191
|
+
<TableColumnDragHandle
|
|
192
|
+
onMouseDown={handleMouseDown}
|
|
193
|
+
overlayActive={overlayActive}
|
|
194
|
+
isFirstColumn={isFirstColumn}
|
|
195
|
+
isLastColumn={isLastColumn}
|
|
196
|
+
columnDragAriaLabel={columnDragAriaLabel}
|
|
150
197
|
/>
|
|
151
|
-
|
|
198
|
+
)}
|
|
152
199
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
200
|
+
<div
|
|
201
|
+
className={cx(`${rootClassName}__inner`)}
|
|
202
|
+
style={{
|
|
203
|
+
maxWidth: widthInRemString,
|
|
204
|
+
minWidth: widthInRemString,
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
<div className={cx(`${rootClassName}__content`)}>
|
|
208
|
+
<div className={cx(`${rootClassName}__title`)}>
|
|
209
|
+
<HeaderContent
|
|
210
|
+
header={header}
|
|
211
|
+
tanstackTable={tanstackTable}
|
|
212
|
+
canSort={canSort}
|
|
213
|
+
isCheckBox={isCheckBox}
|
|
214
|
+
isRadiobutton={isRadiobutton}
|
|
215
|
+
sortingAriaLabels={sortingAriaLabels}
|
|
216
|
+
/>
|
|
217
|
+
</div>
|
|
218
|
+
{hasFilter && !isCheckBox && (
|
|
219
|
+
<div className={cx(`${rootClassName}__filter-wrapper`)}>
|
|
220
|
+
<Filter header={header} />
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
{/* Placeholder element for columns without filters when table has filters. Its only used within the drag overlay */}
|
|
224
|
+
{tableHasFilters && !hasFilter && !isCheckBox && !isRadiobutton && overlayActive && (
|
|
225
|
+
<div className={cx(`${rootClassName}__filter-placeholder`)}></div>
|
|
226
|
+
)}
|
|
156
227
|
</div>
|
|
157
|
-
|
|
158
|
-
</
|
|
159
|
-
|
|
228
|
+
</div>
|
|
229
|
+
</th>
|
|
230
|
+
</>
|
|
160
231
|
);
|
|
161
232
|
};
|
|
162
233
|
|