@gridkitjs/react 0.6.0 → 0.8.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,19 +1,24 @@
1
1
  // src/DataGrid/DataGrid.tsx
2
2
  import {
3
+ useEffect as useEffect2,
3
4
  useImperativeHandle,
4
- useMemo as useMemo2,
5
+ useMemo as useMemo4,
5
6
  useRef as useRef4,
6
7
  useState as useState5
7
8
  } from "react";
8
9
  import {
9
10
  applyColumnOrder,
11
+ computeAggregates,
10
12
  defineColumnsFromRows,
11
13
  fitColumnsToWidth,
14
+ groupRows,
12
15
  moveColumnBefore,
16
+ paginateRows as paginateRows2,
13
17
  resolveColumnWidths,
14
18
  resolveRowId,
15
19
  resolveShownRows,
16
- totalColumnWidth
20
+ totalColumnWidth,
21
+ withGroupAggregates
17
22
  } from "@gridkitjs/core";
18
23
 
19
24
  // src/DataGrid/components/GridHeader.tsx
@@ -33,6 +38,7 @@ function classNames(...values) {
33
38
  // src/DataGrid/useGridNavigation.ts
34
39
  import {
35
40
  useEffect,
41
+ useMemo,
36
42
  useRef,
37
43
  useState
38
44
  } from "react";
@@ -47,18 +53,26 @@ function tabIndexFor(columnIndex, focusedColumnIndex) {
47
53
  }
48
54
  function cellAt(table, focus) {
49
55
  const row = focus.rowIndex === HEADER_ROW ? table.tHead?.rows[0] ?? null : table.tBodies[0]?.rows[focus.rowIndex] ?? null;
50
- return row?.cells[focus.columnIndex] ?? null;
56
+ if (row === null) {
57
+ return null;
58
+ }
59
+ const columnIndex = Math.min(focus.columnIndex, row.cells.length - 1);
60
+ return row.cells[columnIndex] ?? null;
51
61
  }
52
62
  function useGridNavigation({
53
63
  tableRef,
54
64
  rowCount,
55
- columnCount
65
+ columnCount,
66
+ isSkippableRow
56
67
  }) {
57
68
  const [stored, setStored] = useState({
58
69
  rowIndex: HEADER_ROW,
59
70
  columnIndex: 0
60
71
  });
61
- const focus = clampFocus(stored, rowCount, columnCount);
72
+ const focus = useMemo(
73
+ () => clampFocus(stored, rowCount, columnCount),
74
+ [stored, rowCount, columnCount]
75
+ );
62
76
  const pending = useRef(false);
63
77
  useEffect(() => {
64
78
  if (!pending.current) {
@@ -103,7 +117,8 @@ function useGridNavigation({
103
117
  focus,
104
118
  rowCount,
105
119
  columnCount,
106
- pageSize()
120
+ pageSize(),
121
+ isSkippableRow
107
122
  );
108
123
  if (next === null) {
109
124
  return;
@@ -124,7 +139,7 @@ function useGridNavigation({
124
139
 
125
140
  // src/DataGrid/useGridSelection.ts
126
141
  import {
127
- useMemo,
142
+ useMemo as useMemo2,
128
143
  useRef as useRef2
129
144
  } from "react";
130
145
  import {
@@ -164,16 +179,16 @@ function countMessage(count, noun) {
164
179
  return count === 1 ? `1 ${noun} selected` : `${String(count)} ${noun}s selected`;
165
180
  }
166
181
  function useAsSet(values) {
167
- return useMemo(() => new Set(values), [values]);
182
+ return useMemo2(() => new Set(values), [values]);
168
183
  }
169
184
  function useIndexedBy(values, key) {
170
- return useMemo(
185
+ return useMemo2(
171
186
  () => new Map(values.map((value) => [value[key], value])),
172
187
  [values, key]
173
188
  );
174
189
  }
175
190
  function useMappedArray(values, key) {
176
- return useMemo(() => values.map((value) => value[key]), [values, key]);
191
+ return useMemo2(() => values.map((value) => value[key]), [values, key]);
177
192
  }
178
193
  function useGridSelection({
179
194
  rows,
@@ -366,16 +381,26 @@ function ChevronsUpDownIcon() {
366
381
  function ChevronUpIcon() {
367
382
  return /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) });
368
383
  }
384
+ function GroupIcon() {
385
+ return /* @__PURE__ */ jsxs(Icon, { children: [
386
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }),
387
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }),
388
+ /* @__PURE__ */ jsx("rect", { x: "8.5", y: "14", width: "7", height: "7", rx: "1" })
389
+ ] });
390
+ }
369
391
  function GridHeader({
370
392
  columns,
371
393
  resize,
372
394
  drag,
373
395
  sort,
374
396
  sortableColumns,
397
+ grouping,
398
+ groupableColumns,
399
+ groupToggleIconColumns,
375
400
  nav,
376
401
  selection
377
402
  }) {
378
- const { beforeId } = drag.dropTarget ?? {};
403
+ const beforeId = drag.dropTarget?.kind === "column-order" ? drag.dropTarget.beforeId : void 0;
379
404
  const draggedEntry = columns.find((entry) => entry.id === drag.draggedColumnId) ?? null;
380
405
  return /* @__PURE__ */ jsxs("thead", { children: [
381
406
  /* @__PURE__ */ jsx("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
@@ -388,10 +413,16 @@ function GridHeader({
388
413
  const sortable = column.sortable ?? sortableColumns;
389
414
  const sortDirection = sort.directionFor(entry.id);
390
415
  const sortPriority = sort.priorityFor(entry.id);
416
+ const groupable = column.groupable ?? groupableColumns;
417
+ const showGroupToggleIcon = column.groupToggleIcon ?? groupToggleIconColumns;
418
+ const isGrouped = grouping.groupBy.some(
419
+ (level) => level.columnId === entry.id
420
+ );
391
421
  const shortcuts = buildKeyShortcuts({
392
422
  reorderable: entry.reorderable,
393
423
  resizable: entry.resizable,
394
- sortable
424
+ sortable,
425
+ groupable
395
426
  });
396
427
  return /* @__PURE__ */ jsxs(
397
428
  "th",
@@ -413,7 +444,7 @@ function GridHeader({
413
444
  if (drag.justDragged()) {
414
445
  return;
415
446
  }
416
- if (event.target instanceof Element && (event.target.closest(".header-resize-handle") !== null || event.target.closest(".header-sort-toggle") !== null)) {
447
+ if (event.target instanceof Element && (event.target.closest(".header-resize-handle") !== null || event.target.closest(".header-sort-toggle") !== null || event.target.closest(".header-group-toggle") !== null)) {
417
448
  return;
418
449
  }
419
450
  selection.selectColumn(entry.id, intentOf(event));
@@ -441,6 +472,11 @@ function GridHeader({
441
472
  sort.toggle(entry, { shiftKey: event.shiftKey });
442
473
  return;
443
474
  }
475
+ if (event.key === "ArrowDown" && event.altKey && groupable) {
476
+ event.preventDefault();
477
+ grouping.toggleGroupBy(entry.id);
478
+ return;
479
+ }
444
480
  if (event.key === " " || event.key === "Enter") {
445
481
  event.preventDefault();
446
482
  selection.selectColumn(
@@ -453,7 +489,11 @@ function GridHeader({
453
489
  },
454
490
  className: classNames(
455
491
  "header-cell",
456
- entry.reorderable ? "is-reorderable" : "",
492
+ // The grab affordance a header showing either drag
493
+ // destination shares — a column dragged only for grouping
494
+ // (`groupByDraggable` without `reorderable`) grabs the same
495
+ // way one dragged only for reordering does.
496
+ entry.reorderable || entry.groupByDraggable ? "is-reorderable" : "",
457
497
  selected ? "is-selected" : "",
458
498
  resizing ? "is-resizing" : "",
459
499
  dragging ? "is-dragging" : "",
@@ -468,12 +508,40 @@ function GridHeader({
468
508
  "aria-sort",
469
509
  sortDirection === "asc" ? "ascending" : "descending"
470
510
  ),
471
- ...entry.reorderable && {
511
+ ...(entry.reorderable || entry.groupByDraggable) && {
472
512
  onPointerDown: (event) => {
473
513
  drag.startDrag(entry, event);
474
514
  }
475
515
  },
476
516
  children: [
517
+ groupable && showGroupToggleIcon && /*
518
+ * Inline, before the label, rather than absolutely
519
+ * positioned like the sort toggle and resize handle: those
520
+ * two already share the header's right edge, and a third
521
+ * overlay there would need its own carve-out. Sitting in
522
+ * normal flow costs nothing but a few pixels of label width.
523
+ * A pointer affordance only, same as the other two — the
524
+ * keyboard equivalent is Alt+ArrowDown on the header itself.
525
+ */
526
+ /* @__PURE__ */ jsx(
527
+ "span",
528
+ {
529
+ className: classNames(
530
+ "header-group-toggle",
531
+ isGrouped ? "is-grouped" : ""
532
+ ),
533
+ "aria-hidden": "true",
534
+ tabIndex: -1,
535
+ onPointerDown: (event) => {
536
+ event.stopPropagation();
537
+ },
538
+ onClick: (event) => {
539
+ event.stopPropagation();
540
+ grouping.toggleGroupBy(entry.id);
541
+ },
542
+ children: /* @__PURE__ */ jsx(GroupIcon, {})
543
+ }
544
+ ),
477
545
  entry.label,
478
546
  sortable && /*
479
547
  * A pointer affordance only, and hidden from assistive
@@ -546,17 +614,224 @@ function GridHeader({
546
614
  }
547
615
 
548
616
  // src/DataGrid/components/GridBody.tsx
549
- import { intentOf as intentOf2 } from "@gridkitjs/core";
617
+ import { useMemo as useMemo3 } from "react";
618
+ import {
619
+ groupRowId,
620
+ intentOf as intentOf2
621
+ } from "@gridkitjs/core";
622
+
623
+ // src/DataGrid/components/GridGroupRow.tsx
624
+ import { memo } from "react";
625
+
626
+ // src/DataGrid/components/formatAggregateValue.ts
627
+ function formatAggregateValue(value) {
628
+ if (value === null || value === void 0) {
629
+ return "\u2014";
630
+ }
631
+ if (value instanceof Date) {
632
+ return value.toLocaleString();
633
+ }
634
+ switch (typeof value) {
635
+ case "string":
636
+ case "boolean":
637
+ case "bigint":
638
+ case "symbol":
639
+ return String(value);
640
+ case "number":
641
+ return value.toLocaleString();
642
+ case "function":
643
+ return "(function)";
644
+ default:
645
+ return JSON.stringify(value);
646
+ }
647
+ }
648
+
649
+ // src/DataGrid/components/GridGroupRow.tsx
650
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
651
+ function formatGroupValue(value) {
652
+ if (value === null || value === void 0) {
653
+ return "(blank)";
654
+ }
655
+ if (value instanceof Date) {
656
+ return value.toLocaleString();
657
+ }
658
+ switch (typeof value) {
659
+ case "string":
660
+ case "number":
661
+ case "boolean":
662
+ case "bigint":
663
+ case "symbol":
664
+ return String(value);
665
+ case "function":
666
+ return "(function)";
667
+ default:
668
+ return JSON.stringify(value);
669
+ }
670
+ }
671
+ function GridGroupRowComponent({
672
+ columnCount,
673
+ groupId,
674
+ level,
675
+ columnLabel,
676
+ value,
677
+ expanded,
678
+ count,
679
+ datasetIndex,
680
+ posinset,
681
+ setsize,
682
+ focused,
683
+ aggregates,
684
+ results,
685
+ columns
686
+ }) {
687
+ const byId = new Map(columns.map((entry) => [entry.id, entry]));
688
+ return /* @__PURE__ */ jsx2(
689
+ "tr",
690
+ {
691
+ role: "row",
692
+ "aria-rowindex": datasetIndex + 2,
693
+ "aria-level": level + 1,
694
+ "aria-expanded": expanded,
695
+ "aria-setsize": setsize,
696
+ "aria-posinset": posinset,
697
+ "data-gridkit-group": groupId,
698
+ className: classNames(
699
+ "grid-group-row",
700
+ `is-group-level-${String(level)}`
701
+ ),
702
+ children: /* @__PURE__ */ jsx2(
703
+ "td",
704
+ {
705
+ role: "gridcell",
706
+ colSpan: columnCount,
707
+ tabIndex: focused ? 0 : -1,
708
+ "aria-keyshortcuts": "Space Enter",
709
+ className: "grid-group-cell",
710
+ children: /* @__PURE__ */ jsxs2(
711
+ "div",
712
+ {
713
+ className: "grid-group-cell-content",
714
+ style: { paddingInlineStart: `${String(level * 1.25 + 0.5)}rem` },
715
+ children: [
716
+ /* @__PURE__ */ jsx2(
717
+ "span",
718
+ {
719
+ className: classNames(
720
+ "group-toggle",
721
+ expanded ? "is-expanded" : "is-collapsed"
722
+ ),
723
+ "aria-hidden": "true"
724
+ }
725
+ ),
726
+ /* @__PURE__ */ jsxs2("span", { className: "group-label", children: [
727
+ columnLabel,
728
+ ": ",
729
+ formatGroupValue(value)
730
+ ] }),
731
+ /* @__PURE__ */ jsxs2("span", { className: "group-count", children: [
732
+ "(",
733
+ count,
734
+ ")"
735
+ ] }),
736
+ aggregates.length > 0 && /* @__PURE__ */ jsx2("span", { className: "group-aggregates", children: aggregates.map((spec) => {
737
+ const key = spec.id ?? spec.columnId;
738
+ const aggregateValue = results.get(key);
739
+ const column = byId.get(spec.columnId);
740
+ const rendered = column?.column.footerTemplate ? column.column.footerTemplate({
741
+ value: aggregateValue,
742
+ rows: []
743
+ }) : formatAggregateValue(aggregateValue);
744
+ const aggregateLabel = spec.id ?? column?.label ?? spec.columnId;
745
+ return /* @__PURE__ */ jsxs2("span", { className: "group-aggregate", children: [
746
+ aggregateLabel,
747
+ ": ",
748
+ rendered
749
+ ] }, key);
750
+ }) })
751
+ ]
752
+ }
753
+ )
754
+ }
755
+ )
756
+ }
757
+ );
758
+ }
759
+ var GridGroupRow = memo(
760
+ GridGroupRowComponent
761
+ );
762
+ var GridGroupRow_default = GridGroupRow;
763
+
764
+ // src/DataGrid/components/GridGroupSummaryRow.tsx
765
+ import { memo as memo2 } from "react";
766
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
767
+ function GridGroupSummaryRowComponent({
768
+ groupId,
769
+ level,
770
+ datasetIndex,
771
+ results,
772
+ aggregates,
773
+ columns
774
+ }) {
775
+ return /* @__PURE__ */ jsx3(
776
+ "tr",
777
+ {
778
+ role: "row",
779
+ "aria-rowindex": datasetIndex + 2,
780
+ "data-gridkit-group-summary": groupId,
781
+ className: classNames(
782
+ "grid-group-summary-row",
783
+ `is-group-level-${String(level)}`
784
+ ),
785
+ children: columns.map((entry) => {
786
+ const specs = aggregates.filter(
787
+ (candidate) => candidate.columnId === entry.id
788
+ );
789
+ const { column } = entry;
790
+ return /* @__PURE__ */ jsxs3(
791
+ "td",
792
+ {
793
+ role: "gridcell",
794
+ "data-gridkit-column": entry.id,
795
+ className: "grid-group-summary-cell",
796
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
797
+ children: [
798
+ entry.id === columns[0]?.id && /* @__PURE__ */ jsx3(
799
+ "span",
800
+ {
801
+ className: "grid-group-summary-indent",
802
+ style: { width: `${String(level * 1.25 + 0.5)}rem` },
803
+ "aria-hidden": "true"
804
+ }
805
+ ),
806
+ specs.map((spec) => {
807
+ const value = results.get(spec.id ?? spec.columnId);
808
+ return column.footerTemplate ? column.footerTemplate({ value, rows: [] }) : formatAggregateValue(value);
809
+ }).flatMap(
810
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
811
+ )
812
+ ]
813
+ },
814
+ entry.id
815
+ );
816
+ })
817
+ }
818
+ );
819
+ }
820
+ var GridGroupSummaryRow = memo2(
821
+ GridGroupSummaryRowComponent
822
+ );
823
+ var GridGroupSummaryRow_default = GridGroupSummaryRow;
550
824
 
551
825
  // src/DataGrid/components/GridRow.tsx
552
826
  import { accessDotted } from "@gridkitjs/core";
553
- import { memo } from "react";
554
- import { jsx as jsx2 } from "react/jsx-runtime";
827
+ import { memo as memo3 } from "react";
828
+ import { jsx as jsx4 } from "react/jsx-runtime";
555
829
  function GridRowComponent({
556
830
  columns,
557
831
  rowId,
558
832
  row,
559
833
  rowIndex,
834
+ datasetIndex,
560
835
  activeColumnId,
561
836
  selectedColumnIds,
562
837
  selected,
@@ -565,17 +840,17 @@ function GridRowComponent({
565
840
  rowsSelectable,
566
841
  cellsSelectable
567
842
  }) {
568
- return /* @__PURE__ */ jsx2(
843
+ return /* @__PURE__ */ jsx4(
569
844
  "tr",
570
845
  {
571
846
  role: "row",
572
- "aria-rowindex": rowIndex + 2,
847
+ "aria-rowindex": datasetIndex + 2,
573
848
  ...ariaAttr(rowsSelectable, "aria-selected", selected),
574
849
  className: classNames("grid-row", selected ? "is-selected" : ""),
575
850
  children: columns.map(({ id, column, alignment }, columnIndex) => {
576
851
  const value = accessDotted(row, column.field);
577
852
  const cellSelected = id === selectedColumnId;
578
- return /* @__PURE__ */ jsx2(
853
+ return /* @__PURE__ */ jsx4(
579
854
  "td",
580
855
  {
581
856
  role: "gridcell",
@@ -592,7 +867,14 @@ function GridRowComponent({
592
867
  column.cellClassName ?? ""
593
868
  ),
594
869
  style: { textAlign: alignment },
595
- children: column.cellTemplate ? column.cellTemplate({ value, row, rowIndex, rowId, selected }) : value
870
+ children: column.cellTemplate ? column.cellTemplate({
871
+ value,
872
+ row,
873
+ rowIndex,
874
+ datasetIndex,
875
+ rowId,
876
+ selected
877
+ }) : value
596
878
  },
597
879
  id
598
880
  );
@@ -600,11 +882,11 @@ function GridRowComponent({
600
882
  }
601
883
  );
602
884
  }
603
- var GridRow = memo(GridRowComponent);
885
+ var GridRow = memo3(GridRowComponent);
604
886
  var GridRow_default = GridRow;
605
887
 
606
888
  // src/DataGrid/components/GridBody.tsx
607
- import { jsx as jsx3 } from "react/jsx-runtime";
889
+ import { jsx as jsx5 } from "react/jsx-runtime";
608
890
  function cellFrom(target) {
609
891
  if (!(target instanceof Element)) {
610
892
  return null;
@@ -621,18 +903,57 @@ function cellFrom(target) {
621
903
  columnId
622
904
  };
623
905
  }
906
+ function groupRowFrom(target) {
907
+ if (!(target instanceof Element)) {
908
+ return null;
909
+ }
910
+ const row = target.closest("tr[data-gridkit-group]");
911
+ const groupId = row?.getAttribute("data-gridkit-group");
912
+ if (!(row instanceof HTMLTableRowElement) || groupId === null || groupId === void 0) {
913
+ return null;
914
+ }
915
+ return { rowIndex: row.sectionRowIndex, groupId };
916
+ }
917
+ function groupAriaMeta(rows) {
918
+ const totals = /* @__PURE__ */ new Map();
919
+ for (const entry of rows) {
920
+ if (!("kind" in entry) || entry.kind !== "group") continue;
921
+ const parentKey = groupRowId(entry.path.slice(0, -1));
922
+ totals.set(parentKey, (totals.get(parentKey) ?? 0) + 1);
923
+ }
924
+ const seen = /* @__PURE__ */ new Map();
925
+ const meta = /* @__PURE__ */ new Map();
926
+ for (const entry of rows) {
927
+ if (!("kind" in entry) || entry.kind !== "group") continue;
928
+ const parentKey = groupRowId(entry.path.slice(0, -1));
929
+ const posinset = (seen.get(parentKey) ?? 0) + 1;
930
+ seen.set(parentKey, posinset);
931
+ meta.set(entry.groupId, {
932
+ posinset,
933
+ setsize: totals.get(parentKey) ?? posinset
934
+ });
935
+ }
936
+ return meta;
937
+ }
624
938
  function GridBody({
625
939
  columns,
626
940
  rows,
627
941
  activeColumnId,
628
942
  nav,
629
- selection
943
+ selection,
944
+ grouping,
945
+ aggregates,
946
+ groupAggregateDisplay
630
947
  }) {
631
948
  const { selectedCell, rowMode, cellMode } = selection;
949
+ const ariaMeta = useMemo3(() => groupAriaMeta(rows), [rows]);
632
950
  function targetOf(target) {
633
951
  const position = cellFrom(target);
634
952
  const entry = position === null ? void 0 : rows[position.rowIndex];
635
- return position === null || entry === void 0 ? null : { position, rowId: entry.rowId };
953
+ if (position === null || entry === void 0 || "kind" in entry) {
954
+ return null;
955
+ }
956
+ return { position, rowId: entry.rowId };
636
957
  }
637
958
  function select(rowId, columnId, modifiers) {
638
959
  const intent = intentOf2(modifiers);
@@ -643,21 +964,45 @@ function GridBody({
643
964
  selection.selectCell({ rowId, columnId }, intent);
644
965
  }
645
966
  }
646
- return /* @__PURE__ */ jsx3(
967
+ return /* @__PURE__ */ jsx5(
647
968
  "tbody",
648
969
  {
649
970
  className: "grid-body",
650
971
  onFocus: (event) => {
972
+ const group = groupRowFrom(event.target);
973
+ if (group !== null) {
974
+ nav.focusCell(group.rowIndex, 0);
975
+ return;
976
+ }
651
977
  const found = targetOf(event.target);
652
978
  if (found === null) return;
653
979
  nav.focusCell(found.position.rowIndex, found.position.columnIndex);
654
980
  },
655
981
  onClick: (event) => {
982
+ const group = groupRowFrom(event.target);
983
+ if (group !== null) {
984
+ grouping.toggleExpansion(group.groupId);
985
+ return;
986
+ }
656
987
  const found = targetOf(event.target);
657
988
  if (found === null) return;
658
989
  select(found.rowId, found.position.columnId, event);
659
990
  },
660
991
  onKeyDown: (event) => {
992
+ const group = groupRowFrom(event.target);
993
+ if (group !== null) {
994
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
995
+ event.preventDefault();
996
+ return;
997
+ }
998
+ if (event.key === " " || event.key === "Enter") {
999
+ event.preventDefault();
1000
+ grouping.toggleExpansion(group.groupId);
1001
+ return;
1002
+ }
1003
+ nav.onKeyDown(event);
1004
+ return;
1005
+ }
661
1006
  if (event.key === " " || event.key === "Enter") {
662
1007
  const found = targetOf(event.target);
663
1008
  if (found === null) return;
@@ -671,37 +1016,193 @@ function GridBody({
671
1016
  }
672
1017
  nav.onKeyDown(event);
673
1018
  },
674
- children: rows.map(({ rowId, row, rowIndex }) => /* @__PURE__ */ jsx3(
675
- GridRow_default,
676
- {
677
- columns,
678
- rowId,
679
- row,
680
- rowIndex,
681
- activeColumnId,
682
- selectedColumnIds: selection.selectedColumnIds,
683
- selected: selection.selectedRowIds.has(rowId),
684
- selectedColumnId: selectedCell?.rowId === rowId ? selectedCell.columnId : null,
685
- focusedColumnIndex: nav.focus.rowIndex === rowIndex ? nav.focus.columnIndex : null,
686
- rowsSelectable: rowMode !== false,
687
- cellsSelectable: cellMode !== false
688
- },
689
- rowId
690
- ))
1019
+ children: rows.map((entry) => {
1020
+ if (!("kind" in entry)) {
1021
+ return /* @__PURE__ */ jsx5(
1022
+ GridRow_default,
1023
+ {
1024
+ columns,
1025
+ rowId: entry.rowId,
1026
+ row: entry.row,
1027
+ rowIndex: entry.rowIndex,
1028
+ datasetIndex: entry.datasetIndex,
1029
+ activeColumnId,
1030
+ selectedColumnIds: selection.selectedColumnIds,
1031
+ selected: selection.selectedRowIds.has(entry.rowId),
1032
+ selectedColumnId: selectedCell?.rowId === entry.rowId ? selectedCell.columnId : null,
1033
+ focusedColumnIndex: nav.focus.rowIndex === entry.rowIndex ? nav.focus.columnIndex : null,
1034
+ rowsSelectable: rowMode !== false,
1035
+ cellsSelectable: cellMode !== false
1036
+ },
1037
+ entry.rowId
1038
+ );
1039
+ }
1040
+ if (entry.kind === "group-summary") {
1041
+ return /* @__PURE__ */ jsx5(
1042
+ GridGroupSummaryRow_default,
1043
+ {
1044
+ groupId: entry.groupId,
1045
+ level: entry.level,
1046
+ datasetIndex: entry.datasetIndex,
1047
+ results: entry.aggregates,
1048
+ aggregates,
1049
+ columns
1050
+ },
1051
+ `${entry.groupId}-summary`
1052
+ );
1053
+ }
1054
+ return /* @__PURE__ */ jsx5(
1055
+ GridGroupRow_default,
1056
+ {
1057
+ columnCount: columns.length,
1058
+ groupId: entry.groupId,
1059
+ level: entry.level,
1060
+ columnLabel: columns.find((column) => column.id === entry.columnId)?.label ?? entry.columnId,
1061
+ value: entry.value,
1062
+ expanded: entry.expanded,
1063
+ count: entry.count,
1064
+ datasetIndex: entry.datasetIndex,
1065
+ posinset: ariaMeta.get(entry.groupId)?.posinset ?? 1,
1066
+ setsize: ariaMeta.get(entry.groupId)?.setsize ?? 1,
1067
+ aggregates: groupAggregateDisplay === "inline" ? aggregates : [],
1068
+ results: entry.aggregates,
1069
+ columns,
1070
+ focused: nav.focus.rowIndex === entry.rowIndex
1071
+ },
1072
+ entry.groupId
1073
+ );
1074
+ })
691
1075
  }
692
1076
  );
693
1077
  }
694
1078
 
695
- // src/DataGrid/useColumnDrag.ts
1079
+ // src/DataGrid/components/GridFooter.tsx
1080
+ import { jsx as jsx6 } from "react/jsx-runtime";
1081
+ function GridFooter({
1082
+ columns,
1083
+ aggregates,
1084
+ results,
1085
+ rows
1086
+ }) {
1087
+ return /* @__PURE__ */ jsx6("tfoot", { children: /* @__PURE__ */ jsx6("tr", { className: "grid-footer", role: "row", children: columns.map((entry) => {
1088
+ const specs = aggregates.filter(
1089
+ (candidate) => candidate.columnId === entry.id
1090
+ );
1091
+ const { column } = entry;
1092
+ return /* @__PURE__ */ jsx6(
1093
+ "td",
1094
+ {
1095
+ role: "gridcell",
1096
+ "data-gridkit-column": entry.id,
1097
+ className: "grid-footer-cell",
1098
+ style: { textAlign: specs[0]?.alignment ?? entry.alignment },
1099
+ children: specs.map((spec) => {
1100
+ const value = results.get(spec.id ?? spec.columnId);
1101
+ return column.footerTemplate ? column.footerTemplate({ value, rows }) : formatAggregateValue(value);
1102
+ }).flatMap(
1103
+ (rendered, index) => index === 0 ? [rendered] : [", ", rendered]
1104
+ )
1105
+ },
1106
+ entry.id
1107
+ );
1108
+ }) }) });
1109
+ }
1110
+
1111
+ // src/DataGrid/components/GridPager.tsx
1112
+ import { paginationWindow } from "@gridkitjs/core";
1113
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1114
+ function GridPager({ pager, config }) {
1115
+ const { pagination, pageCount, currentPage } = pager;
1116
+ const variant = config?.variant ?? "compact";
1117
+ const sizeOptions = config?.sizeOptions;
1118
+ return /* @__PURE__ */ jsxs4("div", { className: "gridkit-grid-pager", role: "group", "aria-label": "Pagination", children: [
1119
+ /* @__PURE__ */ jsx7(
1120
+ "button",
1121
+ {
1122
+ type: "button",
1123
+ className: "grid-pager-button",
1124
+ disabled: pagination.pageIndex <= 0,
1125
+ onClick: () => {
1126
+ pager.previousPage();
1127
+ },
1128
+ children: "Previous"
1129
+ }
1130
+ ),
1131
+ variant === "numbered" ? paginationWindow(currentPage, pageCount, {
1132
+ boundaryCount: config?.boundaryCount,
1133
+ siblingCount: config?.siblingCount
1134
+ }).map(
1135
+ (entry, index) => entry === "ellipsis" ? /* @__PURE__ */ jsx7(
1136
+ "span",
1137
+ {
1138
+ className: "grid-pager-ellipsis",
1139
+ "aria-hidden": "true",
1140
+ children: "\u2026"
1141
+ },
1142
+ `ellipsis-${String(index)}`
1143
+ ) : /* @__PURE__ */ jsx7(
1144
+ "button",
1145
+ {
1146
+ type: "button",
1147
+ className: "grid-pager-button",
1148
+ "aria-label": `Page ${String(entry)}`,
1149
+ "aria-current": entry === currentPage ? "page" : void 0,
1150
+ onClick: () => {
1151
+ pager.goToPage(entry - 1);
1152
+ },
1153
+ children: entry
1154
+ },
1155
+ entry
1156
+ )
1157
+ ) : /* @__PURE__ */ jsxs4("span", { className: "grid-pager-status", children: [
1158
+ "Page ",
1159
+ pageCount === 0 ? 0 : currentPage,
1160
+ " of ",
1161
+ pageCount
1162
+ ] }),
1163
+ /* @__PURE__ */ jsx7(
1164
+ "button",
1165
+ {
1166
+ type: "button",
1167
+ className: "grid-pager-button",
1168
+ disabled: pagination.pageIndex >= pageCount - 1,
1169
+ onClick: () => {
1170
+ pager.nextPage();
1171
+ },
1172
+ children: "Next"
1173
+ }
1174
+ ),
1175
+ sizeOptions !== void 0 && sizeOptions.length > 0 && /* @__PURE__ */ jsxs4("label", { className: "grid-pager-page-size", children: [
1176
+ "Rows per page",
1177
+ /* @__PURE__ */ jsx7(
1178
+ "select",
1179
+ {
1180
+ value: pagination.pageSize,
1181
+ onChange: (event) => {
1182
+ pager.setPageSize(Number(event.target.value));
1183
+ },
1184
+ children: sizeOptions.map((size) => /* @__PURE__ */ jsx7("option", { value: size, children: size }, size))
1185
+ }
1186
+ )
1187
+ ] })
1188
+ ] });
1189
+ }
1190
+
1191
+ // src/DataGrid/components/GroupByBar.tsx
1192
+ import { createPortal as createPortal2 } from "react-dom";
1193
+ import { resolveKeyboardDropTarget } from "@gridkitjs/core";
1194
+
1195
+ // src/DataGrid/groupByDropTarget.ts
1196
+ import {
1197
+ movesGroupBy,
1198
+ resolveDropBefore
1199
+ } from "@gridkitjs/core";
1200
+
1201
+ // src/DataGrid/useDragReorder.ts
696
1202
  import {
697
1203
  useRef as useRef3,
698
1204
  useState as useState2
699
1205
  } from "react";
700
- import {
701
- movesColumn,
702
- resolveDropBefore,
703
- resolveKeyboardDropTarget
704
- } from "@gridkitjs/core";
705
1206
 
706
1207
  // src/DataGrid/pointerGesture.ts
707
1208
  function startPointerGesture(element, pointerId, handlers) {
@@ -737,21 +1238,20 @@ function startPointerGesture(element, pointerId, handlers) {
737
1238
  window.addEventListener("keydown", onKeyDown);
738
1239
  }
739
1240
 
740
- // src/DataGrid/useColumnDrag.ts
1241
+ // src/DataGrid/useDragReorder.ts
741
1242
  var DRAG_THRESHOLD = 4;
742
- function headerAt(x, y) {
743
- const element = document.elementFromPoint(x, y);
744
- return element?.closest("th[data-gridkit-column]") ?? null;
1243
+ function elementAt(x, y, selector) {
1244
+ return document.elementFromPoint(x, y)?.closest(selector) ?? null;
745
1245
  }
746
1246
  function sideOf(element, x) {
747
1247
  const rect = element.getBoundingClientRect();
748
1248
  return x < rect.left + rect.width / 2 ? "before" : "after";
749
1249
  }
750
- function useColumnDrag({
751
- order,
1250
+ function useDragReorder({
1251
+ resolveTarget,
752
1252
  onDrop
753
1253
  }) {
754
- const [draggedColumnId, setDraggedColumnId] = useState2(null);
1254
+ const [draggedId, setDraggedId] = useState2(null);
755
1255
  const [dropTarget, setDropTarget] = useState2(null);
756
1256
  const [ghostTransform, setGhostTransform] = useState2(null);
757
1257
  const dragged = useRef3(false);
@@ -760,10 +1260,9 @@ function useColumnDrag({
760
1260
  dragged.current = false;
761
1261
  return answer;
762
1262
  }
763
- function startDrag(entry, event) {
764
- const header = event.currentTarget;
1263
+ function startDrag(id, grabElement, event) {
765
1264
  const { pointerId, clientX: startX } = event;
766
- const grabX = startX - header.getBoundingClientRect().left;
1265
+ const grabX = startX - grabElement.getBoundingClientRect().left;
767
1266
  let dragging = false;
768
1267
  let target = null;
769
1268
  function placeGhost(x, y) {
@@ -771,10 +1270,10 @@ function useColumnDrag({
771
1270
  }
772
1271
  function reset() {
773
1272
  setGhostTransform(null);
774
- setDraggedColumnId(null);
1273
+ setDraggedId(null);
775
1274
  setDropTarget(null);
776
1275
  }
777
- startPointerGesture(header, pointerId, {
1276
+ startPointerGesture(grabElement, pointerId, {
778
1277
  onMove(moveEvent) {
779
1278
  const { clientX, clientY } = moveEvent;
780
1279
  if (!dragging) {
@@ -783,20 +1282,17 @@ function useColumnDrag({
783
1282
  }
784
1283
  dragging = true;
785
1284
  dragged.current = true;
786
- setDraggedColumnId(entry.id);
1285
+ setDraggedId(id);
787
1286
  }
788
1287
  placeGhost(clientX, clientY);
789
- const over = headerAt(clientX, clientY);
790
- const overId = over?.getAttribute("data-gridkit-column");
791
- const beforeId = over && overId ? resolveDropBefore(order, overId, sideOf(over, clientX)) : void 0;
792
- target = beforeId !== void 0 && movesColumn(order, entry.id, beforeId) ? { kind: "column-order", beforeId } : null;
1288
+ target = resolveTarget(clientX, clientY, id);
793
1289
  setDropTarget(target);
794
1290
  },
795
1291
  onEnd() {
796
1292
  const dropped = target;
797
1293
  reset();
798
1294
  if (dragging && dropped !== null) {
799
- onDrop(dropped, entry.id);
1295
+ onDrop(dropped, id);
800
1296
  }
801
1297
  },
802
1298
  // Escape and a cancelled pointer both leave the order as it was.
@@ -806,20 +1302,214 @@ function useColumnDrag({
806
1302
  }
807
1303
  });
808
1304
  }
1305
+ return {
1306
+ draggedId,
1307
+ dropTarget,
1308
+ ghostTransform,
1309
+ startDrag,
1310
+ justDragged
1311
+ };
1312
+ }
1313
+
1314
+ // src/DataGrid/groupByDropTarget.ts
1315
+ function resolveGroupByDropTarget(clientX, clientY, movedId, groupBy) {
1316
+ const overBar = elementAt(clientX, clientY, "[data-gridkit-group-by-bar]");
1317
+ if (overBar === null) {
1318
+ return null;
1319
+ }
1320
+ const overChip = elementAt(clientX, clientY, "[data-gridkit-group-chip]");
1321
+ const chipId = overChip?.getAttribute("data-gridkit-group-chip");
1322
+ const beforeColumnId = overChip && chipId ? resolveDropBefore(
1323
+ groupBy.map((entry) => entry.columnId),
1324
+ chipId,
1325
+ sideOf(overChip, clientX)
1326
+ ) : null;
1327
+ return movesGroupBy(groupBy, movedId, beforeColumnId) ? { kind: "group-by", beforeColumnId } : null;
1328
+ }
1329
+
1330
+ // src/DataGrid/useGroupByDrag.ts
1331
+ function useGroupByDrag({
1332
+ groupBy,
1333
+ onDrop
1334
+ }) {
1335
+ const drag = useDragReorder({
1336
+ resolveTarget: (clientX, clientY, movedId) => resolveGroupByDropTarget(clientX, clientY, movedId, groupBy),
1337
+ onDrop: (target, movedId) => {
1338
+ onDrop(movedId, target.beforeColumnId);
1339
+ }
1340
+ });
1341
+ return {
1342
+ draggedColumnId: drag.draggedId,
1343
+ dropTarget: drag.dropTarget,
1344
+ ghostTransform: drag.ghostTransform,
1345
+ startDrag: (columnId, event) => {
1346
+ drag.startDrag(columnId, event.currentTarget, event);
1347
+ },
1348
+ justDragged: drag.justDragged
1349
+ };
1350
+ }
1351
+
1352
+ // src/DataGrid/components/GroupByBar.tsx
1353
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1354
+ function GroupByBar({
1355
+ columns,
1356
+ grouping,
1357
+ columnName,
1358
+ headerDragEligible,
1359
+ headerDragBlocked,
1360
+ headerDropTarget
1361
+ }) {
1362
+ const { groupBy } = grouping;
1363
+ const groupByIds = groupBy.map((entry) => entry.columnId);
1364
+ const chipDrag = useGroupByDrag({
1365
+ groupBy,
1366
+ onDrop: (movedId, beforeColumnId) => {
1367
+ grouping.moveGroupBy(movedId, beforeColumnId);
1368
+ }
1369
+ });
1370
+ const activeDropTarget = chipDrag.dropTarget ?? headerDropTarget;
1371
+ const isDragTarget = chipDrag.draggedColumnId !== null || headerDragEligible;
1372
+ const showPlaceholder = groupBy.length === 0 && columns.some((entry) => entry.groupByDraggable);
1373
+ function labelFor(columnId) {
1374
+ return columns.find((entry) => entry.id === columnId)?.label ?? columnId;
1375
+ }
1376
+ function moveByKeyboard(columnId, direction) {
1377
+ const beforeColumnId = resolveKeyboardDropTarget(
1378
+ groupByIds,
1379
+ columnId,
1380
+ direction
1381
+ );
1382
+ if (beforeColumnId === void 0) {
1383
+ return;
1384
+ }
1385
+ grouping.moveGroupBy(columnId, beforeColumnId);
1386
+ }
1387
+ return /* @__PURE__ */ jsxs5(
1388
+ "div",
1389
+ {
1390
+ className: classNames(
1391
+ "gridkit-group-by-bar",
1392
+ isDragTarget ? "is-drag-target" : "",
1393
+ headerDragBlocked ? "is-drag-blocked" : ""
1394
+ ),
1395
+ "data-gridkit-group-by-bar": "",
1396
+ role: "group",
1397
+ "aria-label": "Grouped by",
1398
+ children: [
1399
+ showPlaceholder && /* @__PURE__ */ jsx8("span", { className: "group-by-bar-placeholder", children: "Drag a column here to group by it" }),
1400
+ groupBy.map((entry, index) => {
1401
+ const dropBefore = activeDropTarget?.beforeColumnId === entry.columnId;
1402
+ const dropAfter = activeDropTarget?.beforeColumnId === null && index === groupBy.length - 1;
1403
+ const dragging = chipDrag.draggedColumnId === entry.columnId;
1404
+ return /* @__PURE__ */ jsxs5(
1405
+ "span",
1406
+ {
1407
+ className: classNames(
1408
+ "group-by-chip",
1409
+ dragging ? "is-dragging" : "",
1410
+ dropBefore ? "is-drop-before" : "",
1411
+ dropAfter ? "is-drop-after" : ""
1412
+ ),
1413
+ "data-gridkit-group-chip": entry.columnId,
1414
+ tabIndex: 0,
1415
+ "aria-label": groupBy.length > 1 ? `${columnName(entry.columnId)}, level ${String(index + 1)} of ${String(groupBy.length)}` : columnName(entry.columnId),
1416
+ "aria-keyshortcuts": "Control+ArrowLeft Control+ArrowRight",
1417
+ onPointerDown: (event) => {
1418
+ chipDrag.startDrag(entry.columnId, event);
1419
+ },
1420
+ onKeyDown: (event) => {
1421
+ const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
1422
+ if (horizontal && event.ctrlKey) {
1423
+ event.preventDefault();
1424
+ moveByKeyboard(
1425
+ entry.columnId,
1426
+ event.key === "ArrowLeft" ? -1 : 1
1427
+ );
1428
+ }
1429
+ },
1430
+ children: [
1431
+ /* @__PURE__ */ jsx8("span", { className: "group-by-chip-label", children: labelFor(entry.columnId) }),
1432
+ /* @__PURE__ */ jsx8(
1433
+ "button",
1434
+ {
1435
+ type: "button",
1436
+ className: "group-by-chip-remove",
1437
+ "aria-label": `Stop grouping by ${columnName(entry.columnId)}`,
1438
+ onPointerDown: (event) => {
1439
+ event.stopPropagation();
1440
+ },
1441
+ onClick: () => {
1442
+ grouping.toggleGroupBy(entry.columnId);
1443
+ },
1444
+ children: "\xD7"
1445
+ }
1446
+ )
1447
+ ]
1448
+ },
1449
+ entry.columnId
1450
+ );
1451
+ }),
1452
+ chipDrag.draggedColumnId !== null && chipDrag.ghostTransform !== null && createPortal2(
1453
+ /* @__PURE__ */ jsx8(
1454
+ "span",
1455
+ {
1456
+ className: "group-by-chip drag-ghost",
1457
+ style: { transform: chipDrag.ghostTransform },
1458
+ "aria-hidden": "true",
1459
+ children: labelFor(chipDrag.draggedColumnId)
1460
+ }
1461
+ ),
1462
+ document.body
1463
+ )
1464
+ ]
1465
+ }
1466
+ );
1467
+ }
1468
+
1469
+ // src/DataGrid/useColumnDrag.ts
1470
+ import {
1471
+ movesColumn,
1472
+ resolveDropBefore as resolveDropBefore2,
1473
+ resolveKeyboardDropTarget as resolveKeyboardDropTarget2
1474
+ } from "@gridkitjs/core";
1475
+ function useColumnDrag({
1476
+ order,
1477
+ groupBy,
1478
+ groupByDraggableIds,
1479
+ onDrop
1480
+ }) {
1481
+ const drag = useDragReorder({
1482
+ resolveTarget(clientX, clientY, movedId) {
1483
+ const over = elementAt(clientX, clientY, "th[data-gridkit-column]");
1484
+ const overId = over?.getAttribute("data-gridkit-column");
1485
+ const beforeId = over && overId ? resolveDropBefore2(order, overId, sideOf(over, clientX)) : void 0;
1486
+ if (beforeId !== void 0) {
1487
+ return movesColumn(order, movedId, beforeId) ? { kind: "column-order", beforeId } : null;
1488
+ }
1489
+ const alreadyGrouped = groupBy.some(
1490
+ (entry) => entry.columnId === movedId
1491
+ );
1492
+ return groupByDraggableIds.has(movedId) && !alreadyGrouped ? resolveGroupByDropTarget(clientX, clientY, movedId, groupBy) : null;
1493
+ },
1494
+ onDrop
1495
+ });
1496
+ function startDrag(entry, event) {
1497
+ drag.startDrag(entry.id, event.currentTarget, event);
1498
+ }
809
1499
  function moveByKeyboard(entry, direction) {
810
- const beforeId = resolveKeyboardDropTarget(order, entry.id, direction);
1500
+ const beforeId = resolveKeyboardDropTarget2(order, entry.id, direction);
811
1501
  if (beforeId === void 0) {
812
1502
  return;
813
1503
  }
814
1504
  onDrop({ kind: "column-order", beforeId }, entry.id);
815
1505
  }
816
1506
  return {
817
- draggedColumnId,
818
- dropTarget,
819
- ghostTransform,
1507
+ draggedColumnId: drag.draggedId,
1508
+ dropTarget: drag.dropTarget,
1509
+ ghostTransform: drag.ghostTransform,
820
1510
  startDrag,
821
1511
  moveByKeyboard,
822
- justDragged
1512
+ justDragged: drag.justDragged
823
1513
  };
824
1514
  }
825
1515
 
@@ -994,8 +1684,110 @@ function useElementWidth(ref, enabled) {
994
1684
  return width;
995
1685
  }
996
1686
 
1687
+ // src/DataGrid/usePagination.ts
1688
+ import {
1689
+ paginateRows
1690
+ } from "@gridkitjs/core";
1691
+ function usePagination({
1692
+ pagination,
1693
+ setPagination,
1694
+ rows,
1695
+ onPaginationChange
1696
+ }) {
1697
+ const { pageCount, pageIndex } = paginateRows(rows, pagination);
1698
+ function commit(next) {
1699
+ commitIfChanged(pagination, next, setPagination, (committed) => {
1700
+ const { pageCount: committedPageCount } = paginateRows(rows, committed);
1701
+ onPaginationChange?.({
1702
+ pagination: committed,
1703
+ pageCount: committedPageCount
1704
+ });
1705
+ });
1706
+ }
1707
+ function goToPage(nextPageIndex) {
1708
+ const { pageIndex: clamped } = paginateRows(rows, {
1709
+ ...pagination,
1710
+ pageIndex: nextPageIndex
1711
+ });
1712
+ commit({ ...pagination, pageIndex: clamped });
1713
+ }
1714
+ return {
1715
+ pagination,
1716
+ pageCount,
1717
+ currentPage: Math.min(pagination.pageIndex + 1, pageCount),
1718
+ goToPage,
1719
+ nextPage: () => {
1720
+ goToPage(pageIndex + 1);
1721
+ },
1722
+ previousPage: () => {
1723
+ goToPage(pageIndex - 1);
1724
+ },
1725
+ setPageSize: (pageSize) => {
1726
+ const next = { pageIndex: 0, pageSize };
1727
+ const { pageIndex: clamped } = paginateRows(rows, next);
1728
+ commit({ ...next, pageIndex: clamped });
1729
+ }
1730
+ };
1731
+ }
1732
+
1733
+ // src/DataGrid/useRowGrouping.ts
1734
+ import {
1735
+ collapseAllGroups,
1736
+ expandAllGroups,
1737
+ moveGroupByBefore,
1738
+ toggleGroupExpansion
1739
+ } from "@gridkitjs/core";
1740
+ function useRowGrouping({
1741
+ groupBy,
1742
+ setGroupBy,
1743
+ expansion,
1744
+ setExpansion,
1745
+ onGroupByChange,
1746
+ onGroupExpansionChange
1747
+ }) {
1748
+ function commitExpansion(next, groupId) {
1749
+ commitIfChanged(expansion, next, setExpansion, (committed) => {
1750
+ onGroupExpansionChange?.({ groupId, expansion: committed });
1751
+ });
1752
+ }
1753
+ function commitGroupBy(next, columnId) {
1754
+ commitIfChanged(groupBy, next, setGroupBy, (committed) => {
1755
+ onGroupByChange?.({ columnId, groupBy: committed });
1756
+ });
1757
+ }
1758
+ function toggleGroupBy(columnId) {
1759
+ const isGrouped = groupBy.some((entry) => entry.columnId === columnId);
1760
+ const next = isGrouped ? groupBy.filter((entry) => entry.columnId !== columnId) : [...groupBy, { columnId }];
1761
+ commitGroupBy(next, columnId);
1762
+ }
1763
+ function moveGroupBy(columnId, beforeColumnId) {
1764
+ commitGroupBy(
1765
+ moveGroupByBefore(groupBy, columnId, beforeColumnId),
1766
+ columnId
1767
+ );
1768
+ }
1769
+ return {
1770
+ groupBy,
1771
+ expansion,
1772
+ isExpanded: (groupId) => !expansion.includes(groupId),
1773
+ toggleExpansion: (groupId) => {
1774
+ commitExpansion(toggleGroupExpansion(expansion, groupId), groupId);
1775
+ },
1776
+ expandAll: () => {
1777
+ commitExpansion(expandAllGroups(expansion), null);
1778
+ },
1779
+ collapseAll: (rows) => {
1780
+ commitExpansion(collapseAllGroups(rows), null);
1781
+ },
1782
+ toggleGroupBy,
1783
+ moveGroupBy
1784
+ };
1785
+ }
1786
+
997
1787
  // src/DataGrid/DataGrid.tsx
998
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1788
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1789
+ var DEFAULT_PAGE_SIZE = 25;
1790
+ var NO_AGGREGATES = [];
999
1791
  function DataGridComponent({
1000
1792
  dataSource,
1001
1793
  columns,
@@ -1017,7 +1809,21 @@ function DataGridComponent({
1017
1809
  sortableColumns = false,
1018
1810
  defaultColumnSort,
1019
1811
  onColumnSortChange,
1812
+ groupableColumns = false,
1813
+ groupToggleIconColumns = true,
1814
+ groupByDraggableColumns = false,
1815
+ groupByBarVisibility = "auto",
1816
+ defaultGroupBy,
1817
+ onGroupByChange,
1818
+ defaultGroupExpansion,
1819
+ onGroupExpansionChange,
1020
1820
  defaultFilter,
1821
+ paginated = false,
1822
+ defaultPagination,
1823
+ pager,
1824
+ onPaginationChange,
1825
+ aggregates,
1826
+ groupAggregateDisplay = "inline",
1021
1827
  label,
1022
1828
  labelledBy,
1023
1829
  ref,
@@ -1029,6 +1835,7 @@ function DataGridComponent({
1029
1835
  const viewportRef = useRef4(null);
1030
1836
  const tableRef = useRef4(null);
1031
1837
  const viewportWidth = useElementWidth(viewportRef, resizeMode === "fit");
1838
+ const subscribersRef = useRef4(/* @__PURE__ */ new Set());
1032
1839
  const [sizing, setSizing] = useState5(
1033
1840
  defaultColumnSizing ?? {}
1034
1841
  );
@@ -1036,7 +1843,14 @@ function DataGridComponent({
1036
1843
  defaultColumnOrder ?? []
1037
1844
  );
1038
1845
  const [sort, setSort] = useState5(defaultColumnSort ?? []);
1846
+ const [groupBy, setGroupBy] = useState5(defaultGroupBy ?? []);
1847
+ const [expansion, setExpansion] = useState5(
1848
+ defaultGroupExpansion ?? []
1849
+ );
1039
1850
  const [filter] = useState5(defaultFilter ?? []);
1851
+ const [pagination, setPagination] = useState5(
1852
+ defaultPagination ?? { pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE }
1853
+ );
1040
1854
  const [announcement, setAnnouncement] = useState5("");
1041
1855
  const [rowSelection, setRowSelection] = useState5(
1042
1856
  defaultRowSelection ?? []
@@ -1047,29 +1861,31 @@ function DataGridComponent({
1047
1861
  const [cellSelection, setCellSelection] = useState5(
1048
1862
  defaultCellSelection ?? null
1049
1863
  );
1050
- const rows = useMemo2(
1864
+ const rows = useMemo4(
1051
1865
  () => dataSource?.map((row, rowIndex) => ({
1052
1866
  rowId: resolveRowId(row, rowIndex, getRowId),
1053
1867
  row,
1054
- rowIndex
1868
+ rowIndex,
1869
+ datasetIndex: rowIndex
1055
1870
  })) ?? [],
1056
1871
  [dataSource, getRowId]
1057
1872
  );
1058
- const definedColumns = useMemo2(() => {
1873
+ const definedColumns = useMemo4(() => {
1059
1874
  if (columns && columns.length !== 0) return columns;
1060
1875
  if (dataSource && dataSource.length > 0)
1061
1876
  return defineColumnsFromRows(dataSource);
1062
1877
  return [];
1063
1878
  }, [columns, dataSource]);
1064
- const orderedColumns = useMemo2(
1879
+ const orderedColumns = useMemo4(
1065
1880
  () => applyColumnOrder(definedColumns, order),
1066
1881
  [definedColumns, order]
1067
1882
  );
1068
- const resolved = useMemo2(() => {
1883
+ const resolved = useMemo4(() => {
1069
1884
  const widths = resolveColumnWidths(orderedColumns, sizing, {
1070
1885
  sizes: columnSizeDefaults,
1071
1886
  resizable: resizableColumns,
1072
- reorderable: reorderableColumns
1887
+ reorderable: reorderableColumns,
1888
+ groupByDraggable: groupByDraggableColumns
1073
1889
  });
1074
1890
  return resizeMode === "fit" && viewportWidth !== null ? fitColumnsToWidth(widths, viewportWidth, columnSizeDefaults) : widths;
1075
1891
  }, [
@@ -1079,12 +1895,61 @@ function DataGridComponent({
1079
1895
  viewportWidth,
1080
1896
  columnSizeDefaults,
1081
1897
  resizableColumns,
1082
- reorderableColumns
1898
+ reorderableColumns,
1899
+ groupByDraggableColumns
1083
1900
  ]);
1084
- const shownRows = useMemo2(
1901
+ const shownRows = useMemo4(
1085
1902
  () => resolveShownRows(rows, filter, sort, resolved),
1086
1903
  [rows, filter, sort, resolved]
1087
1904
  );
1905
+ const displayRows = useMemo4(
1906
+ () => groupRows(shownRows, groupBy, expansion, resolved),
1907
+ [shownRows, groupBy, expansion, resolved]
1908
+ );
1909
+ const activeAggregates = aggregates ?? NO_AGGREGATES;
1910
+ const aggregatedRows = useMemo4(
1911
+ () => withGroupAggregates(
1912
+ displayRows,
1913
+ shownRows,
1914
+ groupBy,
1915
+ activeAggregates,
1916
+ resolved,
1917
+ groupAggregateDisplay
1918
+ ),
1919
+ [
1920
+ displayRows,
1921
+ shownRows,
1922
+ groupBy,
1923
+ activeAggregates,
1924
+ resolved,
1925
+ groupAggregateDisplay
1926
+ ]
1927
+ );
1928
+ const grandTotal = useMemo4(
1929
+ () => computeAggregates(
1930
+ shownRows.map((entry) => entry.row),
1931
+ activeAggregates,
1932
+ resolved
1933
+ ),
1934
+ [shownRows, activeAggregates, resolved]
1935
+ );
1936
+ const paginatedRows = useMemo4(
1937
+ () => paginated ? paginateRows2(aggregatedRows, pagination) : { rows: aggregatedRows, pageCount: 1, pageIndex: 0 },
1938
+ [paginated, aggregatedRows, pagination]
1939
+ );
1940
+ const paginationSnapshot = useMemo4(
1941
+ () => ({
1942
+ pageIndex: paginatedRows.pageIndex,
1943
+ pageSize: pagination.pageSize
1944
+ }),
1945
+ [paginatedRows.pageIndex, pagination.pageSize]
1946
+ );
1947
+ const displayDataRows = useMemo4(
1948
+ () => groupBy.length === 0 && !paginated ? shownRows : paginatedRows.rows.filter(
1949
+ (entry) => !("kind" in entry)
1950
+ ),
1951
+ [paginatedRows, groupBy.length, paginated, shownRows]
1952
+ );
1088
1953
  function columnName(columnId) {
1089
1954
  const entry = resolved.find((candidate) => candidate.id === columnId);
1090
1955
  if (entry === void 0) {
@@ -1110,16 +1975,73 @@ function DataGridComponent({
1110
1975
  columnSizeDefaults,
1111
1976
  onColumnResize: handleColumnResize
1112
1977
  });
1978
+ function handleGroupByChange(event) {
1979
+ onGroupByChange?.(event);
1980
+ const entry = event.groupBy.find(
1981
+ (candidate) => candidate.columnId === event.columnId
1982
+ );
1983
+ if (entry === void 0) {
1984
+ announce(`${columnName(event.columnId)}, grouping removed`);
1985
+ return;
1986
+ }
1987
+ announce(
1988
+ event.groupBy.length > 1 ? `Grouped by ${columnName(event.columnId)}, level ${String(event.groupBy.indexOf(entry) + 1)} of ${String(event.groupBy.length)}` : `Grouped by ${columnName(event.columnId)}`
1989
+ );
1990
+ }
1991
+ function handleGroupExpansionChange(event) {
1992
+ onGroupExpansionChange?.(event);
1993
+ if (event.groupId === null) {
1994
+ announce(
1995
+ event.expansion.length === 0 ? "All groups expanded" : "All groups collapsed"
1996
+ );
1997
+ return;
1998
+ }
1999
+ announce(
2000
+ event.expansion.includes(event.groupId) ? "Group collapsed" : "Group expanded"
2001
+ );
2002
+ }
2003
+ const grouping = useRowGrouping({
2004
+ groupBy,
2005
+ setGroupBy,
2006
+ expansion,
2007
+ setExpansion,
2008
+ onGroupByChange: handleGroupByChange,
2009
+ onGroupExpansionChange: handleGroupExpansionChange
2010
+ });
1113
2011
  const nav = useGridNavigation({
1114
2012
  tableRef,
1115
- rowCount: shownRows.length,
1116
- columnCount: resolved.length
2013
+ // Group headers are addressable rows too — the whole point of the flat
2014
+ // `DisplayRow[]` shape is that they share one position space with data
2015
+ // rows, so navigation counts them the same way. Page-relative
2016
+ // (`paginatedRows`, not `displayRows`): arrow keys operate on what's
2017
+ // actually in the DOM, which is the current page — a different "page"
2018
+ // concept than `useGridNavigation`'s own Page Up/Down viewport scrolling.
2019
+ rowCount: paginatedRows.rows.length,
2020
+ columnCount: resolved.length,
2021
+ // A group's own summary row (`groupAggregateDisplay: "row"`) occupies a
2022
+ // real slot in `rowCount` above — its DOM position has to line up with
2023
+ // everything else — but is presentational, never a tab stop: arrow-key
2024
+ // vertical movement steps over it rather than landing there.
2025
+ isSkippableRow: (rowIndex) => {
2026
+ const entry = paginatedRows.rows[rowIndex];
2027
+ return entry !== void 0 && "kind" in entry && entry.kind === "group-summary";
2028
+ }
1117
2029
  });
1118
- const displayedIds = useMemo2(
2030
+ const displayedIds = useMemo4(
1119
2031
  () => orderedColumns.map((column) => column.id ?? column.field),
1120
2032
  [orderedColumns]
1121
2033
  );
2034
+ const groupByDraggableIds = useMemo4(
2035
+ () => new Set(
2036
+ resolved.filter((entry) => entry.groupByDraggable).map((entry) => entry.id)
2037
+ ),
2038
+ [resolved]
2039
+ );
1122
2040
  function handleDrop(target, movedId) {
2041
+ if (target.kind === "group-by") {
2042
+ grouping.moveGroupBy(movedId, target.beforeColumnId);
2043
+ return;
2044
+ }
1123
2045
  const next = moveColumnBefore(displayedIds, movedId, target.beforeId);
1124
2046
  if (next === displayedIds) {
1125
2047
  return;
@@ -1132,7 +2054,16 @@ function DataGridComponent({
1132
2054
  );
1133
2055
  nav.focusCell(HEADER_ROW, position);
1134
2056
  }
1135
- const drag = useColumnDrag({ order: displayedIds, onDrop: handleDrop });
2057
+ const drag = useColumnDrag({
2058
+ order: displayedIds,
2059
+ groupBy,
2060
+ groupByDraggableIds,
2061
+ onDrop: handleDrop
2062
+ });
2063
+ const draggedColumnAlreadyGrouped = drag.draggedColumnId !== null && groupBy.some((entry) => entry.columnId === drag.draggedColumnId);
2064
+ const dragEligibleForGroupBy = drag.draggedColumnId !== null && groupByDraggableIds.has(drag.draggedColumnId) && !draggedColumnAlreadyGrouped;
2065
+ const showGroupByBar = groupByBarVisibility === "always" || groupByBarVisibility === "auto" && (groupBy.length > 0 || dragEligibleForGroupBy);
2066
+ const headerGroupByDropTarget = drag.dropTarget?.kind === "group-by" ? drag.dropTarget : null;
1136
2067
  function handleColumnSortChange(event) {
1137
2068
  onColumnSortChange?.(event);
1138
2069
  const entry = event.sort.find(
@@ -1152,8 +2083,27 @@ function DataGridComponent({
1152
2083
  setSort,
1153
2084
  onColumnSortChange: handleColumnSortChange
1154
2085
  });
2086
+ function handlePaginationChange(event) {
2087
+ onPaginationChange?.(event);
2088
+ announce(
2089
+ `Page ${String(event.pagination.pageIndex + 1)} of ${String(event.pageCount)}`
2090
+ );
2091
+ }
2092
+ const paginationApi = usePagination({
2093
+ pagination,
2094
+ setPagination,
2095
+ rows: displayRows,
2096
+ onPaginationChange: handlePaginationChange
2097
+ });
2098
+ const [previous, setPrevious] = useState5({ filter, sort, groupBy });
2099
+ if (previous.filter !== filter || previous.sort !== sort || previous.groupBy !== groupBy) {
2100
+ setPrevious({ filter, sort, groupBy });
2101
+ if (pagination.pageIndex !== 0) {
2102
+ setPagination({ ...pagination, pageIndex: 0 });
2103
+ }
2104
+ }
1155
2105
  const selection = useGridSelection({
1156
- rows: shownRows,
2106
+ rows: displayDataRows,
1157
2107
  columns: resolved,
1158
2108
  selectable,
1159
2109
  rowSelection,
@@ -1167,6 +2117,27 @@ function DataGridComponent({
1167
2117
  columnName
1168
2118
  });
1169
2119
  const multiselectable = selection.rowMode === "multiple" || selection.columnMode === "multiple";
2120
+ useEffect2(() => {
2121
+ for (const listener of subscribersRef.current) {
2122
+ listener();
2123
+ }
2124
+ }, [
2125
+ shownRows,
2126
+ aggregatedRows,
2127
+ resolved,
2128
+ sizing,
2129
+ order,
2130
+ sort,
2131
+ groupBy,
2132
+ expansion,
2133
+ paginationSnapshot,
2134
+ paginatedRows.pageCount,
2135
+ grandTotal,
2136
+ rowSelection,
2137
+ columnSelection,
2138
+ cellSelection,
2139
+ nav.focus
2140
+ ]);
1170
2141
  useImperativeHandle(ref, () => ({
1171
2142
  get element() {
1172
2143
  return viewportRef.current;
@@ -1175,10 +2146,20 @@ function DataGridComponent({
1175
2146
  return tableRef.current;
1176
2147
  },
1177
2148
  getRows: () => shownRows,
2149
+ // `aggregatedRows`, not the plain `displayRows` it's derived from: the
2150
+ // same array shape and content, except every group header's own
2151
+ // `aggregates` field is populated — the whole reason `getDisplayRows()`
2152
+ // is where a consumer reads a specific group's computed results from.
2153
+ getDisplayRows: () => aggregatedRows,
1178
2154
  getColumns: () => resolved,
1179
2155
  getColumnSizing: () => sizing,
1180
2156
  getColumnOrder: () => order,
1181
2157
  getColumnSort: () => sort,
2158
+ getGroupBy: () => groupBy,
2159
+ getGroupExpansion: () => expansion,
2160
+ getPagination: () => paginationSnapshot,
2161
+ getPageCount: () => paginatedRows.pageCount,
2162
+ getAggregates: () => grandTotal,
1182
2163
  getRowSelection: () => rowSelection,
1183
2164
  getColumnSelection: () => columnSelection,
1184
2165
  getCellSelection: () => cellSelection,
@@ -1186,8 +2167,18 @@ function DataGridComponent({
1186
2167
  focusCell: nav.focusCell,
1187
2168
  clearSelection: selection.clear,
1188
2169
  selectAllRows: selection.selectAllRows,
2170
+ expandAllGroups: grouping.expandAll,
2171
+ collapseAllGroups: () => {
2172
+ grouping.collapseAll(displayRows);
2173
+ },
2174
+ goToPage: paginationApi.goToPage,
2175
+ nextPage: paginationApi.nextPage,
2176
+ previousPage: paginationApi.previousPage,
2177
+ setPageSize: paginationApi.setPageSize,
1189
2178
  scrollToRow: (rowId, options) => {
1190
- const index = shownRows.findIndex((entry) => entry.rowId === rowId);
2179
+ const index = paginatedRows.rows.findIndex(
2180
+ (entry) => !("kind" in entry) && entry.rowId === rowId
2181
+ );
1191
2182
  if (index === -1) return;
1192
2183
  tableRef.current?.tBodies[0]?.rows[index]?.scrollIntoView(options);
1193
2184
  },
@@ -1197,15 +2188,32 @@ function DataGridComponent({
1197
2188
  (entry) => entry.getAttribute("data-gridkit-column") === columnId
1198
2189
  ) : void 0;
1199
2190
  cell?.scrollIntoView(options);
2191
+ },
2192
+ subscribe: (listener) => {
2193
+ subscribersRef.current.add(listener);
2194
+ return () => {
2195
+ subscribersRef.current.delete(listener);
2196
+ };
1200
2197
  }
1201
2198
  }));
1202
- return /* @__PURE__ */ jsxs2("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
1203
- /* @__PURE__ */ jsxs2(
2199
+ return /* @__PURE__ */ jsxs6("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
2200
+ showGroupByBar && /* @__PURE__ */ jsx9(
2201
+ GroupByBar,
2202
+ {
2203
+ columns: resolved,
2204
+ grouping,
2205
+ columnName,
2206
+ headerDragEligible: dragEligibleForGroupBy,
2207
+ headerDragBlocked: draggedColumnAlreadyGrouped,
2208
+ headerDropTarget: headerGroupByDropTarget
2209
+ }
2210
+ ),
2211
+ /* @__PURE__ */ jsxs6(
1204
2212
  "table",
1205
2213
  {
1206
2214
  ref: tableRef,
1207
- role: "grid",
1208
- "aria-rowcount": shownRows.length + 1,
2215
+ role: groupBy.length > 0 ? "treegrid" : "grid",
2216
+ "aria-rowcount": aggregatedRows.length + 1,
1209
2217
  "aria-colcount": resolved.length,
1210
2218
  ...ariaAttr(multiselectable, "aria-multiselectable", true),
1211
2219
  ...ariaAttr(labelledBy !== void 0, "aria-labelledby", labelledBy),
@@ -1242,8 +2250,8 @@ function DataGridComponent({
1242
2250
  selection.cellMode === false ? "" : "selectable-cells"
1243
2251
  ),
1244
2252
  children: [
1245
- /* @__PURE__ */ jsx4("colgroup", { children: resolved.map((entry) => /* @__PURE__ */ jsx4("col", { style: { width: entry.width } }, entry.id)) }),
1246
- /* @__PURE__ */ jsx4(
2253
+ /* @__PURE__ */ jsx9("colgroup", { children: resolved.map((entry) => /* @__PURE__ */ jsx9("col", { style: { width: entry.width } }, entry.id)) }),
2254
+ /* @__PURE__ */ jsx9(
1247
2255
  GridHeader,
1248
2256
  {
1249
2257
  columns: resolved,
@@ -1251,27 +2259,217 @@ function DataGridComponent({
1251
2259
  drag,
1252
2260
  sort: columnSort,
1253
2261
  sortableColumns,
2262
+ grouping,
2263
+ groupableColumns,
2264
+ groupToggleIconColumns,
1254
2265
  nav,
1255
2266
  selection
1256
2267
  }
1257
2268
  ),
1258
- /* @__PURE__ */ jsx4(
2269
+ /* @__PURE__ */ jsx9(
1259
2270
  GridBody,
1260
2271
  {
1261
2272
  columns: resolved,
1262
- rows: shownRows,
2273
+ rows: paginatedRows.rows,
1263
2274
  activeColumnId: resize.activeColumnId,
1264
2275
  nav,
1265
- selection
2276
+ selection,
2277
+ grouping,
2278
+ aggregates: activeAggregates,
2279
+ groupAggregateDisplay
2280
+ }
2281
+ ),
2282
+ activeAggregates.length > 0 && /* @__PURE__ */ jsx9(
2283
+ GridFooter,
2284
+ {
2285
+ columns: resolved,
2286
+ aggregates: activeAggregates,
2287
+ results: grandTotal,
2288
+ rows: shownRows.map((entry) => entry.row)
1266
2289
  }
1267
2290
  )
1268
2291
  ]
1269
2292
  }
1270
2293
  ),
1271
- /* @__PURE__ */ jsx4("div", { className: "gridkit-sr-only", role: "status", "aria-live": "polite", children: announcement })
2294
+ paginated && (pager?.template ? pager.template({
2295
+ pagination: paginationApi.pagination,
2296
+ currentPage: paginationApi.currentPage,
2297
+ pageCount: paginationApi.pageCount,
2298
+ pageSizeOptions: pager.sizeOptions,
2299
+ goToPage: paginationApi.goToPage,
2300
+ nextPage: paginationApi.nextPage,
2301
+ previousPage: paginationApi.previousPage,
2302
+ setPageSize: paginationApi.setPageSize
2303
+ }) : /* @__PURE__ */ jsx9(GridPager, { pager: paginationApi, config: pager })),
2304
+ /* @__PURE__ */ jsx9("div", { className: "gridkit-sr-only", role: "status", "aria-live": "polite", children: announcement })
1272
2305
  ] });
1273
2306
  }
2307
+
2308
+ // src/DataGrid/hooks/usePaginationState.ts
2309
+ import { useCallback as useCallback2 } from "react";
2310
+
2311
+ // src/DataGrid/hooks/useGridSnapshot.ts
2312
+ import { useCallback, useSyncExternalStore } from "react";
2313
+ function useGridSnapshot(gridRef, getSnapshot, fallback) {
2314
+ const subscribe = useCallback(
2315
+ (listener) => {
2316
+ const api = gridRef.current;
2317
+ if (api === null) {
2318
+ return () => {
2319
+ };
2320
+ }
2321
+ return api.subscribe(listener);
2322
+ },
2323
+ [gridRef]
2324
+ );
2325
+ return useSyncExternalStore(subscribe, () => {
2326
+ const api = gridRef.current;
2327
+ return api === null ? fallback : getSnapshot(api);
2328
+ });
2329
+ }
2330
+
2331
+ // src/DataGrid/hooks/usePaginationState.ts
2332
+ var FALLBACK_PAGINATION = { pageIndex: 0, pageSize: 0 };
2333
+ function usePaginationState(gridRef) {
2334
+ const pagination = useGridSnapshot(
2335
+ gridRef,
2336
+ useCallback2((api) => api.getPagination(), []),
2337
+ FALLBACK_PAGINATION
2338
+ );
2339
+ const pageCount = useGridSnapshot(
2340
+ gridRef,
2341
+ useCallback2((api) => api.getPageCount(), []),
2342
+ 0
2343
+ );
2344
+ return {
2345
+ pagination,
2346
+ pageCount,
2347
+ goToPage: (pageIndex) => {
2348
+ gridRef.current?.goToPage(pageIndex);
2349
+ },
2350
+ nextPage: () => {
2351
+ gridRef.current?.nextPage();
2352
+ },
2353
+ previousPage: () => {
2354
+ gridRef.current?.previousPage();
2355
+ },
2356
+ setPageSize: (pageSize) => {
2357
+ gridRef.current?.setPageSize(pageSize);
2358
+ }
2359
+ };
2360
+ }
2361
+
2362
+ // src/DataGrid/hooks/useGroupByState.ts
2363
+ import { useCallback as useCallback3 } from "react";
2364
+ var FALLBACK_GROUP_BY = [];
2365
+ var FALLBACK_EXPANSION = [];
2366
+ function useGroupByState(gridRef) {
2367
+ const groupBy = useGridSnapshot(
2368
+ gridRef,
2369
+ useCallback3((api) => api.getGroupBy(), []),
2370
+ FALLBACK_GROUP_BY
2371
+ );
2372
+ const groupExpansion = useGridSnapshot(
2373
+ gridRef,
2374
+ useCallback3((api) => api.getGroupExpansion(), []),
2375
+ FALLBACK_EXPANSION
2376
+ );
2377
+ return {
2378
+ groupBy,
2379
+ groupExpansion,
2380
+ expandAllGroups: () => {
2381
+ gridRef.current?.expandAllGroups();
2382
+ },
2383
+ collapseAllGroups: () => {
2384
+ gridRef.current?.collapseAllGroups();
2385
+ }
2386
+ };
2387
+ }
2388
+
2389
+ // src/DataGrid/hooks/useColumnSortState.ts
2390
+ import { useCallback as useCallback4 } from "react";
2391
+ var FALLBACK_SORT = [];
2392
+ function useColumnSortState(gridRef) {
2393
+ return useGridSnapshot(
2394
+ gridRef,
2395
+ useCallback4((api) => api.getColumnSort(), []),
2396
+ FALLBACK_SORT
2397
+ );
2398
+ }
2399
+
2400
+ // src/DataGrid/hooks/useSelectionState.ts
2401
+ import { useCallback as useCallback5 } from "react";
2402
+ var FALLBACK_SELECTION = [];
2403
+ var FALLBACK_CELL_SELECTION = null;
2404
+ function useSelectionState(gridRef) {
2405
+ const rowSelection = useGridSnapshot(
2406
+ gridRef,
2407
+ useCallback5((api) => api.getRowSelection(), []),
2408
+ FALLBACK_SELECTION
2409
+ );
2410
+ const columnSelection = useGridSnapshot(
2411
+ gridRef,
2412
+ useCallback5((api) => api.getColumnSelection(), []),
2413
+ FALLBACK_SELECTION
2414
+ );
2415
+ const cellSelection = useGridSnapshot(
2416
+ gridRef,
2417
+ useCallback5((api) => api.getCellSelection(), []),
2418
+ FALLBACK_CELL_SELECTION
2419
+ );
2420
+ return {
2421
+ rowSelection,
2422
+ columnSelection,
2423
+ cellSelection,
2424
+ clearSelection: () => {
2425
+ gridRef.current?.clearSelection();
2426
+ },
2427
+ selectAllRows: () => {
2428
+ gridRef.current?.selectAllRows();
2429
+ }
2430
+ };
2431
+ }
2432
+
2433
+ // src/DataGrid/hooks/useAggregateState.ts
2434
+ import { useCallback as useCallback6 } from "react";
2435
+ var FALLBACK_AGGREGATES = /* @__PURE__ */ new Map();
2436
+ function useAggregateState(gridRef) {
2437
+ return useGridSnapshot(
2438
+ gridRef,
2439
+ useCallback6((api) => api.getAggregates(), []),
2440
+ FALLBACK_AGGREGATES
2441
+ );
2442
+ }
2443
+
2444
+ // src/DataGrid/hooks/useColumnSizingState.ts
2445
+ import { useCallback as useCallback7 } from "react";
2446
+ var FALLBACK_SIZING = {};
2447
+ function useColumnSizingState(gridRef) {
2448
+ return useGridSnapshot(
2449
+ gridRef,
2450
+ useCallback7((api) => api.getColumnSizing(), []),
2451
+ FALLBACK_SIZING
2452
+ );
2453
+ }
2454
+
2455
+ // src/DataGrid/hooks/useColumnOrderState.ts
2456
+ import { useCallback as useCallback8 } from "react";
2457
+ var FALLBACK_ORDER = [];
2458
+ function useColumnOrderState(gridRef) {
2459
+ return useGridSnapshot(
2460
+ gridRef,
2461
+ useCallback8((api) => api.getColumnOrder(), []),
2462
+ FALLBACK_ORDER
2463
+ );
2464
+ }
1274
2465
  export {
1275
- DataGridComponent
2466
+ DataGridComponent,
2467
+ useAggregateState,
2468
+ useColumnOrderState,
2469
+ useColumnSizingState,
2470
+ useColumnSortState,
2471
+ useGroupByState,
2472
+ usePaginationState,
2473
+ useSelectionState
1276
2474
  };
1277
2475
  //# sourceMappingURL=index.js.map