blixify-ui-web 1.2.162 → 1.2.165

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 (32) hide show
  1. package/lib/components/data/dataTemplate/activityChanges.d.ts +12 -4
  2. package/lib/components/data/dataTemplate/activityChanges.d.ts.map +1 -1
  3. package/lib/components/data/dataTemplate/activityChanges.js +256 -53
  4. package/lib/components/data/dataTemplate/activityChanges.js.map +1 -1
  5. package/lib/components/data/dataTemplate/activityModal.d.ts +3 -1
  6. package/lib/components/data/dataTemplate/activityModal.d.ts.map +1 -1
  7. package/lib/components/data/dataTemplate/activityModal.js +45 -28
  8. package/lib/components/data/dataTemplate/activityModal.js.map +1 -1
  9. package/lib/components/data/dataTemplate/dataStateModel.d.ts +1 -0
  10. package/lib/components/data/dataTemplate/dataStateModel.d.ts.map +1 -1
  11. package/lib/components/data/dataTemplate/dataStateModel.js.map +1 -1
  12. package/lib/components/data/dataTemplate/index.d.ts.map +1 -1
  13. package/lib/components/data/dataTemplate/index.js +398 -61
  14. package/lib/components/data/dataTemplate/index.js.map +1 -1
  15. package/lib/components/data/dataTemplate/viewStatePersistence.d.ts +1 -0
  16. package/lib/components/data/dataTemplate/viewStatePersistence.d.ts.map +1 -1
  17. package/lib/components/data/dataTemplate/viewStatePersistence.js +2 -1
  18. package/lib/components/data/dataTemplate/viewStatePersistence.js.map +1 -1
  19. package/lib/components/data/utils.d.ts +1 -0
  20. package/lib/components/data/utils.d.ts.map +1 -1
  21. package/lib/components/data/utils.js +20 -8
  22. package/lib/components/data/utils.js.map +1 -1
  23. package/lib/components/display/newTable/index.d.ts +13 -1
  24. package/lib/components/display/newTable/index.d.ts.map +1 -1
  25. package/lib/components/display/newTable/index.js +292 -54
  26. package/lib/components/display/newTable/index.js.map +1 -1
  27. package/lib/components/navigation/leftNav/index.d.ts +25 -0
  28. package/lib/components/navigation/leftNav/index.d.ts.map +1 -1
  29. package/lib/components/navigation/leftNav/index.js +62 -0
  30. package/lib/components/navigation/leftNav/index.js.map +1 -1
  31. package/lib/tail.css +2 -2
  32. package/package.json +1 -1
@@ -102,6 +102,7 @@ var select_1 = require("../../input/select");
102
102
  var textInput_1 = require("../../input/textInput");
103
103
  var link_1 = require("../../structure/link");
104
104
  var detailList_1 = require("../detailList");
105
+ var loading_1 = require("../loading");
105
106
  var modal_1 = require("../modal");
106
107
  var newPagination_1 = require("../newPagination");
107
108
  var scrollShadow_1 = require("../scrollShadow");
