@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.
Files changed (138) hide show
  1. package/Breadcrumb/Breadcrumb.js +40 -12
  2. package/Breadcrumb/typings.d.ts +8 -3
  3. package/Drawer/Drawer.d.ts +47 -6
  4. package/Drawer/Drawer.js +36 -11
  5. package/Empty/Empty.js +26 -3
  6. package/Empty/typings.d.ts +16 -7
  7. package/PageHeader/PageHeader.d.ts +5 -1
  8. package/PageHeader/PageHeader.js +8 -3
  9. package/PageToolbar/PageToolbar.d.ts +73 -26
  10. package/PageToolbar/PageToolbar.js +10 -101
  11. package/PageToolbar/utils.d.ts +23 -0
  12. package/PageToolbar/utils.js +165 -0
  13. package/Pagination/PaginationItem.js +1 -3
  14. package/Pagination/usePagination.js +0 -18
  15. package/Radio/Radio.d.ts +36 -3
  16. package/Radio/Radio.js +21 -11
  17. package/Radio/RadioGroup.d.ts +36 -7
  18. package/Radio/RadioGroup.js +5 -4
  19. package/Radio/RadioGroupContext.d.ts +2 -1
  20. package/Radio/index.d.ts +3 -3
  21. package/Tab/Tab.d.ts +32 -0
  22. package/Tab/Tab.js +57 -0
  23. package/Tab/TabItem.d.ts +27 -0
  24. package/Tab/TabItem.js +18 -0
  25. package/Tab/index.d.ts +4 -0
  26. package/Tab/index.js +2 -0
  27. package/Table/Table.d.ts +75 -94
  28. package/Table/Table.js +216 -161
  29. package/Table/TableContext.d.ts +114 -51
  30. package/Table/TableContext.js +21 -3
  31. package/Table/components/TableBody.d.ts +5 -0
  32. package/Table/components/TableBody.js +102 -0
  33. package/Table/components/TableCell.d.ts +17 -0
  34. package/Table/components/TableCell.js +74 -0
  35. package/Table/components/TableColGroup.d.ts +4 -0
  36. package/Table/components/TableColGroup.js +206 -0
  37. package/Table/components/TableDragHandleCell.d.ts +9 -0
  38. package/Table/components/TableDragHandleCell.js +37 -0
  39. package/Table/components/TableExpandCell.d.ts +11 -0
  40. package/Table/components/TableExpandCell.js +44 -0
  41. package/Table/components/TableExpandedRow.d.ts +9 -0
  42. package/Table/components/TableExpandedRow.js +46 -0
  43. package/Table/components/TableHeader.d.ts +4 -0
  44. package/Table/components/TableHeader.js +125 -0
  45. package/Table/components/TablePagination.d.ts +3 -0
  46. package/Table/components/TablePagination.js +11 -0
  47. package/Table/components/TableResizeHandle.d.ts +13 -0
  48. package/Table/components/TableResizeHandle.js +115 -0
  49. package/Table/components/TableRow.d.ts +12 -0
  50. package/Table/components/TableRow.js +126 -0
  51. package/Table/components/TableSelectionCell.d.ts +13 -0
  52. package/Table/components/TableSelectionCell.js +35 -0
  53. package/Table/components/index.d.ts +10 -0
  54. package/Table/components/index.js +10 -0
  55. package/Table/hooks/index.d.ts +9 -0
  56. package/Table/hooks/index.js +8 -0
  57. package/Table/hooks/typings.d.ts +14 -0
  58. package/Table/hooks/useTableColumns.d.ts +8 -0
  59. package/Table/hooks/useTableColumns.js +91 -0
  60. package/Table/hooks/useTableDataSource.d.ts +57 -0
  61. package/Table/hooks/useTableDataSource.js +183 -0
  62. package/Table/hooks/useTableExpansion.d.ts +7 -0
  63. package/Table/hooks/useTableExpansion.js +52 -0
  64. package/Table/hooks/useTableFixedOffsets.d.ts +29 -0
  65. package/Table/hooks/useTableFixedOffsets.js +241 -0
  66. package/Table/hooks/useTableScroll.d.ts +12 -0
  67. package/Table/hooks/useTableScroll.js +58 -0
  68. package/Table/hooks/useTableSelection.d.ts +7 -0
  69. package/Table/hooks/useTableSelection.js +94 -0
  70. package/Table/hooks/useTableSorting.d.ts +6 -0
  71. package/Table/hooks/useTableSorting.js +32 -0
  72. package/Table/hooks/useTableVirtualization.d.ts +22 -0
  73. package/Table/hooks/useTableVirtualization.js +115 -0
  74. package/Table/index.d.ts +7 -10
  75. package/Table/index.js +22 -6
  76. package/Table/utils/index.d.ts +2 -0
  77. package/Table/utils/index.js +1 -0
  78. package/Table/utils/useTableRowSelection.d.ts +18 -0
  79. package/Table/utils/useTableRowSelection.js +63 -0
  80. package/_internal/InputCheck/InputCheck.d.ts +15 -1
  81. package/_internal/InputCheck/InputCheck.js +6 -2
  82. package/_internal/InputCheck/InputCheckGroup.d.ts +11 -1
  83. package/_internal/InputCheck/InputCheckGroup.js +4 -2
  84. package/_internal/SlideFadeOverlay/SlideFadeOverlay.d.ts +1 -1
  85. package/_internal/SlideFadeOverlay/SlideFadeOverlay.js +1 -1
  86. package/index.d.ts +7 -5
  87. package/index.js +6 -9
  88. package/package.json +5 -4
  89. package/utils/flatten-children.d.ts +12 -0
  90. package/utils/flatten-children.js +37 -0
  91. package/utils/get-css-variable-value.d.ts +1 -0
  92. package/utils/get-css-variable-value.js +4 -1
  93. package/Table/TableBody.d.ts +0 -10
  94. package/Table/TableBody.js +0 -31
  95. package/Table/TableBodyRow.d.ts +0 -11
  96. package/Table/TableBodyRow.js +0 -65
  97. package/Table/TableCell.d.ts +0 -19
  98. package/Table/TableCell.js +0 -24
  99. package/Table/TableExpandedTable.d.ts +0 -11
  100. package/Table/TableExpandedTable.js +0 -29
  101. package/Table/TableHeader.d.ts +0 -3
  102. package/Table/TableHeader.js +0 -36
  103. package/Table/draggable/useTableDraggable.d.ts +0 -14
  104. package/Table/draggable/useTableDraggable.js +0 -64
  105. package/Table/editable/TableEditRenderWrapper.d.ts +0 -7
  106. package/Table/editable/TableEditRenderWrapper.js +0 -16
  107. package/Table/expandable/TableExpandable.d.ts +0 -27
  108. package/Table/expandable/TableExpandable.js +0 -24
  109. package/Table/pagination/TablePagination.d.ts +0 -10
  110. package/Table/pagination/TablePagination.js +0 -26
  111. package/Table/pagination/useTablePagination.d.ts +0 -8
  112. package/Table/pagination/useTablePagination.js +0 -30
  113. package/Table/refresh/TableRefresh.d.ts +0 -10
  114. package/Table/refresh/TableRefresh.js +0 -22
  115. package/Table/rowSelection/TableRowSelection.d.ts +0 -18
  116. package/Table/rowSelection/TableRowSelection.js +0 -93
  117. package/Table/rowSelection/useTableRowSelection.d.ts +0 -6
  118. package/Table/rowSelection/useTableRowSelection.js +0 -53
  119. package/Table/sorting/TableSortingIcon.d.ts +0 -10
  120. package/Table/sorting/TableSortingIcon.js +0 -33
  121. package/Table/sorting/useTableSorting.d.ts +0 -11
  122. package/Table/sorting/useTableSorting.js +0 -121
  123. package/Table/useTableFetchMore.d.ts +0 -10
  124. package/Table/useTableFetchMore.js +0 -50
  125. package/Table/useTableLoading.d.ts +0 -5
  126. package/Table/useTableLoading.js +0 -19
  127. package/Table/useTableScroll.d.ts +0 -592
  128. package/Table/useTableScroll.js +0 -296
  129. package/Tabs/Tab.d.ts +0 -18
  130. package/Tabs/Tab.js +0 -16
  131. package/Tabs/TabPane.d.ts +0 -14
  132. package/Tabs/TabPane.js +0 -19
  133. package/Tabs/Tabs.d.ts +0 -39
  134. package/Tabs/Tabs.js +0 -52
  135. package/Tabs/index.d.ts +0 -6
  136. package/Tabs/index.js +0 -3
  137. package/Tabs/useTabsOverflow.d.ts +0 -8
  138. 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 { TableProps } from './Table';
2
- export { default } from './Table';
3
- export { default as TableRefresh } from './refresh/TableRefresh';
4
- export type { TableRefreshProps } from './refresh/TableRefresh';
5
- export type { TableCellProps } from './TableCell';
6
- export { default as TableCell } from './TableCell';
7
- export type { EditableBodyCellProps } from './editable/TableEditRenderWrapper';
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 { default } from './Table.js';
2
- export { default as TableRefresh } from './refresh/TableRefresh.js';
3
- export { default as TableCell } from './TableCell.js';
4
- export { useTableDraggable } from './draggable/useTableDraggable.js';
5
- export { SELECTED_ALL_KEY, useTableRowSelection } from './rowSelection/useTableRowSelection.js';
6
- export { default as useTableScroll } from './useTableScroll.js';
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,2 @@
1
+ export type { UseTableRowSelectionProps } from './useTableRowSelection';
2
+ export { useTableRowSelection } from './useTableRowSelection';
@@ -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
+ };