pixelize-design-library 2.4.2-beta.47 → 2.4.2-beta.48
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/CHANGELOG.md +53 -0
- package/COMPONENT-GOTCHAS.md +39 -6
- package/dist/Components/Common/chipTriggerSize.d.ts +10 -0
- package/dist/Components/Common/chipTriggerSize.js +15 -0
- package/dist/Components/SearchSelect/SearchSelect.js +48 -7
- package/dist/Components/SearchSelect/SearchSelectProps.d.ts +5 -1
- package/dist/Components/Select/SelectNative.d.ts +2 -0
- package/dist/Components/Select/SelectPopup.js +97 -36
- package/dist/Components/Select/SelectProps.d.ts +4 -0
- package/dist/Components/SelectV2/SelectV2.d.ts +2 -9
- package/dist/Components/SelectV2/SelectV2.js +2 -10
- package/dist/Components/Table/Table.js +6 -7
- package/dist/Components/Table/TableProps.d.ts +9 -1
- package/dist/Components/Table/components/Pagination.js +8 -5
- package/dist/Components/Table/components/TableBody.d.ts +2 -1
- package/dist/Components/Table/components/TableBody.js +82 -15
- package/dist/Components/Table/components/TableHeader.d.ts +1 -1
- package/dist/Components/Table/components/TableHeader.js +8 -9
- package/dist/Components/Table/components/rowMetrics.d.ts +0 -1
- package/dist/Components/Table/components/rowMetrics.js +1 -2
- package/dist/Components/Table/hooks/useTable.d.ts +5 -1
- package/dist/Components/Table/hooks/useTable.js +43 -7
- package/dist/esm/Components/Common/chipTriggerSize.js +11 -0
- package/dist/esm/Components/SearchSelect/SearchSelect.js +48 -7
- package/dist/esm/Components/Select/SelectPopup.js +97 -36
- package/dist/esm/Components/SelectV2/SelectV2.js +2 -9
- package/dist/esm/Components/Table/Table.js +6 -7
- package/dist/esm/Components/Table/components/Pagination.js +8 -5
- package/dist/esm/Components/Table/components/TableBody.js +81 -16
- package/dist/esm/Components/Table/components/TableHeader.js +8 -9
- package/dist/esm/Components/Table/components/rowMetrics.js +0 -1
- package/dist/esm/Components/Table/hooks/useTable.js +44 -8
- package/package.json +1 -1
|
@@ -11,17 +11,37 @@ import TableGroupRow from "./TableGroupRow.js";
|
|
|
11
11
|
import { useTableVariant } from "../variants/TableVariantContext.js";
|
|
12
12
|
import { resolvePillInk } from "../variants/pillInk.js";
|
|
13
13
|
import EmptyState from "../../EmptyState/EmptyState.js";
|
|
14
|
-
import {
|
|
14
|
+
import { DENSITY_ROWS, EMPTY_STATE_HEIGHT, GROUP_HEADER_HEIGHT } from "./rowMetrics.js";
|
|
15
15
|
import { transitionFor } from "../../../Theme/tokens/primitives/motion.js";
|
|
16
16
|
import { reducedMotionSx } from "../../../Theme/chakra/motion.js";
|
|
17
17
|
const getRowId = (row) => row.id;
|
|
18
18
|
const getRowContent = (row) => typeof row.content === "function"
|
|
19
19
|
? row.content
|
|
20
20
|
: undefined;
|
|
21
|
+
// Smallest index i in [0, total] where offsets[i+1] > target; offsets is sorted.
|
|
22
|
+
export const findFirstVisibleIndex = (offsets, total, target) => {
|
|
23
|
+
let lo = 0;
|
|
24
|
+
let hi = total;
|
|
25
|
+
while (lo < hi) {
|
|
26
|
+
const mid = (lo + hi) >>> 1;
|
|
27
|
+
if (offsets[mid + 1] <= target) {
|
|
28
|
+
lo = mid + 1;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
hi = mid;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return lo;
|
|
35
|
+
};
|
|
21
36
|
// Monday-style status cell: the whole cell is filled with the status color and
|
|
22
37
|
// the label is centered in whichever of white/black clears AA against that fill —
|
|
23
38
|
// the fill can be a consumer hex we can't know in advance is light or dark.
|
|
24
|
-
const
|
|
39
|
+
const BLEED_JUSTIFY = {
|
|
40
|
+
start: "flex-start",
|
|
41
|
+
center: "center",
|
|
42
|
+
end: "flex-end",
|
|
43
|
+
};
|
|
44
|
+
const StatusCell = ({ value, onSolid, align, }) => (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: BLEED_JUSTIFY[align !== null && align !== void 0 ? align : "center"], w: "100%", h: "100%", px: "0.5rem", fontSize: "0.75rem", fontWeight: 600, color: onSolid, textAlign: align !== null && align !== void 0 ? align : "center", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: value }));
|
|
25
45
|
// Studio's status cell: a contained chip on the tone's soft rung. Same tone pair
|
|
26
46
|
// the group header already uses, so an override hex behaves identically in both.
|
|
27
47
|
const StatusPill = ({ value, soft, ink, radius, }) => (_jsx(Box, { display: "inline-flex", alignItems: "center", maxW: "100%", px: "0.5rem", py: "0.125rem", borderRadius: radius, bg: soft, color: ink, fontSize: "0.75rem", fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: value }));
|
|
@@ -110,13 +130,13 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
110
130
|
const bleedBg = statusColor === null || statusColor === void 0 ? void 0 : statusColor.soft;
|
|
111
131
|
const bleedInk = statusColor === null || statusColor === void 0 ? void 0 : statusColor.text;
|
|
112
132
|
const canHover = !isBleed && !((cellStyles === null || cellStyles === void 0 ? void 0 : cellStyles[header.id]) || rowStyle);
|
|
113
|
-
return (_jsx(Td, { maxWidth: 500, minWidth: 120, height: `${rowHeight}px`, py: isBleed ? 0 : cellPy, px: isBleed ? 0 : undefined, onClick: () => handleCellClick(header), fontSize: 14, fontWeight: 400, position: isFrozen ? "sticky" : undefined, left: isFrozen ? leftOffsets[headerIndex] : undefined, zIndex: isFrozen ? 1 : 0, background: isBleed ? bleedBg : isChecked ? (isFrozen ? selectedFrozenBg : selectedBg) : isFrozen ? frozenCellBg : restBg, textAlign: isBleed ? undefined : header.align, textOverflow: "ellipsis", boxShadow: isFirstDataCell ? leadSx : undefined, borderBottom: noBorders
|
|
133
|
+
return (_jsx(Td, { maxWidth: 500, minWidth: 120, height: `${rowHeight}px`, py: isBleed ? 0 : cellPy, px: isBleed ? 0 : undefined, onClick: () => handleCellClick(header), fontSize: 14, fontWeight: 400, position: isFrozen ? "sticky" : undefined, left: isFrozen ? `${leftOffsets[headerIndex]}px` : undefined, zIndex: isFrozen ? 1 : 0, background: isBleed ? bleedBg : isChecked ? (isFrozen ? selectedFrozenBg : selectedBg) : isFrozen ? frozenCellBg : restBg, textAlign: isBleed ? undefined : header.align, textOverflow: "ellipsis", boxShadow: isFirstDataCell ? leadSx : undefined, borderBottom: noBorders
|
|
114
134
|
? "none"
|
|
115
135
|
: `0.063rem solid ${isFrozen || isChecked
|
|
116
136
|
? variant.row.frozenRuleColor
|
|
117
137
|
: variant.row.ruleColor}`, className: `columns ${isFrozen ? "sticky-columns" : "scrollable-columns"}`, boxSizing: "border-box", color: isBleed ? bleedInk : variant.cells.ink, "data-hoverable": canHover ? "true" : "false", _hover: variant.row.hoverScope === "row" || !canHover
|
|
118
138
|
? EMPTY_HOVER
|
|
119
|
-
: hoverStyle, children: isBleed ? (_jsx(StatusCell, { value: normalizeTableCellValue(row[header.id]), onSolid: bleedInk })) : isStatus && statusColor ? (_jsx(StatusPill, { value: normalizeTableCellValue(row[header.id]), soft: statusColor.soft, ink: resolvePillInk(statusColor.text, statusColor.soft, theme.colors.background[50]), radius: variant.cells.pillRadius })) : (_jsx(Box, { display: "block", overflow: "hidden", whiteSpace: "normal", overflowWrap: "break-word", children: normalizeTableCellValue(header.node ? header.node(row) : row[header.id]) })) }, headerIndex + 1));
|
|
139
|
+
: hoverStyle, children: isBleed ? (_jsx(StatusCell, { value: normalizeTableCellValue(row[header.id]), onSolid: bleedInk, align: header.align })) : isStatus && statusColor ? (_jsx(StatusPill, { value: normalizeTableCellValue(row[header.id]), soft: statusColor.soft, ink: resolvePillInk(statusColor.text, statusColor.soft, theme.colors.background[50]), radius: variant.cells.pillRadius })) : (_jsx(Box, { display: "block", overflow: "hidden", whiteSpace: "normal", overflowWrap: "break-word", children: normalizeTableCellValue(header.node ? header.node(row) : row[header.id]) })) }, headerIndex + 1));
|
|
120
140
|
}), isLink && (_jsx(Td, { w: 2, p: 0, fontSize: 14, background: isChecked ? (isActionFreeze ? selectedFrozenBg : selectedBg) : isActionFreeze ? frozenCellBg : restBg, color: freezedTextColor, position: isActionFreeze ? "sticky" : "relative", borderBottom: borderStyle, boxSizing: "border-box", right: 0, zIndex: 1, className: `columns sticky-columns${isActionFreeze ? "-right" : ""}`, children: (row.onLink || row.onEdit || row.onDelete) ? (_jsx(TableActions, { row: row })) : null }))] }), rowContent && isExpanded && (_jsx(Tr, { ref: expandedRowRef, children: _jsx(Td, { colSpan: columns.length +
|
|
121
141
|
(isCheckbox ? 1 : 0) +
|
|
122
142
|
(isContent ? 1 : 0) +
|
|
@@ -131,7 +151,7 @@ const TableEmpty = ({ emptyState }) => {
|
|
|
131
151
|
return _jsx(_Fragment, { children: emptyState });
|
|
132
152
|
return (_jsx(EmptyState, { size: "sm", minH: `${EMPTY_STATE_HEIGHT}px`, title: structured === null || structured === void 0 ? void 0 : structured.title, description: structured === null || structured === void 0 ? void 0 : structured.description, icon: icon }));
|
|
133
153
|
};
|
|
134
|
-
const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, selections, isLoading, onRowClick, isContent, isLink, isActionFreeze, loadingSkeletonRows, scrollContainerRef, density, stripe = false, accentColors, groups, onAddItem, emptyState, }) => {
|
|
154
|
+
const TableBody = ({ data, isCheckbox, columns, startRow, rowsPerPage, columnWidths, slotWidth, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, selections, isLoading, onRowClick, isContent, isLink, isActionFreeze, loadingSkeletonRows, scrollContainerRef, density, stripe = false, accentColors, groups, onAddItem, emptyState, }) => {
|
|
135
155
|
var _a;
|
|
136
156
|
const theme = useCustomTheme();
|
|
137
157
|
const variant = useTableVariant();
|
|
@@ -180,7 +200,15 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
180
200
|
const el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
|
|
181
201
|
if (!el || !(shouldVirtualize || groupVirtualize))
|
|
182
202
|
return;
|
|
183
|
-
|
|
203
|
+
let rafId = null;
|
|
204
|
+
const onScroll = () => {
|
|
205
|
+
if (rafId != null)
|
|
206
|
+
return;
|
|
207
|
+
rafId = requestAnimationFrame(() => {
|
|
208
|
+
rafId = null;
|
|
209
|
+
setScrollTop(el.scrollTop);
|
|
210
|
+
});
|
|
211
|
+
};
|
|
184
212
|
const measure = () => setViewportH(el.clientHeight);
|
|
185
213
|
measure();
|
|
186
214
|
setScrollTop(el.scrollTop);
|
|
@@ -189,23 +217,30 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
189
217
|
return () => {
|
|
190
218
|
el.removeEventListener("scroll", onScroll);
|
|
191
219
|
window.removeEventListener("resize", measure);
|
|
220
|
+
if (rafId != null)
|
|
221
|
+
cancelAnimationFrame(rafId);
|
|
192
222
|
};
|
|
193
223
|
}, [scrollContainerRef, shouldVirtualize, groupVirtualize, data.length, groupRowCount]);
|
|
194
|
-
//
|
|
224
|
+
// Reset only on a real page change, never merely from the virtualization
|
|
225
|
+
// threshold flipping or rows being appended (infinite scroll/load-more).
|
|
226
|
+
const sliceKey = `${startRow}|${rowsPerPage}`;
|
|
227
|
+
const prevSliceKeyRef = useRef(sliceKey);
|
|
195
228
|
useEffect(() => {
|
|
196
|
-
|
|
229
|
+
const changed = prevSliceKeyRef.current !== sliceKey;
|
|
230
|
+
prevSliceKeyRef.current = sliceKey;
|
|
231
|
+
if (!changed)
|
|
197
232
|
return;
|
|
198
233
|
const el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
|
|
199
234
|
if (el)
|
|
200
235
|
el.scrollTop = 0;
|
|
201
236
|
setScrollTop(0);
|
|
202
|
-
}, [
|
|
237
|
+
}, [sliceKey, scrollContainerRef]);
|
|
203
238
|
const total = data.length;
|
|
204
239
|
// Prefix-sum offsets using measured heights (exact for rendered rows) and
|
|
205
240
|
// estimates for not-yet-measured rows (ROW_HEIGHT, + ESTIMATED_EXPAND if expanded).
|
|
206
241
|
// Independent of scrollTop, so scrolling reuses it instead of rebuilding it.
|
|
207
242
|
const offsets = useMemo(() => {
|
|
208
|
-
if (!shouldVirtualize)
|
|
243
|
+
if (!shouldVirtualize || groups)
|
|
209
244
|
return null;
|
|
210
245
|
const acc = new Array(total + 1);
|
|
211
246
|
acc[0] = 0;
|
|
@@ -218,7 +253,7 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
218
253
|
acc[i + 1] = acc[i] + h;
|
|
219
254
|
}
|
|
220
255
|
return acc;
|
|
221
|
-
}, [shouldVirtualize, total, data, measuredHeights, expandedRows, startRow, ROW_HEIGHT, ESTIMATED_EXPAND]);
|
|
256
|
+
}, [shouldVirtualize, groups, total, data, measuredHeights, expandedRows, startRow, ROW_HEIGHT, ESTIMATED_EXPAND]);
|
|
222
257
|
let firstIndex = 0;
|
|
223
258
|
let lastIndex = total;
|
|
224
259
|
let topPad = 0;
|
|
@@ -228,9 +263,7 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
228
263
|
const totalH = offsets[total];
|
|
229
264
|
const top = scrollTop - OVERSCAN * ROW_HEIGHT;
|
|
230
265
|
const bottom = scrollTop + vh + OVERSCAN * ROW_HEIGHT;
|
|
231
|
-
firstIndex =
|
|
232
|
-
while (firstIndex < total && offsets[firstIndex + 1] <= top)
|
|
233
|
-
firstIndex++;
|
|
266
|
+
firstIndex = findFirstVisibleIndex(offsets, total, top);
|
|
234
267
|
lastIndex = firstIndex;
|
|
235
268
|
while (lastIndex < total && offsets[lastIndex] < bottom)
|
|
236
269
|
lastIndex++;
|
|
@@ -265,9 +298,9 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
265
298
|
(isContent ? 1 : 0) +
|
|
266
299
|
(isLink ? 1 : 0), [columns, isCheckbox, isContent, isLink]);
|
|
267
300
|
const leftOffsets = useMemo(() => {
|
|
268
|
-
const checkboxOffset = isCheckbox ?
|
|
301
|
+
const checkboxOffset = isCheckbox ? slotWidth : 0;
|
|
269
302
|
return columnWidths.map((_, idx) => columnWidths.slice(0, idx).reduce((sum, w) => sum + w, 0) + checkboxOffset);
|
|
270
|
-
}, [columnWidths, isCheckbox]);
|
|
303
|
+
}, [columnWidths, isCheckbox, slotWidth]);
|
|
271
304
|
const borderStyle = useMemo(() => noBorders ? "none" : `0.063rem solid ${variant.row.ruleColor}`, [noBorders, variant.row.ruleColor]);
|
|
272
305
|
// Selected-row tint applied per-cell (NOT a blanket `& td` override) so status
|
|
273
306
|
// cells keep their solid color + white text instead of white-on-tint.
|
|
@@ -304,6 +337,38 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
304
337
|
}
|
|
305
338
|
return { heights, offsets, totalH: acc };
|
|
306
339
|
}, [groupItems, groupVirtualize, rowHeight]);
|
|
340
|
+
// Clamp scrollTop back when a shorter data set leaves it past the new content height.
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
const totalH = shouldVirtualize && offsets ? offsets[total] : groupVirtualize && groupMetrics ? groupMetrics.totalH : null;
|
|
343
|
+
if (totalH == null)
|
|
344
|
+
return;
|
|
345
|
+
const maxScrollTop = Math.max(0, totalH - viewportH);
|
|
346
|
+
if (scrollTop > maxScrollTop) {
|
|
347
|
+
const el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
|
|
348
|
+
if (el)
|
|
349
|
+
el.scrollTop = maxScrollTop;
|
|
350
|
+
setScrollTop(maxScrollTop);
|
|
351
|
+
}
|
|
352
|
+
}, [shouldVirtualize, offsets, total, groupVirtualize, groupMetrics, viewportH, scrollTop, scrollContainerRef]);
|
|
353
|
+
// Prune measured heights for rows no longer in data so the map stays bounded.
|
|
354
|
+
useEffect(() => {
|
|
355
|
+
setMeasuredHeights((prev) => {
|
|
356
|
+
if (prev.size === 0)
|
|
357
|
+
return prev;
|
|
358
|
+
const ids = new Set(data.map(getRowId));
|
|
359
|
+
let changed = false;
|
|
360
|
+
const next = new Map();
|
|
361
|
+
prev.forEach((h, id) => {
|
|
362
|
+
if (ids.has(id)) {
|
|
363
|
+
next.set(id, h);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
changed = true;
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return changed ? next : prev;
|
|
370
|
+
});
|
|
371
|
+
}, [data]);
|
|
307
372
|
if (isLoading && data.length === 0) {
|
|
308
373
|
return (_jsx(TableBodyLoading, { noOfColumns: totalVisibleColumns, borderColor: theme.colors.boxborder[200], rowsCount: typeof loadingSkeletonRows === "number" ? loadingSkeletonRows : 4, rowHeight: rowHeight }));
|
|
309
374
|
}
|
|
@@ -8,24 +8,23 @@ import TableFilters from "../filters/TableFilters.js";
|
|
|
8
8
|
import { ChevronDown, ChevronsUpDown, ChevronUp } from "lucide-react";
|
|
9
9
|
import CheckBox from "../../Checkbox/Checkbox.js";
|
|
10
10
|
import { useTableVariant } from "../variants/TableVariantContext.js";
|
|
11
|
-
import { CHECKBOX_SLOT_OFFSET } from "./rowMetrics.js";
|
|
12
11
|
import { transitionFor } from "../../../Theme/tokens/primitives/motion.js";
|
|
13
12
|
import { reducedMotionSx } from "../../../Theme/chakra/motion.js";
|
|
14
13
|
import { focusVisibleRing } from "../../../Theme/chakra/focusRing.styles.js";
|
|
15
14
|
const LABEL_JUSTIFY = { start: "space-between", center: "center", end: "flex-start" };
|
|
16
|
-
const TableHeader = ({ columns, isCheckbox, handleSort, headerRefs, columnWidths, columnsSort, freezedTextColor, noBorders, handleCheckbox, checked, isLoading, isContent, isLink, isActionFreeze, setColumnsSearch, columnsSearch, isSelecting, }) => {
|
|
15
|
+
const TableHeader = ({ columns, isCheckbox, handleSort, headerRefs, columnWidths, slotRef, slotWidth, columnsSort, freezedTextColor, noBorders, handleCheckbox, checked, isLoading, isContent, isLink, isActionFreeze, setColumnsSearch, columnsSearch, isSelecting, }) => {
|
|
17
16
|
const [openFilterId, setOpenFilterId] = useState(null);
|
|
18
|
-
return (_jsxs(Tr, { pr: 0, "aria-rowindex": 1, children: [isContent && _jsx(ContentSpacerCell, { noBorders: noBorders }), isCheckbox && (_jsx(SelectAllCell, { freezedTextColor: freezedTextColor, noBorders: noBorders, isSelecting: isSelecting, isLoading: isLoading, checked: checked, handleCheckbox: handleCheckbox })), columns.map((header, index) => {
|
|
17
|
+
return (_jsxs(Tr, { pr: 0, "aria-rowindex": 1, children: [isContent && _jsx(ContentSpacerCell, { noBorders: noBorders }), isCheckbox && (_jsx(SelectAllCell, { slotRef: slotRef, freezedTextColor: freezedTextColor, noBorders: noBorders, isSelecting: isSelecting, isLoading: isLoading, checked: checked, handleCheckbox: handleCheckbox })), columns.map((header, index) => {
|
|
19
18
|
if (header === null || header === void 0 ? void 0 : header.isHidden)
|
|
20
19
|
return null;
|
|
21
|
-
return (_jsx(ColumnHeader, { header: header, index: index, columnWidths: columnWidths, handleSort: handleSort, isCheckbox: isCheckbox, noBorders: noBorders, columnsSort: columnsSort, headerRefs: headerRefs, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch, openFilterId: openFilterId, setOpenFilterId: setOpenFilterId }, header.label));
|
|
20
|
+
return (_jsx(ColumnHeader, { header: header, index: index, columnWidths: columnWidths, slotWidth: slotWidth, handleSort: handleSort, isCheckbox: isCheckbox, noBorders: noBorders, columnsSort: columnsSort, headerRefs: headerRefs, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch, openFilterId: openFilterId, setOpenFilterId: setOpenFilterId }, header.label));
|
|
22
21
|
}), isLink && _jsx(ViewSpacerCell, { noBorders: noBorders, isActionFreeze: isActionFreeze })] }));
|
|
23
22
|
};
|
|
24
23
|
export default memo(TableHeader);
|
|
25
|
-
const SelectAllCell = memo(({ freezedTextColor, noBorders, isSelecting, isLoading, checked, handleCheckbox, }) => {
|
|
24
|
+
const SelectAllCell = memo(({ slotRef, freezedTextColor, noBorders, isSelecting, isLoading, checked, handleCheckbox, }) => {
|
|
26
25
|
const theme = useCustomTheme();
|
|
27
26
|
const variant = useTableVariant();
|
|
28
|
-
return (_jsx(Th, { scope: "col", w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: 14, fontWeight: 600, color: freezedTextColor, textTransform: "capitalize", backgroundColor: variant.header.bg, position: "sticky", className: `columns sticky-columns`, left: "0px", zIndex: 9, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, children: _jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", children: isSelecting ? (_jsx(Spinner, { size: "sm", color: theme.colors.accentText })) : (_jsx(CheckBox, { "aria-label": "Select all rows", variant: "outline", onChange: () => handleCheckbox(0), isChecked: isLoading ? false : checked === true, isIndeterminate: checked === "indeterminate" })) }) }));
|
|
27
|
+
return (_jsx(Th, { ref: slotRef, scope: "col", w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: 14, fontWeight: 600, color: freezedTextColor, textTransform: "capitalize", backgroundColor: variant.header.bg, position: "sticky", className: `columns sticky-columns`, left: "0px", zIndex: 9, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, children: _jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", children: isSelecting ? (_jsx(Spinner, { size: "sm", color: theme.colors.accentText })) : (_jsx(CheckBox, { "aria-label": "Select all rows", variant: "outline", onChange: () => handleCheckbox(0), isChecked: isLoading ? false : checked === true, isIndeterminate: checked === "indeterminate" })) }) }));
|
|
29
28
|
});
|
|
30
29
|
const ContentSpacerCell = memo(({ noBorders }) => {
|
|
31
30
|
const variant = useTableVariant();
|
|
@@ -43,7 +42,7 @@ const SortingIcon = memo(({ label, sortState, isVisible, handleSortClick, }) =>
|
|
|
43
42
|
color: accentTextOnCanvas(theme.colors.secondary, variant.header.bg),
|
|
44
43
|
}, children: [sortState === "none" && _jsx(ChevronsUpDown, { size: 14 }), sortState === "asc" && _jsx(ChevronUp, { size: 14 }), sortState === "desc" && _jsx(ChevronDown, { size: 14 })] }));
|
|
45
44
|
});
|
|
46
|
-
const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders, columnsSort, headerRefs, columnsSearch, setColumnsSearch, handleSort, openFilterId, setOpenFilterId, }) => {
|
|
45
|
+
const ColumnHeader = memo(({ header, index, columnWidths, slotWidth, isCheckbox, noBorders, columnsSort, headerRefs, columnsSearch, setColumnsSearch, handleSort, openFilterId, setOpenFilterId, }) => {
|
|
47
46
|
var _a, _b;
|
|
48
47
|
const [isHovered, setIsHovered] = useState(false);
|
|
49
48
|
const theme = useCustomTheme();
|
|
@@ -51,7 +50,7 @@ const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders,
|
|
|
51
50
|
const isFrozen = header.isFreeze;
|
|
52
51
|
const align = (_a = header.align) !== null && _a !== void 0 ? _a : "start";
|
|
53
52
|
const leftOffset = isFrozen
|
|
54
|
-
? calculateLeftOffset(columnWidths, index) + (isCheckbox ?
|
|
53
|
+
? calculateLeftOffset(columnWidths, index) + (isCheckbox ? slotWidth : 0)
|
|
55
54
|
: undefined;
|
|
56
55
|
const isSorting = header.isSort
|
|
57
56
|
? columnsSort.find((o) => o.column === header.id)
|
|
@@ -90,7 +89,7 @@ const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders,
|
|
|
90
89
|
isSortActive ||
|
|
91
90
|
openFilterId === header.id ||
|
|
92
91
|
!!columnsSearch[header.id];
|
|
93
|
-
return (_jsx(Th, { scope: "col", ref: (el) => (headerRefs.current[index] = el), "aria-sort": ariaSort, textAlign: align, textTransform: variant.header.textTransform, fontSize: variant.header.fontSize, fontWeight: variant.header.fontWeight, letterSpacing: variant.header.letterSpacing, position: isFrozen ? "sticky" : undefined, left: isFrozen ? leftOffset : undefined, minWidth: 120, backgroundColor: variant.header.bg, maxWidth: 500, zIndex: isFrozen ? 2 : 1, py: 2, pr: align === "end" ? undefined : 0, overflow: "hidden", borderTop: "none", borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, borderRight: noBorders || !variant.header.showColumnRule
|
|
92
|
+
return (_jsx(Th, { scope: "col", ref: (el) => (headerRefs.current[index] = el), "aria-sort": ariaSort, textAlign: align, textTransform: variant.header.textTransform, fontSize: variant.header.fontSize, fontWeight: variant.header.fontWeight, letterSpacing: variant.header.letterSpacing, position: isFrozen ? "sticky" : undefined, left: isFrozen ? `${leftOffset}px` : undefined, minWidth: 120, backgroundColor: variant.header.bg, maxWidth: 500, zIndex: isFrozen ? 2 : 1, py: 2, pr: align === "end" ? undefined : 0, overflow: "hidden", borderTop: "none", borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, borderRight: noBorders || !variant.header.showColumnRule
|
|
94
93
|
? "none"
|
|
95
94
|
: `0.063rem solid ${variant.header.ruleColor}`, className: `columns ${isFrozen ? "sticky-columns" : "scrollable-columns"}`, height: variant.header.height, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: _jsxs(Box, { display: "flex", color: variant.header.ink, lineHeight: "1.25rem", justifyContent: LABEL_JUSTIFY[align], flexDirection: align === "end" ? "row-reverse" : "row", gap: align === "end" ? 1 : undefined, alignItems: "center", position: "relative", children: [_jsx(Box, { textAlign: align, textOverflow: "ellipsis", whiteSpace: "nowrap", display: "inline-block", overflow: "hidden", children: header.label }), _jsxs(Box, { display: "inline-flex", alignItems: "center", opacity: showActions ? 1 : 0, transition: transitionFor(["opacity"], "base"), sx: reducedMotionSx(), _focusWithin: { opacity: 1 }, children: [header.isSort && (_jsx(SortingIcon, { label: String(header.label), sortState: (_b = isSorting === null || isSorting === void 0 ? void 0 : isSorting.direction) !== null && _b !== void 0 ? _b : "none", isVisible: isHovered || isSortActive, handleSortClick: () => {
|
|
96
95
|
let direction = "asc";
|
|
@@ -6,6 +6,5 @@ export const DENSITY_ROWS = {
|
|
|
6
6
|
export const TOOLBAR_HEIGHT = 45;
|
|
7
7
|
export const GROUP_HEADER_HEIGHT = 42;
|
|
8
8
|
export const EMPTY_STATE_HEIGHT = 200;
|
|
9
|
-
export const CHECKBOX_SLOT_OFFSET = 50;
|
|
10
9
|
export const rowHeightFor = (density) => DENSITY_ROWS[density !== null && density !== void 0 ? density : "normal"].height;
|
|
11
10
|
export const pxOf = (length) => parseFloat(length);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { SortMultiColumnData } from "../../../Utils/table.js";
|
|
2
|
+
import { searchAndSortData, SortMultiColumnData } from "../../../Utils/table.js";
|
|
3
3
|
export const DEFAULT_PAGE_SIZE = 20;
|
|
4
4
|
const defaultPageSize = DEFAULT_PAGE_SIZE;
|
|
5
5
|
const mergeColumnWithPreference = (pref, originalCol) => {
|
|
@@ -15,10 +15,25 @@ const mergeColumnWithPreference = (pref, originalCol) => {
|
|
|
15
15
|
}
|
|
16
16
|
return next;
|
|
17
17
|
};
|
|
18
|
-
const useTable = ({ data, isPagination, selections, onSelection, tablePreferences, savePreferences, noOfRowsPerPage, isServerPagination, totalRecords, columns, onNoOfRowsPerPageChange, defaultVisibleColumns, }) => {
|
|
18
|
+
const useTable = ({ data, isPagination, selections, onSelection, tablePreferences, savePreferences, noOfRowsPerPage, isServerPagination, totalRecords, columns, onNoOfRowsPerPageChange, defaultVisibleColumns, columnsSearch, }) => {
|
|
19
19
|
var _a, _b;
|
|
20
20
|
const headerRefs = useRef([]);
|
|
21
|
+
const slotObserver = useRef(null);
|
|
21
22
|
const [columnWidths, setColumnWidths] = useState([]);
|
|
23
|
+
const [slotWidth, setSlotWidth] = useState(0);
|
|
24
|
+
// Frozen columns butt against the select-all cell, so its width is watched, not assumed.
|
|
25
|
+
const slotRef = useCallback((node) => {
|
|
26
|
+
var _a;
|
|
27
|
+
(_a = slotObserver.current) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
28
|
+
slotObserver.current = null;
|
|
29
|
+
if (!node) {
|
|
30
|
+
setSlotWidth(0);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const observer = new ResizeObserver(() => setSlotWidth(node.offsetWidth));
|
|
34
|
+
observer.observe(node);
|
|
35
|
+
slotObserver.current = observer;
|
|
36
|
+
}, []);
|
|
22
37
|
const [selection, setSelection] = useState(selections !== null && selections !== void 0 ? selections : []);
|
|
23
38
|
const [columnsSort, setColumnsSort] = useState([]);
|
|
24
39
|
const [currentPage, setCurrentPage] = useState(0);
|
|
@@ -80,27 +95,45 @@ const useTable = ({ data, isPagination, selections, onSelection, tablePreference
|
|
|
80
95
|
if (!isServerPagination)
|
|
81
96
|
setCurrentPage(0);
|
|
82
97
|
}, [tableData.length, isServerPagination]);
|
|
98
|
+
const hasColumnSearch = useMemo(() => Object.values(columnsSearch).some((value) => value.trim() !== ""), [columnsSearch]);
|
|
99
|
+
// Column search runs before the page slice, or it would only ever see one page.
|
|
100
|
+
const searchedData = useMemo(() => (hasColumnSearch ? searchAndSortData(tableData, columnsSearch) : tableData), [hasColumnSearch, columnsSearch, tableData]);
|
|
83
101
|
const pages = useMemo(() => {
|
|
84
102
|
if (isServerPagination) {
|
|
85
103
|
return Math.ceil(totalRecords / (rowsPerPage || 1));
|
|
86
104
|
}
|
|
87
|
-
return isPagination ? Math.ceil(
|
|
105
|
+
return isPagination ? Math.ceil(searchedData.length / rowsPerPage) : 0;
|
|
88
106
|
}, [
|
|
89
107
|
isPagination,
|
|
90
108
|
rowsPerPage,
|
|
91
|
-
|
|
109
|
+
searchedData.length,
|
|
92
110
|
totalRecords,
|
|
93
111
|
isServerPagination,
|
|
94
112
|
]);
|
|
95
113
|
const startRow = useMemo(() => (!isPagination ? 0 : currentPage * rowsPerPage), [currentPage, isPagination, rowsPerPage]);
|
|
96
|
-
const endRow = useMemo(() => (!isPagination ?
|
|
114
|
+
const endRow = useMemo(() => (!isPagination ? searchedData.length : startRow + rowsPerPage), [isPagination, rowsPerPage, startRow, searchedData.length]);
|
|
97
115
|
const filteredData = useMemo(() => {
|
|
98
116
|
if (isServerPagination) {
|
|
99
|
-
return
|
|
117
|
+
return searchedData;
|
|
100
118
|
}
|
|
101
|
-
const sortedData = SortMultiColumnData(
|
|
119
|
+
const sortedData = SortMultiColumnData(searchedData, columnsSort);
|
|
102
120
|
return sortedData.slice(startRow, endRow);
|
|
103
|
-
}, [columnsSort, startRow, endRow,
|
|
121
|
+
}, [columnsSort, startRow, endRow, searchedData, isServerPagination]);
|
|
122
|
+
const searchKey = useMemo(() => JSON.stringify(columnsSearch), [columnsSearch]);
|
|
123
|
+
const prevSearchKey = useRef(searchKey);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (prevSearchKey.current === searchKey)
|
|
126
|
+
return;
|
|
127
|
+
prevSearchKey.current = searchKey;
|
|
128
|
+
setCurrentPage(0);
|
|
129
|
+
}, [searchKey]);
|
|
130
|
+
// A narrower result set can strand the user on a page that no longer exists.
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (isServerPagination || !isPagination)
|
|
133
|
+
return;
|
|
134
|
+
const lastPage = Math.max(0, pages - 1);
|
|
135
|
+
setCurrentPage((page) => Math.min(page, lastPage));
|
|
136
|
+
}, [pages, isServerPagination, isPagination]);
|
|
104
137
|
const handlePageSizeChange = (size) => {
|
|
105
138
|
const value = Number(size);
|
|
106
139
|
setRowsPerPage(value !== 0 ? value : defaultPageSize);
|
|
@@ -195,9 +228,12 @@ const useTable = ({ data, isPagination, selections, onSelection, tablePreference
|
|
|
195
228
|
isLink,
|
|
196
229
|
headerRefs,
|
|
197
230
|
columnWidths,
|
|
231
|
+
slotRef,
|
|
232
|
+
slotWidth,
|
|
198
233
|
handleSort,
|
|
199
234
|
handleCheckbox,
|
|
200
235
|
filteredData,
|
|
236
|
+
searchedData,
|
|
201
237
|
startRow,
|
|
202
238
|
endRow,
|
|
203
239
|
selection,
|
package/package.json
CHANGED