@@ -374,6 +375,14 @@ var NewTable = function (props) {
374
375
  var _16 = (0, react_2.useState)(null), hoveredCell = _16[0], setHoveredCell = _16[1];
375
376
  var _17 = (0, react_2.useState)(false), modalEditVisible = _17[0], setModalEditVisible = _17[1];
376
377
  var _18 = (0, react_2.useState)([]), collapsedGroupIds = _18[0], setCollapsedGroupIds = _18[1];
378
+ // INFO: per-group scroll pagination. One sentinel element per band, observed
379
+ // by a single IntersectionObserver that fires props.onGroupLoadMore when a
380
+ // band's sentinel scrolls into view. The debounce guard stops repeat fires
381
+ // while that group's next page is in flight.
382
+ var groupSentinelRefs = (0, react_2.useRef)({});
383
+ var groupLoadMoreDebounce = (0, react_2.useRef)({});
384
+ var onGroupLoadMoreRef = (0, react_2.useRef)(props.onGroupLoadMore);
385
+ onGroupLoadMoreRef.current = props.onGroupLoadMore;
377
386
  // INFO: sheet-style row selection — Shift-click extends from the last
378
387
  // clicked row, a mouse-down-and-drag down the checkbox column paints a
379
388
  // range. The drag applies the start row itself, so the click that lands on
@@ -385,22 +394,32 @@ var NewTable = function (props) {
385
394
  var _20 = (0, react_2.useState)(0), copiedCount = _20[0], setCopiedCount = _20[1];
386
395
  var _21 = (0, react_2.useState)({}), editDataValue = _21[0], setEditDataValue = _21[1];
387
396
  var _22 = (0, react_2.useState)(false), editDataIsLoading = _22[0], setEditDataIsLoading = _22[1];
397
+ // INFO: true while the empty bottom row is being filled in; the row itself is
398
+ // always there in an addable table
388
399
  var _23 = (0, react_2.useState)(false), addData = _23[0], setAddData = _23[1];
389
400
  var _24 = (0, react_2.useState)({}), addDataValue = _24[0], setAddDataValue = _24[1];
390
- var _25 = (0, react_2.useState)({}), referencesOptions = _25[0], setReferencesOptions = _25[1];
401
+ // INFO: bumped after each saved row, so the next one starts with fresh
402
+ // inputs and its first cell takes the focus
403
+ var _25 = (0, react_2.useState)(0), addRowKey = _25[0], setAddRowKey = _25[1];
404
+ var _26 = (0, react_2.useState)({}), referencesOptions = _26[0], setReferencesOptions = _26[1];
391
405
  // INFO: seeded options sit under the local map so a cell's own search
392
406
  // results still win per key
393
407
  var mergedReferencesOptions = __assign(__assign({}, ((_l = props.referencesOptions) !== null && _l !== void 0 ? _l : {})), referencesOptions);
394
- var _26 = (0, react_2.useState)({}), referencesDetails = _26[0], setReferencesDetails = _26[1];
395
- var _27 = (0, react_2.useState)(false), referenceLoading = _27[0], setReferenceLoading = _27[1];
408
+ var _27 = (0, react_2.useState)({}), referencesDetails = _27[0], setReferencesDetails = _27[1];
409
+ var _28 = (0, react_2.useState)(false), referenceLoading = _28[0], setReferenceLoading = _28[1];
396
410
  // INFO: measured pixel widths for pinned columns without an explicit width.
397
- var _28 = (0, react_2.useState)({}), measuredPinWidths = _28[0], setMeasuredPinWidths = _28[1];
411
+ var _29 = (0, react_2.useState)({}), measuredPinWidths = _29[0], setMeasuredPinWidths = _29[1];
398
412
  var editDataValueRef = (0, react_2.useRef)({});
399
413
  var editDataRowIdRef = (0, react_2.useRef)("");
400
414
  var editDataHeaderKeyRef = (0, react_2.useRef)("");
401
415
  var editDataInputRefs = (0, react_2.useRef)([]);
402
416
  var addDataValueRef = (0, react_2.useRef)({});
403
417
  var addDataInputRefs = (0, react_2.useRef)([]);
418
+ var addRowRef = (0, react_2.useRef)(null);
419
+ // INFO: the column clicked to start a row, focused once its input mounts
420
+ var pendingAddFocusRef = (0, react_2.useRef)("");
421
+ // INFO: the latest save, for the document listener set up once per row
422
+ var commitAddRef = (0, react_2.useRef)(function () { });
404
423
  // INFO: holds the original row snapshot at the moment editing starts —
405
424
  // never mutated during editing so we can diff against it on blur.
406
425
  var originalDataRef = (0, react_2.useRef)({});
@@ -473,6 +492,40 @@ var NewTable = function (props) {
473
492
  Promise.resolve(onUpdateDataRef.current(rowId, columnId, editDataValueRef.current)).catch(function () { });
474
493
  };
475
494
  }, []);
