pixelize-design-library 2.3.1-beta.2 → 2.3.1-beta.4

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.
@@ -90,19 +90,26 @@ var SearchSelect = function (_a) {
90
90
  var _y = (0, react_1.useState)(true), hasMore = _y[0], setHasMore = _y[1];
91
91
  var inputRef = (0, react_1.useRef)(null);
92
92
  var containerRef = (0, react_1.useRef)(null);
93
+ var controlRef = (0, react_1.useRef)(null);
94
+ var _z = (0, react_1.useState)({ top: 0, left: 0, width: 0 }), dropdownPos = _z[0], setDropdownPos = _z[1];
93
95
  var scrollTimeoutRef = (0, react_1.useRef)(null);
94
- var _z = (0, react_1.useState)(false), focused = _z[0], setFocused = _z[1];
95
- var _0 = (0, react_1.useState)(false), customSelectOpen = _0[0], setCustomSelectOpen = _0[1];
96
+ var _0 = (0, react_1.useState)(false), focused = _0[0], setFocused = _0[1];
97
+ var _1 = (0, react_1.useState)(false), customSelectOpen = _1[0], setCustomSelectOpen = _1[1];
96
98
  var customSelectRef = (0, react_1.useRef)(null);
97
- var _1 = (0, react_1.useState)({
99
+ var _2 = (0, react_1.useState)({
98
100
  top: 0,
99
101
  left: 0,
100
102
  width: 0,
101
- }), customSelectPos = _1[0], setCustomSelectPos = _1[1];
103
+ }), customSelectPos = _2[0], setCustomSelectPos = _2[1];
102
104
  var theme = (0, useCustomTheme_1.useCustomTheme)();
103
105
  (0, react_2.useOutsideClick)({
104
106
  ref: containerRef,
105
- handler: function () {
107
+ handler: function (e) {
108
+ // The options dropdown is portaled to <body>, so a click on it is "outside"
109
+ // the container — ignore those so selecting an option doesn't close first.
110
+ var dropdown = document.getElementById("searchselect-dropdown-portal");
111
+ if (dropdown && dropdown.contains(e.target))
112
+ return;
106
113
  setIsOpen(false);
107
114
  },
108
115
  });
@@ -223,10 +230,34 @@ var SearchSelect = function (_a) {
223
230
  });
224
231
  }
225
232
  }, [customSelectOpen]);
233
+ // Keep the portaled options dropdown anchored to the control (on open + while
234
+ // open if the page/modal scrolls or resizes).
235
+ var updateDropdownPos = (0, react_1.useCallback)(function () {
236
+ if (!controlRef.current)
237
+ return;
238
+ var rect = controlRef.current.getBoundingClientRect();
239
+ setDropdownPos({
240
+ top: rect.bottom + window.scrollY,
241
+ left: rect.left + window.scrollX,
242
+ width: rect.width,
243
+ });
244
+ }, []);
245
+ (0, react_1.useEffect)(function () {
246
+ if (!isOpen)
247
+ return;
248
+ updateDropdownPos();
249
+ // `true` (capture) catches scrolling inside any ancestor (e.g. modal body).
250
+ window.addEventListener("scroll", updateDropdownPos, true);
251
+ window.addEventListener("resize", updateDropdownPos);
252
+ return function () {
253
+ window.removeEventListener("scroll", updateDropdownPos, true);
254
+ window.removeEventListener("resize", updateDropdownPos);
255
+ };
256
+ }, [isOpen, updateDropdownPos]);
226
257
  return (react_1.default.createElement(react_2.Box, { ref: containerRef, width: width, position: "relative" },
227
258
  label && (react_1.default.createElement(FormLabel_1.TextLabel, { label: label, id: id, isRequired: isRequired, isInformation: isInformation !== null && isInformation !== void 0 ? isInformation : information === null || information === void 0 ? void 0 : information.isInformation, informationMessage: informationMessage !== null && informationMessage !== void 0 ? informationMessage : information === null || information === void 0 ? void 0 : information.informationMessage })),
228
259
  react_1.default.createElement(react_2.InputGroup, { size: size },
229
- react_1.default.createElement(react_2.Box, { as: "div", w: "100%", minH: s.minH, px: s.px, pr: s.pr, position: "relative", display: "flex", alignItems: "center", flexWrap: "wrap", gap: s.gap, border: "0.063rem solid", borderColor: error
260
+ react_1.default.createElement(react_2.Box, { as: "div", ref: controlRef, w: "100%", minH: s.minH, px: s.px, pr: s.pr, position: "relative", display: "flex", alignItems: "center", flexWrap: "wrap", gap: s.gap, border: "0.063rem solid", borderColor: error
230
261
  ? theme.colors.semantic.error[500]
231
262
  : focused
232
263
  ? theme.colors.primary[500]
@@ -275,22 +306,23 @@ var SearchSelect = function (_a) {
275
306
  e.stopPropagation();
276
307
  setIsOpen(function (prev) { return !prev; });
277
308
  }, "aria-label": "toggle-dropdown", display: "flex", alignItems: "center" }, isOpen ? (react_1.default.createElement(lucide_react_1.ChevronUp, { size: s.chevronSize })) : (react_1.default.createElement(lucide_react_1.ChevronDown, { size: s.chevronSize }))))))),
278
- isOpen && (react_1.default.createElement(react_2.Box, { position: "absolute", zIndex: 10, width: "100%", maxH: "20rem", borderWidth: 1, borderRadius: "sm", bg: theme.colors.white, boxShadow: "md", display: "flex", flexDirection: "column" },
279
- isMultiple && isSelectAll && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: 2, borderBottom: "0.063rem solid ".concat(theme.colors.gray[100]) },
280
- react_1.default.createElement(Checkbox_1.default, { isChecked: allFilteredSelected, sx: { pointerEvents: "none" }, size: s.checkboxSize }),
281
- react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, allFilteredSelected ? "Unselect All" : "Select All"))),
282
- react_1.default.createElement(react_2.Box, { flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", borderBottom: "0.063rem solid", borderColor: theme.colors.gray[200] }, isOptionLoading && filteredOptions.length === 0 ? (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, display: "flex", alignItems: "center", gap: 2, minH: s.optionRowMinH },
283
- react_1.default.createElement(react_2.Spinner, { size: s.spinnerSize }),
284
- react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, loadingText))) : !isOptionLoading && filteredOptions.length === 0 ? (react_1.default.createElement(react_2.Text, { p: 3, fontSize: s.dropdownTextFontSize, color: theme.colors.gray[500] }, "No results found")) : (react_1.default.createElement(react_1.default.Fragment, null,
285
- filteredOptions.map(function (option) { return (react_1.default.createElement(react_2.Box, { key: option.id, py: s.optionRowPy, px: 3, minH: s.optionRowMinH, cursor: "pointer", bg: isSelected(option.id) ? theme.colors.gray[100] : "transparent", _hover: { bg: theme.colors.gray[50] }, onClick: function () { return handleSelect(option); }, display: "flex", alignItems: "center", gap: 2, borderBottom: "0.063rem solid ".concat(theme.colors.gray[100]) },
286
- isMultiple && (react_1.default.createElement(Checkbox_1.default, { isChecked: isSelected(option.id), sx: { pointerEvents: "none" }, size: s.checkboxSize })),
287
- option.view ? (react_1.default.createElement(react_1.default.Fragment, null, option.view)) : (react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, option.label)))); }),
288
- ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, display: "flex", alignItems: "center", justifyContent: "center", gap: 2, minH: s.optionRowMinH },
309
+ isOpen && (react_1.default.createElement(react_2.Portal, null,
310
+ react_1.default.createElement(react_2.Box, { id: "searchselect-dropdown-portal", position: "absolute", top: "".concat(dropdownPos.top, "px"), left: "".concat(dropdownPos.left, "px"), width: "".concat(dropdownPos.width, "px"), zIndex: 1500, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.gray[200], borderRadius: "sm", bg: theme.colors.white, boxShadow: "md", display: "flex", flexDirection: "column" },
311
+ isMultiple && isSelectAll && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: 2, borderBottom: "0.063rem solid ".concat(theme.colors.gray[100]) },
312
+ react_1.default.createElement(Checkbox_1.default, { isChecked: allFilteredSelected, sx: { pointerEvents: "none" }, size: s.checkboxSize }),
313
+ react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, allFilteredSelected ? "Unselect All" : "Select All"))),
314
+ react_1.default.createElement(react_2.Box, { flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", borderBottom: "0.063rem solid", borderColor: theme.colors.gray[200] }, isOptionLoading && filteredOptions.length === 0 ? (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, display: "flex", alignItems: "center", gap: 2, minH: s.optionRowMinH },
289
315
  react_1.default.createElement(react_2.Spinner, { size: s.spinnerSize }),
290
- react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, loadingText)))))),
291
- (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, borderTop: "0.063rem solid", borderColor: theme.colors.gray[200], bg: theme.colors.white, display: "flex", alignItems: "center", gap: 2, minH: s.optionRowMinH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: theme.colors.gray[50] } },
292
- addNew.icon || react_1.default.createElement(lucide_react_1.Plus, { size: s.addNewIconSize }),
293
- react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, addNew.text || "Add New"))))),
316
+ react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, loadingText))) : !isOptionLoading && filteredOptions.length === 0 ? (react_1.default.createElement(react_2.Text, { p: 3, fontSize: s.dropdownTextFontSize, color: theme.colors.gray[500] }, "No results found")) : (react_1.default.createElement(react_1.default.Fragment, null,
317
+ filteredOptions.map(function (option) { return (react_1.default.createElement(react_2.Box, { key: option.id, py: s.optionRowPy, px: 3, minH: s.optionRowMinH, cursor: "pointer", bg: isSelected(option.id) ? theme.colors.gray[100] : "transparent", _hover: { bg: theme.colors.gray[50] }, onClick: function () { return handleSelect(option); }, display: "flex", alignItems: "center", gap: 2, borderBottom: "0.063rem solid ".concat(theme.colors.gray[100]) },
318
+ isMultiple && (react_1.default.createElement(Checkbox_1.default, { isChecked: isSelected(option.id), sx: { pointerEvents: "none" }, size: s.checkboxSize })),
319
+ option.view ? (react_1.default.createElement(react_1.default.Fragment, null, option.view)) : (react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, option.label)))); }),
320
+ ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, display: "flex", alignItems: "center", justifyContent: "center", gap: 2, minH: s.optionRowMinH },
321
+ react_1.default.createElement(react_2.Spinner, { size: s.spinnerSize }),
322
+ react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, loadingText)))))),
323
+ (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (react_1.default.createElement(react_2.Box, { py: s.optionRowPy, px: 3, borderTop: "0.063rem solid", borderColor: theme.colors.gray[200], bg: theme.colors.white, display: "flex", alignItems: "center", gap: 2, minH: s.optionRowMinH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: theme.colors.gray[50] } },
324
+ addNew.icon || react_1.default.createElement(lucide_react_1.Plus, { size: s.addNewIconSize }),
325
+ react_1.default.createElement(react_2.Text, { fontSize: s.dropdownTextFontSize }, addNew.text || "Add New")))))),
294
326
  helperText && !error && react_1.default.createElement(HelperText_1.default, { helperText: helperText }),
