@gridkitjs/react 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,22 +1,32 @@
1
1
  // src/DataGrid/DataGrid.tsx
2
2
  import {
3
+ useEffect as useEffect4,
3
4
  useImperativeHandle,
4
- useMemo as useMemo3,
5
- useRef as useRef4,
6
- useState as useState5
5
+ useMemo as useMemo4,
6
+ useRef as useRef6,
7
+ useState as useState6
7
8
  } from "react";
8
9
  import {
9
10
  applyColumnOrder,
11
+ computeAggregates,
10
12
  defineColumnsFromRows,
11
13
  fitColumnsToWidth,
12
14
  groupRows,
13
15
  moveColumnBefore,
16
+ paginateRows as paginateRows2,
14
17
  resolveColumnWidths,
15
18
  resolveRowId,
16
19
  resolveShownRows,
17
- totalColumnWidth
20
+ totalColumnWidth,
21
+ withGroupAggregates
18
22
  } from "@gridkitjs/core";
19
23
 
24
+ // src/DataGrid/components/GridColgroup.tsx
25
+ import { jsx } from "react/jsx-runtime";
26
+ function GridColgroup({ columns }) {
27
+ return /* @__PURE__ */ jsx("colgroup", { children: columns.map((entry) => /* @__PURE__ */ jsx("col", { style: { width: entry.width } }, entry.id)) });
28
+ }
29
+
20
30
  // src/DataGrid/components/GridHeader.tsx
21
31
  import { createPortal } from "react-dom";
22
32
  import { buildKeyShortcuts, intentOf, KEYBOARD_STEP } from "@gridkitjs/core";