495
+ // INFO: the clicked cell of a new row takes the focus once its input is on
496
+ // screen, so typing starts where the click landed
497
+ (0, react_2.useEffect)(function () {
498
+ if (!addData || !pendingAddFocusRef.current)
499
+ return;
500
+ var colKey = pendingAddFocusRef.current;
501
+ pendingAddFocusRef.current = "";
502
+ var frameId = requestAnimationFrame(function () {
503
+ var _a, _b;
504
+ var field = (_b = (_a = addRowRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("[data-add-cell=\"".concat(colKey, "\"]"))) === null || _b === void 0 ? void 0 : _b.querySelector("input, textarea, button");
505
+ field === null || field === void 0 ? void 0 : field.focus();
506
+ });
507
+ return function () { return cancelAnimationFrame(frameId); };
508
+ }, [addData, addRowKey]);
509
+ // INFO: clicking another row of the table saves the row being filled in, as
510
+ // moving to another row does in a spreadsheet. A click anywhere else — a
511
+ // dropdown's options, a date picker, the page around — leaves it open.
512
+ (0, react_2.useEffect)(function () {
513
+ if (!addData)
514
+ return;
515
+ var handlePointerDown = function (event) {
516
+ var _a, _b;
517
+ var target = event.target;
518
+ if (!target || ((_a = addRowRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)))
519
+ return;
520
+ var body = (_b = tableRef.current) === null || _b === void 0 ? void 0 : _b.querySelector("tbody");
521
+ if (body === null || body === void 0 ? void 0 : body.contains(target))
522
+ commitAddRef.current(false, true);
523
+ };
524
+ document.addEventListener("mousedown", handlePointerDown, true);
525
+ return function () {
526
+ return document.removeEventListener("mousedown", handlePointerDown, true);
527
+ };
528
+ }, [addData]);
476
529
  // INFO: when selection is active, cancel any open add row and clear edit state —
477
530
  // selecting rows and editing/adding simultaneously would be confusing UX.
478
531
  (0, react_2.useEffect)(function () {
@@ -511,6 +564,58 @@ var NewTable = function (props) {
511
564
  window.addEventListener("scroll", handleScroll);
512
565
  return function () { return window.removeEventListener("scroll", handleScroll); };
513
566
  }, [props.fixedPagination]);
567
+ // INFO: per-group scroll pagination observer. Watches every visible band
568
+ // sentinel and fires props.onGroupLoadMore(groupId) as it enters view, so
569
+ // each group loads its own next page Monday-board style. Only active in
570
+ // grouped scroll mode with a load-more handler wired; otherwise a no-op.
571
+ (0, react_2.useEffect)(function () {
572
+ if (props.paginationType !== "scroll")
573
+ return;
574
+ if (!props.groups || props.groups.length === 0)
575
+ return;
576
+ if (!props.onGroupLoadMore)
577
+ return;
578
+ var observer = new IntersectionObserver(function (entries) {
579
+ entries.forEach(function (entry) {
580
+ var _a;
581
+ var groupId = entry.target.dataset.groupSentinel;
582
+ if (!groupId)
583
+ return;
584
+ if (!entry.isIntersecting)
585
+ return;
586
+ if (groupLoadMoreDebounce.current[groupId])
587
+ return;
588
+ groupLoadMoreDebounce.current[groupId] = true;
589
+ (_a = onGroupLoadMoreRef.current) === null || _a === void 0 ? void 0 : _a.call(onGroupLoadMoreRef, groupId);
590
+ });
591
+ }, { rootMargin: "200px", threshold: 0 });
592
+ Object.values(groupSentinelRefs.current).forEach(function (sentinel) {
593
+ if (sentinel)
594
+ observer.observe(sentinel);
595
+ });
596
+ return function () { return observer.disconnect(); };
597
+ /* eslint-disable */
598
+ }, [
599
+ props.paginationType,
600
+ props.groups,
601
+ props.data.length,
602
+ props.onGroupLoadMore,
603
+ collapsedGroupIds,
604
+ props.groupLoadingStatus,
605
+ ]);
606
+ /* eslint-enable */
607
+ // INFO: release each group's load-more debounce once its loading settles, so
608
+ // the next scroll into that band can fire the following page.
609
+ (0, react_2.useEffect)(function () {
610
+ var _a;
611
+ var status = (_a = props.groupLoadingStatus) !== null && _a !== void 0 ? _a : {};
612
+ Object.keys(groupLoadMoreDebounce.current).forEach(function (groupId) {
613
+ var _a;
614
+ if (!((_a = status[groupId]) === null || _a === void 0 ? void 0 : _a.loadingMore)) {
615
+ groupLoadMoreDebounce.current[groupId] = false;
616
+ }
617
+ });
618
+ }, [props.groupLoadingStatus, props.data.length]);
514
619
  // INFO: one-time measurement of pinned columns without an explicit width.
515
620
  // Measures the first body <td> content after data renders — Capacitor-safe.
516
621
  (0, react_2.useEffect)(function () {
@@ -557,14 +662,118 @@ var NewTable = function (props) {
557
662
  setAddData(false);
558
663
  setAddDataValue({});
559
664
  };
665
+ // INFO: flatColumns already leaves id out unless showId is on
666
+ var handleGetAddColumns = function () { return flatColumns; };
667
+ var handleIsAddColumn = function (col) { var _a; return !col.disabled && utils_1.supportedNewTableAddList.includes((_a = col.type) !== null && _a !== void 0 ? _a : "string"); };
668
+ // INFO: a row can be added as soon as one column can be filled in — the
669
+ // rest stay empty and are filled in afterwards
560
670
  var handleValidateAddColumns = function () {
561
- var filtered = props.showId
562
- ? flatColumns
563
- : flatColumns.filter(function (c) { return c.key !== "id" && c.key !== "_id"; });
564
- if (filtered.length === 0)
565
- return false;
566
- return filtered.every(function (col) { return !col.type || utils_1.supportedTableAddList.includes(col.type); });
671
+ return handleGetAddColumns().some(handleIsAddColumn);
672
+ };
673
+ var handleHasAddValue = function () {
674
+ var _a;
675
+ return Object.values((_a = addDataValueRef.current) !== null && _a !== void 0 ? _a : {}).some(function (eachValue) {
676
+ return (0, utils_1.handleIsValueFilled)(eachValue);
677
+ });
678
+ };
679
+ var handleStartAdd = function (colKey) {
680
+ var _a, _b, _c;
681
+ if (!((_a = props.addable) === null || _a === void 0 ? void 0 : _a.active) || !handleValidateAddColumns())
682
+ return;
683
+ var col = handleGetAddColumns().find(function (each) { return each.key === colKey; });
684
+ pendingAddFocusRef.current =
685
+ col && handleIsAddColumn(col)
686
+ ? colKey
687
+ : ((_c = (_b = handleGetAddColumns().find(handleIsAddColumn)) === null || _b === void 0 ? void 0 : _b.key) !== null && _c !== void 0 ? _c : "");
688
+ handleCreateFormInputRefs("add");
689
+ setAddData(true);
690
+ };
691
+ /**
692
+ * INFO: saves the row being filled in. keepAdding opens a fresh row with its
693
+ * first cell focused, as Enter moves down in a spreadsheet; otherwise the row
694
+ * closes. An untouched row just closes. One that fails a field's check stays
695
+ * open with the field marked — or, when the click went to another row, closes
696
+ * keeping what was typed, which comes back the next time the row is opened.
697
+ */
698
+ var handleCommitAdd = function (keepAdding_1) {
699
+ var args_1 = [];
700
+ for (var _i = 1; _i < arguments.length; _i++) {
701
+ args_1[_i - 1] = arguments[_i];
702
+ }
703
+ return __awaiter(void 0, __spreadArray([keepAdding_1], args_1, true), void 0, function (keepAdding, leaving) {
704
+ var valid, data, _a;
705
+ var _b, _c, _d;
706
+ if (leaving === void 0) { leaving = false; }
707
+ return __generator(this, function (_e) {
708
+ switch (_e.label) {
709
+ case 0:
710
+ if (!handleHasAddValue()) {
711
+ if (!keepAdding)
712
+ handleCancelAdd();
713
+ return [2 /*return*/];
714
+ }
715
+ return [4 /*yield*/, handleCheckFormInputRefs("add")];
716
+ case 1:
717
+ valid = _e.sent();
718
+ if (!valid) {
719
+ if (leaving)
720
+ setAddData(false);
721
+ return [2 /*return*/];
722
+ }
723
+ data = addDataValueRef.current;
724
+ addDataValueRef.current = {};
725
+ setAddDataValue({});
726
+ if (keepAdding) {
727
+ pendingAddFocusRef.current =
728
+ (_c = (_b = handleGetAddColumns().find(handleIsAddColumn)) === null || _b === void 0 ? void 0 : _b.key) !== null && _c !== void 0 ? _c : "";
729
+ handleCreateFormInputRefs("add");
730
+ setAddRowKey(function (previous) { return previous + 1; });
731
+ }
732
+ else
733
+ setAddData(false);
734
+ _e.label = 2;
735
+ case 2:
736
+ _e.trys.push([2, 4, , 5]);
737
+ return [4 /*yield*/, ((_d = props.addable) === null || _d === void 0 ? void 0 : _d.onAddData(data))];
738
+ case 3:
739
+ _e.sent();
740
+ return [3 /*break*/, 5];
741
+ case 4:
742
+ _a = _e.sent();
743
+ if (handleHasAddValue())
744
+ return [2 /*return*/];
745
+ addDataValueRef.current = data;
746
+ setAddDataValue(data);
747
+ handleCreateFormInputRefs("add");
748
+ setAddData(true);
749
+ return [3 /*break*/, 5];
750
+ case 5: return [2 /*return*/];
751
+ }
752
+ });
753
+ });
754
+ };
755
+ var handleAddRowKeyDown = function (event) {
756
+ // INFO: a field that used the key itself — a date picker closing, a
757
+ // dropdown picking an option — keeps the row as it is, and a button keeps
758
+ // Enter for its own click
759
+ if (event.defaultPrevented)
760
+ return;
761
+ var target = event.target;
762
+ if (event.key === "Escape") {
763
+ event.preventDefault();
764
+ handleCancelAdd();
765
+ return;
766
+ }
767
+ // INFO: Enter saves and opens the next row, except in a multi-line field
768
+ if (event.key === "Enter" &&
769
+ !event.shiftKey &&
770
+ target.tagName !== "TEXTAREA" &&
771
+ !target.closest("button")) {
772
+ event.preventDefault();
773
+ handleCommitAdd(true);
774
+ }
567
775
  };
776
+ commitAddRef.current = handleCommitAdd;
568
777
  // INFO: a list column with a palette edits through the ordinary Select, whose
569
778
  // options render as the same coloured tags — one dropdown for every cell
570
779
  var handleColumnShowsTags = function (col) { var _a; return ((_a = col.type) !== null && _a !== void 0 ? _a : "string") === "list" && !!col.listSelectionColour; };
@@ -820,7 +1029,7 @@ var NewTable = function (props) {
820
1029
  // resize is a per-viewer preference and there is no storage for it yet.
821
1030
  // Tailwind cannot emit a runtime w-[Npx], so this drives an inline style and
822
1031
  // wins over the col.width class.
823
- var _29 = (0, react_2.useState)({}), columnWidths = _29[0], setColumnWidths = _29[1];
1032
+ var _30 = (0, react_2.useState)({}), columnWidths = _30[0], setColumnWidths = _30[1];
824
1033
  var resizeRef = (0, react_2.useRef)(null);
825
1034
  // INFO: a consumer may deliberately set a column narrower than the default
826
1035
  // floor (a 40px icon column, say) — never force it wider than it was asked to
@@ -1574,54 +1783,37 @@ var NewTable = function (props) {
1574
1783
  return react_2.default.createElement("td", { className: "".concat(cellPadding, " text-sm") });
1575
1784
  };
1576
1785
  var renderAddDataRow = function () {
1577
- var filteredCols = props.showId
1578
- ? flatColumns
1579
- : flatColumns.filter(function (c) { return c.key !== "id" && c.key !== "_id"; });
1786
+ var filteredCols = handleGetAddColumns();
1580
1787
  var colsAsDS = handleConvertColumnToDataStructure(filteredCols);
1581
- return (react_2.default.createElement("tr", null,
1788
+ return (react_2.default.createElement("tr", { key: "add-row-".concat(addRowKey), ref: addRowRef, "data-testid": "table-add-row-editing", onKeyDown: handleAddRowKeyDown },
1582
1789
  props.checkbox && react_2.default.createElement("td", null),
1583
1790
  colsAsDS.map(function (ds, index) {
1584
- var _a;
1585
- var originalType = (_a = filteredCols[index]) === null || _a === void 0 ? void 0 : _a.type;
1586
- if (originalType && utils_1.supportedTableAddList.includes(originalType)) {
1587
- return (react_2.default.createElement("td", { key: ds.id + String(index), className: "px-4 py-4 whitespace-nowrap align-top" },
1588
- react_2.default.createElement("div", { className: "min-w-[10rem]" }, renderFormCell(addDataValue, ds, addDataInputRefs.current[index], { active: true, onBlur: function () { } }))));
1791
+ if (handleIsAddColumn(filteredCols[index])) {
1792
+ return (react_2.default.createElement("td", { key: ds.id + String(index), "data-add-cell": ds.id, className: "px-4 py-4 whitespace-nowrap align-top" },
1793
+ react_2.default.createElement("div", { className: "min-w-[10rem]" }, renderFormCell(addDataValue, ds, addDataInputRefs.current[flatColumns.findIndex(function (col) { return col.key === ds.id; })], { active: true, onBlur: function () { } }))));
1589
1794
  }
1590
- return react_2.default.createElement("td", { key: ds.id + String(index) });
1795
+ return react_2.default.createElement("td", { key: ds.id + String(index), "data-add-cell": ds.id });
1591
1796
  }),
1592
1797
  renderAddColumnCell(),
1593
1798
  react_2.default.createElement("td", { className: "".concat(cellPadding, " text-sm") },
1594
1799
  react_2.default.createElement("div", { className: "flex flex-row gap-2" },
1595
- react_2.default.createElement(iconButton_1.IconButton, { icon: react_2.default.createElement(solid_2.CheckIcon, { className: "w-5 h-5 text-green-500" }), onClick: function () { return __awaiter(void 0, void 0, void 0, function () {
1596
- var valid;
1597
- var _a;
1598
- return __generator(this, function (_b) {
1599
- switch (_b.label) {
1600
- case 0: return [4 /*yield*/, handleCheckFormInputRefs("add")];
1601
- case 1:
1602
- valid = _b.sent();
1603
- if (valid) {
1604
- (_a = props.addable) === null || _a === void 0 ? void 0 : _a.onAddData(addDataValueRef.current);
1605
- handleCancelAdd();
1606
- }
1607
- return [2 /*return*/];
1608
- }
1609
- });
1610
- }); } }),
1800
+ react_2.default.createElement(iconButton_1.IconButton, { icon: react_2.default.createElement(solid_2.CheckIcon, { className: "w-5 h-5 text-green-500" }), onClick: function () { return handleCommitAdd(false); } }),
1611
1801
  react_2.default.createElement(iconButton_1.IconButton, { icon: react_2.default.createElement(solid_2.XMarkIcon, { className: "h-5 w-5 text-red-500" }), onClick: handleCancelAdd })))));
1612
1802
  };
1613
- var renderAddableSection = function () {
1614
- var _a;
1615
- if (!((_a = props.addable) === null || _a === void 0 ? void 0 : _a.active) || addData)
1803
+ // INFO: the empty row at the bottom of an addable table — clicking any of
1804
+ // its cells starts a new record there, as the next empty row of a
1805
+ // spreadsheet does
1806
+ var renderEmptyAddRow = function () {
1807
+ var _a, _b, _c;
1808
+ if (!((_a = props.addable) === null || _a === void 0 ? void 0 : _a.active) || !handleValidateAddColumns())
1616
1809
  return null;
1617
- var canAdd = handleValidateAddColumns();
1618
- return (react_2.default.createElement("div", { className: "sticky left-0 w-full flex justify-center border-t ".concat(borderColor, " ").concat(bgColor, " px-4 py-3") },
1619
- react_2.default.createElement(textButton_1.TextButton, { text: "Create New Item", onClick: function () {
1620
- if (canAdd) {
1621
- handleCreateFormInputRefs("add");
1622
- setAddData(true);
1623
- }
1624
- }, disable: !canAdd, className: "text-sm" })));
1810
+ if (((_c = (_b = props.selectedIds) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0 || props.selectAll)
1811
+ return null;
1812
+ return (react_2.default.createElement("tr", { "data-testid": "table-add-row", className: "cursor-text ".concat(props.darkMode ? "hover:bg-gray-800" : "hover:bg-gray-50") },
1813
+ props.checkbox && react_2.default.createElement("td", { className: "w-10 ".concat(cellPadding) }),
1814
+ handleGetAddColumns().map(function (col, index) { return (react_2.default.createElement("td", { key: col.key, "data-add-cell": col.key, className: "".concat(cellPadding, " h-10"), onClick: function () { return handleStartAdd(col.key); } }, index === 0 && (react_2.default.createElement(solid_1.PlusIcon, { className: "h-4 w-4 ".concat(props.darkMode ? "text-gray-500" : "text-gray-400"), "aria-hidden": "true" })))); }),
1815
+ renderAddColumnCell(),
1816
+ handleShowsActionColumn() && react_2.default.createElement("td", { className: cellPadding })));
1625
1817
  };
1626
1818
  var renderSelectAll = function () {
1627
1819
  var _a, _b;
@@ -1849,7 +2041,7 @@ var NewTable = function (props) {
1849
2041
  (((_a = props.advSettings) === null || _a === void 0 ? void 0 : _a.addColumnEnabled) ? 1 : 0) +
1850
2042
  (handleShowsActionColumn() ? 1 : 0);
1851
2043
  return props.groups.map(function (group) {
1852
- var _a, _b;
2044
+ var _a, _b, _c;
1853
2045
  var colourKey = (_a = group.colour) !== null && _a !== void 0 ? _a : "none";
1854
2046
  var isCollapsed = collapsedGroupIds.includes(group.id);
1855
2047
  var memberIndexes = group.rowIds
@@ -1868,13 +2060,59 @@ var NewTable = function (props) {
1868
2060
  } },
1869
2061
  isCollapsed ? (react_2.default.createElement(solid_2.ChevronRightIcon, { className: "h-4 w-4" })) : (react_2.default.createElement(solid_2.ChevronDownIcon, { className: "h-4 w-4" })),
1870
2062
  react_2.default.createElement("span", null, group.title),
1871
- react_2.default.createElement("span", { className: "rounded-full bg-white/70 px-2 py-0.5 text-[11px] font-medium normal-case tracking-normal" }, memberIndexes.length)))),
2063
+ react_2.default.createElement("span", { className: "rounded-full bg-white/70 px-2 py-0.5 text-[11px] font-medium normal-case tracking-normal" }, (_c = group.count) !== null && _c !== void 0 ? _c : memberIndexes.length)))),
1872
2064
  !isCollapsed &&
1873
2065
  memberIndexes.map(function (rowIndex) {
1874
2066
  return renderDesktopRow(props.data[rowIndex], rowIndex, colourKey);
1875
- })));
2067
+ }),
2068
+ renderGroupLoadMoreRow(group.id, columnCount, isCollapsed),
2069
+ renderGroupPagerRow(group, columnCount, isCollapsed)));
1876
2070
  });