295
327
  error && react_1.default.createElement(ErrorMessage_1.default, { errorMessage: errorMessage })));
296
328
  };
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import { TableProps } from "./TableProps";
3
- export default function Table({ data, columns, onSelection, isLoading, isCheckbox, headerBgColor, freezedBgColor, headerTextColor, freezedTextColor, tableBorderColor, noBorders, isPagination, onRowClick, selections, isActionFreeze, preferences, paginationMode, infiniteScroll, hasMore, isLoadingMore, noOfRowsPerPage, totalRecords, onPagination, isTableSettings, headerActions, onGlobalSearch, onNoOfRowsPerPageChange, paginationSelectOptions, tableMaxHeight, minVisibleRows, maxVisibleRows, autoFitViewport, tableSettings, filterSidebar, loadingSkeletonRows, defaultVisibleColumns, density, stripe, groupBy, groupColors, onAddItem, emptyState, }: TableProps): React.JSX.Element;
3
+ export default function Table({ data, columns, onSelection, isLoading, isCheckbox, headerBgColor, freezedBgColor, headerTextColor, freezedTextColor, tableBorderColor, noBorders, isPagination, onRowClick, selections, isActionFreeze, preferences, paginationMode, infiniteScroll, hasMore, isLoadingMore, groupLoadMore, loadMoreText, loadMorePosition, loadMoreChunkSize, noOfRowsPerPage, totalRecords, onPagination, isTableSettings, headerActions, onGlobalSearch, onNoOfRowsPerPageChange, paginationSelectOptions, tableMaxHeight, minVisibleRows, maxVisibleRows, autoFitViewport, tableSettings, filterSidebar, loadingSkeletonRows, defaultVisibleColumns, density, stripe, groupColors, onAddItem, emptyState, }: TableProps): React.JSX.Element;
@@ -65,33 +65,34 @@ var HeaderActions_1 = __importDefault(require("./components/HeaderActions"));
65
65
  var Divider_1 = __importDefault(require("../Divider/Divider"));
66
66
  var TableSearch_1 = __importDefault(require("./components/TableSearch"));
67
67
  var ActiveFilters_1 = __importDefault(require("./filters/ActiveFilters"));
68
+ var Button_1 = __importDefault(require("../Button/Button"));
68
69
  var lucide_react_1 = require("lucide-react");
69
70
  var MotionBox = (0, framer_motion_1.motion)(react_2.Box);
