@mezzanine-ui/react 1.0.0-alpha.0 → 1.0.0-beta.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/Breadcrumb/Breadcrumb.js +40 -12
- package/Breadcrumb/typings.d.ts +8 -3
- package/Drawer/Drawer.d.ts +47 -6
- package/Drawer/Drawer.js +36 -11
- package/Empty/Empty.js +26 -3
- package/Empty/typings.d.ts +16 -7
- package/PageHeader/PageHeader.d.ts +5 -1
- package/PageHeader/PageHeader.js +8 -3
- package/PageToolbar/PageToolbar.d.ts +73 -26
- package/PageToolbar/PageToolbar.js +10 -101
- package/PageToolbar/utils.d.ts +23 -0
- package/PageToolbar/utils.js +165 -0
- package/Pagination/PaginationItem.js +1 -3
- package/Pagination/usePagination.js +0 -18
- package/Radio/Radio.d.ts +36 -3
- package/Radio/Radio.js +21 -11
- package/Radio/RadioGroup.d.ts +36 -7
- package/Radio/RadioGroup.js +5 -4
- package/Radio/RadioGroupContext.d.ts +2 -1
- package/Radio/index.d.ts +3 -3
- package/Tab/Tab.d.ts +32 -0
- package/Tab/Tab.js +57 -0
- package/Tab/TabItem.d.ts +27 -0
- package/Tab/TabItem.js +18 -0
- package/Tab/index.d.ts +4 -0
- package/Tab/index.js +2 -0
- package/Table/Table.d.ts +75 -94
- package/Table/Table.js +216 -161
- package/Table/TableContext.d.ts +114 -51
- package/Table/TableContext.js +21 -3
- package/Table/components/TableBody.d.ts +5 -0
- package/Table/components/TableBody.js +102 -0
- package/Table/components/TableCell.d.ts +17 -0
- package/Table/components/TableCell.js +74 -0
- package/Table/components/TableColGroup.d.ts +4 -0
- package/Table/components/TableColGroup.js +206 -0
- package/Table/components/TableDragHandleCell.d.ts +9 -0
- package/Table/components/TableDragHandleCell.js +37 -0
- package/Table/components/TableExpandCell.d.ts +11 -0
- package/Table/components/TableExpandCell.js +44 -0
- package/Table/components/TableExpandedRow.d.ts +9 -0
- package/Table/components/TableExpandedRow.js +46 -0
- package/Table/components/TableHeader.d.ts +4 -0
- package/Table/components/TableHeader.js +125 -0
- package/Table/components/TablePagination.d.ts +3 -0
- package/Table/components/TablePagination.js +11 -0
- package/Table/components/TableResizeHandle.d.ts +13 -0
- package/Table/components/TableResizeHandle.js +115 -0
- package/Table/components/TableRow.d.ts +12 -0
- package/Table/components/TableRow.js +126 -0
- package/Table/components/TableSelectionCell.d.ts +13 -0
- package/Table/components/TableSelectionCell.js +35 -0
- package/Table/components/index.d.ts +10 -0
- package/Table/components/index.js +10 -0
- package/Table/hooks/index.d.ts +9 -0
- package/Table/hooks/index.js +8 -0
- package/Table/hooks/typings.d.ts +14 -0
- package/Table/hooks/useTableColumns.d.ts +8 -0
- package/Table/hooks/useTableColumns.js +91 -0
- package/Table/hooks/useTableDataSource.d.ts +57 -0
- package/Table/hooks/useTableDataSource.js +183 -0
- package/Table/hooks/useTableExpansion.d.ts +7 -0
- package/Table/hooks/useTableExpansion.js +52 -0
- package/Table/hooks/useTableFixedOffsets.d.ts +29 -0
- package/Table/hooks/useTableFixedOffsets.js +241 -0
- package/Table/hooks/useTableScroll.d.ts +12 -0
- package/Table/hooks/useTableScroll.js +58 -0
- package/Table/hooks/useTableSelection.d.ts +7 -0
- package/Table/hooks/useTableSelection.js +94 -0
- package/Table/hooks/useTableSorting.d.ts +6 -0
- package/Table/hooks/useTableSorting.js +32 -0
- package/Table/hooks/useTableVirtualization.d.ts +22 -0
- package/Table/hooks/useTableVirtualization.js +115 -0
- package/Table/index.d.ts +7 -10
- package/Table/index.js +22 -6
- package/Table/utils/index.d.ts +2 -0
- package/Table/utils/index.js +1 -0
- package/Table/utils/useTableRowSelection.d.ts +18 -0
- package/Table/utils/useTableRowSelection.js +63 -0
- package/_internal/InputCheck/InputCheck.d.ts +15 -1
- package/_internal/InputCheck/InputCheck.js +6 -2
- package/_internal/InputCheck/InputCheckGroup.d.ts +11 -1
- package/_internal/InputCheck/InputCheckGroup.js +4 -2
- package/_internal/SlideFadeOverlay/SlideFadeOverlay.d.ts +1 -1
- package/_internal/SlideFadeOverlay/SlideFadeOverlay.js +1 -1
- package/index.d.ts +7 -5
- package/index.js +6 -9
- package/package.json +5 -4
- package/utils/flatten-children.d.ts +12 -0
- package/utils/flatten-children.js +37 -0
- package/utils/get-css-variable-value.d.ts +1 -0
- package/utils/get-css-variable-value.js +4 -1
- package/Table/TableBody.d.ts +0 -10
- package/Table/TableBody.js +0 -31
- package/Table/TableBodyRow.d.ts +0 -11
- package/Table/TableBodyRow.js +0 -65
- package/Table/TableCell.d.ts +0 -19
- package/Table/TableCell.js +0 -24
- package/Table/TableExpandedTable.d.ts +0 -11
- package/Table/TableExpandedTable.js +0 -29
- package/Table/TableHeader.d.ts +0 -3
- package/Table/TableHeader.js +0 -36
- package/Table/draggable/useTableDraggable.d.ts +0 -14
- package/Table/draggable/useTableDraggable.js +0 -64
- package/Table/editable/TableEditRenderWrapper.d.ts +0 -7
- package/Table/editable/TableEditRenderWrapper.js +0 -16
- package/Table/expandable/TableExpandable.d.ts +0 -27
- package/Table/expandable/TableExpandable.js +0 -24
- package/Table/pagination/TablePagination.d.ts +0 -10
- package/Table/pagination/TablePagination.js +0 -26
- package/Table/pagination/useTablePagination.d.ts +0 -8
- package/Table/pagination/useTablePagination.js +0 -30
- package/Table/refresh/TableRefresh.d.ts +0 -10
- package/Table/refresh/TableRefresh.js +0 -22
- package/Table/rowSelection/TableRowSelection.d.ts +0 -18
- package/Table/rowSelection/TableRowSelection.js +0 -93
- package/Table/rowSelection/useTableRowSelection.d.ts +0 -6
- package/Table/rowSelection/useTableRowSelection.js +0 -53
- package/Table/sorting/TableSortingIcon.d.ts +0 -10
- package/Table/sorting/TableSortingIcon.js +0 -33
- package/Table/sorting/useTableSorting.d.ts +0 -11
- package/Table/sorting/useTableSorting.js +0 -121
- package/Table/useTableFetchMore.d.ts +0 -10
- package/Table/useTableFetchMore.js +0 -50
- package/Table/useTableLoading.d.ts +0 -5
- package/Table/useTableLoading.js +0 -19
- package/Table/useTableScroll.d.ts +0 -592
- package/Table/useTableScroll.js +0 -296
- package/Tabs/Tab.d.ts +0 -18
- package/Tabs/Tab.js +0 -16
- package/Tabs/TabPane.d.ts +0 -14
- package/Tabs/TabPane.js +0 -19
- package/Tabs/Tabs.d.ts +0 -39
- package/Tabs/Tabs.js +0 -52
- package/Tabs/index.d.ts +0 -6
- package/Tabs/index.js +0 -3
- package/Tabs/useTabsOverflow.d.ts +0 -8
- package/Tabs/useTabsOverflow.js +0 -62
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
3
|
+
import { DRAG_HANDLE_KEY, DRAG_HANDLE_COLUMN_WIDTH, EXPANSION_KEY, EXPANSION_COLUMN_WIDTH, SELECTION_KEY, SELECTION_COLUMN_WIDTH } from '@mezzanine-ui/core/table';
|
|
4
|
+
import { useTableSuperContext } from '../TableContext.js';
|
|
5
|
+
|
|
6
|
+
const parseFixed = (fixed) => {
|
|
7
|
+
if (fixed === true || fixed === 'start')
|
|
8
|
+
return 'start';
|
|
9
|
+
if (fixed === 'end')
|
|
10
|
+
return 'end';
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
13
|
+
function useTableFixedOffsets(options) {
|
|
14
|
+
const { expansionLeftPadding = 0 } = useTableSuperContext();
|
|
15
|
+
const { actionConfig, columns, getResizedColumnWidth } = options;
|
|
16
|
+
// Store measured widths
|
|
17
|
+
const [measuredWidths, setMeasuredWidths] = useState(new Map());
|
|
18
|
+
const { hasDragHandle, hasExpansion, hasSelection } = actionConfig;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const innerMap = new Map();
|
|
21
|
+
if (hasDragHandle) {
|
|
22
|
+
innerMap.set(DRAG_HANDLE_KEY, DRAG_HANDLE_COLUMN_WIDTH);
|
|
23
|
+
}
|
|
24
|
+
if (hasExpansion) {
|
|
25
|
+
innerMap.set(EXPANSION_KEY, EXPANSION_COLUMN_WIDTH);
|
|
26
|
+
}
|
|
27
|
+
if (hasSelection) {
|
|
28
|
+
innerMap.set(SELECTION_KEY, SELECTION_COLUMN_WIDTH);
|
|
29
|
+
}
|
|
30
|
+
setMeasuredWidths(innerMap);
|
|
31
|
+
}, [hasDragHandle, hasExpansion, hasSelection]);
|
|
32
|
+
// Get width for a column (prioritize measured, then computed, then defined, then fallback)
|
|
33
|
+
const getWidth = useCallback((key, fallback = 0) => {
|
|
34
|
+
// Get static widths first
|
|
35
|
+
const measured = measuredWidths.get(key);
|
|
36
|
+
if (measured !== undefined && measured > 0) {
|
|
37
|
+
return measured;
|
|
38
|
+
}
|
|
39
|
+
// get resized column width
|
|
40
|
+
const computed = getResizedColumnWidth === null || getResizedColumnWidth === void 0 ? void 0 : getResizedColumnWidth(key);
|
|
41
|
+
if (computed !== undefined) {
|
|
42
|
+
return computed;
|
|
43
|
+
}
|
|
44
|
+
// Then try to find column definition width
|
|
45
|
+
const column = columns.find((col) => col.key === key);
|
|
46
|
+
if ((column === null || column === void 0 ? void 0 : column.width) !== undefined) {
|
|
47
|
+
return column.width;
|
|
48
|
+
}
|
|
49
|
+
return fallback;
|
|
50
|
+
}, [columns, getResizedColumnWidth, measuredWidths]);
|
|
51
|
+
// Build ordered list of all fixed columns
|
|
52
|
+
const { fixedEndKeys, fixedStartKeys } = useMemo(() => {
|
|
53
|
+
const startKeys = [];
|
|
54
|
+
const endKeys = [];
|
|
55
|
+
// Action columns first (in order: drag handle, expansion, selection)
|
|
56
|
+
if (actionConfig.hasDragHandle && actionConfig.dragHandleFixed) {
|
|
57
|
+
startKeys.push(DRAG_HANDLE_KEY);
|
|
58
|
+
}
|
|
59
|
+
if (actionConfig.hasExpansion && actionConfig.expansionFixed) {
|
|
60
|
+
startKeys.push(EXPANSION_KEY);
|
|
61
|
+
}
|
|
62
|
+
if (actionConfig.hasSelection && actionConfig.selectionFixed) {
|
|
63
|
+
startKeys.push(SELECTION_KEY);
|
|
64
|
+
}
|
|
65
|
+
// Then data columns
|
|
66
|
+
columns.forEach((column) => {
|
|
67
|
+
const side = parseFixed(column.fixed);
|
|
68
|
+
if (side === 'start') {
|
|
69
|
+
startKeys.push(column.key);
|
|
70
|
+
}
|
|
71
|
+
else if (side === 'end') {
|
|
72
|
+
endKeys.push(column.key);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return { fixedEndKeys: endKeys, fixedStartKeys: startKeys };
|
|
76
|
+
}, [actionConfig, columns]);
|
|
77
|
+
// Calculate all fixed offsets
|
|
78
|
+
const fixedOffsets = useMemo(() => {
|
|
79
|
+
const startOffsets = new Map();
|
|
80
|
+
const endOffsets = new Map();
|
|
81
|
+
// Calculate start offsets
|
|
82
|
+
let currentStartOffset = 0;
|
|
83
|
+
fixedStartKeys.forEach((key) => {
|
|
84
|
+
startOffsets.set(key, {
|
|
85
|
+
offset: currentStartOffset,
|
|
86
|
+
side: 'start',
|
|
87
|
+
});
|
|
88
|
+
currentStartOffset += getWidth(key);
|
|
89
|
+
});
|
|
90
|
+
// Calculate end offsets (from right to left)
|
|
91
|
+
let currentEndOffset = 0;
|
|
92
|
+
for (let i = fixedEndKeys.length - 1; i >= 0; i--) {
|
|
93
|
+
const key = fixedEndKeys[i];
|
|
94
|
+
endOffsets.set(key, {
|
|
95
|
+
offset: currentEndOffset,
|
|
96
|
+
side: 'end',
|
|
97
|
+
});
|
|
98
|
+
currentEndOffset += getWidth(key);
|
|
99
|
+
}
|
|
100
|
+
return { endOffsets, startOffsets };
|
|
101
|
+
}, [fixedEndKeys, fixedStartKeys, getWidth]);
|
|
102
|
+
// Build ordered list of all columns (for position calculation)
|
|
103
|
+
const allColumnKeys = useMemo(() => {
|
|
104
|
+
const keys = [];
|
|
105
|
+
// Action columns first
|
|
106
|
+
if (hasDragHandle) {
|
|
107
|
+
keys.push(DRAG_HANDLE_KEY);
|
|
108
|
+
}
|
|
109
|
+
if (hasExpansion) {
|
|
110
|
+
keys.push(EXPANSION_KEY);
|
|
111
|
+
}
|
|
112
|
+
if (hasSelection) {
|
|
113
|
+
keys.push(SELECTION_KEY);
|
|
114
|
+
}
|
|
115
|
+
// Then data columns
|
|
116
|
+
columns.forEach((column) => {
|
|
117
|
+
keys.push(column.key);
|
|
118
|
+
});
|
|
119
|
+
return keys;
|
|
120
|
+
}, [hasDragHandle, hasSelection, hasExpansion, columns]);
|
|
121
|
+
// Calculate original positions (left edge) for all columns
|
|
122
|
+
const originalPositions = useMemo(() => {
|
|
123
|
+
const positions = new Map();
|
|
124
|
+
let currentPosition = 0;
|
|
125
|
+
allColumnKeys.forEach((key) => {
|
|
126
|
+
positions.set(key, currentPosition);
|
|
127
|
+
currentPosition += getWidth(key);
|
|
128
|
+
});
|
|
129
|
+
return positions;
|
|
130
|
+
}, [allColumnKeys, getWidth]);
|
|
131
|
+
/**
|
|
132
|
+
* Determine if a column should show shadow.
|
|
133
|
+
*/
|
|
134
|
+
const shouldShowShadow = useCallback((key, scrollLeft, containerWidth) => {
|
|
135
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
136
|
+
const offsetInfo = (_a = fixedOffsets.startOffsets.get(key)) !== null && _a !== void 0 ? _a : fixedOffsets.endOffsets.get(key);
|
|
137
|
+
if (!offsetInfo)
|
|
138
|
+
return false;
|
|
139
|
+
if (offsetInfo.side === 'start') {
|
|
140
|
+
// For start-fixed columns
|
|
141
|
+
const keyIndex = fixedStartKeys.indexOf(key);
|
|
142
|
+
if (keyIndex === -1)
|
|
143
|
+
return false;
|
|
144
|
+
const originalPos = (_b = originalPositions.get(key)) !== null && _b !== void 0 ? _b : 0;
|
|
145
|
+
const isSticky = scrollLeft - expansionLeftPadding > originalPos - offsetInfo.offset;
|
|
146
|
+
if (!isSticky)
|
|
147
|
+
return false;
|
|
148
|
+
// Now check if there's a gap to the right (shadow should show)
|
|
149
|
+
// Find the next fixed-start column
|
|
150
|
+
const nextIndex = keyIndex + 1;
|
|
151
|
+
if (nextIndex < fixedStartKeys.length) {
|
|
152
|
+
const nextKey = fixedStartKeys[nextIndex];
|
|
153
|
+
const nextOriginalPos = (_c = originalPositions.get(nextKey)) !== null && _c !== void 0 ? _c : 0;
|
|
154
|
+
const nextOffset = (_e = (_d = fixedOffsets.startOffsets.get(nextKey)) === null || _d === void 0 ? void 0 : _d.offset) !== null && _e !== void 0 ? _e : 0;
|
|
155
|
+
// Gap closes when scroll reaches the point where next column becomes sticky
|
|
156
|
+
const isNextSticky = scrollLeft - expansionLeftPadding >= nextOriginalPos - nextOffset;
|
|
157
|
+
return !isNextSticky;
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// For end-fixed columns
|
|
163
|
+
const keyIndex = fixedEndKeys.indexOf(key);
|
|
164
|
+
if (keyIndex === -1)
|
|
165
|
+
return false;
|
|
166
|
+
// Get this column's original position from left
|
|
167
|
+
const originalPos = (_f = originalPositions.get(key)) !== null && _f !== void 0 ? _f : 0;
|
|
168
|
+
const columnWidth = getWidth(key);
|
|
169
|
+
const isSticky = scrollLeft + containerWidth <
|
|
170
|
+
originalPos + columnWidth + offsetInfo.offset + expansionLeftPadding;
|
|
171
|
+
if (!isSticky)
|
|
172
|
+
return false;
|
|
173
|
+
// Check if there's a gap to the left (shadow should show)
|
|
174
|
+
const prevIndex = keyIndex - 1;
|
|
175
|
+
if (~prevIndex) {
|
|
176
|
+
const prevKey = fixedEndKeys[prevIndex];
|
|
177
|
+
const prevOriginalPos = (_g = originalPositions.get(prevKey)) !== null && _g !== void 0 ? _g : 0;
|
|
178
|
+
const prevOffset = (_j = (_h = fixedOffsets.endOffsets.get(prevKey)) === null || _h === void 0 ? void 0 : _h.offset) !== null && _j !== void 0 ? _j : 0;
|
|
179
|
+
const prevColumnWidth = getWidth(prevKey);
|
|
180
|
+
const isPrevSticky = scrollLeft + containerWidth <
|
|
181
|
+
prevOriginalPos +
|
|
182
|
+
prevColumnWidth +
|
|
183
|
+
prevOffset +
|
|
184
|
+
expansionLeftPadding;
|
|
185
|
+
// If both are sticky, the gap is closed if this column's
|
|
186
|
+
// left visual edge touches the prev column's right visual edge
|
|
187
|
+
return !isPrevSticky;
|
|
188
|
+
}
|
|
189
|
+
// This is the leftmost fixed-end column
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}, [
|
|
193
|
+
fixedEndKeys,
|
|
194
|
+
fixedOffsets,
|
|
195
|
+
fixedStartKeys,
|
|
196
|
+
getWidth,
|
|
197
|
+
originalPositions,
|
|
198
|
+
expansionLeftPadding,
|
|
199
|
+
]);
|
|
200
|
+
const getColumnOffset = useCallback((key) => {
|
|
201
|
+
var _a, _b;
|
|
202
|
+
return ((_b = (_a = fixedOffsets.startOffsets.get(key)) !== null && _a !== void 0 ? _a : fixedOffsets.endOffsets.get(key)) !== null && _b !== void 0 ? _b : null);
|
|
203
|
+
}, [fixedOffsets]);
|
|
204
|
+
const getDragHandleOffset = useCallback(() => {
|
|
205
|
+
var _a;
|
|
206
|
+
if (!actionConfig.hasDragHandle || !actionConfig.dragHandleFixed) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return (_a = fixedOffsets.startOffsets.get(DRAG_HANDLE_KEY)) !== null && _a !== void 0 ? _a : null;
|
|
210
|
+
}, [actionConfig, fixedOffsets]);
|
|
211
|
+
const getSelectionOffset = useCallback(() => {
|
|
212
|
+
var _a;
|
|
213
|
+
if (!actionConfig.hasSelection || !actionConfig.selectionFixed) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return (_a = fixedOffsets.startOffsets.get(SELECTION_KEY)) !== null && _a !== void 0 ? _a : null;
|
|
217
|
+
}, [actionConfig, fixedOffsets]);
|
|
218
|
+
const getExpansionOffset = useCallback(() => {
|
|
219
|
+
var _a;
|
|
220
|
+
if (!actionConfig.hasExpansion || !actionConfig.expansionFixed) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
return (_a = fixedOffsets.startOffsets.get(EXPANSION_KEY)) !== null && _a !== void 0 ? _a : null;
|
|
224
|
+
}, [actionConfig, fixedOffsets]);
|
|
225
|
+
// Memoize return object to prevent unnecessary re-renders
|
|
226
|
+
return useMemo(() => ({
|
|
227
|
+
getColumnOffset,
|
|
228
|
+
getDragHandleOffset,
|
|
229
|
+
getExpansionOffset,
|
|
230
|
+
getSelectionOffset,
|
|
231
|
+
shouldShowShadow,
|
|
232
|
+
}), [
|
|
233
|
+
getColumnOffset,
|
|
234
|
+
getDragHandleOffset,
|
|
235
|
+
getExpansionOffset,
|
|
236
|
+
getSelectionOffset,
|
|
237
|
+
shouldShowShadow,
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { useTableFixedOffsets };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface UseTableScrollReturn {
|
|
2
|
+
/** Container width (viewport width) */
|
|
3
|
+
containerWidth: number;
|
|
4
|
+
handleScroll: (event: React.UIEvent<HTMLDivElement>) => void;
|
|
5
|
+
isScrollingHorizontally: boolean;
|
|
6
|
+
scrollLeft: number;
|
|
7
|
+
/** Set refs for measuring container width */
|
|
8
|
+
setContainerRef: (element: HTMLDivElement | null) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function useTableScroll({ enabled, }: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
}): UseTableScrollReturn;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
function useTableScroll({ enabled, }) {
|
|
5
|
+
const [isScrollingHorizontally, setIsScrollingHorizontally] = useState(false);
|
|
6
|
+
const [scrollLeft, setScrollLeft] = useState(0);
|
|
7
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
8
|
+
const containerRef = useRef(null);
|
|
9
|
+
const setContainerRef = useCallback((element) => {
|
|
10
|
+
containerRef.current = element;
|
|
11
|
+
}, []);
|
|
12
|
+
const measureDimensions = useCallback(() => {
|
|
13
|
+
if (containerRef.current) {
|
|
14
|
+
const newContainerWidth = containerRef.current.clientWidth;
|
|
15
|
+
setContainerWidth((prev) => prev !== newContainerWidth ? newContainerWidth : prev);
|
|
16
|
+
}
|
|
17
|
+
}, []);
|
|
18
|
+
const handleScroll = useCallback((event) => {
|
|
19
|
+
const target = event.currentTarget;
|
|
20
|
+
const newScrollLeft = target.scrollLeft;
|
|
21
|
+
setScrollLeft(newScrollLeft);
|
|
22
|
+
setIsScrollingHorizontally(newScrollLeft > 0);
|
|
23
|
+
}, []);
|
|
24
|
+
// Set up ResizeObserver to track dimension changes
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const { current: container } = containerRef;
|
|
27
|
+
if (!container || !enabled)
|
|
28
|
+
return;
|
|
29
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
30
|
+
measureDimensions();
|
|
31
|
+
});
|
|
32
|
+
resizeObserver.observe(container);
|
|
33
|
+
return () => {
|
|
34
|
+
resizeObserver.disconnect();
|
|
35
|
+
};
|
|
36
|
+
}, [measureDimensions, enabled]);
|
|
37
|
+
// Initial measurement after mount
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (enabled) {
|
|
40
|
+
measureDimensions();
|
|
41
|
+
}
|
|
42
|
+
}, [measureDimensions, enabled]);
|
|
43
|
+
return useMemo(() => ({
|
|
44
|
+
containerWidth,
|
|
45
|
+
handleScroll,
|
|
46
|
+
isScrollingHorizontally,
|
|
47
|
+
scrollLeft,
|
|
48
|
+
setContainerRef,
|
|
49
|
+
}), [
|
|
50
|
+
containerWidth,
|
|
51
|
+
handleScroll,
|
|
52
|
+
isScrollingHorizontally,
|
|
53
|
+
scrollLeft,
|
|
54
|
+
setContainerRef,
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { useTableScroll };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type TableDataSource, type TableRowSelection } from '@mezzanine-ui/core/table';
|
|
2
|
+
import type { TableSelectionState } from '../TableContext';
|
|
3
|
+
export interface UseTableSelectionOptions<T extends TableDataSource> {
|
|
4
|
+
dataSource: T[];
|
|
5
|
+
rowSelection?: TableRowSelection<T>;
|
|
6
|
+
}
|
|
7
|
+
export declare function useTableSelection<T extends TableDataSource>({ dataSource, rowSelection, }: UseTableSelectionOptions<T>): TableSelectionState<T> | undefined;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo, useCallback } from 'react';
|
|
3
|
+
import { getRowKey } from '@mezzanine-ui/core/table';
|
|
4
|
+
|
|
5
|
+
function useTableSelection({ dataSource, rowSelection, }) {
|
|
6
|
+
const { selectedRowKeys = [], isCheckboxDisabled, onChange, onSelectAll, preserveSelectedRowKeys = false, } = rowSelection || {};
|
|
7
|
+
const selectableKeys = useMemo(() => {
|
|
8
|
+
if (!rowSelection)
|
|
9
|
+
return [];
|
|
10
|
+
return dataSource
|
|
11
|
+
.filter((record) => {
|
|
12
|
+
if (!isCheckboxDisabled)
|
|
13
|
+
return true;
|
|
14
|
+
const disabled = isCheckboxDisabled(record);
|
|
15
|
+
return !disabled;
|
|
16
|
+
})
|
|
17
|
+
.map((record) => getRowKey(record));
|
|
18
|
+
}, [dataSource, isCheckboxDisabled, rowSelection]);
|
|
19
|
+
const isRowSelected = useCallback((key) => {
|
|
20
|
+
return selectedRowKeys.includes(key);
|
|
21
|
+
}, [selectedRowKeys]);
|
|
22
|
+
const isRowDisabled = useCallback((record) => {
|
|
23
|
+
if (!isCheckboxDisabled)
|
|
24
|
+
return false;
|
|
25
|
+
const disabled = isCheckboxDisabled(record);
|
|
26
|
+
return disabled;
|
|
27
|
+
}, [isCheckboxDisabled]);
|
|
28
|
+
const isAllSelected = useMemo(() => {
|
|
29
|
+
if (!selectableKeys.length)
|
|
30
|
+
return false;
|
|
31
|
+
return selectableKeys.every((key) => selectedRowKeys.includes(key));
|
|
32
|
+
}, [selectableKeys, selectedRowKeys]);
|
|
33
|
+
const isIndeterminate = useMemo(() => {
|
|
34
|
+
if (!selectableKeys.length)
|
|
35
|
+
return false;
|
|
36
|
+
const selectedCount = selectableKeys.filter((key) => selectedRowKeys.includes(key)).length;
|
|
37
|
+
return selectedCount > 0 && selectedCount < selectableKeys.length;
|
|
38
|
+
}, [selectableKeys, selectedRowKeys]);
|
|
39
|
+
const toggleRow = useCallback((key) => {
|
|
40
|
+
const newKeys = selectedRowKeys.includes(key)
|
|
41
|
+
? selectedRowKeys.filter((k) => k !== key)
|
|
42
|
+
: [...selectedRowKeys, key];
|
|
43
|
+
const selectedRow = dataSource.find((r) => getRowKey(r) === key) || null;
|
|
44
|
+
const selectedRows = dataSource.filter((r) => newKeys.includes(getRowKey(r)));
|
|
45
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newKeys, selectedRow, selectedRows);
|
|
46
|
+
}, [selectedRowKeys, dataSource, onChange]);
|
|
47
|
+
const toggleAll = useCallback(() => {
|
|
48
|
+
let newKeys;
|
|
49
|
+
let type;
|
|
50
|
+
if (isAllSelected) {
|
|
51
|
+
if (preserveSelectedRowKeys) {
|
|
52
|
+
const currentDataKeys = dataSource.map(getRowKey);
|
|
53
|
+
newKeys = selectedRowKeys.filter((key) => !currentDataKeys.includes(String(key)));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
newKeys = selectedRowKeys.filter((key) => !selectableKeys.includes(String(key)));
|
|
57
|
+
}
|
|
58
|
+
type = 'none';
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const existingNonDataKeys = preserveSelectedRowKeys
|
|
62
|
+
? selectedRowKeys.filter((key) => !dataSource.some((r) => getRowKey(r) === String(key)))
|
|
63
|
+
: [];
|
|
64
|
+
newKeys = [...existingNonDataKeys, ...selectableKeys];
|
|
65
|
+
type = 'all';
|
|
66
|
+
}
|
|
67
|
+
const selectedRows = dataSource.filter((r) => newKeys.includes(getRowKey(r)));
|
|
68
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newKeys, null, selectedRows);
|
|
69
|
+
onSelectAll === null || onSelectAll === void 0 ? void 0 : onSelectAll(type);
|
|
70
|
+
}, [
|
|
71
|
+
dataSource,
|
|
72
|
+
isAllSelected,
|
|
73
|
+
onSelectAll,
|
|
74
|
+
onChange,
|
|
75
|
+
preserveSelectedRowKeys,
|
|
76
|
+
selectableKeys,
|
|
77
|
+
selectedRowKeys,
|
|
78
|
+
]);
|
|
79
|
+
if (!rowSelection) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
config: rowSelection,
|
|
84
|
+
isAllSelected,
|
|
85
|
+
isIndeterminate,
|
|
86
|
+
isRowDisabled,
|
|
87
|
+
isRowSelected,
|
|
88
|
+
selectedRowKeys,
|
|
89
|
+
toggleAll,
|
|
90
|
+
toggleRow,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { useTableSelection };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TableColumn, TableDataSource } from '@mezzanine-ui/core/table';
|
|
2
|
+
import type { TableSortingState } from '../TableContext';
|
|
3
|
+
export interface UseTableSortingOptions<T extends TableDataSource> {
|
|
4
|
+
columns: TableColumn<T>[];
|
|
5
|
+
}
|
|
6
|
+
export declare function useTableSorting<T extends TableDataSource>({ columns, }: UseTableSortingOptions<T>): TableSortingState;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
|
|
4
|
+
function useTableSorting({ columns, }) {
|
|
5
|
+
const onSort = useCallback((key) => {
|
|
6
|
+
var _a;
|
|
7
|
+
const column = columns.find((col) => col.key === key);
|
|
8
|
+
if (!column || !column.onSort)
|
|
9
|
+
return;
|
|
10
|
+
const sortedKey = column.key;
|
|
11
|
+
const sortedDirection = column.sortOrder;
|
|
12
|
+
let nextDirection;
|
|
13
|
+
if (sortedKey !== key) {
|
|
14
|
+
nextDirection = 'ascend';
|
|
15
|
+
}
|
|
16
|
+
else if (!sortedDirection) {
|
|
17
|
+
nextDirection = 'ascend';
|
|
18
|
+
}
|
|
19
|
+
else if (sortedDirection === 'ascend') {
|
|
20
|
+
nextDirection = 'descend';
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
nextDirection = null;
|
|
24
|
+
}
|
|
25
|
+
(_a = column.onSort) === null || _a === void 0 ? void 0 : _a.call(column, key, nextDirection);
|
|
26
|
+
}, [columns]);
|
|
27
|
+
return {
|
|
28
|
+
onSort,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { useTableSorting };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
2
|
+
import { type TableDataSource } from '@mezzanine-ui/core/table';
|
|
3
|
+
export interface UseTableVirtualizationOptions<T extends TableDataSource> {
|
|
4
|
+
dataSource: T[];
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
isRowExpanded?: (key: string | number) => boolean;
|
|
7
|
+
overscan?: number;
|
|
8
|
+
scrollContainerRef: React.RefObject<HTMLDivElement | null>;
|
|
9
|
+
}
|
|
10
|
+
export interface UseTableVirtualizationReturn {
|
|
11
|
+
/** Bottom padding for the tbody to maintain scroll height */
|
|
12
|
+
paddingBottom: number;
|
|
13
|
+
/** Top padding for the tbody to offset visible rows */
|
|
14
|
+
paddingTop: number;
|
|
15
|
+
measureElement: (node: HTMLElement | null) => void;
|
|
16
|
+
scrollToIndex: (index: number, options?: {
|
|
17
|
+
align?: 'start' | 'center' | 'end';
|
|
18
|
+
}) => void;
|
|
19
|
+
totalSize: number;
|
|
20
|
+
virtualItems: VirtualItem[];
|
|
21
|
+
}
|
|
22
|
+
export declare function useTableVirtualization<T extends TableDataSource>({ dataSource, enabled, isRowExpanded, overscan, scrollContainerRef, }: UseTableVirtualizationOptions<T>): UseTableVirtualizationReturn | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
3
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
4
|
+
import { getRowKey } from '@mezzanine-ui/core/table';
|
|
5
|
+
import { useTableContext } from '../TableContext.js';
|
|
6
|
+
|
|
7
|
+
function useTableVirtualization({ dataSource, enabled = true, isRowExpanded, overscan = 5, scrollContainerRef, }) {
|
|
8
|
+
var _a, _b, _c, _d;
|
|
9
|
+
const { rowHeight } = useTableContext();
|
|
10
|
+
const [isContainerReady, setIsContainerReady] = useState(false);
|
|
11
|
+
const expandedRowHeightsRef = useRef(new Map());
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (scrollContainerRef.current && enabled) {
|
|
14
|
+
setIsContainerReady(true);
|
|
15
|
+
}
|
|
16
|
+
}, [scrollContainerRef, enabled]);
|
|
17
|
+
// Estimate size callback that accounts for expanded rows
|
|
18
|
+
const estimateSize = useCallback((index) => {
|
|
19
|
+
const record = dataSource[index];
|
|
20
|
+
const key = getRowKey(record);
|
|
21
|
+
const measuredHeight = expandedRowHeightsRef.current.get(key);
|
|
22
|
+
if (measuredHeight !== undefined) {
|
|
23
|
+
return measuredHeight;
|
|
24
|
+
}
|
|
25
|
+
return rowHeight;
|
|
26
|
+
}, [dataSource, rowHeight]);
|
|
27
|
+
const getItemKey = useCallback((index) => {
|
|
28
|
+
const record = dataSource[index];
|
|
29
|
+
return getRowKey(record);
|
|
30
|
+
}, [dataSource]);
|
|
31
|
+
/* eslint-disable-next-line react-hooks/incompatible-library */
|
|
32
|
+
const virtualizer = useVirtualizer({
|
|
33
|
+
count: enabled && isContainerReady ? dataSource.length : 0,
|
|
34
|
+
enabled: enabled && isContainerReady,
|
|
35
|
+
estimateSize,
|
|
36
|
+
getItemKey,
|
|
37
|
+
getScrollElement: () => scrollContainerRef.current,
|
|
38
|
+
overscan,
|
|
39
|
+
});
|
|
40
|
+
// Force re-measure when container becomes ready
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isContainerReady && enabled) {
|
|
43
|
+
virtualizer.measure();
|
|
44
|
+
}
|
|
45
|
+
}, [isContainerReady, enabled, virtualizer]);
|
|
46
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
47
|
+
const totalSize = virtualizer.getTotalSize();
|
|
48
|
+
// Custom measure function that measures both the main row and its expanded row
|
|
49
|
+
const measureElement = useCallback((node) => {
|
|
50
|
+
var _a;
|
|
51
|
+
if (!node)
|
|
52
|
+
return;
|
|
53
|
+
const index = node.dataset.index;
|
|
54
|
+
if (index === undefined)
|
|
55
|
+
return;
|
|
56
|
+
const rowIndex = parseInt(index, 10);
|
|
57
|
+
const record = dataSource[rowIndex];
|
|
58
|
+
if (!record)
|
|
59
|
+
return;
|
|
60
|
+
const key = getRowKey(record);
|
|
61
|
+
const expanded = (_a = isRowExpanded === null || isRowExpanded === void 0 ? void 0 : isRowExpanded(key)) !== null && _a !== void 0 ? _a : false;
|
|
62
|
+
// Get the main row height
|
|
63
|
+
let totalHeight = node.getBoundingClientRect().height;
|
|
64
|
+
// If expanded, find and measure the expanded row (next sibling)
|
|
65
|
+
if (expanded && node.nextElementSibling) {
|
|
66
|
+
const expandedRow = node.nextElementSibling;
|
|
67
|
+
const expandedRowKey = expandedRow.dataset.rowKey;
|
|
68
|
+
if (expandedRowKey === `${key}-expanded`) {
|
|
69
|
+
totalHeight += expandedRow.getBoundingClientRect().height;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Store the combined height
|
|
73
|
+
expandedRowHeightsRef.current.set(key, totalHeight);
|
|
74
|
+
// Use the virtualizer's measure with the combined height
|
|
75
|
+
virtualizer.measureElement(node);
|
|
76
|
+
}, [dataSource, isRowExpanded, virtualizer]);
|
|
77
|
+
const scrollToIndex = useCallback((index, options) => {
|
|
78
|
+
virtualizer.scrollToIndex(index, options);
|
|
79
|
+
}, [virtualizer]);
|
|
80
|
+
// Re-measure when expansion state changes
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!enabled || !isContainerReady)
|
|
83
|
+
return;
|
|
84
|
+
expandedRowHeightsRef.current.clear();
|
|
85
|
+
virtualizer.measure();
|
|
86
|
+
}, [isRowExpanded, enabled, isContainerReady, virtualizer]);
|
|
87
|
+
const paddingTop = virtualItems.length > 0 ? ((_b = (_a = virtualItems[0]) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : 0) : 0;
|
|
88
|
+
const paddingBottom = virtualItems.length > 0
|
|
89
|
+
? totalSize - ((_d = (_c = virtualItems[virtualItems.length - 1]) === null || _c === void 0 ? void 0 : _c.end) !== null && _d !== void 0 ? _d : totalSize)
|
|
90
|
+
: 0;
|
|
91
|
+
const result = useMemo(() => {
|
|
92
|
+
if (!enabled) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
measureElement,
|
|
97
|
+
paddingBottom,
|
|
98
|
+
paddingTop,
|
|
99
|
+
scrollToIndex,
|
|
100
|
+
totalSize,
|
|
101
|
+
virtualItems,
|
|
102
|
+
};
|
|
103
|
+
}, [
|
|
104
|
+
enabled,
|
|
105
|
+
measureElement,
|
|
106
|
+
paddingBottom,
|
|
107
|
+
paddingTop,
|
|
108
|
+
scrollToIndex,
|
|
109
|
+
totalSize,
|
|
110
|
+
virtualItems,
|
|
111
|
+
]);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { useTableVirtualization };
|
package/Table/index.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export { default } from './Table';
|
|
3
|
-
export {
|
|
4
|
-
export type
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export { useTableDraggable } from './draggable/useTableDraggable';
|
|
9
|
-
export { useTableRowSelection, SELECTED_ALL_KEY, } from './rowSelection/useTableRowSelection';
|
|
10
|
-
export { default as useTableScroll } from './useTableScroll';
|
|
1
|
+
export { tableClasses as classes, getCellAlignClass, getColumnStyle, getRowKey, type FixedType, type SortOrder, type TableColumn, type TableDataSource, type TableDraggable, type TableExpandable, type TableRowSelection, type TableScroll, type TableSize, } from '@mezzanine-ui/core/table';
|
|
2
|
+
export { default, type TableBaseProps, type TableNonVirtualizedProps, type TableProps, type TableVirtualizedProps, } from './Table';
|
|
3
|
+
export { TableContext, TableDataContext, useTableContext, useTableDataContext, type TableColumnState, type TableContextValue, type TableDataContextValue, type TableDraggableState, type TableExpansionState, type TableSelectionState, type TableSortingState, type TableTransitionState, } from './TableContext';
|
|
4
|
+
export { type UpdateDataSourceOptions } from './hooks';
|
|
5
|
+
export * from './hooks';
|
|
6
|
+
export * from './components';
|
|
7
|
+
export * from './utils';
|
package/Table/index.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
1
|
+
export { tableClasses as classes, getCellAlignClass, getColumnStyle, getRowKey } from '@mezzanine-ui/core/table';
|
|
2
|
+
export { Table as default } from './Table.js';
|
|
3
|
+
export { TableContext, TableDataContext, useTableContext, useTableDataContext } from './TableContext.js';
|
|
4
|
+
export { useTableSorting } from './hooks/useTableSorting.js';
|
|
5
|
+
export { useTableSelection } from './hooks/useTableSelection.js';
|
|
6
|
+
export { useTableColumns } from './hooks/useTableColumns.js';
|
|
7
|
+
export { useTableExpansion } from './hooks/useTableExpansion.js';
|
|
8
|
+
export { useTableFixedOffsets } from './hooks/useTableFixedOffsets.js';
|
|
9
|
+
export { useTableScroll } from './hooks/useTableScroll.js';
|
|
10
|
+
export { useTableVirtualization } from './hooks/useTableVirtualization.js';
|
|
11
|
+
export { useTableDataSource } from './hooks/useTableDataSource.js';
|
|
12
|
+
export { TableBody } from './components/TableBody.js';
|
|
13
|
+
export { TableCell } from './components/TableCell.js';
|
|
14
|
+
export { TableColGroup } from './components/TableColGroup.js';
|
|
15
|
+
export { TableExpandCell } from './components/TableExpandCell.js';
|
|
16
|
+
export { TableExpandedRow } from './components/TableExpandedRow.js';
|
|
17
|
+
export { TableHeader } from './components/TableHeader.js';
|
|
18
|
+
export { TablePagination } from './components/TablePagination.js';
|
|
19
|
+
export { TableResizeHandle } from './components/TableResizeHandle.js';
|
|
20
|
+
export { TableRow } from './components/TableRow.js';
|
|
21
|
+
export { TableSelectionCell } from './components/TableSelectionCell.js';
|
|
22
|
+
export { useTableRowSelection } from './utils/useTableRowSelection.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTableRowSelection } from './useTableRowSelection.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TableDataSource, TableRowSelection } from '@mezzanine-ui/core/table';
|
|
2
|
+
export interface UseTableRowSelectionProps<T extends TableDataSource> {
|
|
3
|
+
getSubData: (record: T) => T[] | undefined;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Custom hook for users to manage row selection state in a table with expandable rows.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useTableRowSelection<T extends TableDataSource = TableDataSource>(props: UseTableRowSelectionProps<T>): {
|
|
9
|
+
parentSelectedKeys: (string | number)[];
|
|
10
|
+
parentOnChange: (selectedRowKeys: (string | number)[], selectedRow: T | null, selectedRows: T[]) => void;
|
|
11
|
+
parentGetCheckboxProps: (record: T) => {
|
|
12
|
+
indeterminate?: boolean;
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
};
|
|
15
|
+
getChildOnChange: (record: T) => TableRowSelection<T>["onChange"];
|
|
16
|
+
getChildSelectedRowKeys: (record: T) => (string | number)[];
|
|
17
|
+
totalSelectionCount: number;
|
|
18
|
+
};
|