1877
2071
  };
2072
+ // INFO: per-group scroll sentinel row. Rendered after a band's member rows in
2073
+ // grouped scroll mode so the group lazily loads its next page as it scrolls
2074
+ // into view; shows a spinner while that group's page is in flight. Nothing is
2075
+ // rendered when the band is collapsed, exhausted, or when per-group paging is
2076
+ // not wired (preserving existing group behaviour).
2077
+ var renderGroupLoadMoreRow = function (groupId, columnCount, isCollapsed) {
2078
+ var _a;
2079
+ if (props.paginationType !== "scroll" || !props.onGroupLoadMore)
2080
+ return null;
2081
+ if (isCollapsed)
2082
+ return null;
2083
+ var status = (_a = props.groupLoadingStatus) === null || _a === void 0 ? void 0 : _a[groupId];
2084
+ if (status === null || status === void 0 ? void 0 : status.exhausted)
2085
+ return null;
2086
+ return (react_2.default.createElement("tr", { "data-testid": "table-group-sentinel-".concat(groupId), className: "border-l-4 border-transparent" },
2087
+ react_2.default.createElement("td", { colSpan: columnCount, className: "px-3 py-2" },
2088
+ react_2.default.createElement("div", { "data-group-sentinel": groupId, ref: function (node) {
2089
+ groupSentinelRefs.current[groupId] = node;
2090
+ }, className: "flex w-full items-center justify-center" }, (status === null || status === void 0 ? void 0 : status.loadingMore) && react_2.default.createElement(loading_1.Loading, null)))));
2091
+ };
2092
+ // INFO: page mode pager under a band's rows — the list's own footer
2093
+ // (numbered pages, chevrons, go-to-page) scoped to that group; hidden while
2094
+ // collapsed and when the whole group fits one page
2095
+ var renderGroupPagerRow = function (group, columnCount, isCollapsed) {
2096
+ var _a, _b, _c;
2097
+ if (!props.onGroupPageChange || isCollapsed)
2098
+ return null;
2099
+ var status = (_a = props.groupLoadingStatus) === null || _a === void 0 ? void 0 : _a[group.id];
2100
+ if (!(status === null || status === void 0 ? void 0 : status.fetched))
2101
+ return null;
2102
+ var size = (_b = status.size) !== null && _b !== void 0 ? _b : 0;
2103
+ if (size <= (props.limit || 10))
2104
+ return null;
2105
+ return (react_2.default.createElement("tr", { "data-testid": "table-group-pager-".concat(group.id), className: "border-l-4 border-transparent" },
2106
+ react_2.default.createElement("td", { colSpan: columnCount, className: "px-3 pb-2" },
2107
+ react_2.default.createElement("div", { className: "flex items-center gap-x-2" },
2108
+ react_2.default.createElement(newPagination_1.NewPagination, { paginationType: "page", pageIndex: (_c = status.page) !== null && _c !== void 0 ? _c : 0, limit: props.limit, size: size, darkMode: props.darkMode, darkModeBg: props.darkModeBg, className: "flex-1", handleUpdatePageIndex: function (value) {
2109
+ var _a;
2110
+ if (typeof value !== "number" || status.loadingMore)
2111
+ return;
2112
+ (_a = props.onGroupPageChange) === null || _a === void 0 ? void 0 : _a.call(props, group.id, value);
2113
+ } }),
2114
+ status.loadingMore && react_2.default.createElement(loading_1.Loading, null)))));
2115
+ };
1878
2116
  var renderFloatingActionBar = function () {
1879
2117
  var _a, _b, _c, _d, _e;
1880
2118
  var visibleBtns = ((_a = props.multiActionFloatingBtns) !== null && _a !== void 0 ? _a : []).filter(function (btn) { return !btn.hide; });
@@ -2312,9 +2550,9 @@ var NewTable = function (props) {
2312
2550
  flatColumns.map(function (col) { return (react_2.default.createElement("td", { key: col.key, className: cellPadding },
2313
2551
  react_2.default.createElement("div", { className: "h-5 rounded-md animate-pulse ".concat(props.darkMode ? "bg-gray-700" : "bg-gray-200") }))); }))); }),