70
71
  function Table(_a) {
71
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
72
- var data = _a.data, columns = _a.columns, onSelection = _a.onSelection, isLoading = _a.isLoading, _9 = _a.isCheckbox, isCheckbox = _9 === void 0 ? false : _9, headerBgColor = _a.headerBgColor, freezedBgColor = _a.freezedBgColor, headerTextColor = _a.headerTextColor, freezedTextColor = _a.freezedTextColor, tableBorderColor = _a.tableBorderColor, _10 = _a.noBorders, noBorders = _10 === void 0 ? false : _10, _11 = _a.isPagination, isPagination = _11 === void 0 ? true : _11, onRowClick = _a.onRowClick, selections = _a.selections, _12 = _a.isActionFreeze, isActionFreeze = _12 === void 0 ? true : _12, _13 = _a.preferences, preferences = _13 === void 0 ? {
72
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13;
73
+ var data = _a.data, columns = _a.columns, onSelection = _a.onSelection, isLoading = _a.isLoading, _14 = _a.isCheckbox, isCheckbox = _14 === void 0 ? false : _14, headerBgColor = _a.headerBgColor, freezedBgColor = _a.freezedBgColor, headerTextColor = _a.headerTextColor, freezedTextColor = _a.freezedTextColor, tableBorderColor = _a.tableBorderColor, _15 = _a.noBorders, noBorders = _15 === void 0 ? false : _15, _16 = _a.isPagination, isPagination = _16 === void 0 ? true : _16, onRowClick = _a.onRowClick, selections = _a.selections, _17 = _a.isActionFreeze, isActionFreeze = _17 === void 0 ? true : _17, _18 = _a.preferences, preferences = _18 === void 0 ? {
73
74
  url: "",
74
75
  token: "",
75
76
  key: "",
76
77
  name: "",
77
78
  page: "",
78
79
  orgId: "",
79
- } : _13, _14 = _a.paginationMode, paginationMode = _14 === void 0 ? "client" : _14, _15 = _a.infiniteScroll, infiniteScroll = _15 === void 0 ? false : _15, hasMore = _a.hasMore, _16 = _a.isLoadingMore, isLoadingMore = _16 === void 0 ? false : _16, _17 = _a.noOfRowsPerPage, noOfRowsPerPage = _17 === void 0 ? 50 : _17, _18 = _a.totalRecords, totalRecords = _18 === void 0 ? 0 : _18, onPagination = _a.onPagination, _19 = _a.isTableSettings, isTableSettings = _19 === void 0 ? false : _19, headerActions = _a.headerActions, onGlobalSearch = _a.onGlobalSearch, onNoOfRowsPerPageChange = _a.onNoOfRowsPerPageChange, paginationSelectOptions = _a.paginationSelectOptions, tableMaxHeight = _a.tableMaxHeight, minVisibleRows = _a.minVisibleRows, maxVisibleRows = _a.maxVisibleRows, autoFitViewport = _a.autoFitViewport, tableSettings = _a.tableSettings, filterSidebar = _a.filterSidebar, loadingSkeletonRows = _a.loadingSkeletonRows, defaultVisibleColumns = _a.defaultVisibleColumns, _20 = _a.density, density = _20 === void 0 ? "normal" : _20, stripe = _a.stripe, groupBy = _a.groupBy, groupColors = _a.groupColors, onAddItem = _a.onAddItem, emptyState = _a.emptyState;
80
+ } : _18, _19 = _a.paginationMode, paginationMode = _19 === void 0 ? "client" : _19, _20 = _a.infiniteScroll, infiniteScroll = _20 === void 0 ? false : _20, hasMore = _a.hasMore, _21 = _a.isLoadingMore, isLoadingMore = _21 === void 0 ? false : _21, _22 = _a.groupLoadMore, groupLoadMore = _22 === void 0 ? false : _22, _23 = _a.loadMoreText, loadMoreText = _23 === void 0 ? "Load more" : _23, _24 = _a.loadMorePosition, loadMorePosition = _24 === void 0 ? "top" : _24, loadMoreChunkSize = _a.loadMoreChunkSize, _25 = _a.noOfRowsPerPage, noOfRowsPerPage = _25 === void 0 ? 50 : _25, _26 = _a.totalRecords, totalRecords = _26 === void 0 ? 0 : _26, onPagination = _a.onPagination, _27 = _a.isTableSettings, isTableSettings = _27 === void 0 ? false : _27, headerActions = _a.headerActions, onGlobalSearch = _a.onGlobalSearch, onNoOfRowsPerPageChange = _a.onNoOfRowsPerPageChange, paginationSelectOptions = _a.paginationSelectOptions, tableMaxHeight = _a.tableMaxHeight, minVisibleRows = _a.minVisibleRows, maxVisibleRows = _a.maxVisibleRows, autoFitViewport = _a.autoFitViewport, tableSettings = _a.tableSettings, filterSidebar = _a.filterSidebar, loadingSkeletonRows = _a.loadingSkeletonRows, defaultVisibleColumns = _a.defaultVisibleColumns, _28 = _a.density, density = _28 === void 0 ? "normal" : _28, stripe = _a.stripe, groupColors = _a.groupColors, onAddItem = _a.onAddItem, emptyState = _a.emptyState;
80
81
  var theme = (0, useCustomTheme_1.useCustomTheme)();
81
- var _21 = (0, react_1.useState)({}), columnsSearch = _21[0], setColumnsSearch = _21[1];
82
+ var _29 = (0, react_1.useState)({}), columnsSearch = _29[0], setColumnsSearch = _29[1];
82
83
  var tableContainerRef = (0, react_1.useRef)(null);
83
- var _22 = (0, react_2.useDisclosure)(), isFilterModalOpen = _22.isOpen, onFilterModalOpen = _22.onOpen, onFilterModalClose = _22.onClose;
84
+ var _30 = (0, react_2.useDisclosure)(), isFilterModalOpen = _30.isOpen, onFilterModalOpen = _30.onOpen, onFilterModalClose = _30.onClose;
84
85
  var filterMode = (_b = filterSidebar === null || filterSidebar === void 0 ? void 0 : filterSidebar.filterMode) !== null && _b !== void 0 ? _b : "sidebar";
85
- var _23 = (0, react_1.useState)(500), viewportAvailableH = _23[0], setViewportAvailableH = _23[1];
86
+ var _31 = (0, react_1.useState)(500), viewportAvailableH = _31[0], setViewportAvailableH = _31[1];
86
87
  var isServerPagination = paginationMode === "server";
87
- var _24 = (0, usePreferences_1.useGetPreferences)({
88
+ var _32 = (0, usePreferences_1.useGetPreferences)({
88
89
  baseUrl: preferences.url,
89
90
  page: preferences.page,
90
91
  key: preferences.key,
91
92
  name: preferences.name,
92
93
  authToken: preferences.token,
93
94
  orgId: preferences.orgId,
94
- }), tablePreferencesData = _24.preferences, loading = _24.loading;
95
+ }), tablePreferencesData = _32.preferences, loading = _32.loading;
95
96
  var savePreferences = (0, usePreferences_1.useSavePreferences)({
96
97
  baseUrl: preferences.url,
97
98
  page: preferences.page,
@@ -109,7 +110,7 @@ function Table(_a) {
109
110
  }
110
111
  return {};
111
112
  }, [tablePreferencesData]);
112
- var _25 = (0, useTable_1.default)({
113
+ var _33 = (0, useTable_1.default)({
113
114
  tableBorderColor: tableBorderColor,
114
115
  data: data,
115
116
  isPagination: isPagination,
@@ -123,26 +124,48 @@ function Table(_a) {
123
124
  isServerPagination: isServerPagination,
124
125
  onNoOfRowsPerPageChange: onNoOfRowsPerPageChange,
125
126
  defaultVisibleColumns: defaultVisibleColumns,
126
- }), tableData = _25.tableData, isContent = _25.isContent, isLink = _25.isLink, headerRefs = _25.headerRefs, columnWidths = _25.columnWidths, handleSort = _25.handleSort, handleCheckbox = _25.handleCheckbox, filteredData = _25.filteredData, startRow = _25.startRow, endRow = _25.endRow, selection = _25.selection, columnsSort = _25.columnsSort, currentPage = _25.currentPage, pages = _25.pages, rowsPerPage = _25.rowsPerPage, handlePageSizeChange = _25.handlePageSizeChange, setCurrentPage = _25.setCurrentPage, columnsList = _25.columnsList, handleColumnPreferences = _25.handleColumnPreferences, isSelecting = _25.isSelecting;
127
- // Density is managed here so the Table Settings tab can change + persist it
128
- // (seeded from the prop, then from saved preferences when they load).
129
- var _26 = (0, react_1.useState)(density), densityState = _26[0], setDensityState = _26[1];
127
+ }), tableData = _33.tableData, isContent = _33.isContent, isLink = _33.isLink, headerRefs = _33.headerRefs, columnWidths = _33.columnWidths, handleSort = _33.handleSort, handleCheckbox = _33.handleCheckbox, filteredData = _33.filteredData, startRow = _33.startRow, endRow = _33.endRow, selection = _33.selection, columnsSort = _33.columnsSort, currentPage = _33.currentPage, pages = _33.pages, rowsPerPage = _33.rowsPerPage, handlePageSizeChange = _33.handlePageSizeChange, setCurrentPage = _33.setCurrentPage, columnsList = _33.columnsList, handleColumnPreferences = _33.handleColumnPreferences, isSelecting = _33.isSelecting;
128
+ // Density + grouping are managed here so the Table Settings tabs can change +
129
+ // persist them (seeded from the prop / saved preferences when they load).
130
+ var _34 = (0, react_1.useState)(density), densityState = _34[0], setDensityState = _34[1];
131
+ // Grouping is chosen by the user in Table Settings > Group and persisted to
132
+ // preferences (`json.groupBy`); there is no `groupBy` prop.
133
+ var _35 = (0, react_1.useState)(undefined), groupByState = _35[0], setGroupByState = _35[1];
130
134
  (0, react_1.useEffect)(function () {
131
135
  if (tablePreferences === null || tablePreferences === void 0 ? void 0 : tablePreferences.density)
132
136
  setDensityState(tablePreferences.density);
133
137
  }, [tablePreferences === null || tablePreferences === void 0 ? void 0 : tablePreferences.density]);
138
+ (0, react_1.useEffect)(function () {
139
+ if (tablePreferences && "groupBy" in tablePreferences) {
140
+ var saved = tablePreferences.groupBy;
141
+ setGroupByState(saved === null || saved === undefined || saved === "" ? undefined : saved);
142
+ }
143
+ }, [tablePreferences === null || tablePreferences === void 0 ? void 0 : tablePreferences.groupBy]);
134
144
  var handleDensityChange = function (d) {
135
145
  setDensityState(d);
136
- savePreferences === null || savePreferences === void 0 ? void 0 : savePreferences(__assign(__assign({}, tablePreferences), { columns: columnsList, density: d }));
146
+ savePreferences === null || savePreferences === void 0 ? void 0 : savePreferences(__assign(__assign({}, tablePreferences), { columns: columnsList, groupBy: groupByState !== null && groupByState !== void 0 ? groupByState : null, density: d }));
147
+ };
148
+ var handleGroupByChange = function (value) {
149
+ setGroupByState(value);
150
+ savePreferences === null || savePreferences === void 0 ? void 0 : savePreferences(__assign(__assign({}, tablePreferences), { columns: columnsList, density: densityState, groupBy: value !== null && value !== void 0 ? value : null }));
137
151
  };
138
152
  var _filteredData = (0, react_1.useMemo)(function () {
139
153
  return (0, table_1.searchAndSortData)(filteredData, columnsSearch);
140
154
  }, [columnsSearch, filteredData]);
141
- // --- Monday-style grouping (client-side). When active, pagination is hidden
142
- // and the body renders colored, collapsible groups over the full dataset. ---
143
- var isGrouped = !!groupBy && !isServerPagination;
155
+ // --- Monday-style grouping. Active whenever the user has picked a group column
156
+ // (in Settings > Group). When active, the numbered pager / infinite scroll are
157
+ // hidden and the body renders colored, collapsible groups over the loaded rows.
158
+ // `groupLoadMore` adds a "Load more" button on top to fetch more into the groups. ---
159
+ var isGrouped = !!groupByState;
144
160
  // Infinite scroll (server mode, not grouped): load more on scroll-near-bottom.
145
161
  var canInfinite = infiniteScroll && !isGrouped;
162
+ // Grouped "Load more": chunked, button-triggered loading that merges into groups.
163
+ var hasMoreEffective = typeof hasMore === "boolean"
164
+ ? hasMore
165
+ : isServerPagination
166
+ ? tableData.length < totalRecords
167
+ : false;
168
+ var canGroupLoadMore = isGrouped && groupLoadMore && hasMoreEffective;
146
169
  var groupPalette = (0, react_1.useMemo)(function () { return (0, table_1.buildTablePalette)(theme); }, [theme]);
147
170
  var groupedSource = (0, react_1.useMemo)(function () {
148
171
  if (!isGrouped)
@@ -152,8 +175,8 @@ function Table(_a) {
152
175
  var renderGroups = (0, react_1.useMemo)(function () {
153
176
  if (!isGrouped)
154
177
  return undefined;
155
- return (0, table_1.groupRows)(groupedSource, groupBy).map(function (g) { return (__assign(__assign({}, g), { color: (0, table_1.pickTableColor)(g.value, groupPalette, groupColors === null || groupColors === void 0 ? void 0 : groupColors[g.value]) })); });
156
- }, [isGrouped, groupedSource, groupBy, groupPalette, groupColors]);
178
+ return (0, table_1.groupRows)(groupedSource, groupByState).map(function (g) { return (__assign(__assign({}, g), { color: (0, table_1.pickTableColor)(g.value, groupPalette, groupColors === null || groupColors === void 0 ? void 0 : groupColors[g.value]) })); });
179
+ }, [isGrouped, groupedSource, groupByState, groupPalette, groupColors]);
157
180
  var onPaginationRef = (0, react_1.useRef)(onPagination);
158
181
  onPaginationRef.current = onPagination;
159
182
  var prevPageRef = (0, react_1.useRef)(currentPage);
@@ -180,6 +203,18 @@ function Table(_a) {
180
203
  prevPageRef.current = currentPage;
181
204
  }
182
205
  }, [currentPage, rowsPerPage]);
206
+ // Grouped "Load more": request the next chunk; parent appends it to `data` and
207
+ // the table re-groups so new rows land in their respective groups.
208
+ var handleGroupLoadMore = function () {
209
+ var _a;
210
+ if (isLoadingMore || isTableLoading || !hasMoreEffective)
211
+ return;
212
+ var chunk = loadMoreChunkSize !== null && loadMoreChunkSize !== void 0 ? loadMoreChunkSize : rowsPerPage;
213
+ var nextPage = Math.floor(tableData.length / (chunk || 1)) + 1;
214
+ var lastRecord = tableData.length > 0 ? tableData[tableData.length - 1] : undefined;
215
+ (_a = onPaginationRef.current) === null || _a === void 0 ? void 0 : _a.call(onPaginationRef, nextPage, chunk, lastRecord, "next");
216
+ };
217
+ var groupLoadMoreCaption = totalRecords > 0 ? "Showing ".concat(tableData.length, " of ").concat(totalRecords) : undefined;
183
218
  var tablePaginationText = (0, react_1.useMemo)(function () { return isServerPagination
184
219
  ? "".concat(startRow + 1, " - ").concat(Math.min(startRow + rowsPerPage, totalRecords), " of ").concat(totalRecords)
185
220
  : "".concat(startRow + 1, " - ").concat(endRow > tableData.length ? tableData.length : endRow, " of ").concat(tableData.length); }, [startRow, rowsPerPage, totalRecords, endRow, tableData.length]);
@@ -292,17 +327,20 @@ function Table(_a) {
292
327
  react_1.default.createElement(TableSearch_1.default, { onSearch: onGlobalSearch }),
293
328
  isTableSettings && (react_1.default.createElement(react_1.default.Fragment, null,
294
329
  react_1.default.createElement(Divider_1.default, null),
295
- react_1.default.createElement(TableSettings_1.default, { columns: columnsList, onSave: function (cols) { return handleColumnPreferences(cols); }, tableSettings: tableSettings, density: densityState, onDensityChange: handleDensityChange }))),
330
+ react_1.default.createElement(TableSettings_1.default, { columns: columnsList, onSave: function (cols) { return handleColumnPreferences(cols); }, tableSettings: tableSettings, density: densityState, onDensityChange: handleDensityChange, groupBy: groupByState, onGroupByChange: handleGroupByChange }))),
296
331
  headerActions && (react_1.default.createElement(react_1.default.Fragment, null,
297
332
  react_1.default.createElement(Divider_1.default, null),
298
333
  react_1.default.createElement(HeaderActions_1.default, { actions: headerActions, selections: selection }))),