@@ -34,6 +44,7 @@ function classNames(...values) {
34
44
  // src/DataGrid/useGridNavigation.ts
35
45
  import {
36
46
  useEffect,
47
+ useMemo,
37
48
  useRef,
38
49
  useState
39
50
  } from "react";
@@ -46,8 +57,8 @@ var FALLBACK_PAGE = 10;
46
57
  function tabIndexFor(columnIndex, focusedColumnIndex) {
47
58
  return columnIndex === focusedColumnIndex ? 0 : -1;
48
59
  }
49
- function cellAt(table, focus) {
50
- const row = focus.rowIndex === HEADER_ROW ? table.tHead?.rows[0] ?? null : table.tBodies[0]?.rows[focus.rowIndex] ?? null;
60
+ function cellAt(headerTable, bodyTable, focus) {
61
+ const row = focus.rowIndex === HEADER_ROW ? headerTable?.tHead?.rows[0] ?? null : bodyTable?.tBodies[0]?.rows[focus.rowIndex] ?? null;
51
62
  if (row === null) {
52
63
  return null;
53
64
  }
@@ -55,29 +66,49 @@ function cellAt(table, focus) {
55
66
  return row.cells[columnIndex] ?? null;
56
67
  }
57
68
  function useGridNavigation({
58
- tableRef,
69
+ headerTableRef,
70
+ bodyTableRef,
59
71
  rowCount,
60
- columnCount
72
+ columnCount,
73
+ isSkippableRow,
74
+ virtualRange
61
75
  }) {
62
76
  const [stored, setStored] = useState({
63
77
  rowIndex: HEADER_ROW,
64
78
  columnIndex: 0
65
79
  });
66
- const focus = clampFocus(stored, rowCount, columnCount);
80
+ const focus = useMemo(
81
+ () => clampFocus(stored, rowCount, columnCount),
82
+ [stored, rowCount, columnCount]
83
+ );
67
84
  const pending = useRef(false);
68
85
  useEffect(() => {
69
86
  if (!pending.current) {
70
87
  return;
71
88
  }
72
- pending.current = false;
73
- const table = tableRef.current;
74
- if (table === null) {
89
+ const cell = cellAt(headerTableRef.current, bodyTableRef.current, {
90
+ rowIndex: focus.rowIndex,
91
+ columnIndex: focus.columnIndex
92
+ });
93
+ if (cell !== null) {
94
+ pending.current = false;
95
+ cell.focus();
75
96
  return;
76
97
  }
77
- cellAt(table, { rowIndex: focus.rowIndex, columnIndex: focus.columnIndex })?.focus();
78
- }, [tableRef, focus.rowIndex, focus.columnIndex]);
98
+ if (virtualRange != null && focus.rowIndex !== HEADER_ROW && (focus.rowIndex < virtualRange.startIndex || focus.rowIndex > virtualRange.endIndex)) {
99
+ virtualRange.scrollToIndex(focus.rowIndex);
100
+ return;
101
+ }
102
+ pending.current = false;
103
+ }, [
104
+ headerTableRef,
105
+ bodyTableRef,
106
+ focus.rowIndex,
107
+ focus.columnIndex,
108
+ virtualRange
109
+ ]);
79
110
  function pageSize() {
80
- const table = tableRef.current;
111
+ const table = bodyTableRef.current;
81
112
  const firstRow = table?.tBodies[0]?.rows[0];
82
113
  const viewport = table?.parentElement;
83
114
  if (firstRow === void 0 || viewport === null || viewport === void 0 || firstRow.offsetHeight === 0) {
@@ -108,7 +139,8 @@ function useGridNavigation({
108
139
  focus,
109
140
  rowCount,
110
141
  columnCount,
111
- pageSize()
142
+ pageSize(),
143
+ isSkippableRow
112
144
  );
113
145
  if (next === null) {
114
146
  return;
@@ -129,7 +161,7 @@ function useGridNavigation({
129
161
 
130
162
  // src/DataGrid/useGridSelection.ts
131
163
  import {
132
- useMemo,
164
+ useMemo as useMemo2,
133
165
  useRef as useRef2
134
166
  } from "react";
135
167
  import {
@@ -169,16 +201,16 @@ function countMessage(count, noun) {
169
201
  return count === 1 ? `1 ${noun} selected` : `${String(count)} ${noun}s selected`;
170
202
  }
171
203
  function useAsSet(values) {
172
- return useMemo(() => new Set(values), [values]);
204
+ return useMemo2(() => new Set(values), [values]);
173
205
  }
174
206
  function useIndexedBy(values, key) {
175
- return useMemo(
207
+ return useMemo2(
176
208
  () => new Map(values.map((value) => [value[key], value])),
177
209
  [values, key]
178
210
  );
179
211
  }
180
212
  function useMappedArray(values, key) {
181
- return useMemo(() => values.map((value) => value[key]), [values, key]);
213
+ return useMemo2(() => values.map((value) => value[key]), [values, key]);
182
214
  }
183
215
  function useGridSelection({
184
216
  rows,
@@ -346,9 +378,9 @@ function useGridSelection({
346
378
  }
347
379
 
348
380
  // src/DataGrid/components/GridHeader.tsx
349
- import { jsx, jsxs } from "react/jsx-runtime";
381
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
350
382
  function Icon({ children }) {
351
- return /* @__PURE__ */ jsx(
383
+ return /* @__PURE__ */ jsx2(
352
384
  "svg",
353
385
  {
354
386
  viewBox: "0 0 24 24",
@@ -364,18 +396,18 @@ function Icon({ children }) {
364
396
  }
365
397
  function ChevronsUpDownIcon() {
366
398
  return /* @__PURE__ */ jsxs(Icon, { children: [
367
- /* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
368
- /* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
399
+ /* @__PURE__ */ jsx2("path", { d: "m7 15 5 5 5-5" }),
400
+ /* @__PURE__ */ jsx2("path", { d: "m7 9 5-5 5 5" })
369
401
  ] });
370
402
  }
371
403
  function ChevronUpIcon() {
372
- return /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) });
404
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "m18 15-6-6-6 6" }) });
373
405
  }
374
406
  function GroupIcon() {
375
407
  return /* @__PURE__ */ jsxs(Icon, { children: [
376
- /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
377
- /* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
378
- /* @__PURE__ */ jsx("rect", { x: "8.5", y: "14", width: "7", height: "7", rx: "1" })
408
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
409
+ /* @__PURE__ */ jsx2("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
410
+ /* @__PURE__ */ jsx2("rect", { x: "8.5", y: "14", width: "7", height: "7", rx: "1" })
379
411
  ] });
380
412
  }
381
413
  function GridHeader({
@@ -393,7 +425,7 @@ function GridHeader({
393
425
  const beforeId = drag.dropTarget?.kind === "column-order" ? drag.dropTarget.beforeId : void 0;
394
426
  const draggedEntry = columns.find((entry) => entry.id === drag.draggedColumnId) ?? null;
395
427
  return /* @__PURE__ */ jsxs("thead", { children: [
396
- /* @__PURE__ */ jsx("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
428
+ /* @__PURE__ */ jsx2("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
397
429
  const { column } = entry;
398
430
  const resizing = resize.activeColumnId === entry.id;
399
431
  const dragging = drag.draggedColumnId === entry.id;
@@ -513,7 +545,7 @@ function GridHeader({
513
545
  * A pointer affordance only, same as the other two — the
514
546
  * keyboard equivalent is Alt+ArrowDown on the header itself.
515
547
  */
516
- /* @__PURE__ */ jsx(
548
+ /* @__PURE__ */ jsx2(
517
549
  "span",
518
550
  {
519
551
  className: classNames(
@@ -529,7 +561,7 @@ function GridHeader({
529
561
  event.stopPropagation();
530
562
  grouping.toggleGroupBy(entry.id);
531
563
  },
532
- children: /* @__PURE__ */ jsx(GroupIcon, {})
564
+ children: /* @__PURE__ */ jsx2(GroupIcon, {})
533
565
  }
534
566
  ),
535
567
  entry.label,
@@ -555,8 +587,8 @@ function GridHeader({
555
587
  sort.toggle(entry, { shiftKey: event.shiftKey });
556
588
  },
557
589
  children: [
558
- sortDirection === null ? /* @__PURE__ */ jsx(ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsx(ChevronUpIcon, {}),
559
- sortPriority !== null && sortPriority > 1 && /* @__PURE__ */ jsx("span", { className: "header-sort-priority", children: sortPriority })
590
+ sortDirection === null ? /* @__PURE__ */ jsx2(ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsx2(ChevronUpIcon, {}),
591
+ sortPriority !== null && sortPriority > 1 && /* @__PURE__ */ jsx2("span", { className: "header-sort-priority", children: sortPriority })
560
592
  ]
561
593
  }
562
594
  ),
@@ -566,7 +598,7 @@ function GridHeader({
566
598
  * put N tab stops inside a widget that is meant to have one.
567
599
  * The keyboard resizes from the header itself, under Alt.
568
600
  */
569
- /* @__PURE__ */ jsx(
601
+ /* @__PURE__ */ jsx2(
570
602
  "span",
571
603
  {
572
604
  className: "header-resize-handle",
@@ -586,7 +618,7 @@ function GridHeader({
586
618
  );
587
619
  }) }),
588
620
  draggedEntry !== null && drag.ghostTransform !== null && createPortal(
589
- /* @__PURE__ */ jsx(
621
+ /* @__PURE__ */ jsx2(
590
622
  "div",
591
623
  {
592
624
  className: "gridkit-data-grid header-cell drag-ghost",
@@ -604,12 +636,40 @@ function GridHeader({
604
636
  }
605
637
 
606
638
  // src/DataGrid/components/GridBody.tsx
607
- import { useMemo as useMemo2 } from "react";
608
- import { groupRowId, intentOf as intentOf2 } from "@gridkitjs/core";
639
+ import { useMemo as useMemo3 } from "react";
640
+ import {
641
+ groupRowId,
642
+ intentOf as intentOf2
643
+ } from "@gridkitjs/core";
609
644
 
610
645
  // src/DataGrid/components/GridGroupRow.tsx
611
646
  import { memo } from "react";
612
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
647
+
648
+ // src/DataGrid/components/formatAggregateValue.ts
649
+ function formatAggregateValue(value) {
650
+ if (value === null || value === void 0) {
651
+ return "\u2014";
652
+ }
653
+ if (value instanceof Date) {
654
+ return value.toLocaleString();
655
+ }
656
+ switch (typeof value) {
657
+ case "string":
658
+ case "boolean":
659
+ case "bigint":
660
+ case "symbol":
661
+ return String(value);
662
+ case "number":
663
+ return value.toLocaleString();
664
+ case "function":
665
+ return "(function)";
666
+ default:
667
+ return JSON.stringify(value);
668
+ }
669
+ }
670
+
671
+ // src/DataGrid/components/GridGroupRow.tsx
672
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
613
673
  function formatGroupValue(value) {
614
674
  if (value === null || value === void 0) {
615
675
  return "(blank)";
@@ -639,25 +699,33 @@ function GridGroupRowComponent({
639
699
  expanded,
640
700
  count,
641
701
  rowIndex,
702
+ datasetIndex,
642
703
  posinset,
643
704
  setsize,
644
- focused
705
+ focused,
706
+ aggregates,
707
+ results,
708
+ columns,
709
+ ref
645
710
  }) {
646
- return /* @__PURE__ */ jsx2(
711
+ const byId = new Map(columns.map((entry) => [entry.id, entry]));
712
+ return /* @__PURE__ */ jsx3(
647
713
  "tr",
648
714
  {
715
+ ref,
649
716
  role: "row",
650
- "aria-rowindex": rowIndex + 2,
717
+ "aria-rowindex": datasetIndex + 2,
651
718
  "aria-level": level + 1,
652
719
  "aria-expanded": expanded,
653
720
  "aria-setsize": setsize,
654
721
  "aria-posinset": posinset,
655
722
  "data-gridkit-group": groupId,
723
+ "data-gridkit-row-index": rowIndex,
656
724
  className: classNames(
657
725
  "grid-group-row",
658
726
  `is-group-level-${String(level)}`
659
727
  ),
660
- children: /* @__PURE__ */ jsx2(
728
+ children: /* @__PURE__ */ jsx3(
661
729
  "td",
662
730
  {
663
731
  role: "gridcell",
@@ -671,7 +739,7 @@ function GridGroupRowComponent({
671
739
  className: "grid-group-cell-content",
672
740
  style: { paddingInlineStart: `${String(level * 1.25 + 0.5)}rem` },
673
741
  children: [
674
- /* @__PURE__ */ jsx2(
742
+ /* @__PURE__ */ jsx3(
675
743
  "span",
676
744
  {
677
745
  className: classNames(
@@ -690,7 +758,22 @@ function GridGroupRowComponent({
690
758
  "(",
691
759
  count,
692
760
  ")"
693
- ] })
761
+ ] }),
762
+ aggregates.length > 0 && /* @__PURE__ */ jsx3("span", { className: "group-aggregates", children: aggregates.map((spec) => {
763
+ const key = spec.id ?? spec.columnId;
764
+ const aggregateValue = results.get(key);
765
+ const column = byId.get(spec.columnId);
766
+ const rendered = column?.column.footerTemplate ? column.column.footerTemplate({
767
+ value: aggregateValue,
768
+ rows: []
769
+ }) : formatAggregateValue(aggregateValue);
770
+ const aggregateLabel = spec.id ?? column?.label ?? spec.columnId;
771
+ return /* @__PURE__ */ jsxs2("span", { className: "group-aggregate", children: [
772
+ aggregateLabel,
773
+ ": ",
774
+ rendered
775
+ ] }, key);
776
+ }) })
694
777
  ]
695
778
  }
696
779
  )
@@ -699,37 +782,108 @@ function GridGroupRowComponent({
699
782
  }
700
783
  );
701
784
  }
702
- var GridGroupRow = memo(GridGroupRowComponent);
785
+ var GridGroupRow = memo(
786
+ GridGroupRowComponent
787
+ );
703
788
  var GridGroupRow_default = GridGroupRow;
704
789
 
790
+ // src/DataGrid/components/GridGroupSummaryRow.tsx
791
+ import { memo as memo2 } from "react";
792
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
793
+ function GridGroupSummaryRowComponent({
794
+ groupId,
795
+ level,
796
+ rowIndex,
797
+ datasetIndex,
798
+ results,
799
+ aggregates,
800
+ columns,
801
+ ref
802
+ }) {
803
+ return /* @__PURE__ */ jsx4(
804
+ "tr",
805
+ {
806
+ ref,
807
+ role: "row",
808
+ "aria-rowindex": datasetIndex + 2,
809
+ "data-gridkit-group-summary": groupId,
810
+ "data-gridkit-row-index": rowIndex,
811
+ className: classNames(
812
+ "grid-group-summary-row",
813
+ `is-group-level-${String(level)}`
814
+ ),
815
+ children: columns.map((entry) => {
816
+ const specs = aggregates.filter(
817
+ (candidate) => candidate.columnId === entry.id
818
+ );
819
+ const { column } = entry;
820
+ return /* @__PURE__ */ jsxs3(
821
+ "td",
822
+ {
823
+ role: "gridcell",
824
+ "data-gridkit-column": entry.id,
825
+ className: "grid-group-summary-cell",
826
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
827
+ children: [
828
+ entry.id === columns[0]?.id && /* @__PURE__ */ jsx4(
829
+ "span",
830
+ {
831
+ className: "grid-group-summary-indent",
832
+ style: { width: `${String(level * 1.25 + 0.5)}rem` },
833
+ "aria-hidden": "true"
834
+ }
835
+ ),
836
+ specs.map((spec) => {
837
+ const value = results.get(spec.id ?? spec.columnId);
838
+ return column.footerTemplate ? column.footerTemplate({ value, rows: [] }) : formatAggregateValue(value);
839
+ }).flatMap(
840
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
841
+ )
842
+ ]
843
+ },
844
+ entry.id
845
+ );
846
+ })
847
+ }
848
+ );
849
+ }
850
+ var GridGroupSummaryRow = memo2(
851
+ GridGroupSummaryRowComponent
852
+ );
853
+ var GridGroupSummaryRow_default = GridGroupSummaryRow;
854
+
705
855
  // src/DataGrid/components/GridRow.tsx
706
856
  import { accessDotted } from "@gridkitjs/core";
707
- import { memo as memo2 } from "react";
708
- import { jsx as jsx3 } from "react/jsx-runtime";
857
+ import { memo as memo3 } from "react";
858
+ import { jsx as jsx5 } from "react/jsx-runtime";
709
859
  function GridRowComponent({
710
860
  columns,
711
861
  rowId,
712
862
  row,
713
863
  rowIndex,
864
+ datasetIndex,
714
865
  activeColumnId,
715
866
  selectedColumnIds,
716
867
  selected,
717
868
  selectedColumnId,
718
869
  focusedColumnIndex,
719
870
  rowsSelectable,
720
- cellsSelectable
871
+ cellsSelectable,
872
+ ref
721
873
  }) {
722
- return /* @__PURE__ */ jsx3(
874
+ return /* @__PURE__ */ jsx5(
723
875
  "tr",
724
876
  {
877
+ ref,
725
878
  role: "row",
726
- "aria-rowindex": rowIndex + 2,
879
+ "aria-rowindex": datasetIndex + 2,
880
+ "data-gridkit-row-index": rowIndex,
727
881
  ...ariaAttr(rowsSelectable, "aria-selected", selected),
728
882
  className: classNames("grid-row", selected ? "is-selected" : ""),
729
883
  children: columns.map(({ id, column, alignment }, columnIndex) => {
730
884
  const value = accessDotted(row, column.field);
731
885
  const cellSelected = id === selectedColumnId;
732
- return /* @__PURE__ */ jsx3(
886
+ return /* @__PURE__ */ jsx5(
733
887
  "td",
734
888
  {
735
889
  role: "gridcell",
@@ -746,7 +900,14 @@ function GridRowComponent({
746
900
  column.cellClassName ?? ""
747
901
  ),
748
902
  style: { textAlign: alignment },
749
- children: column.cellTemplate ? column.cellTemplate({ value, row, rowIndex, rowId, selected }) : value
903
+ children: column.cellTemplate ? column.cellTemplate({
904
+ value,
905
+ row,
906
+ rowIndex,
907
+ datasetIndex,
908
+ rowId,
909
+ selected
910
+ }) : value
750
911
  },
751
912
  id
752
913
  );
@@ -754,11 +915,15 @@ function GridRowComponent({
754
915
  }
755
916
  );
756
917
  }
757
- var GridRow = memo2(GridRowComponent);
918
+ var GridRow = memo3(GridRowComponent);
758
919
  var GridRow_default = GridRow;
759
920
 
760
921
  // src/DataGrid/components/GridBody.tsx
761
- import { jsx as jsx4 } from "react/jsx-runtime";
922
+ import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
923
+ function rowIndexOf(row) {
924
+ const attribute = row.getAttribute("data-gridkit-row-index");
925
+ return attribute === null ? null : Number(attribute);
926
+ }
762
927
  function cellFrom(target) {
763
928
  if (!(target instanceof Element)) {
764
929
  return null;
@@ -766,11 +931,12 @@ function cellFrom(target) {
766
931
  const cell = target.closest("td[data-gridkit-column]");
767
932
  const row = cell?.parentElement;
768
933
  const columnId = cell?.getAttribute("data-gridkit-column");
769
- if (!(cell instanceof HTMLTableCellElement) || !(row instanceof HTMLTableRowElement) || columnId === null || columnId === void 0) {
934
+ const rowIndex = row === null || row === void 0 ? null : rowIndexOf(row);
935
+ if (!(cell instanceof HTMLTableCellElement) || !(row instanceof HTMLTableRowElement) || columnId === null || columnId === void 0 || rowIndex === null) {
770
936
  return null;
771
937
  }
772
938
  return {
773
- rowIndex: row.sectionRowIndex,
939
+ rowIndex,
774
940
  columnIndex: cell.cellIndex,
775
941
  columnId
776
942
  };
@@ -781,22 +947,23 @@ function groupRowFrom(target) {
781
947
  }
782
948
  const row = target.closest("tr[data-gridkit-group]");
783
949
  const groupId = row?.getAttribute("data-gridkit-group");
784
- if (!(row instanceof HTMLTableRowElement) || groupId === null || groupId === void 0) {
950
+ const rowIndex = row === null ? null : rowIndexOf(row);
951
+ if (!(row instanceof HTMLTableRowElement) || groupId === null || groupId === void 0 || rowIndex === null) {
785
952
  return null;
786
953
  }
787
- return { rowIndex: row.sectionRowIndex, groupId };
954
+ return { rowIndex, groupId };
788
955
  }
789
956
  function groupAriaMeta(rows) {
790
957
  const totals = /* @__PURE__ */ new Map();
791
958
  for (const entry of rows) {
792
- if (!("kind" in entry)) continue;
959
+ if (!("kind" in entry) || entry.kind !== "group") continue;
793
960
  const parentKey = groupRowId(entry.path.slice(0, -1));
794
961
  totals.set(parentKey, (totals.get(parentKey) ?? 0) + 1);
795
962
  }
796
963
  const seen = /* @__PURE__ */ new Map();
797
964
  const meta = /* @__PURE__ */ new Map();
798
965
  for (const entry of rows) {
799
- if (!("kind" in entry)) continue;
966
+ if (!("kind" in entry) || entry.kind !== "group") continue;
800
967
  const parentKey = groupRowId(entry.path.slice(0, -1));
801
968
  const posinset = (seen.get(parentKey) ?? 0) + 1;
802
969
  seen.set(parentKey, posinset);
@@ -813,10 +980,16 @@ function GridBody({
813
980
  activeColumnId,
814
981
  nav,
815
982
  selection,
816
- grouping
983
+ grouping,
984
+ aggregates,
985
+ groupAggregateDisplay,
986
+ virtualRange,
987
+ infiniteScroll,
988
+ sentinelRef
817
989
  }) {
818
990
  const { selectedCell, rowMode, cellMode } = selection;
819
- const ariaMeta = useMemo2(() => groupAriaMeta(rows), [rows]);
991
+ const ariaMeta = useMemo3(() => groupAriaMeta(rows), [rows]);
992
+ const visibleRows = virtualRange === null ? rows : rows.slice(virtualRange.startIndex, virtualRange.endIndex + 1);
820
993
  function targetOf(target) {
821
994
  const position = cellFrom(target);
822
995
  const entry = position === null ? void 0 : rows[position.rowIndex];
@@ -834,7 +1007,7 @@ function GridBody({
834
1007
  selection.selectCell({ rowId, columnId }, intent);
835
1008
  }
836
1009
  }
837
- return /* @__PURE__ */ jsx4(
1010
+ return /* @__PURE__ */ jsxs4(
838
1011
  "tbody",
839
1012
  {
840
1013
  className: "grid-body",
@@ -886,45 +1059,229 @@ function GridBody({
886
1059
  }
887
1060
  nav.onKeyDown(event);
888
1061
  },
889
- children: rows.map(
890
- (entry) => "kind" in entry ? /* @__PURE__ */ jsx4(
891
- GridGroupRow_default,
1062
+ children: [
1063
+ virtualRange !== null && virtualRange.offsetBefore > 0 && // `aria-hidden` and carrying neither `data-gridkit-column` nor
1064
+ // `data-gridkit-group`/`data-gridkit-row-index`, so `cellFrom`/
1065
+ // `groupRowFrom` never mistake it for a real row — it exists purely
1066
+ // to give the browser's scrollbar the correct total height and thumb
1067
+ // position, the same way `GridFooter`'s `<tfoot>` isn't a "row" to
1068
+ // any of this grid's own row-counting logic.
1069
+ /* @__PURE__ */ jsx6("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1070
+ "td",
892
1071
  {
893
- columnCount: columns.length,
894
- groupId: entry.groupId,
895
- level: entry.level,
896
- columnLabel: columns.find((column) => column.id === entry.columnId)?.label ?? entry.columnId,
897
- value: entry.value,
898
- expanded: entry.expanded,
899
- count: entry.count,
900
- rowIndex: entry.rowIndex,
901
- posinset: ariaMeta.get(entry.groupId)?.posinset ?? 1,
902
- setsize: ariaMeta.get(entry.groupId)?.setsize ?? 1,
903
- focused: nav.focus.rowIndex === entry.rowIndex
904
- },
905
- entry.groupId
906
- ) : /* @__PURE__ */ jsx4(
907
- GridRow_default,
1072
+ style: {
1073
+ height: virtualRange.offsetBefore,
1074
+ padding: 0,
1075
+ border: "none"
1076
+ }
1077
+ }
1078
+ ) }),
1079
+ visibleRows.map((entry) => {
1080
+ if (!("kind" in entry)) {
1081
+ return /* @__PURE__ */ jsx6(
1082
+ GridRow_default,
1083
+ {
1084
+ ref: virtualRange?.measureRow(entry.rowIndex),
1085
+ columns,
1086
+ rowId: entry.rowId,
1087
+ row: entry.row,
1088
+ rowIndex: entry.rowIndex,
1089
+ datasetIndex: entry.datasetIndex,
1090
+ activeColumnId,
1091
+ selectedColumnIds: selection.selectedColumnIds,
1092
+ selected: selection.selectedRowIds.has(entry.rowId),
1093
+ selectedColumnId: selectedCell?.rowId === entry.rowId ? selectedCell.columnId : null,
1094
+ focusedColumnIndex: nav.focus.rowIndex === entry.rowIndex ? nav.focus.columnIndex : null,
1095
+ rowsSelectable: rowMode !== false,
1096
+ cellsSelectable: cellMode !== false
1097
+ },
1098
+ entry.rowId
1099
+ );
1100
+ }
1101
+ if (entry.kind === "group-summary") {
1102
+ return /* @__PURE__ */ jsx6(
1103
+ GridGroupSummaryRow_default,
1104
+ {
1105
+ ref: virtualRange?.measureRow(entry.rowIndex),
1106
+ groupId: entry.groupId,
1107
+ level: entry.level,
1108
+ rowIndex: entry.rowIndex,
1109
+ datasetIndex: entry.datasetIndex,
1110
+ results: entry.aggregates,
1111
+ aggregates,
1112
+ columns
1113
+ },
1114
+ `${entry.groupId}-summary`
1115
+ );
1116
+ }
1117
+ return /* @__PURE__ */ jsx6(
1118
+ GridGroupRow_default,
1119
+ {
1120
+ ref: virtualRange?.measureRow(entry.rowIndex),
1121
+ columnCount: columns.length,
1122
+ groupId: entry.groupId,
1123
+ level: entry.level,
1124
+ columnLabel: columns.find((column) => column.id === entry.columnId)?.label ?? entry.columnId,
1125
+ value: entry.value,
1126
+ expanded: entry.expanded,
1127
+ count: entry.count,
1128
+ rowIndex: entry.rowIndex,
1129
+ datasetIndex: entry.datasetIndex,
1130
+ posinset: ariaMeta.get(entry.groupId)?.posinset ?? 1,
1131
+ setsize: ariaMeta.get(entry.groupId)?.setsize ?? 1,
1132
+ aggregates: groupAggregateDisplay === "inline" ? aggregates : [],
1133
+ results: entry.aggregates,
1134
+ columns,
1135
+ focused: nav.focus.rowIndex === entry.rowIndex
1136
+ },
1137
+ entry.groupId
1138
+ );
1139
+ }),
1140
+ virtualRange !== null && virtualRange.offsetAfter > 0 && /* @__PURE__ */ jsx6("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1141
+ "td",
908
1142
  {
909
- columns,
910
- rowId: entry.rowId,
911
- row: entry.row,
912
- rowIndex: entry.rowIndex,
913
- activeColumnId,
914
- selectedColumnIds: selection.selectedColumnIds,
915
- selected: selection.selectedRowIds.has(entry.rowId),
916
- selectedColumnId: selectedCell?.rowId === entry.rowId ? selectedCell.columnId : null,
917
- focusedColumnIndex: nav.focus.rowIndex === entry.rowIndex ? nav.focus.columnIndex : null,
918
- rowsSelectable: rowMode !== false,
919
- cellsSelectable: cellMode !== false
920
- },
921
- entry.rowId
922
- )
923
- )
1143
+ style: {
1144
+ height: virtualRange.offsetAfter,
1145
+ padding: 0,
1146
+ border: "none"
1147
+ }
1148
+ }
1149
+ ) }),
1150
+ infiniteScroll?.hasMore && /* @__PURE__ */ jsxs4(Fragment, { children: [
1151
+ infiniteScroll.isLoadingMore === true && /* @__PURE__ */ jsx6("tr", { "aria-hidden": "true", className: "grid-loading-row", children: /* @__PURE__ */ jsx6("td", { colSpan: columns.length, className: "grid-loading-cell", children: infiniteScroll.loadingTemplate ? infiniteScroll.loadingTemplate() : "Loading more\u2026" }) }),
1152
+ /* @__PURE__ */ jsx6(
1153
+ "tr",
1154
+ {
1155
+ "aria-hidden": "true",
1156
+ ref: sentinelRef,
1157
+ className: "grid-sentinel-row",
1158
+ children: /* @__PURE__ */ jsx6(
1159
+ "td",
1160
+ {
1161
+ colSpan: columns.length,
1162
+ style: { padding: 0, border: "none" }
1163
+ }
1164
+ )
1165
+ }
1166
+ )
1167
+ ] })
1168
+ ]
924
1169
  }
925
1170
  );
926
1171
  }
927
1172
 
1173
+ // src/DataGrid/components/GridFooter.tsx
1174
+ import { jsx as jsx7 } from "react/jsx-runtime";
1175
+ function GridFooter({
1176
+ columns,
1177
+ aggregates,
1178
+ results,
1179
+ rows
1180
+ }) {
1181
+ return /* @__PURE__ */ jsx7("tfoot", { children: /* @__PURE__ */ jsx7("tr", { className: "grid-footer", role: "row", children: columns.map((entry) => {
1182
+ const specs = aggregates.filter(
1183
+ (candidate) => candidate.columnId === entry.id
1184
+ );
1185
+ const { column } = entry;
1186
+ return /* @__PURE__ */ jsx7(
1187
+ "td",
1188
+ {
1189
+ role: "gridcell",
1190
+ "data-gridkit-column": entry.id,
1191
+ className: "grid-footer-cell",
1192
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
1193
+ children: specs.map((spec) => {
1194
+ const value = results.get(spec.id ?? spec.columnId);
1195
+ return column.footerTemplate ? column.footerTemplate({ value, rows }) : formatAggregateValue(value);
1196
+ }).flatMap(
1197
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
1198
+ )
1199
+ },
1200
+ entry.id
1201
+ );
1202
+ }) }) });
1203
+ }
1204
+
1205
+ // src/DataGrid/components/GridPager.tsx
1206
+ import { paginationWindow } from "@gridkitjs/core";
1207
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1208
+ function GridPager({ pager, config }) {
1209
+ const { pagination, pageCount, currentPage } = pager;
1210
+ const variant = config?.variant ?? "compact";
1211
+ const sizeOptions = config?.sizeOptions;
1212
+ return /* @__PURE__ */ jsxs5("div", { className: "gridkit-grid-pager", role: "group", "aria-label": "Pagination", children: [
1213
+ /* @__PURE__ */ jsx8(
1214
+ "button",
1215
+ {
1216
+ type: "button",
1217
+ className: "grid-pager-button",
1218
+ disabled: pagination.pageIndex <= 0,
1219
+ onClick: () => {
1220
+ pager.previousPage();
1221
+ },
1222
+ children: "Previous"
1223
+ }
1224
+ ),
1225
+ variant === "numbered" ? paginationWindow(currentPage, pageCount, {
1226
+ boundaryCount: config?.boundaryCount,
1227
+ siblingCount: config?.siblingCount
1228
+ }).map(
1229
+ (entry, index) => entry === "ellipsis" ? /* @__PURE__ */ jsx8(
1230
+ "span",
1231
+ {
1232
+ className: "grid-pager-ellipsis",
1233
+ "aria-hidden": "true",
1234
+ children: "\u2026"
1235
+ },
1236
+ `ellipsis-${String(index)}`
1237
+ ) : /* @__PURE__ */ jsx8(
1238
+ "button",
1239
+ {
1240
+ type: "button",
1241
+ className: "grid-pager-button",
1242
+ "aria-label": `Page ${String(entry)}`,
1243
+ "aria-current": entry === currentPage ? "page" : void 0,
1244
+ onClick: () => {
1245
+ pager.goToPage(entry - 1);
1246
+ },
1247
+ children: entry
1248
+ },
1249
+ entry
1250
+ )
1251
+ ) : /* @__PURE__ */ jsxs5("span", { className: "grid-pager-status", children: [
1252
+ "Page ",
1253
+ pageCount === 0 ? 0 : currentPage,
1254
+ " of ",
1255
+ pageCount
1256
+ ] }),
1257
+ /* @__PURE__ */ jsx8(
1258
+ "button",
1259
+ {
1260
+ type: "button",
1261
+ className: "grid-pager-button",
1262
+ disabled: pagination.pageIndex >= pageCount - 1,
1263
+ onClick: () => {
1264
+ pager.nextPage();
1265
+ },
1266
+ children: "Next"
1267
+ }
1268
+ ),
1269
+ sizeOptions !== void 0 && sizeOptions.length > 0 && /* @__PURE__ */ jsxs5("label", { className: "grid-pager-page-size", children: [
1270
+ "Rows per page",
1271
+ /* @__PURE__ */ jsx8(
1272
+ "select",
1273
+ {
1274
+ value: pagination.pageSize,
1275
+ onChange: (event) => {
1276
+ pager.setPageSize(Number(event.target.value));
1277
+ },
1278
+ children: sizeOptions.map((size) => /* @__PURE__ */ jsx8("option", { value: size, children: size }, size))
1279
+ }
1280
+ )
1281
+ ] })
1282
+ ] });
1283
+ }
1284
+
928
1285
  // src/DataGrid/components/GroupByBar.tsx
929
1286
  import { createPortal as createPortal2 } from "react-dom";
930
1287
  import { resolveKeyboardDropTarget } from "@gridkitjs/core";
@@ -1087,7 +1444,7 @@ function useGroupByDrag({
1087
1444
  }
1088
1445
 
1089
1446
  // src/DataGrid/components/GroupByBar.tsx
1090
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1447
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1091
1448
  function GroupByBar({
1092
1449
  columns,
1093
1450
  grouping,
@@ -1121,7 +1478,7 @@ function GroupByBar({
1121
1478
  }
1122
1479
  grouping.moveGroupBy(columnId, beforeColumnId);
1123
1480
  }
1124
- return /* @__PURE__ */ jsxs3(
1481
+ return /* @__PURE__ */ jsxs6(
1125
1482
  "div",
1126
1483
  {
1127
1484
  className: classNames(
@@ -1133,12 +1490,12 @@ function GroupByBar({
1133
1490
  role: "group",
1134
1491
  "aria-label": "Grouped by",
1135
1492
  children: [
1136
- showPlaceholder && /* @__PURE__ */ jsx5("span", { className: "group-by-bar-placeholder", children: "Drag a column here to group by it" }),
1493
+ showPlaceholder && /* @__PURE__ */ jsx9("span", { className: "group-by-bar-placeholder", children: "Drag a column here to group by it" }),
1137
1494
  groupBy.map((entry, index) => {
1138
1495
  const dropBefore = activeDropTarget?.beforeColumnId === entry.columnId;
1139
1496
  const dropAfter = activeDropTarget?.beforeColumnId === null && index === groupBy.length - 1;
1140
1497
  const dragging = chipDrag.draggedColumnId === entry.columnId;
1141
- return /* @__PURE__ */ jsxs3(
1498
+ return /* @__PURE__ */ jsxs6(
1142
1499
  "span",
1143
1500
  {
1144
1501
  className: classNames(
@@ -1165,8 +1522,8 @@ function GroupByBar({
1165
1522
  }
1166
1523
  },
1167
1524
  children: [
1168
- /* @__PURE__ */ jsx5("span", { className: "group-by-chip-label", children: labelFor(entry.columnId) }),
1169
- /* @__PURE__ */ jsx5(
1525
+ /* @__PURE__ */ jsx9("span", { className: "group-by-chip-label", children: labelFor(entry.columnId) }),
1526
+ /* @__PURE__ */ jsx9(
1170
1527
  "button",
1171
1528
  {
1172
1529
  type: "button",
@@ -1187,7 +1544,7 @@ function GroupByBar({
1187
1544
  );
1188
1545
  }),
1189
1546
  chipDrag.draggedColumnId !== null && chipDrag.ghostTransform !== null && createPortal2(
1190
- /* @__PURE__ */ jsx5(
1547
+ /* @__PURE__ */ jsx9(
1191
1548
  "span",
1192
1549
  {
1193
1550
  className: "group-by-chip drag-ghost",
@@ -1421,6 +1778,120 @@ function useElementWidth(ref, enabled) {
1421
1778
  return width;
1422
1779
  }
1423
1780
 
1781
+ // src/DataGrid/useInfiniteScroll.ts
1782
+ import { useCallback, useEffect as useEffect2, useRef as useRef4 } from "react";
1783
+ function useInfiniteScroll({
1784
+ rootRef,
1785
+ sentinelRef,
1786
+ config
1787
+ }) {
1788
+ const hasMore = config?.hasMore ?? false;
1789
+ const isLoadingMore = config?.isLoadingMore ?? false;
1790
+ const rootMargin = config?.rootMargin ?? "200px";
1791
+ const latestRef = useRef4({
1792
+ hasMore,
1793
+ isLoadingMore,
1794
+ onLoadMore: config?.onLoadMore
1795
+ });
1796
+ useEffect2(() => {
1797
+ latestRef.current = {
1798
+ hasMore,
1799
+ isLoadingMore,
1800
+ onLoadMore: config?.onLoadMore
1801
+ };
1802
+ });
1803
+ const firedRef = useRef4(false);
1804
+ const intersectingRef = useRef4(false);
1805
+ const maybeLoadMore = useCallback(() => {
1806
+ const current = latestRef.current;
1807
+ if (!intersectingRef.current || !current.hasMore || current.isLoadingMore || firedRef.current) {
1808
+ return;
1809
+ }
1810
+ firedRef.current = true;
1811
+ current.onLoadMore?.();
1812
+ }, []);
1813
+ const wasLoadingRef = useRef4(isLoadingMore);
1814
+ useEffect2(() => {
1815
+ if (wasLoadingRef.current && !isLoadingMore) {
1816
+ firedRef.current = false;
1817
+ maybeLoadMore();
1818
+ }
1819
+ wasLoadingRef.current = isLoadingMore;
1820
+ }, [isLoadingMore, maybeLoadMore]);
1821
+ useEffect2(() => {
1822
+ if (!hasMore) {
1823
+ return void 0;
1824
+ }
1825
+ const root = rootRef.current;
1826
+ const sentinel = sentinelRef.current;
1827
+ if (root === null || sentinel === null) {
1828
+ return void 0;
1829
+ }
1830
+ const observer = new IntersectionObserver(
1831
+ (entries) => {
1832
+ const entry = entries[entries.length - 1];
1833
+ if (entry === void 0) {
1834
+ return;
1835
+ }
1836
+ intersectingRef.current = entry.isIntersecting;
1837
+ maybeLoadMore();
1838
+ },
1839
+ { root, rootMargin }
1840
+ );
1841
+ observer.observe(sentinel);
1842
+ return () => {
1843
+ observer.disconnect();
1844
+ intersectingRef.current = false;
1845
+ };
1846
+ }, [hasMore, rootMargin, rootRef, sentinelRef, maybeLoadMore]);
1847
+ }
1848
+
1849
+ // src/DataGrid/usePagination.ts
1850
+ import {
1851
+ paginateRows
1852
+ } from "@gridkitjs/core";
1853
+ function usePagination({
1854
+ pagination,
1855
+ setPagination,
1856
+ rows,
1857
+ onPaginationChange
1858
+ }) {
1859
+ const { pageCount, pageIndex } = paginateRows(rows, pagination);
1860
+ function commit(next) {
1861
+ commitIfChanged(pagination, next, setPagination, (committed) => {
1862
+ const { pageCount: committedPageCount } = paginateRows(rows, committed);
1863
+ onPaginationChange?.({
1864
+ pagination: committed,
1865
+ pageCount: committedPageCount
1866
+ });
1867
+ });
1868
+ }
1869
+ function goToPage(nextPageIndex) {
1870
+ const { pageIndex: clamped } = paginateRows(rows, {
1871
+ ...pagination,
1872
+ pageIndex: nextPageIndex
1873
+ });
1874
+ commit({ ...pagination, pageIndex: clamped });
1875
+ }
1876
+ return {
1877
+ pagination,
1878
+ pageCount,
1879
+ currentPage: Math.min(pagination.pageIndex + 1, pageCount),
1880
+ goToPage,
1881
+ nextPage: () => {
1882
+ goToPage(pageIndex + 1);
1883
+ },
1884
+ previousPage: () => {
1885
+ goToPage(pageIndex - 1);
1886
+ },
1887
+ setPageSize: (pageSize) => {
1888
+ const next = { pageIndex: 0, pageSize };
1889
+ const { pageIndex: clamped } = paginateRows(rows, next);
1890
+ commit({ ...next, pageIndex: clamped });
1891
+ }
1892
+ };
1893
+ }
1894
+
1424
1895
  // src/DataGrid/useRowGrouping.ts
1425
1896
  import {
1426
1897
  collapseAllGroups,
@@ -1475,8 +1946,185 @@ function useRowGrouping({
1475
1946
  };
1476
1947
  }
1477
1948
 
1949
+ // src/DataGrid/useRowVirtualizer.ts
1950
+ import {
1951
+ useCallback as useCallback2,
1952
+ useEffect as useEffect3,
1953
+ useLayoutEffect as useLayoutEffect2,
1954
+ useRef as useRef5,
1955
+ useState as useState5
1956
+ } from "react";
1957
+ import { visibleRange } from "@gridkitjs/core";
1958
+ var EMPTY_RANGE = {
1959
+ startIndex: 0,
1960
+ endIndex: -1,
1961
+ offsetBefore: 0,
1962
+ offsetAfter: 0
1963
+ };
1964
+ function useRowVirtualizer({
1965
+ enabled,
1966
+ rows,
1967
+ bodyScrollRef,
1968
+ estimatedRowHeight,
1969
+ overscan
1970
+ }) {
1971
+ const rowCount = rows.length;
1972
+ const heightsRef = useRef5([]);
1973
+ const scrollTopRef = useRef5(0);
1974
+ const viewportHeightRef = useRef5(0);
1975
+ const elementsRef = useRef5(/* @__PURE__ */ new Map());
1976
+ const indexByElementRef = useRef5(/* @__PURE__ */ new Map());
1977
+ const refCallbacksRef = useRef5(
1978
+ /* @__PURE__ */ new Map()
1979
+ );
1980
+ const observerRef = useRef5(null);
1981
+ const pendingScrollRef = useRef5(void 0);
1982
+ const [range, setRange] = useState5(EMPTY_RANGE);
1983
+ const recompute = useCallback2(() => {
1984
+ setRange(
1985
+ enabled ? visibleRange(
1986
+ heightsRef.current,
1987
+ estimatedRowHeight,
1988
+ scrollTopRef.current,
1989
+ viewportHeightRef.current,
1990
+ overscan
1991
+ ) : rowCount === 0 ? EMPTY_RANGE : {
1992
+ startIndex: 0,
1993
+ endIndex: rowCount - 1,
1994
+ offsetBefore: 0,
1995
+ offsetAfter: 0
1996
+ }
1997
+ );
1998
+ }, [enabled, rowCount, estimatedRowHeight, overscan]);
1999
+ useLayoutEffect2(() => {
2000
+ heightsRef.current = new Array(rowCount).fill(
2001
+ void 0
2002
+ );
2003
+ recompute();
2004
+ }, [rows, rowCount, recompute]);
2005
+ useLayoutEffect2(() => {
2006
+ if (!enabled) {
2007
+ return void 0;
2008
+ }
2009
+ const element = bodyScrollRef.current;
2010
+ if (element === null) {
2011
+ return void 0;
2012
+ }
2013
+ scrollTopRef.current = element.scrollTop;
2014
+ viewportHeightRef.current = element.clientHeight;
2015
+ recompute();
2016
+ const handleScroll = () => {
2017
+ scrollTopRef.current = element.scrollTop;
2018
+ recompute();
2019
+ };
2020
+ element.addEventListener("scroll", handleScroll, { passive: true });
2021
+ const resizeObserver = new ResizeObserver(() => {
2022
+ viewportHeightRef.current = element.clientHeight;
2023
+ recompute();
2024
+ });
2025
+ resizeObserver.observe(element);
2026
+ return () => {
2027
+ element.removeEventListener("scroll", handleScroll);
2028
+ resizeObserver.disconnect();
2029
+ };
2030
+ }, [enabled, bodyScrollRef, recompute]);
2031
+ useEffect3(() => {
2032
+ if (!enabled) {
2033
+ return void 0;
2034
+ }
2035
+ const observer = new ResizeObserver((entries) => {
2036
+ let changed = false;
2037
+ for (const entry of entries) {
2038
+ const index = indexByElementRef.current.get(entry.target);
2039
+ if (index === void 0) continue;
2040
+ const height = entry.target.getBoundingClientRect().height;
2041
+ if (heightsRef.current[index] !== height) {
2042
+ heightsRef.current[index] = height;
2043
+ changed = true;
2044
+ }
2045
+ }
2046
+ if (changed) {
2047
+ recompute();
2048
+ }
2049
+ });
2050
+ observerRef.current = observer;
2051
+ for (const element of elementsRef.current.values()) {
2052
+ observer.observe(element);
2053
+ }
2054
+ return () => {
2055
+ observer.disconnect();
2056
+ observerRef.current = null;
2057
+ };
2058
+ }, [enabled, recompute]);
2059
+ function measureRow(index) {
2060
+ let callback = refCallbacksRef.current.get(index);
2061
+ if (callback === void 0) {
2062
+ callback = (element) => {
2063
+ const observer = observerRef.current;
2064
+ const previous = elementsRef.current.get(index);
2065
+ if (previous !== void 0) {
2066
+ observer?.unobserve(previous);
2067
+ indexByElementRef.current.delete(previous);
2068
+ elementsRef.current.delete(index);
2069
+ }
2070
+ if (element === null) {
2071
+ return;
2072
+ }
2073
+ elementsRef.current.set(index, element);
2074
+ indexByElementRef.current.set(element, index);
2075
+ observer?.observe(element);
2076
+ };
2077
+ refCallbacksRef.current.set(index, callback);
2078
+ }
2079
+ return callback;
2080
+ }
2081
+ useEffect3(() => {
2082
+ const pending = pendingScrollRef.current;
2083
+ if (pending === void 0) {
2084
+ return;
2085
+ }
2086
+ const element = elementsRef.current.get(pending.index);
2087
+ if (element === void 0) {
2088
+ return;
2089
+ }
2090
+ element.scrollIntoView(pending.options);
2091
+ pendingScrollRef.current = void 0;
2092
+ }, [range]);
2093
+ function estimateOffsetBefore(index) {
2094
+ let total = 0;
2095
+ for (let current = 0; current < index; current++) {
2096
+ total += heightsRef.current[current] ?? estimatedRowHeight;
2097
+ }
2098
+ return total;
2099
+ }
2100
+ function scrollToIndex(index, options) {
2101
+ const element = bodyScrollRef.current;
2102
+ if (element === null || rowCount === 0) {
2103
+ return;
2104
+ }
2105
+ const clamped = Math.max(0, Math.min(index, rowCount - 1));
2106
+ const mounted = elementsRef.current.get(clamped);
2107
+ if (mounted !== void 0) {
2108
+ mounted.scrollIntoView(options);
2109
+ return;
2110
+ }
2111
+ pendingScrollRef.current = { index: clamped, options };
2112
+ element.scrollTop = estimateOffsetBefore(clamped);
2113
+ }
2114
+ return {
2115
+ startIndex: range.startIndex,
2116
+ endIndex: range.endIndex,
2117
+ offsetBefore: range.offsetBefore,
2118
+ offsetAfter: range.offsetAfter,
2119
+ measureRow,
2120
+ scrollToIndex
2121
+ };
2122
+ }
2123
+
1478
2124
  // src/DataGrid/DataGrid.tsx
1479
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2125
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2126
+ var DEFAULT_PAGE_SIZE = 25;
2127
+ var NO_AGGREGATES = [];
1480
2128
  function DataGridComponent({
1481
2129
  dataSource,
1482
2130
  columns,
@@ -1507,6 +2155,17 @@ function DataGridComponent({
1507
2155
  defaultGroupExpansion,
1508
2156
  onGroupExpansionChange,
1509
2157
  defaultFilter,
2158
+ paginated = false,
2159
+ defaultPagination,
2160
+ pager,
2161
+ onPaginationChange,
2162
+ infiniteScroll,
2163
+ aggregates,
2164
+ groupAggregateDisplay = "inline",
2165
+ height,
2166
+ virtualized = false,
2167
+ overscan = 4,
2168
+ estimatedRowHeight = 40,
1510
2169
  label,
1511
2170
  labelledBy,
1512
2171
  ref,
@@ -1515,50 +2174,58 @@ function DataGridComponent({
1515
2174
  const hoverRows = hoverable?.rows ?? true;
1516
2175
  const hoverColumns = hoverable?.columns ?? true;
1517
2176
  const hoverCells = hoverable?.cells ?? true;
1518
- const viewportRef = useRef4(null);
1519
- const tableRef = useRef4(null);
2177
+ const viewportRef = useRef6(null);
2178
+ const headerTableRef = useRef6(null);
2179
+ const bodyTableRef = useRef6(null);
2180
+ const bodyScrollRef = useRef6(null);
2181
+ const sentinelRef = useRef6(null);
1520
2182
  const viewportWidth = useElementWidth(viewportRef, resizeMode === "fit");
1521
- const [sizing, setSizing] = useState5(
2183
+ const subscribersRef = useRef6(/* @__PURE__ */ new Set());
2184
+ const [sizing, setSizing] = useState6(
1522
2185
  defaultColumnSizing ?? {}
1523
2186
  );
1524
- const [order, setOrder] = useState5(
2187
+ const [order, setOrder] = useState6(
1525
2188
  defaultColumnOrder ?? []
1526
2189
  );
1527
- const [sort, setSort] = useState5(defaultColumnSort ?? []);
1528
- const [groupBy, setGroupBy] = useState5(defaultGroupBy ?? []);
1529
- const [expansion, setExpansion] = useState5(
2190
+ const [sort, setSort] = useState6(defaultColumnSort ?? []);
2191
+ const [groupBy, setGroupBy] = useState6(defaultGroupBy ?? []);
2192
+ const [expansion, setExpansion] = useState6(
1530
2193
  defaultGroupExpansion ?? []
1531
2194
  );
1532
- const [filter] = useState5(defaultFilter ?? []);
1533
- const [announcement, setAnnouncement] = useState5("");
1534
- const [rowSelection, setRowSelection] = useState5(
2195
+ const [filter] = useState6(defaultFilter ?? []);
2196
+ const [pagination, setPagination] = useState6(
2197
+ defaultPagination ?? { pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE }
2198
+ );
2199
+ const [announcement, setAnnouncement] = useState6("");
2200
+ const [rowSelection, setRowSelection] = useState6(
1535
2201
  defaultRowSelection ?? []
1536
2202
  );
1537
- const [columnSelection, setColumnSelection] = useState5(
2203
+ const [columnSelection, setColumnSelection] = useState6(
1538
2204
  defaultColumnSelection ?? []
1539
2205
  );
1540
- const [cellSelection, setCellSelection] = useState5(
2206
+ const [cellSelection, setCellSelection] = useState6(
1541
2207
  defaultCellSelection ?? null
1542
2208
  );
1543
- const rows = useMemo3(
2209
+ const rows = useMemo4(
1544
2210
  () => dataSource?.map((row, rowIndex) => ({
1545
2211
  rowId: resolveRowId(row, rowIndex, getRowId),
1546
2212
  row,
1547
- rowIndex
2213
+ rowIndex,
2214
+ datasetIndex: rowIndex
1548
2215
  })) ?? [],
1549
2216
  [dataSource, getRowId]
1550
2217
  );
1551
- const definedColumns = useMemo3(() => {
2218
+ const definedColumns = useMemo4(() => {
1552
2219
  if (columns && columns.length !== 0) return columns;
1553
2220
  if (dataSource && dataSource.length > 0)
1554
2221
  return defineColumnsFromRows(dataSource);
1555
2222
  return [];
1556
2223
  }, [columns, dataSource]);
1557
- const orderedColumns = useMemo3(
2224
+ const orderedColumns = useMemo4(
1558
2225
  () => applyColumnOrder(definedColumns, order),
1559
2226
  [definedColumns, order]
1560
2227
  );
1561
- const resolved = useMemo3(() => {
2228
+ const resolved = useMemo4(() => {
1562
2229
  const widths = resolveColumnWidths(orderedColumns, sizing, {
1563
2230
  sizes: columnSizeDefaults,
1564
2231
  resizable: resizableColumns,
@@ -1576,19 +2243,84 @@ function DataGridComponent({
1576
2243
  reorderableColumns,
1577
2244
  groupByDraggableColumns
1578
2245
  ]);
1579
- const shownRows = useMemo3(
2246
+ const shownRows = useMemo4(
1580
2247
  () => resolveShownRows(rows, filter, sort, resolved),
1581
2248
  [rows, filter, sort, resolved]
1582
2249
  );
1583
- const displayRows = useMemo3(
2250
+ const displayRows = useMemo4(
1584
2251
  () => groupRows(shownRows, groupBy, expansion, resolved),
1585
2252
  [shownRows, groupBy, expansion, resolved]
1586
2253
  );
1587
- const displayDataRows = useMemo3(
1588
- () => groupBy.length === 0 ? shownRows : displayRows.filter(
2254
+ const activeAggregates = aggregates ?? NO_AGGREGATES;
2255
+ const aggregatedRows = useMemo4(
2256
+ () => withGroupAggregates(
2257
+ displayRows,
2258
+ shownRows,
2259
+ groupBy,
2260
+ activeAggregates,
2261
+ resolved,
2262
+ groupAggregateDisplay
2263
+ ),
2264
+ [
2265
+ displayRows,
2266
+ shownRows,
2267
+ groupBy,
2268
+ activeAggregates,
2269
+ resolved,
2270
+ groupAggregateDisplay
2271
+ ]
2272
+ );
2273
+ const grandTotal = useMemo4(
2274
+ () => computeAggregates(
2275
+ shownRows.map((entry) => entry.row),
2276
+ activeAggregates,
2277
+ resolved
2278
+ ),
2279
+ [shownRows, activeAggregates, resolved]
2280
+ );
2281
+ const paginatedRows = useMemo4(
2282
+ () => paginated ? paginateRows2(aggregatedRows, pagination) : { rows: aggregatedRows, pageCount: 1, pageIndex: 0 },
2283
+ [paginated, aggregatedRows, pagination]
2284
+ );
2285
+ const virtualizer = useRowVirtualizer({
2286
+ enabled: virtualized,
2287
+ rows: paginatedRows.rows,
2288
+ bodyScrollRef,
2289
+ estimatedRowHeight,
2290
+ overscan
2291
+ });
2292
+ useEffect4(() => {
2293
+ if (virtualized && height === void 0) {
2294
+ console.error(
2295
+ "DataGridComponent: `virtualized` has no effect without `height` \u2014 a body with no bounded height has no viewport to window rows against."
2296
+ );
2297
+ }
2298
+ }, [virtualized, height]);
2299
+ const hasInfiniteScroll = infiniteScroll !== void 0;
2300
+ useEffect4(() => {
2301
+ if (paginated && hasInfiniteScroll) {
2302
+ console.error(
2303
+ "DataGridComponent: `paginated` and `infiniteScroll` are mutually exclusive \u2014 only `paginated` takes effect while both are set."
2304
+ );
2305
+ }
2306
+ }, [paginated, hasInfiniteScroll]);
2307
+ useInfiniteScroll({
2308
+ rootRef: bodyScrollRef,
2309
+ sentinelRef,
2310
+ config: infiniteScroll
2311
+ });
2312
+ const paginationSnapshot = useMemo4(
2313
+ () => ({
2314
+ pageIndex: paginatedRows.pageIndex,
2315
+ pageSize: pagination.pageSize
2316
+ }),
2317
+ [paginatedRows.pageIndex, pagination.pageSize]
2318
+ );
2319
+ const displayDataRows = useMemo4(
2320
+ () => groupBy.length === 0 && !paginated ? shownRows : paginatedRows.rows.filter(
1589
2321
  (entry) => !("kind" in entry)
1590
2322
  ),
1591
- [displayRows, groupBy.length, shownRows]
2323
+ [paginatedRows, groupBy.length, paginated, shownRows]
1592
2324
  );
1593
2325
  function columnName(columnId) {
1594
2326
  const entry = resolved.find((candidate) => candidate.id === columnId);
@@ -1609,7 +2341,10 @@ function DataGridComponent({
1609
2341
  }
1610
2342
  }
1611
2343
  const resize = useColumnResize({
1612
- tableRef,
2344
+ // `measureColumnContent` only ever matches `td[data-gridkit-column]`
2345
+ // (never `th`), so the header table has nothing for it to find — the
2346
+ // body table alone was already the effective scope before this split.
2347
+ tableRef: bodyTableRef,
1613
2348
  sizing,
1614
2349
  setSizing,
1615
2350
  columnSizeDefaults,
@@ -1649,18 +2384,33 @@ function DataGridComponent({
1649
2384
  onGroupExpansionChange: handleGroupExpansionChange
1650
2385
  });
1651
2386
  const nav = useGridNavigation({
1652
- tableRef,
2387
+ headerTableRef,
2388
+ bodyTableRef,
2389
+ // `null` when virtualization is off, in which case behavior is
2390
+ // byte-for-byte unchanged from before this feature existed.
2391
+ virtualRange: virtualized ? virtualizer : null,
1653
2392
  // Group headers are addressable rows too — the whole point of the flat
1654
2393
  // `DisplayRow[]` shape is that they share one position space with data
1655
- // rows, so navigation counts them the same way.
1656
- rowCount: displayRows.length,
1657
- columnCount: resolved.length
2394
+ // rows, so navigation counts them the same way. Page-relative
2395
+ // (`paginatedRows`, not `displayRows`): arrow keys operate on what's
2396
+ // actually in the DOM, which is the current page — a different "page"
2397
+ // concept than `useGridNavigation`'s own Page Up/Down viewport scrolling.
2398
+ rowCount: paginatedRows.rows.length,
2399
+ columnCount: resolved.length,
2400
+ // A group's own summary row (`groupAggregateDisplay: "row"`) occupies a
2401
+ // real slot in `rowCount` above — its DOM position has to line up with
2402
+ // everything else — but is presentational, never a tab stop: arrow-key
2403
+ // vertical movement steps over it rather than landing there.
2404
+ isSkippableRow: (rowIndex) => {
2405
+ const entry = paginatedRows.rows[rowIndex];
2406
+ return entry !== void 0 && "kind" in entry && entry.kind === "group-summary";
2407
+ }
1658
2408
  });
1659
- const displayedIds = useMemo3(
2409
+ const displayedIds = useMemo4(
1660
2410
  () => orderedColumns.map((column) => column.id ?? column.field),
1661
2411
  [orderedColumns]
1662
2412
  );
1663
- const groupByDraggableIds = useMemo3(
2413
+ const groupByDraggableIds = useMemo4(
1664
2414
  () => new Set(
1665
2415
  resolved.filter((entry) => entry.groupByDraggable).map((entry) => entry.id)
1666
2416
  ),
@@ -1712,6 +2462,25 @@ function DataGridComponent({
1712
2462
  setSort,
1713
2463
  onColumnSortChange: handleColumnSortChange
1714
2464
  });
2465
+ function handlePaginationChange(event) {
2466
+ onPaginationChange?.(event);
2467
+ announce(
2468
+ `Page ${String(event.pagination.pageIndex + 1)} of ${String(event.pageCount)}`
2469
+ );
2470
+ }
2471
+ const paginationApi = usePagination({
2472
+ pagination,
2473
+ setPagination,
2474
+ rows: displayRows,
2475
+ onPaginationChange: handlePaginationChange
2476
+ });
2477
+ const [previous, setPrevious] = useState6({ filter, sort, groupBy });
2478
+ if (previous.filter !== filter || previous.sort !== sort || previous.groupBy !== groupBy) {
2479
+ setPrevious({ filter, sort, groupBy });
2480
+ if (pagination.pageIndex !== 0) {
2481
+ setPagination({ ...pagination, pageIndex: 0 });
2482
+ }
2483
+ }
1715
2484
  const selection = useGridSelection({
1716
2485
  rows: displayDataRows,
1717
2486
  columns: resolved,
@@ -1727,21 +2496,49 @@ function DataGridComponent({
1727
2496
  columnName
1728
2497
  });
1729
2498
  const multiselectable = selection.rowMode === "multiple" || selection.columnMode === "multiple";
2499
+ useEffect4(() => {
2500
+ for (const listener of subscribersRef.current) {
2501
+ listener();
2502
+ }
2503
+ }, [
2504
+ shownRows,
2505
+ aggregatedRows,
2506
+ resolved,
2507
+ sizing,
2508
+ order,
2509
+ sort,
2510
+ groupBy,
2511
+ expansion,
2512
+ paginationSnapshot,
2513
+ paginatedRows.pageCount,
2514
+ grandTotal,
2515
+ rowSelection,
2516
+ columnSelection,
2517
+ cellSelection,
2518
+ nav.focus
2519
+ ]);
1730
2520
  useImperativeHandle(ref, () => ({
1731
2521
  get element() {
1732
2522
  return viewportRef.current;
1733
2523
  },
1734
2524
  get table() {
1735
- return tableRef.current;
2525
+ return bodyTableRef.current;
1736
2526
  },
1737
2527
  getRows: () => shownRows,
1738
- getDisplayRows: () => displayRows,
2528
+ // `aggregatedRows`, not the plain `displayRows` it's derived from: the
2529
+ // same array shape and content, except every group header's own
2530
+ // `aggregates` field is populated — the whole reason `getDisplayRows()`
2531
+ // is where a consumer reads a specific group's computed results from.
2532
+ getDisplayRows: () => aggregatedRows,
1739
2533
  getColumns: () => resolved,
1740
2534
  getColumnSizing: () => sizing,
1741
2535
  getColumnOrder: () => order,
1742
2536
  getColumnSort: () => sort,
1743
2537
  getGroupBy: () => groupBy,
1744
2538
  getGroupExpansion: () => expansion,
2539
+ getPagination: () => paginationSnapshot,
2540
+ getPageCount: () => paginatedRows.pageCount,
2541
+ getAggregates: () => grandTotal,
1745
2542
  getRowSelection: () => rowSelection,
1746
2543
  getColumnSelection: () => columnSelection,
1747
2544
  getCellSelection: () => cellSelection,
@@ -1753,23 +2550,52 @@ function DataGridComponent({
1753
2550
  collapseAllGroups: () => {
1754
2551
  grouping.collapseAll(displayRows);
1755
2552
  },
2553
+ goToPage: paginationApi.goToPage,
2554
+ nextPage: paginationApi.nextPage,
2555
+ previousPage: paginationApi.previousPage,
2556
+ setPageSize: paginationApi.setPageSize,
1756
2557
  scrollToRow: (rowId, options) => {
1757
- const index = displayRows.findIndex(
2558
+ const index = paginatedRows.rows.findIndex(
1758
2559
  (entry) => !("kind" in entry) && entry.rowId === rowId
1759
2560
  );
1760
2561
  if (index === -1) return;
1761
- tableRef.current?.tBodies[0]?.rows[index]?.scrollIntoView(options);
2562
+ if (virtualized) {
2563
+ virtualizer.scrollToIndex(index, options);
2564
+ return;
2565
+ }
2566
+ bodyTableRef.current?.tBodies[0]?.rows[index]?.scrollIntoView(options);
1762
2567
  },
1763
2568
  scrollToColumn: (columnId, options) => {
1764
- const cells = tableRef.current?.querySelectorAll("[data-gridkit-column]");
2569
+ const cells = viewportRef.current?.querySelectorAll(
2570
+ "[data-gridkit-column]"
2571
+ );
1765
2572
  const cell = cells ? Array.from(cells).find(
1766
2573
  (entry) => entry.getAttribute("data-gridkit-column") === columnId
1767
2574
  ) : void 0;
1768
2575
  cell?.scrollIntoView(options);
2576
+ },
2577
+ subscribe: (listener) => {
2578
+ subscribersRef.current.add(listener);
2579
+ return () => {
2580
+ subscribersRef.current.delete(listener);
2581
+ };
1769
2582
  }
1770
2583
  }));
1771
- return /* @__PURE__ */ jsxs4("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
1772
- showGroupByBar && /* @__PURE__ */ jsx6(
2584
+ const gridTableClassName = classNames(
2585
+ "gridkit-data-grid",
2586
+ borders ? `borders-${borders}` : "",
2587
+ // Hover is on by default and selection off, so one set of classes
2588
+ // turns styling off and the other turns it on. The polarity differs
2589
+ // because the defaults do.
2590
+ hoverRows ? "" : "no-hover-rows",
2591
+ hoverColumns ? "" : "no-hover-columns",
2592
+ hoverCells ? "" : "no-hover-cells",
2593
+ selection.rowMode === false ? "" : "selectable-rows",
2594
+ selection.columnMode === false ? "" : "selectable-columns",
2595
+ selection.cellMode === false ? "" : "selectable-cells"
2596
+ );
2597
+ return /* @__PURE__ */ jsxs7("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
2598
+ showGroupByBar && /* @__PURE__ */ jsx10(
1773
2599
  GroupByBar,
1774
2600
  {
1775
2601
  columns: resolved,
@@ -1780,12 +2606,12 @@ function DataGridComponent({
1780
2606
  headerDropTarget: headerGroupByDropTarget
1781
2607
  }
1782
2608
  ),
1783
- /* @__PURE__ */ jsxs4(
1784
- "table",
2609
+ /* @__PURE__ */ jsxs7(
2610
+ "div",
1785
2611
  {
1786
- ref: tableRef,
2612
+ className: "gridkit-data-grid-tables",
1787
2613
  role: groupBy.length > 0 ? "treegrid" : "grid",
1788
- "aria-rowcount": displayRows.length + 1,
2614
+ "aria-rowcount": aggregatedRows.length + 1,
1789
2615
  "aria-colcount": resolved.length,
1790
2616
  ...ariaAttr(multiselectable, "aria-multiselectable", true),
1791
2617
  ...ariaAttr(labelledBy !== void 0, "aria-labelledby", labelledBy),
@@ -1807,55 +2633,274 @@ function DataGridComponent({
1807
2633
  selection.selectAllRows();
1808
2634
  }
1809
2635
  },
1810
- style: { width: totalColumnWidth(resolved) },
1811
- className: classNames(
1812
- "gridkit-data-grid",
1813
- borders ? `borders-${borders}` : "",
1814
- // Hover is on by default and selection off, so one set of classes
1815
- // turns styling off and the other turns it on. The polarity differs
1816
- // because the defaults do.
1817
- hoverRows ? "" : "no-hover-rows",
1818
- hoverColumns ? "" : "no-hover-columns",
1819
- hoverCells ? "" : "no-hover-cells",
1820
- selection.rowMode === false ? "" : "selectable-rows",
1821
- selection.columnMode === false ? "" : "selectable-columns",
1822
- selection.cellMode === false ? "" : "selectable-cells"
1823
- ),
1824
2636
  children: [
1825
- /* @__PURE__ */ jsx6("colgroup", { children: resolved.map((entry) => /* @__PURE__ */ jsx6("col", { style: { width: entry.width } }, entry.id)) }),
1826
- /* @__PURE__ */ jsx6(
1827
- GridHeader,
2637
+ /* @__PURE__ */ jsxs7(
2638
+ "table",
1828
2639
  {
1829
- columns: resolved,
1830
- resize,
1831
- drag,
1832
- sort: columnSort,
1833
- sortableColumns,
1834
- grouping,
1835
- groupableColumns,
1836
- groupToggleIconColumns,
1837
- nav,
1838
- selection
2640
+ ref: headerTableRef,
2641
+ style: { width: totalColumnWidth(resolved) },
2642
+ className: classNames(gridTableClassName, "gridkit-data-grid-header"),
2643
+ children: [
2644
+ /* @__PURE__ */ jsx10(GridColgroup, { columns: resolved }),
2645
+ /* @__PURE__ */ jsx10(
2646
+ GridHeader,
2647
+ {
2648
+ columns: resolved,
2649
+ resize,
2650
+ drag,
2651
+ sort: columnSort,
2652
+ sortableColumns,
2653
+ grouping,
2654
+ groupableColumns,
2655
+ groupToggleIconColumns,
2656
+ nav,
2657
+ selection
2658
+ }
2659
+ )
2660
+ ]
1839
2661
  }
1840
2662
  ),
1841
- /* @__PURE__ */ jsx6(
1842
- GridBody,
2663
+ /* @__PURE__ */ jsx10(
2664
+ "div",
1843
2665
  {
1844
- columns: resolved,
1845
- rows: displayRows,
1846
- activeColumnId: resize.activeColumnId,
1847
- nav,
1848
- selection,
1849
- grouping
2666
+ className: "gridkit-data-grid-body",
2667
+ ref: bodyScrollRef,
2668
+ style: height === void 0 ? void 0 : {
2669
+ "--gridkit-body-height": typeof height === "number" ? `${String(height)}px` : height
2670
+ },
2671
+ children: /* @__PURE__ */ jsxs7(
2672
+ "table",
2673
+ {
2674
+ ref: bodyTableRef,
2675
+ style: { width: totalColumnWidth(resolved) },
2676
+ className: gridTableClassName,
2677
+ children: [
2678
+ /* @__PURE__ */ jsx10(GridColgroup, { columns: resolved }),
2679
+ /* @__PURE__ */ jsx10(
2680
+ GridBody,
2681
+ {
2682
+ columns: resolved,
2683
+ rows: paginatedRows.rows,
2684
+ activeColumnId: resize.activeColumnId,
2685
+ nav,
2686
+ selection,
2687
+ grouping,
2688
+ aggregates: activeAggregates,
2689
+ groupAggregateDisplay,
2690
+ virtualRange: virtualized ? virtualizer : null,
2691
+ infiniteScroll,
2692
+ sentinelRef
2693
+ }
2694
+ )
2695
+ ]
2696
+ }
2697
+ )
2698
+ }
2699
+ ),
2700
+ activeAggregates.length > 0 && /* @__PURE__ */ jsxs7(
2701
+ "table",
2702
+ {
2703
+ style: { width: totalColumnWidth(resolved) },
2704
+ className: classNames(
2705
+ gridTableClassName,
2706
+ "gridkit-data-grid-footer"
2707
+ ),
2708
+ children: [
2709
+ /* @__PURE__ */ jsx10(GridColgroup, { columns: resolved }),
2710
+ /* @__PURE__ */ jsx10(
2711
+ GridFooter,
2712
+ {
2713
+ columns: resolved,
2714
+ aggregates: activeAggregates,
2715
+ results: grandTotal,
2716
+ rows: shownRows.map((entry) => entry.row)
2717
+ }
2718
+ )
2719
+ ]
1850
2720
  }
1851
2721
  )
1852
2722
  ]
1853
2723
  }
1854
2724
  ),
1855
- /* @__PURE__ */ jsx6("div", { className: "gridkit-sr-only", role: "status", "aria-live": "polite", children: announcement })
2725
+ paginated && (pager?.template ? pager.template({
2726
+ pagination: paginationApi.pagination,
2727
+ currentPage: paginationApi.currentPage,
2728
+ pageCount: paginationApi.pageCount,
2729
+ pageSizeOptions: pager.sizeOptions,
2730
+ goToPage: paginationApi.goToPage,
2731
+ nextPage: paginationApi.nextPage,
2732
+ previousPage: paginationApi.previousPage,
2733
+ setPageSize: paginationApi.setPageSize
2734
+ }) : /* @__PURE__ */ jsx10(GridPager, { pager: paginationApi, config: pager })),
2735
+ /* @__PURE__ */ jsx10("div", { className: "gridkit-sr-only", role: "status", "aria-live": "polite", children: announcement })
1856
2736
  ] });
1857
2737
  }
2738
+
2739
+ // src/DataGrid/hooks/usePaginationState.ts
2740
+ import { useCallback as useCallback4 } from "react";
2741
+
2742
+ // src/DataGrid/hooks/useGridSnapshot.ts
2743
+ import { useCallback as useCallback3, useSyncExternalStore } from "react";
2744
+ function useGridSnapshot(gridRef, getSnapshot, fallback) {
2745
+ const subscribe = useCallback3(
2746
+ (listener) => {
2747
+ const api = gridRef.current;
2748
+ if (api === null) {
2749
+ return () => {
2750
+ };
2751
+ }
2752
+ return api.subscribe(listener);
2753
+ },
2754
+ [gridRef]
2755
+ );
2756
+ return useSyncExternalStore(subscribe, () => {
2757
+ const api = gridRef.current;
2758
+ return api === null ? fallback : getSnapshot(api);
2759
+ });
2760
+ }
2761
+
2762
+ // src/DataGrid/hooks/usePaginationState.ts
2763
+ var FALLBACK_PAGINATION = { pageIndex: 0, pageSize: 0 };
2764
+ function usePaginationState(gridRef) {
2765
+ const pagination = useGridSnapshot(
2766
+ gridRef,
2767
+ useCallback4((api) => api.getPagination(), []),
2768
+ FALLBACK_PAGINATION
2769
+ );
2770
+ const pageCount = useGridSnapshot(
2771
+ gridRef,
2772
+ useCallback4((api) => api.getPageCount(), []),
2773
+ 0
2774
+ );
2775
+ return {
2776
+ pagination,
2777
+ pageCount,
2778
+ goToPage: (pageIndex) => {
2779
+ gridRef.current?.goToPage(pageIndex);
2780
+ },
2781
+ nextPage: () => {
2782
+ gridRef.current?.nextPage();
2783
+ },
2784
+ previousPage: () => {
2785
+ gridRef.current?.previousPage();
2786
+ },
2787
+ setPageSize: (pageSize) => {
2788
+ gridRef.current?.setPageSize(pageSize);
2789
+ }
2790
+ };
2791
+ }
2792
+
2793
+ // src/DataGrid/hooks/useGroupByState.ts
2794
+ import { useCallback as useCallback5 } from "react";
2795
+ var FALLBACK_GROUP_BY = [];
2796
+ var FALLBACK_EXPANSION = [];
2797
+ function useGroupByState(gridRef) {
2798
+ const groupBy = useGridSnapshot(
2799
+ gridRef,
2800
+ useCallback5((api) => api.getGroupBy(), []),
2801
+ FALLBACK_GROUP_BY
2802
+ );
2803
+ const groupExpansion = useGridSnapshot(
2804
+ gridRef,
2805
+ useCallback5((api) => api.getGroupExpansion(), []),
2806
+ FALLBACK_EXPANSION
2807
+ );
2808
+ return {
2809
+ groupBy,
2810
+ groupExpansion,
2811
+ expandAllGroups: () => {
2812
+ gridRef.current?.expandAllGroups();
2813
+ },
2814
+ collapseAllGroups: () => {
2815
+ gridRef.current?.collapseAllGroups();
2816
+ }
2817
+ };
2818
+ }
2819
+
2820
+ // src/DataGrid/hooks/useColumnSortState.ts
2821
+ import { useCallback as useCallback6 } from "react";
2822
+ var FALLBACK_SORT = [];
2823
+ function useColumnSortState(gridRef) {
2824
+ return useGridSnapshot(
2825
+ gridRef,
2826
+ useCallback6((api) => api.getColumnSort(), []),
2827
+ FALLBACK_SORT
2828
+ );
2829
+ }
2830
+
2831
+ // src/DataGrid/hooks/useSelectionState.ts
2832
+ import { useCallback as useCallback7 } from "react";
2833
+ var FALLBACK_SELECTION = [];
2834
+ var FALLBACK_CELL_SELECTION = null;
2835
+ function useSelectionState(gridRef) {
2836
+ const rowSelection = useGridSnapshot(
2837
+ gridRef,
2838
+ useCallback7((api) => api.getRowSelection(), []),
2839
+ FALLBACK_SELECTION
2840
+ );
2841
+ const columnSelection = useGridSnapshot(
2842
+ gridRef,
2843
+ useCallback7((api) => api.getColumnSelection(), []),
2844
+ FALLBACK_SELECTION
2845
+ );
2846
+ const cellSelection = useGridSnapshot(
2847
+ gridRef,
2848
+ useCallback7((api) => api.getCellSelection(), []),
2849
+ FALLBACK_CELL_SELECTION
2850
+ );
2851
+ return {
2852
+ rowSelection,
2853
+ columnSelection,
2854
+ cellSelection,
2855
+ clearSelection: () => {
2856
+ gridRef.current?.clearSelection();
2857
+ },
2858
+ selectAllRows: () => {
2859
+ gridRef.current?.selectAllRows();
2860
+ }
2861
+ };
2862
+ }
2863
+
2864
+ // src/DataGrid/hooks/useAggregateState.ts
2865
+ import { useCallback as useCallback8 } from "react";
2866
+ var FALLBACK_AGGREGATES = /* @__PURE__ */ new Map();
2867
+ function useAggregateState(gridRef) {
2868
+ return useGridSnapshot(
2869
+ gridRef,
2870
+ useCallback8((api) => api.getAggregates(), []),
2871
+ FALLBACK_AGGREGATES
2872
+ );
2873
+ }
2874
+
2875
+ // src/DataGrid/hooks/useColumnSizingState.ts
2876
+ import { useCallback as useCallback9 } from "react";
2877
+ var FALLBACK_SIZING = {};
2878
+ function useColumnSizingState(gridRef) {
2879
+ return useGridSnapshot(
2880
+ gridRef,
2881
+ useCallback9((api) => api.getColumnSizing(), []),
2882
+ FALLBACK_SIZING
2883
+ );
2884
+ }
2885
+
2886
+ // src/DataGrid/hooks/useColumnOrderState.ts
2887
+ import { useCallback as useCallback10 } from "react";
2888
+ var FALLBACK_ORDER = [];
2889
+ function useColumnOrderState(gridRef) {
2890
+ return useGridSnapshot(
2891
+ gridRef,
2892
+ useCallback10((api) => api.getColumnOrder(), []),
2893
+ FALLBACK_ORDER
2894
+ );
2895
+ }
1858
2896
  export {
1859
- DataGridComponent
2897
+ DataGridComponent,
2898
+ useAggregateState,
2899
+ useColumnOrderState,
2900
+ useColumnSizingState,
2901
+ useColumnSortState,
2902
+ useGroupByState,
2903
+ usePaginationState,
2904
+ useSelectionState
1860
2905
  };
1861
2906
  //# sourceMappingURL=index.js.map