2314
2552
  !props.loading && renderDesktopRows(),
2315
- !props.loading && addData && renderAddDataRow())),
2316
- !props.loading && props.data.length === 0 && (react_2.default.createElement("div", { className: "p-4 text-sm ".concat(cellFontColor, " text-center sticky left-0") }, (_2 = props.emptyTitle) !== null && _2 !== void 0 ? _2 : "No items")),
2317
- renderAddableSection()),
2553
+ !props.loading &&
2554
+ (addData ? renderAddDataRow() : renderEmptyAddRow()))),
2555
+ !props.loading && props.data.length === 0 && (react_2.default.createElement("div", { className: "p-4 text-sm ".concat(cellFontColor, " text-center sticky left-0") }, (_2 = props.emptyTitle) !== null && _2 !== void 0 ? _2 : "No items"))),
2318
2556
  react_2.default.createElement(newPagination_1.NewPagination, { paginationType: props.paginationType, pageIndex: props.pageIndex, limit: props.limit, size: props.size, cursor: props.cursor, resultCount: props.data.length, customPaginationSize: props.customPaginationSize, hidePagination: props.hidePagination, darkMode: props.darkMode, darkModeBg: props.darkModeBg, handleUpdatePageIndex: props.handleUpdatePageIndex, className: "".concat(props.fixedPagination
2319
2557
  ? "pagination-container w-full ".concat(borderColor, " ").concat(isFixed ? "fixed bottom-0 left-0 z-10" : "sticky top-0 left-0")
2320
2558
  : "") }),