299
334
  react_1.default.createElement(ActiveFilters_1.default, { columns: columnsList, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch }),
300
335
  react_1.default.createElement(react_2.Box, { ml: "auto", display: "flex", alignItems: "center", gap: 2 },
336
+ canGroupLoadMore && loadMorePosition === "top" && (react_1.default.createElement(react_2.Box, { display: "flex", alignItems: "center", gap: 2, flex: "0 0 auto" },
337
+ groupLoadMoreCaption && (react_1.default.createElement(react_2.Box, { fontSize: "0.75rem", color: (_x = (_w = theme.colors) === null || _w === void 0 ? void 0 : _w.text) === null || _x === void 0 ? void 0 : _x[500], whiteSpace: "nowrap" }, groupLoadMoreCaption)),
338
+ react_1.default.createElement(Button_1.default, { size: "xs", variant: "outline", colorScheme: "gray", isLoading: isLoadingMore, loadingText: loadMoreText, onClick: handleGroupLoadMore, label: loadMoreText }))),
301
339
  (isPagination || isServerPagination) && !isGrouped && !canInfinite && !isCompactHeader && (react_1.default.createElement(react_2.Box, { flex: "0 0 auto" },
302
340
  react_1.default.createElement(Pagination_1.default, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }))),
303
341
  (isPagination || isServerPagination) && !isGrouped && !canInfinite && isCompactHeader && tableData.length > 0 && (react_1.default.createElement(react_2.Popover, { placement: "bottom-end" },
304
342
  react_1.default.createElement(react_2.PopoverTrigger, null,
305
- react_1.default.createElement(react_2.IconButton, { "aria-label": "More", size: "sm", variant: "ghost", icon: react_1.default.createElement(lucide_react_1.EllipsisVertical, { size: 18, color: (_x = (_w = theme.colors) === null || _w === void 0 ? void 0 : _w.text) === null || _x === void 0 ? void 0 : _x[500] }) })),
343
+ react_1.default.createElement(react_2.IconButton, { "aria-label": "More", size: "sm", variant: "ghost", icon: react_1.default.createElement(lucide_react_1.EllipsisVertical, { size: 18, color: (_z = (_y = theme.colors) === null || _y === void 0 ? void 0 : _y.text) === null || _z === void 0 ? void 0 : _z[500] }) })),
306
344
  react_1.default.createElement(react_2.PopoverContent, { maxW: "22rem", p: 2, overflow: "hidden" },
307
345
  react_1.default.createElement(react_2.PopoverBody, { p: 0 },
308
346
  react_1.default.createElement(Pagination_1.default, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }))))))),
@@ -349,15 +387,18 @@ function Table(_a) {
349
387
  zIndex: 999,
350
388
  },
351
389
  } },
352
- react_1.default.createElement(react_3.Thead, { position: "sticky", top: 0, zIndex: 4, bg: (_0 = (_z = (_y = theme.colors.table) === null || _y === void 0 ? void 0 : _y.hover) === null || _z === void 0 ? void 0 : _z[200]) !== null && _0 !== void 0 ? _0 : (_1 = theme.colors.secondary) === null || _1 === void 0 ? void 0 : _1[50] },
353
- react_1.default.createElement(TableHeader_1.default, { columns: columnsList, isCheckbox: isCheckbox, headerBgColor: headerBgColor !== null && headerBgColor !== void 0 ? headerBgColor : theme.colors.backgroundColor.muted, headerTextColor: headerTextColor !== null && headerTextColor !== void 0 ? headerTextColor : (_2 = theme.colors) === null || _2 === void 0 ? void 0 : _2.gray[600], freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_3 = theme.colors) === null || _3 === void 0 ? void 0 : _3.gray[600], handleSort: handleSort, headerRefs: headerRefs, columnWidths: columnWidths, columnsSort: columnsSort, noBorders: noBorders, handleCheckbox: handleCheckbox, isLoading: isTableLoading, checked: tableData.length !== 0 && selection.length === tableData.length
390
+ react_1.default.createElement(react_3.Thead, { position: "sticky", top: 0, zIndex: 4, bg: (_2 = (_1 = (_0 = theme.colors.table) === null || _0 === void 0 ? void 0 : _0.hover) === null || _1 === void 0 ? void 0 : _1[200]) !== null && _2 !== void 0 ? _2 : (_3 = theme.colors.secondary) === null || _3 === void 0 ? void 0 : _3[50] },
391
+ react_1.default.createElement(TableHeader_1.default, { columns: columnsList, isCheckbox: isCheckbox, headerBgColor: headerBgColor !== null && headerBgColor !== void 0 ? headerBgColor : theme.colors.backgroundColor.muted, headerTextColor: headerTextColor !== null && headerTextColor !== void 0 ? headerTextColor : (_4 = theme.colors) === null || _4 === void 0 ? void 0 : _4.gray[600], freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_5 = theme.colors) === null || _5 === void 0 ? void 0 : _5.gray[600], handleSort: handleSort, headerRefs: headerRefs, columnWidths: columnWidths, columnsSort: columnsSort, noBorders: noBorders, handleCheckbox: handleCheckbox, isLoading: isTableLoading, checked: tableData.length !== 0 && selection.length === tableData.length
354
392
  ? true
355
393
  : selection.length === 0
356
394
  ? false
357
395
  : "indeterminate", isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze, setColumnsSearch: setColumnsSearch, columnsSearch: columnsSearch, isSelecting: isSelecting })),
358
396
  react_1.default.createElement(react_3.Tbody, null,
359
- react_1.default.createElement(TableBody_1.default, { data: isGrouped ? groupedSource : _filteredData, groups: renderGroups, onAddItem: onAddItem, columns: columnsList, startRow: isGrouped ? 0 : startRow, endRow: endRow, scrollContainerRef: tableContainerRef, isCheckbox: isCheckbox, columnWidths: columnWidths, noBorders: noBorders, freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_4 = theme.colors) === null || _4 === void 0 ? void 0 : _4.gray[600], handleCheckbox: handleCheckbox, selections: selection, isLoading: isTableLoading, loadingSkeletonRows: loadingSkeletonRows, onRowClick: onRowClick, isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze, density: densityState, stripe: stripe, emptyState: emptyState }))),
397
+ react_1.default.createElement(TableBody_1.default, { data: isGrouped ? groupedSource : _filteredData, groups: renderGroups, onAddItem: onAddItem, columns: columnsList, startRow: isGrouped ? 0 : startRow, endRow: endRow, scrollContainerRef: tableContainerRef, isCheckbox: isCheckbox, columnWidths: columnWidths, noBorders: noBorders, freezedBgColor: freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.backgroundColor.secondary, freezedTextColor: freezedTextColor !== null && freezedTextColor !== void 0 ? freezedTextColor : (_6 = theme.colors) === null || _6 === void 0 ? void 0 : _6.gray[600], handleCheckbox: handleCheckbox, selections: selection, isLoading: isTableLoading, loadingSkeletonRows: loadingSkeletonRows, onRowClick: onRowClick, isContent: isContent, isLink: isLink, isActionFreeze: isActionFreeze, density: densityState, stripe: stripe, emptyState: emptyState }))),
360
398
  canInfinite && isLoadingMore && (react_1.default.createElement(react_2.Flex, { justify: "center", align: "center", py: 3, gap: 2 },
361
- react_1.default.createElement(react_2.Spinner, { size: "sm", color: (_6 = (_5 = theme.colors) === null || _5 === void 0 ? void 0 : _5.primary) === null || _6 === void 0 ? void 0 : _6[500] }),
362
- react_1.default.createElement(react_2.Box, { fontSize: "0.75rem", color: (_8 = (_7 = theme.colors) === null || _7 === void 0 ? void 0 : _7.text) === null || _8 === void 0 ? void 0 : _8[500] }, "Loading more\u2026"))))))));
399
+ react_1.default.createElement(react_2.Spinner, { size: "sm", color: (_8 = (_7 = theme.colors) === null || _7 === void 0 ? void 0 : _7.primary) === null || _8 === void 0 ? void 0 : _8[500] }),
400
+ react_1.default.createElement(react_2.Box, { fontSize: "0.75rem", color: (_10 = (_9 = theme.colors) === null || _9 === void 0 ? void 0 : _9.text) === null || _10 === void 0 ? void 0 : _10[500] }, "Loading more\u2026")))),
401
+ canGroupLoadMore && loadMorePosition === "bottom" && (react_1.default.createElement(react_2.Flex, { justify: "center", align: "center", gap: 3, py: 3, borderTop: "0.063rem solid ".concat((_11 = theme.colors.border) === null || _11 === void 0 ? void 0 : _11[500]) },
402
+ groupLoadMoreCaption && (react_1.default.createElement(react_2.Box, { fontSize: "0.75rem", color: (_13 = (_12 = theme.colors) === null || _12 === void 0 ? void 0 : _12.text) === null || _13 === void 0 ? void 0 : _13[500] }, groupLoadMoreCaption)),
403
+ react_1.default.createElement(Button_1.default, { size: "xs", variant: "outline", colorScheme: "gray", isLoading: isLoadingMore, loadingText: loadMoreText, onClick: handleGroupLoadMore, label: loadMoreText })))))));
363
404
  }
@@ -9,7 +9,6 @@ export type TableEmptyState = {
9
9
  export type TableProps = {
10
10
  data: DataObject[];
11
11
  columns: TableHeaderProps[];
12
- groupBy?: string | number;
13
12
  groupColors?: Record<string | number, string>;
14
13
  onAddItem?: (groupValue: string | number) => void;
15
14
  density?: TableDensity;
@@ -36,6 +35,10 @@ export type TableProps = {
36
35
  infiniteScroll?: boolean;
37
36
  hasMore?: boolean;
38
37
  isLoadingMore?: boolean;
38
+ groupLoadMore?: boolean;
39
+ loadMoreText?: string;
40
+ loadMorePosition?: "top" | "bottom";
41
+ loadMoreChunkSize?: number;
39
42
  noOfRowsPerPage?: number;
40
43
  totalRecords?: number;
41
44
  onPagination?: (page: number, noOfRecords: number, record: DataObject | undefined, direction: "next" | "prev" | "first" | "last") => void;
@@ -6,7 +6,7 @@ type TableRowActions = {
6
6
  actions?: () => ReactNode;
7
7
  [key: string]: any;
8
8
  };
9
- declare const TableActions: ({ row }: {
9
+ declare const TableActions: React.MemoExoticComponent<({ row }: {
10
10
  row?: TableRowActions;
11
- }) => React.JSX.Element | null;
11
+ }) => React.JSX.Element | null>;
12
12
  export default TableActions;
@@ -37,7 +37,7 @@ var react_1 = __importStar(require("react"));
37
37
  var react_2 = require("@chakra-ui/react");
38
38
  var lucide_react_1 = require("lucide-react");
39
39
  var useCustomTheme_1 = require("../../../Theme/useCustomTheme");
40
- var TableActions = function (_a) {
40
+ var TableActions = react_1.default.memo(function (_a) {
41
41
  var _b, _c, _d;
42
42
  var row = _a.row;
43
43
  var _e = (0, react_1.useState)(false), isOpen = _e[0], setIsOpen = _e[1];
@@ -76,7 +76,7 @@ var TableActions = function (_a) {
76
76
  react_1.default.createElement("div", { ref: ref },
77
77
  react_1.default.createElement(react_2.PopoverTrigger, null,
78
78
  react_1.default.createElement(react_2.IconButton, { "aria-label": "Actions", color: "black", icon: react_1.default.createElement(lucide_react_1.EllipsisVertical, { size: 17 }), size: "sm", p: 0, variant: "ghost", _hover: { transform: "scale(1.2)" }, onClick: function () { return (isOpen ? handleClose() : handleOpen()); } })),
79
- react_1.default.createElement(react_2.Portal, null,
79
+ isOpen && (react_1.default.createElement(react_2.Portal, null,
80
80
  react_1.default.createElement(react_2.PopoverContent, { w: "auto", minW: "100px", boxShadow: "lg", p: 0, zIndex: 999 },
81
81
  react_1.default.createElement(react_2.PopoverBody, { p: 0, m: 0 },
82
82
  react_1.default.createElement(react_2.VStack, { align: "stretch", spacing: 1, p: 0, m: 0 },
@@ -102,6 +102,7 @@ var TableActions = function (_a) {
102
102
  react_1.default.createElement(lucide_react_1.Trash2, { size: 17 }),
103
103
  " Delete")),
104
104
  row.actions && ((_d = row === null || row === void 0 ? void 0 : row.actions) === null || _d === void 0 ? void 0 : _d.call(row)),
105
- !row.onLink && !row.onEdit && !row.onDelete && (react_1.default.createElement(react_2.Button, { size: "sm", variant: "ghost", isDisabled: true, justifyContent: "center" }, "No actions")))))))));
106
- };
105
+ !row.onLink && !row.onEdit && !row.onDelete && (react_1.default.createElement(react_2.Button, { size: "sm", variant: "ghost", isDisabled: true, justifyContent: "center" }, "No actions"))))))))));
106
+ });
107
+ TableActions.displayName = "TableActions";
107
108
  exports.default = TableActions;
@@ -63,13 +63,44 @@ var EMPTY_SX = {};
63
63
  var EMPTY_HOVER = {};
64
64
  var TableRow = react_2.default.memo(function (_a) {
65
65
  var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
66
- var row = _a.row, rowIndex = _a.rowIndex, index = _a.index, isChecked = _a.isChecked, isExpanded = _a.isExpanded, isContent = _a.isContent, isLink = _a.isLink, isCheckbox = _a.isCheckbox, isLoading = _a.isLoading, isActionFreeze = _a.isActionFreeze, columns = _a.columns, leftOffsets = _a.leftOffsets, noBorders = _a.noBorders, freezedBgColor = _a.freezedBgColor, freezedTextColor = _a.freezedTextColor, theme = _a.theme, borderStyle = _a.borderStyle, hoverStyle = _a.hoverStyle, rowHeight = _a.rowHeight, cellPy = _a.cellPy, palette = _a.palette, accentColor = _a.accentColor, handleCheckbox = _a.handleCheckbox, onRowClick = _a.onRowClick, toggleRowExpansion = _a.toggleRowExpansion;
66
+ var row = _a.row, rowIndex = _a.rowIndex, index = _a.index, isChecked = _a.isChecked, isExpanded = _a.isExpanded, isContent = _a.isContent, isLink = _a.isLink, isCheckbox = _a.isCheckbox, isLoading = _a.isLoading, isActionFreeze = _a.isActionFreeze, columns = _a.columns, leftOffsets = _a.leftOffsets, noBorders = _a.noBorders, freezedBgColor = _a.freezedBgColor, freezedTextColor = _a.freezedTextColor, theme = _a.theme, borderStyle = _a.borderStyle, hoverStyle = _a.hoverStyle, rowHeight = _a.rowHeight, cellPy = _a.cellPy, palette = _a.palette, accentColor = _a.accentColor, handleCheckbox = _a.handleCheckbox, onRowClick = _a.onRowClick, toggleRowExpansion = _a.toggleRowExpansion, onMeasure = _a.onMeasure;
67
67
  var checkedSx = (0, react_2.useMemo)(function () {
68
68
  var _a, _b, _c;
69
69
  return isChecked ? {
70
70
  "& td": { backgroundColor: (_c = (_b = (_a = theme.colors.primary) === null || _a === void 0 ? void 0 : _a.opacity) === null || _b === void 0 ? void 0 : _b[16]) !== null && _c !== void 0 ? _c : theme.colors.disabled[100] },
71
71
  } : EMPTY_SX;
72
72
  }, [isChecked, (_b = theme.colors.primary) === null || _b === void 0 ? void 0 : _b.opacity, theme.colors.disabled]);
73
+ // Measured-height virtualization: report this row's real height (main + any
74
+ // expanded panel) so the windower can place variable/expandable rows exactly.
75
+ var mainRowRef = (0, react_2.useRef)(null);
76
+ var expandedRowRef = (0, react_2.useRef)(null);
77
+ var hasContent = !!row.content;
78
+ (0, react_2.useLayoutEffect)(function () {
79
+ if (!onMeasure)
80
+ return;
81
+ var measure = function () {
82
+ var _a, _b, _c, _d;
83
+ var main = (_b = (_a = mainRowRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0;
84
+ var exp = isExpanded && hasContent ? (_d = (_c = expandedRowRef.current) === null || _c === void 0 ? void 0 : _c.offsetHeight) !== null && _d !== void 0 ? _d : 0 : 0;
85
+ var h = main + exp;
86
+ if (h > 0)
87
+ onMeasure(row.id, h);
88
+ };
89
+ measure();
90
+ var raf = requestAnimationFrame(measure);
91
+ var ro;
92
+ if (typeof ResizeObserver !== "undefined") {
93
+ ro = new ResizeObserver(measure);
94
+ if (mainRowRef.current)
95
+ ro.observe(mainRowRef.current);
96
+ if (expandedRowRef.current)
97
+ ro.observe(expandedRowRef.current);
98
+ }
99
+ return function () {
100
+ cancelAnimationFrame(raf);
101
+ ro === null || ro === void 0 ? void 0 : ro.disconnect();
102
+ };
103
+ }, [onMeasure, isExpanded, hasContent, row.id]);
73
104
  var handleCellClick = (0, react_2.useCallback)(function (header) {
74
105
  if (!header.node && onRowClick) {
75
106
  onRowClick(row, { label: header.label, id: header.id });
@@ -78,7 +109,7 @@ var TableRow = react_2.default.memo(function (_a) {
78
109
  var firstDataColIndex = columns.findIndex(function (c) { return !c.isHidden; });
79
110
  var accentSx = accentColor ? "inset 3px 0 0 ".concat(accentColor) : undefined;
80
111
  return (react_2.default.createElement(react_2.default.Fragment, null,
81
- react_2.default.createElement(react_1.Tr, { opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "opacity 0.2s", sx: checkedSx },
112
+ react_2.default.createElement(react_1.Tr, { ref: mainRowRef, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "opacity 0.2s", sx: checkedSx },
82
113
  isContent && (react_2.default.createElement(react_1.Td, { w: "6", p: 0, fontSize: 14, backgroundColor: (_d = (_c = theme.colors) === null || _c === void 0 ? void 0 : _c.background) === null || _d === void 0 ? void 0 : _d[50], color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow: accentSx, boxSizing: "border-box", left: 0, zIndex: 1, className: "columns sticky-columns" }, !!(row === null || row === void 0 ? void 0 : row.content) && (react_2.default.createElement(react_1.IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: (_e = theme.colors) === null || _e === void 0 ? void 0 : _e.gray[600], icon: isExpanded ? (react_2.default.createElement(lucide_react_1.ChevronDown, { fontSize: 16 })) : (react_2.default.createElement(lucide_react_1.ChevronRight, { fontSize: 16 })), _hover: { transform: "scale(1.1)" }, size: "sm", onClick: function () { return toggleRowExpansion(rowIndex); }, variant: "ghost" })))),
83
114
  isCheckbox && (react_2.default.createElement(react_1.Td, { w: "6", fontSize: 14, fontWeight: 600, color: (_g = (_f = theme.colors) === null || _f === void 0 ? void 0 : _f.background) === null || _g === void 0 ? void 0 : _g[50], textTransform: "capitalize", backgroundColor: (_j = (_h = theme.colors) === null || _h === void 0 ? void 0 : _h.background) === null || _j === void 0 ? void 0 : _j[50], position: "sticky", borderBottom: borderStyle, boxShadow: !isContent ? accentSx : undefined, boxSizing: "border-box", left: 0, zIndex: 1, className: "columns sticky-columns" },
84
115
  react_2.default.createElement(Checkbox_1.default, { "aria-label": "Select all rows", onChange: function () { return handleCheckbox(row.id); }, isChecked: isChecked }))),
@@ -102,7 +133,7 @@ var TableRow = react_2.default.memo(function (_a) {
102
133
  : hoverStyle }, isStatus && statusColor ? (react_2.default.createElement(StatusCell, { value: (0, table_1.normalizeTableCellValue)(row[header.id]) })) : (react_2.default.createElement(react_1.Box, { display: "block", overflow: "hidden", whiteSpace: "normal", overflowWrap: "break-word" }, (0, table_1.normalizeTableCellValue)(header.node ? header.node(row) : row[header.id])))));
103
134
  }),
104
135
  isLink && (react_2.default.createElement(react_1.Td, { w: 2, p: 0, fontSize: 14, backgroundColor: (_l = (_k = theme === null || theme === void 0 ? void 0 : theme.colors) === null || _k === void 0 ? void 0 : _k.background) === null || _l === void 0 ? void 0 : _l[50], color: freezedTextColor, position: isActionFreeze ? "sticky" : "relative", borderBottom: borderStyle, boxSizing: "border-box", right: 0, zIndex: 1, className: "columns sticky-columns".concat(isActionFreeze ? "-right" : "") }, (row.onLink || row.onEdit || row.onDelete) && (react_2.default.createElement(TableActions_1.default, { row: row }))))),
105
- row.content && isExpanded && (react_2.default.createElement(react_1.Tr, null,
136
+ row.content && isExpanded && (react_2.default.createElement(react_1.Tr, { ref: expandedRowRef },
106
137
  react_2.default.createElement(react_1.Td, { colSpan: columns.length +
107
138
  (isCheckbox ? 1 : 0) +
108
139
  (isContent ? 1 : 0) +
@@ -135,19 +166,32 @@ var TableBody = function (_a) {
135
166
  // remaining height with empty spacer rows. Spacer rows (rather than CSS
136
167
  // transforms) keep the table in normal flow so the sticky header and
137
168
  // `position: sticky` frozen columns keep working.
138
- // Disabled when expandable content rows are present, since those break the
139
- // fixed-height assumption.
169
+ // Expandable `content` rows are supported via measured heights (see
170
+ // `measuredHeights` / `onMeasure` below) — each rendered row reports its real
171
+ // height, so the window places variable/expanded rows exactly.
140
172
  var ROW_HEIGHT = rowHeight;
141
173
  var GROUP_HEADER_H = 42;
142
174
  var OVERSCAN = 6;
143
175
  var VIRTUALIZE_THRESHOLD = 30;
144
- var shouldVirtualize = !isContent && data.length > VIRTUALIZE_THRESHOLD;
176
+ var ESTIMATED_EXPAND = 120; // assumed extra height for an expanded-but-not-yet-measured row
177
+ var shouldVirtualize = data.length > VIRTUALIZE_THRESHOLD;
145
178
  // Grouped mode has no pager, so it virtualizes too (when no expandable rows):
146
179
  // group headers + rows form one flat sequence that we window over.
147
180
  var groupRowCount = groups ? groups.reduce(function (n, g) { return n + g.rows.length; }, 0) : 0;
148
181
  var groupVirtualize = !!groups && !isContent && groupRowCount > VIRTUALIZE_THRESHOLD;
149
182
  var _w = (0, react_2.useState)(0), scrollTop = _w[0], setScrollTop = _w[1];
150
183
  var _x = (0, react_2.useState)(0), viewportH = _x[0], setViewportH = _x[1];
184
+ // Measured row heights keyed by row id (stable across scroll/paging).
185
+ var _y = (0, react_2.useState)(new Map()), measuredHeights = _y[0], setMeasuredHeights = _y[1];
186
+ var reportHeight = (0, react_2.useCallback)(function (id, h) {
187
+ setMeasuredHeights(function (prev) {
188
+ if (prev.get(id) === h)
189
+ return prev;
190
+ var next = new Map(prev);
191
+ next.set(id, h);
192
+ return next;
193
+ });
194
+ }, []);
151
195
  (0, react_2.useEffect)(function () {
152
196
  var el = scrollContainerRef === null || scrollContainerRef === void 0 ? void 0 : scrollContainerRef.current;
153
197
  if (!el || !(shouldVirtualize || groupVirtualize))
@@ -175,15 +219,37 @@ var TableBody = function (_a) {
175
219
  var total = data.length;
176
220
  var firstIndex = 0;
177
221
  var lastIndex = total;
222
+ var topPad = 0;
223
+ var bottomPad = 0;
178
224
  if (shouldVirtualize) {
179
225
  var vh = viewportH || 500;
180
- firstIndex = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
181
- var count = Math.ceil(vh / ROW_HEIGHT) + OVERSCAN * 2;
182
- lastIndex = Math.min(total, firstIndex + count);
226
+ // Prefix-sum offsets using measured heights (exact for rendered rows) and
227
+ // estimates for not-yet-measured rows (ROW_HEIGHT, + ESTIMATED_EXPAND if expanded).
228
+ var offsets = new Array(total + 1);
229
+ offsets[0] = 0;
230
+ for (var i = 0; i < total; i++) {
231
+ var id = data[i].id;
232
+ var measured = measuredHeights.get(id);
233
+ var h = measured != null
234
+ ? measured
235
+ : ROW_HEIGHT + (expandedRows.has(startRow + i) ? ESTIMATED_EXPAND : 0);
236
+ offsets[i + 1] = offsets[i] + h;
237
+ }
238
+ var totalH = offsets[total];
239
+ var top_1 = scrollTop - OVERSCAN * ROW_HEIGHT;
240
+ var bottom = scrollTop + vh + OVERSCAN * ROW_HEIGHT;
241
+ firstIndex = 0;
242
+ while (firstIndex < total && offsets[firstIndex + 1] <= top_1)
243
+ firstIndex++;
244
+ lastIndex = firstIndex;
245
+ while (lastIndex < total && offsets[lastIndex] < bottom)
246
+ lastIndex++;
247
+ if (lastIndex < firstIndex)
248
+ lastIndex = firstIndex;
249
+ topPad = offsets[firstIndex];
250
+ bottomPad = totalH - offsets[lastIndex];
183
251
  }
184
252
  var visibleRows = shouldVirtualize ? data.slice(firstIndex, lastIndex) : data;
185
- var topPad = firstIndex * ROW_HEIGHT;
186
- var bottomPad = (total - lastIndex) * ROW_HEIGHT;
187
253
  var toggleRowExpansion = (0, react_2.useCallback)(function (rowIndex) {
188
254
  setExpandedRows(function (prev) {
189
255
  var newSet = new Set(prev);
@@ -235,8 +301,8 @@ var TableBody = function (_a) {
235
301
  var g = groups_1[_i];
236
302
  items.push({ kind: "header", g: g });
237
303
  if (!collapsedGroups.has(g.value)) {
238
- for (var _y = 0, _z = g.rows; _y < _z.length; _y++) {
239
- var row = _z[_y];
304
+ for (var _z = 0, _0 = g.rows; _z < _0.length; _z++) {
305
+ var row = _0[_z];
240
306
  items.push({ kind: "row", g: g, row: row });
241
307
  }
242
308
  }
@@ -249,16 +315,16 @@ var TableBody = function (_a) {
249
315
  var heights_2 = items.map(function (it) { return (it.kind === "header" ? GROUP_HEADER_H : rowHeight); });
250
316
  var offsets_1 = [];
251
317
  var acc = 0;
252
- for (var _0 = 0, heights_1 = heights_2; _0 < heights_1.length; _0++) {
253
- var h = heights_1[_0];
318
+ for (var _1 = 0, heights_1 = heights_2; _1 < heights_1.length; _1++) {
319
+ var h = heights_1[_1];
254
320
  offsets_1.push(acc);
255
321
  acc += h;
256
322
  }
257
323
  var totalH = acc;
258
324
  var vh = viewportH || 500;
259
- var top_1 = scrollTop - OVERSCAN * rowHeight;
325
+ var top_2 = scrollTop - OVERSCAN * rowHeight;
260
326
  var bottom = scrollTop + vh + OVERSCAN * rowHeight;
261
- firstI_1 = items.findIndex(function (_, i) { return offsets_1[i] + heights_2[i] > top_1; });
327
+ firstI_1 = items.findIndex(function (_, i) { return offsets_1[i] + heights_2[i] > top_2; });
262
328
  if (firstI_1 < 0)
263
329
  firstI_1 = items.length;
264
330
  lastI = items.length;
@@ -295,7 +361,7 @@ var TableBody = function (_a) {
295
361
  var rowIndex = startRow + index;
296
362
  var isExpanded = expandedRows.has(rowIndex);
297
363
  var isChecked = selectionsSet.has(row.id);
298
- return (react_2.default.createElement(TableRow, { key: (_a = row.id) !== null && _a !== void 0 ? _a : rowIndex, row: row, rowIndex: rowIndex, index: index, isChecked: isChecked, isExpanded: isExpanded, isContent: isContent, isLink: isLink, isCheckbox: isCheckbox, isLoading: isLoading, isActionFreeze: isActionFreeze, columns: columns, leftOffsets: leftOffsets, noBorders: noBorders, freezedBgColor: freezedBgColor, freezedTextColor: freezedTextColor, theme: theme, borderStyle: borderStyle, hoverStyle: hoverStyle, rowHeight: rowHeight, cellPy: cellPy, palette: palette, accentColor: accentColors === null || accentColors === void 0 ? void 0 : accentColors[row.id], handleCheckbox: handleCheckbox, onRowClick: onRowClick, toggleRowExpansion: toggleRowExpansion }));
364
+ return (react_2.default.createElement(TableRow, { key: (_a = row.id) !== null && _a !== void 0 ? _a : rowIndex, row: row, rowIndex: rowIndex, index: index, isChecked: isChecked, isExpanded: isExpanded, isContent: isContent, isLink: isLink, isCheckbox: isCheckbox, isLoading: isLoading, isActionFreeze: isActionFreeze, columns: columns, leftOffsets: leftOffsets, noBorders: noBorders, freezedBgColor: freezedBgColor, freezedTextColor: freezedTextColor, theme: theme, borderStyle: borderStyle, hoverStyle: hoverStyle, rowHeight: rowHeight, cellPy: cellPy, palette: palette, accentColor: accentColors === null || accentColors === void 0 ? void 0 : accentColors[row.id], handleCheckbox: handleCheckbox, onRowClick: onRowClick, toggleRowExpansion: toggleRowExpansion, onMeasure: shouldVirtualize ? reportHeight : undefined }));
299
365
  }),
300
366
  shouldVirtualize && bottomPad > 0 && (react_2.default.createElement("tr", { "aria-hidden": "true", style: { height: bottomPad } },
301
367
  react_2.default.createElement("td", { colSpan: totalVisibleColumns, style: { padding: 0, border: "none", height: bottomPad } })))));