@gridkitjs/react 0.11.0 → 0.12.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
@@ -2,16 +2,16 @@
2
2
 
3
3
  // src/DataGrid/DataGrid.tsx
4
4
  import {
5
- useEffect as useEffect5,
6
- useId,
7
- useImperativeHandle,
8
- useMemo as useMemo4,
9
- useRef as useRef13,
10
- useState as useState15
5
+ useEffect as useEffect7,
6
+ useId as useId2,
7
+ useImperativeHandle as useImperativeHandle3,
8
+ useMemo as useMemo5,
9
+ useRef as useRef21,
10
+ useState as useState20
11
11
  } from "react";
12
12
  import {
13
13
  moveColumnBefore,
14
- resolveColumnHeaderText,
14
+ resolveColumnHeaderText as resolveColumnHeaderText2,
15
15
  resolveMessages,
16
16
  resolveRows,
17
17
  totalColumnWidth
@@ -28,7 +28,9 @@ import { createPortal } from "react-dom";
28
28
  import {
29
29
  buildKeyShortcuts,
30
30
  intentOf,
31
+ isColumnFiltered,
31
32
  KEYBOARD_STEP,
33
+ resolveColumnFilterMenu,
32
34
  sortDirectionFor,
33
35
  sortPriorityFor
34
36
  } from "@gridkitjs/core";
@@ -76,6 +78,9 @@ function GroupIcon() {
76
78
  /* @__PURE__ */ jsx2("rect", { x: "8.5", y: "14", width: "7", height: "7", rx: "1" })
77
79
  ] });
78
80
  }
81
+ function FilterIcon() {
82
+ return /* @__PURE__ */ jsx2(Icon, { children: /* @__PURE__ */ jsx2("path", { d: "M22 3H2l8 9.46V19l4 2v-8.54z" }) });
83
+ }
79
84
 
80
85
  // src/DataGrid/isOwnEvent.ts
81
86
  function isOwnEvent(event) {
@@ -195,17 +200,27 @@ function GridHeader({
195
200
  resize,
196
201
  drag,
197
202
  controller,
198
- columnSort,
203
+ state,
199
204
  sortableColumns,
200
- groupBy,
201
205
  groupableColumns,
202
206
  groupToggleIconColumns,
203
207
  headerMenu,
208
+ panels,
209
+ messages,
204
210
  nav,
205
211
  selection
206
212
  }) {
213
+ const { columnSort, groupBy, filter } = state;
207
214
  const beforeId = drag.dropTarget?.kind === "column-order" ? drag.dropTarget.beforeId : void 0;
208
215
  const draggedEntry = columns.find((entry) => entry.id === drag.draggedColumnId) ?? null;
216
+ function toggleFilterMenu(columnId) {
217
+ const { panel } = panels;
218
+ if (panel?.kind === "filter" && panel.target.area === "header" && panel.target.columnId === columnId) {
219
+ panels.close();
220
+ } else {
221
+ panels.open("filter", { area: "header", columnId });
222
+ }
223
+ }
209
224
  return /* @__PURE__ */ jsxs2("thead", { children: [
210
225
  /* @__PURE__ */ jsx3("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
211
226
  const { column } = entry;
@@ -222,11 +237,14 @@ function GridHeader({
222
237
  const isGrouped = groupBy.some(
223
238
  (level) => level.columnId === entry.id
224
239
  );
240
+ const filterable = resolveColumnFilterMenu(controller.getOptions(), column) !== null;
241
+ const filtered = filterable && isColumnFiltered(filter, entry.id);
225
242
  const shortcuts = buildKeyShortcuts({
226
243
  reorderable: entry.reorderable,
227
244
  resizable: entry.resizable,
228
245
  sortable,
229
246
  groupable,
247
+ filterable,
230
248
  contextMenu: headerMenu
231
249
  });
232
250
  return /* @__PURE__ */ jsxs2(
@@ -249,7 +267,7 @@ function GridHeader({
249
267
  if (drag.justDragged()) {
250
268
  return;
251
269
  }
252
- 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)) {
270
+ 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 || event.target.closest(".header-filter-toggle") !== null)) {
253
271
  return;
254
272
  }
255
273
  selection.selectColumn(entry.id, intentOf(event));
@@ -280,7 +298,12 @@ function GridHeader({
280
298
  });
281
299
  return;
282
300
  }
283
- if (event.key === "ArrowDown" && event.altKey && groupable) {
301
+ if (event.key === "ArrowDown" && event.altKey && event.shiftKey && filterable) {
302
+ event.preventDefault();
303
+ toggleFilterMenu(entry.id);
304
+ return;
305
+ }
306
+ if (event.key === "ArrowDown" && event.altKey && !event.shiftKey && groupable) {
284
307
  event.preventDefault();
285
308
  controller.toggleGroupBy(entry.id);
286
309
  return;
@@ -311,6 +334,11 @@ function GridHeader({
311
334
  column.headerClassName ?? ""
312
335
  ),
313
336
  ...ariaAttr(shortcuts !== "", "aria-keyshortcuts", shortcuts),
337
+ ...ariaAttr(
338
+ filtered,
339
+ "aria-description",
340
+ messages.columnFiltered
341
+ ),
314
342
  ...ariaAttr(
315
343
  sortable && sortDirection !== null,
316
344
  "aria-sort",
@@ -380,6 +408,30 @@ function GridHeader({
380
408
  ]
381
409
  }
382
410
  ),
411
+ filterable && /*
412
+ * A pointer affordance only, like the sort toggle: the
413
+ * keyboard opens the menu from the header itself, under
414
+ * Alt+Shift+ArrowDown, and a screen reader hears the
415
+ * filtered state as the header's description.
416
+ */
417
+ /* @__PURE__ */ jsx3(
418
+ "span",
419
+ {
420
+ className: classNames(
421
+ "header-filter-toggle",
422
+ filtered ? "is-filtered" : ""
423
+ ),
424
+ "aria-hidden": "true",
425
+ tabIndex: -1,
426
+ onPointerDown: (event) => {
427
+ event.stopPropagation();
428
+ },
429
+ onClick: () => {
430
+ toggleFilterMenu(entry.id);
431
+ },
432
+ children: /* @__PURE__ */ jsx3(FilterIcon, {})
433
+ }
434
+ ),
383
435
  entry.resizable && /*
384
436
  * A pointer affordance only, and hidden from assistive
385
437
  * technology accordingly: a focusable handle per column would
@@ -472,7 +524,7 @@ function createRowPropsResolver() {
472
524
  // src/DataGrid/components/GridGroupRow.tsx
473
525
  import { memo } from "react";
474
526
  import {
475
- formatCellValue,
527
+ formatGroupValue,
476
528
  resolveColumnLabel
477
529
  } from "@gridkitjs/core";
478
530
 
@@ -492,9 +544,6 @@ function renderAggregate(value, spec, column, rows, locale) {
492
544
 
493
545
  // src/DataGrid/components/GridGroupRow.tsx
494
546
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
495
- function formatGroupValue(value, column, locale, messages) {
496
- return value === null || value === void 0 ? messages.blankGroupValue : formatCellValue(value, column, locale, void 0, messages);
497
- }
498
547
  function GridGroupRowComponent({
499
548
  columnCount,
500
549
  groupId,
@@ -671,7 +720,7 @@ var GridGroupSummaryRow_default = GridGroupSummaryRow;
671
720
  // src/DataGrid/components/GridRow.tsx
672
721
  import {
673
722
  accessDotted,
674
- formatCellValue as formatCellValue2
723
+ formatCellValue
675
724
  } from "@gridkitjs/core";
676
725
  import { memo as memo3 } from "react";
677
726
  import { jsx as jsx6 } from "react/jsx-runtime";
@@ -741,7 +790,7 @@ function GridRowComponent({
741
790
  datasetIndex,
742
791
  rowId,
743
792
  selected
744
- }) : formatCellValue2(value, column, locale, row, cellMessages)
793
+ }) : formatCellValue(value, column, locale, row, cellMessages)
745
794
  },
746
795
  id
747
796
  );
@@ -1498,13 +1547,155 @@ function announcedCallbacks({
1498
1547
  };
1499
1548
  }
1500
1549
 
1550
+ // src/DataGrid/clipboard/useGridCopy.ts
1551
+ import { runCommand } from "@gridkitjs/core";
1552
+ import { isNativeCopy } from "@gridkitjs/dom";
1553
+ function isCopyKey(event) {
1554
+ return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === "c" || event.key === "C");
1555
+ }
1556
+ function useGridCopy({
1557
+ controller,
1558
+ commands,
1559
+ nav
1560
+ }) {
1561
+ function run(commandId, target) {
1562
+ const command = commands.commands.get(commandId);
1563
+ return command !== void 0 && runCommand(command, commands.contextFor(target));
1564
+ }
1565
+ function focusedCell() {
1566
+ const { rowIndex, columnIndex } = nav.focus;
1567
+ if (rowIndex === HEADER_ROW) {
1568
+ return null;
1569
+ }
1570
+ const { pageRows, columns } = controller.getView();
1571
+ const entry = pageRows[rowIndex];
1572
+ const column = columns[columnIndex];
1573
+ return entry === void 0 || "kind" in entry || column === void 0 ? null : { area: "cell", rowId: entry.rowId, columnId: column.id };
1574
+ }
1575
+ return (event) => {
1576
+ if (!isCopyKey(event) || isNativeCopy(event.target)) {
1577
+ return;
1578
+ }
1579
+ const cell = focusedCell();
1580
+ if (run("copy.selection", { area: "grid" }) || cell !== null && run("copy.cell", cell)) {
1581
+ event.preventDefault();
1582
+ }
1583
+ };
1584
+ }
1585
+
1501
1586
  // src/DataGrid/commands/useGridCommands.ts
1502
1587
  import { useMemo as useMemo3 } from "react";
1503
1588
  import {
1504
1589
  createCommandContext,
1505
1590
  resolveCommands,
1506
- runCommand
1591
+ runCommand as runCommand2
1507
1592
  } from "@gridkitjs/core";
1593
+
1594
+ // src/DataGrid/clipboard/copyToClipboard.ts
1595
+ import { writeClipboard } from "@gridkitjs/dom";
1596
+ function copyToClipboard(content, { messages, announce }) {
1597
+ writeClipboard(content).then(
1598
+ () => {
1599
+ announce(messages.copied(content.count, content.unit));
1600
+ },
1601
+ (error) => {
1602
+ console.error("DataGrid: the clipboard could not be written.", error);
1603
+ announce(messages.copyFailed);
1604
+ }
1605
+ );
1606
+ }
1607
+
1608
+ // src/DataGrid/export/useSaveFile.ts
1609
+ import { useRef as useRef2 } from "react";
1610
+ import { downloadBlob } from "@gridkitjs/dom";
1611
+
1612
+ // src/DataGrid/export/exportApi.ts
1613
+ import {
1614
+ exportFile,
1615
+ resolveExportWriters
1616
+ } from "@gridkitjs/core";
1617
+ function exportBlob({ content, mimeType }) {
1618
+ const part = typeof content === "string" ? content : content.slice();
1619
+ return new Blob([part], { type: mimeType });
1620
+ }
1621
+ function exportApi({
1622
+ contextFor
1623
+ }) {
1624
+ return {
1625
+ exportData: async (format, options) => {
1626
+ const context = contextFor({ area: "grid" });
1627
+ const { export: defaults, exportWriters } = context.options;
1628
+ const file = exportFile(
1629
+ context,
1630
+ format,
1631
+ { ...defaults, ...options },
1632
+ resolveExportWriters(exportWriters)
1633
+ );
1634
+ if (file === null) {
1635
+ throw new Error(
1636
+ "DataGrid: there is no export writer for that format \u2014 register one through `exportWriters`."
1637
+ );
1638
+ }
1639
+ return exportBlob(await file);
1640
+ }
1641
+ };
1642
+ }
1643
+
1644
+ // src/DataGrid/export/useSaveFile.ts
1645
+ function download(file) {
1646
+ downloadBlob(exportBlob(file), file.fileName);
1647
+ }
1648
+ function useSaveFile({
1649
+ messages,
1650
+ announce
1651
+ }) {
1652
+ const writingRef = useRef2(false);
1653
+ return (file) => {
1654
+ if (writingRef.current) {
1655
+ console.error(
1656
+ "DataGrid: an export is still being written, so this one was dropped."
1657
+ );
1658
+ return;
1659
+ }
1660
+ if (!(file instanceof Promise)) {
1661
+ download(file);
1662
+ return;
1663
+ }
1664
+ writingRef.current = true;
1665
+ file.then(download, (error) => {
1666
+ console.error("DataGrid: the export's writer failed.", error);
1667
+ announce(messages.exportFailed);
1668
+ }).finally(() => {
1669
+ writingRef.current = false;
1670
+ });
1671
+ };
1672
+ }
1673
+
1674
+ // src/DataGrid/print/printApi.ts
1675
+ import {
1676
+ exportPrintDocument
1677
+ } from "@gridkitjs/core";
1678
+ import { printDocument, printStyles } from "@gridkitjs/dom";
1679
+ function printGrid(document2, { styles = "" }) {
1680
+ return printDocument({
1681
+ title: document2.title,
1682
+ bodyHtml: document2.bodyHtml,
1683
+ styles: printStyles(document2.page) + styles
1684
+ });
1685
+ }
1686
+ function printApi({
1687
+ contextFor
1688
+ }) {
1689
+ return {
1690
+ print: (options) => {
1691
+ const context = contextFor({ area: "grid" });
1692
+ const merged = { ...context.options.print, ...options };
1693
+ return printGrid(exportPrintDocument(context, merged), merged);
1694
+ }
1695
+ };
1696
+ }
1697
+
1698
+ // src/DataGrid/commands/useGridCommands.ts
1508
1699
  function useGridCommands({
1509
1700
  controller,
1510
1701
  commands: appCommands,
@@ -1515,6 +1706,7 @@ function useGridCommands({
1515
1706
  openPanel
1516
1707
  }) {
1517
1708
  const commands = useMemo3(() => resolveCommands(appCommands), [appCommands]);
1709
+ const saveFile = useSaveFile({ messages, announce });
1518
1710
  const host = {
1519
1711
  autoFitColumns: (columnIds) => {
1520
1712
  const { columns } = controller.getView();
@@ -1530,7 +1722,16 @@ function useGridCommands({
1530
1722
  }
1531
1723
  },
1532
1724
  openPanel,
1533
- announce
1725
+ announce,
1726
+ saveFile,
1727
+ writeClipboard: (content) => {
1728
+ copyToClipboard(content, { messages, announce });
1729
+ },
1730
+ print: (document2, options) => {
1731
+ printGrid(document2, options).catch((error) => {
1732
+ console.error("DataGrid: the print failed.", error);
1733
+ });
1734
+ }
1534
1735
  };
1535
1736
  function contextFor(target) {
1536
1737
  return createCommandContext({ controller, target, messages, host });
@@ -1549,7 +1750,7 @@ function useGridCommands({
1549
1750
  console.error(`DataGrid: there is no command \`${commandId}\` to run.`);
1550
1751
  return false;
1551
1752
  }
1552
- if (!runCommand(command, contextFor(target))) {
1753
+ if (!runCommand2(command, contextFor(target))) {
1553
1754
  return false;
1554
1755
  }
1555
1756
  report(commandId, target, source);
@@ -1559,19 +1760,22 @@ function useGridCommands({
1559
1760
  }
1560
1761
 
1561
1762
  // src/DataGrid/menu/GridMenu.tsx
1562
- import { useLayoutEffect as useLayoutEffect3, useRef as useRef3, useState as useState4 } from "react";
1763
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState4 } from "react";
1563
1764
  import {
1564
1765
  attachMenu
1565
1766
  } from "@gridkitjs/dom";
1566
1767
 
1567
1768
  // src/DataGrid/overlay/Overlay.tsx
1568
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
1769
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
1569
1770
  import { createPortal as createPortal3 } from "react-dom";
1570
1771
  import {
1571
1772
  attachOverlay
1572
1773
  } from "@gridkitjs/dom";
1573
1774
  import { jsx as jsx11 } from "react/jsx-runtime";
1574
1775
  var ANCHOR_GAP = 4;
1776
+ function attachedFocus(focus) {
1777
+ return typeof focus === "object" && !(focus instanceof HTMLElement) ? focus.current ?? "first" : focus;
1778
+ }
1575
1779
  function overlayThemeClassName(element) {
1576
1780
  const scope = element?.closest(".gridkit-light, .gridkit-dark");
1577
1781
  if (scope === null || scope === void 0) {
@@ -1587,12 +1791,13 @@ function Overlay({
1587
1791
  initialFocus,
1588
1792
  returnFocusTo,
1589
1793
  ignoreScrollAfterInput,
1794
+ trapFocus,
1590
1795
  themeClassName,
1591
1796
  onDismiss,
1592
1797
  children
1593
1798
  }) {
1594
- const panelRef = useRef2(null);
1595
- const onDismissRef = useRef2(onDismiss);
1799
+ const panelRef = useRef3(null);
1800
+ const onDismissRef = useRef3(onDismiss);
1596
1801
  useLayoutEffect2(() => {
1597
1802
  onDismissRef.current = onDismiss;
1598
1803
  });
@@ -1605,9 +1810,10 @@ function Overlay({
1605
1810
  placement,
1606
1811
  gap,
1607
1812
  offset,
1608
- initialFocus,
1813
+ initialFocus: attachedFocus(initialFocus),
1609
1814
  returnFocusTo,
1610
1815
  ignoreScrollAfterInput,
1816
+ trapFocus,
1611
1817
  onDismiss: (reason) => {
1612
1818
  onDismissRef.current(reason);
1613
1819
  }
@@ -1619,7 +1825,8 @@ function Overlay({
1619
1825
  offset,
1620
1826
  initialFocus,
1621
1827
  returnFocusTo,
1622
- ignoreScrollAfterInput
1828
+ ignoreScrollAfterInput,
1829
+ trapFocus
1623
1830
  ]);
1624
1831
  return createPortal3(
1625
1832
  /* @__PURE__ */ jsx11(
@@ -1662,6 +1869,15 @@ var BUILT_IN_ICONS = {
1662
1869
  /* @__PURE__ */ jsx12("path", { d: "m18 8 4 4-4 4" }),
1663
1870
  /* @__PURE__ */ jsx12("path", { d: "M2 12h20" }),
1664
1871
  /* @__PURE__ */ jsx12("path", { d: "m6 8-4 4 4 4" })
1872
+ ] }),
1873
+ copy: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1874
+ /* @__PURE__ */ jsx12("rect", { x: "8", y: "8", width: "14", height: "14", rx: "2" }),
1875
+ /* @__PURE__ */ jsx12("path", { d: "M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2" })
1876
+ ] }),
1877
+ print: () => /* @__PURE__ */ jsxs8(Icon, { children: [
1878
+ /* @__PURE__ */ jsx12("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
1879
+ /* @__PURE__ */ jsx12("path", { d: "M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6" }),
1880
+ /* @__PURE__ */ jsx12("rect", { x: "6", y: "14", width: "12", height: "8", rx: "1" })
1665
1881
  ] })
1666
1882
  };
1667
1883
  var ICONS = new Map(Object.entries(BUILT_IN_ICONS));
@@ -1675,6 +1891,9 @@ function CheckIcon() {
1675
1891
  function ChevronDownIcon() {
1676
1892
  return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
1677
1893
  }
1894
+ function ChevronLeftIcon() {
1895
+ return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m15 18-6-6 6-6" }) });
1896
+ }
1678
1897
  function ChevronRightIcon() {
1679
1898
  return /* @__PURE__ */ jsx12(Icon, { children: /* @__PURE__ */ jsx12("path", { d: "m9 18 6-6-6-6" }) });
1680
1899
  }
@@ -1694,25 +1913,25 @@ function MenuItem({
1694
1913
  entry,
1695
1914
  expanded
1696
1915
  }) {
1697
- const isItem = entry.type === "item";
1698
- const checked = isItem ? entry.checked : void 0;
1699
- const shortcut = isItem ? entry.shortcut : void 0;
1916
+ const isItem2 = entry.type === "item";
1917
+ const checked = isItem2 ? entry.checked : void 0;
1918
+ const shortcut = isItem2 ? entry.shortcut : void 0;
1700
1919
  return /* @__PURE__ */ jsxs9(
1701
1920
  "div",
1702
1921
  {
1703
1922
  role: checked === void 0 ? "menuitem" : "menuitemcheckbox",
1704
1923
  "data-gridkit-menu-item": entry.id,
1705
1924
  className: "menu-item",
1706
- ...ariaAttr(isItem && entry.disabled, "aria-disabled", true),
1925
+ ...ariaAttr(isItem2 && entry.disabled, "aria-disabled", true),
1707
1926
  ...ariaAttr(checked !== void 0, "aria-checked", checked),
1708
1927
  ...ariaAttr(shortcut !== void 0, "aria-keyshortcuts", shortcut),
1709
- ...ariaAttr(!isItem, "aria-haspopup", "menu"),
1710
- ...ariaAttr(!isItem, "aria-expanded", expanded),
1928
+ ...ariaAttr(!isItem2, "aria-haspopup", "menu"),
1929
+ ...ariaAttr(!isItem2, "aria-expanded", expanded),
1711
1930
  children: [
1712
1931
  /* @__PURE__ */ jsx13("span", { className: "menu-item-icon", "aria-hidden": "true", children: checked === true ? /* @__PURE__ */ jsx13(CheckIcon, {}) : /* @__PURE__ */ jsx13(MenuIcon, { name: entry.icon }) }),
1713
1932
  /* @__PURE__ */ jsx13("span", { className: "menu-item-label", children: entry.label }),
1714
1933
  shortcut !== void 0 && /* @__PURE__ */ jsx13("span", { className: "menu-item-shortcut", "aria-hidden": "true", children: shortcut }),
1715
- !isItem && /* @__PURE__ */ jsx13("span", { className: "menu-submenu-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(ChevronRightIcon, {}) })
1934
+ !isItem2 && /* @__PURE__ */ jsx13("span", { className: "menu-submenu-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(ChevronRightIcon, {}) })
1716
1935
  ]
1717
1936
  }
1718
1937
  );
@@ -1726,11 +1945,11 @@ function GridMenu({
1726
1945
  onCloseSubmenu,
1727
1946
  behaviourRef
1728
1947
  }) {
1729
- const menuRef = useRef3(null);
1730
- const submenuBehaviourRef = useRef3(null);
1948
+ const menuRef = useRef4(null);
1949
+ const submenuBehaviourRef = useRef4(null);
1731
1950
  const [submenu, setSubmenu] = useState4(null);
1732
1951
  const isSubmenu = onCloseSubmenu !== void 0;
1733
- const latest = useRef3({ entries, submenu, onRun, onClose, onCloseSubmenu });
1952
+ const latest = useRef4({ entries, submenu, onRun, onClose, onCloseSubmenu });
1734
1953
  useLayoutEffect3(() => {
1735
1954
  latest.current = { entries, submenu, onRun, onClose, onCloseSubmenu };
1736
1955
  });
@@ -1869,7 +2088,7 @@ function GridContextMenu({
1869
2088
  import {
1870
2089
  useEffect as useEffect2,
1871
2090
  useLayoutEffect as useLayoutEffect4,
1872
- useRef as useRef4,
2091
+ useRef as useRef5,
1873
2092
  useState as useState5
1874
2093
  } from "react";
1875
2094
  import {
@@ -2028,8 +2247,8 @@ function useContextMenu({
2028
2247
  columnName
2029
2248
  }) {
2030
2249
  const [menu, setMenu] = useState5(null);
2031
- const current = useRef4(null);
2032
- const openings = useRef4(0);
2250
+ const current = useRef5(null);
2251
+ const openings = useRef5(0);
2033
2252
  const enabled = contextMenu !== void 0 && contextMenu !== false;
2034
2253
  function parts() {
2035
2254
  const { columns, pageRows } = controller.getView();
@@ -2126,7 +2345,7 @@ function useContextMenu({
2126
2345
  const place = placeAt(target, parts());
2127
2346
  return place !== null && openAt(place, point);
2128
2347
  }
2129
- const latestRequest = useRef4(handleRequest);
2348
+ const latestRequest = useRef5(handleRequest);
2130
2349
  useLayoutEffect4(() => {
2131
2350
  latestRequest.current = handleRequest;
2132
2351
  });
@@ -2139,7 +2358,7 @@ function useContextMenu({
2139
2358
  onRequest: (request) => latestRequest.current(request)
2140
2359
  });
2141
2360
  }, [rootRef, enabled]);
2142
- const shown = useRef4(null);
2361
+ const shown = useRef5(null);
2143
2362
  useLayoutEffect4(() => {
2144
2363
  const closed = shown.current;
2145
2364
  shown.current = menu;
@@ -2168,8 +2387,11 @@ function useContextMenu({
2168
2387
  };
2169
2388
  }
2170
2389
 
2390
+ // src/DataGrid/panels/GridPanels.tsx
2391
+ import { useRef as useRef13 } from "react";
2392
+
2171
2393
  // src/DataGrid/columnChooser/ColumnChooser.tsx
2172
- import { useLayoutEffect as useLayoutEffect5, useRef as useRef5, useState as useState6 } from "react";
2394
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef6, useState as useState6 } from "react";
2173
2395
  import { resolveColumnChooser } from "@gridkitjs/core";
2174
2396
  import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2175
2397
  function ColumnChooser({
@@ -2177,7 +2399,7 @@ function ColumnChooser({
2177
2399
  messages
2178
2400
  }) {
2179
2401
  const [search, setSearch] = useState6("");
2180
- const showAllRef = useRef5(null);
2402
+ const showAllRef = useRef6(null);
2181
2403
  const { entries, selectAll, toggleAll } = resolveColumnChooser({
2182
2404
  columns: controller.getView().dataColumns,
2183
2405
  visibility: controller.getState().columnVisibility,
@@ -2253,37 +2475,902 @@ function ColumnChooser({
2253
2475
  );
2254
2476
  }
2255
2477
 
2478
+ // src/DataGrid/filterMenu/FilterMenu.tsx
2479
+ import { useEffect as useEffect3, useRef as useRef11 } from "react";
2480
+ import {
2481
+ resolveColumnFilterMenu as resolveColumnFilterMenu2,
2482
+ resolveColumnHeaderText,
2483
+ resolveMenu as resolveMenu2
2484
+ } from "@gridkitjs/core";
2485
+
2486
+ // src/DataGrid/filterMenu/ExcelFilterEditor.tsx
2487
+ import {
2488
+ useImperativeHandle as useImperativeHandle2,
2489
+ useLayoutEffect as useLayoutEffect9,
2490
+ useMemo as useMemo4,
2491
+ useReducer,
2492
+ useRef as useRef10,
2493
+ useState as useState10
2494
+ } from "react";
2495
+ import {
2496
+ describeColumnFilter as describeColumnFilter2,
2497
+ draftToFilter as draftToFilter2,
2498
+ filterChecklist,
2499
+ filterEditorReducer as filterEditorReducer2,
2500
+ initialFilterEditorDraft as initialFilterEditorDraft2,
2501
+ selectAllState
2502
+ } from "@gridkitjs/core";
2503
+
2504
+ // src/DataGrid/filterMenu/OperatorMenuButton.tsx
2505
+ import { useLayoutEffect as useLayoutEffect6, useRef as useRef7, useState as useState7 } from "react";
2506
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
2507
+ function gapBeside(button) {
2508
+ const dialog = button.closest('[role="dialog"]') ?? button;
2509
+ return dialog.getBoundingClientRect().right - button.getBoundingClientRect().right + ANCHOR_GAP;
2510
+ }
2511
+ function OperatorMenuButton({
2512
+ menu,
2513
+ active,
2514
+ messages,
2515
+ onChoose,
2516
+ ref
2517
+ }) {
2518
+ const [open, setOpen] = useState7(null);
2519
+ const menuBehaviourRef = useRef7(null);
2520
+ const label = messages.filterOperatorMenu(menu.family);
2521
+ useLayoutEffect6(() => {
2522
+ if (open !== null && open.focusItem > 0) {
2523
+ menuBehaviourRef.current?.focusItem(open.focusItem);
2524
+ }
2525
+ }, [open]);
2526
+ function show(button, onLast = false) {
2527
+ setOpen({
2528
+ anchor: button,
2529
+ themeClassName: overlayThemeClassName(button),
2530
+ gap: gapBeside(button),
2531
+ focusItem: onLast ? menu.operators.length - 1 : 0
2532
+ });
2533
+ }
2534
+ function close() {
2535
+ setOpen(null);
2536
+ }
2537
+ const entries = menu.operators.map((operator) => ({
2538
+ type: "item",
2539
+ id: operator,
2540
+ label: messages.filterOperatorLabel(operator, menu.family),
2541
+ disabled: false,
2542
+ checked: active?.includes(operator),
2543
+ run: () => {
2544
+ close();
2545
+ onChoose(operator);
2546
+ return true;
2547
+ }
2548
+ }));
2549
+ return /* @__PURE__ */ jsxs11(Fragment4, { children: [
2550
+ /* @__PURE__ */ jsxs11(
2551
+ "button",
2552
+ {
2553
+ ref,
2554
+ type: "button",
2555
+ className: "filter-menu-submenu",
2556
+ "aria-haspopup": "menu",
2557
+ "aria-expanded": open !== null,
2558
+ onClick: (event) => {
2559
+ if (open === null) {
2560
+ show(event.currentTarget);
2561
+ } else {
2562
+ close();
2563
+ }
2564
+ },
2565
+ onKeyDown: (event) => {
2566
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && open === null) {
2567
+ event.preventDefault();
2568
+ show(event.currentTarget, event.key === "ArrowUp");
2569
+ }
2570
+ },
2571
+ children: [
2572
+ /* @__PURE__ */ jsx16("span", { className: "filter-menu-submenu-icon", "aria-hidden": "true", children: active !== void 0 && /* @__PURE__ */ jsx16(CheckIcon, {}) }),
2573
+ label,
2574
+ /* @__PURE__ */ jsx16("span", { className: "filter-menu-submenu-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx16(ChevronRightIcon, {}) })
2575
+ ]
2576
+ }
2577
+ ),
2578
+ open !== null && /* @__PURE__ */ jsx16(
2579
+ Overlay,
2580
+ {
2581
+ anchor: open.anchor,
2582
+ placement: "right-start",
2583
+ gap: open.gap,
2584
+ returnFocusTo: open.anchor,
2585
+ themeClassName: open.themeClassName,
2586
+ onDismiss: close,
2587
+ children: /* @__PURE__ */ jsx16(
2588
+ GridMenu,
2589
+ {
2590
+ entries,
2591
+ label,
2592
+ themeClassName: open.themeClassName,
2593
+ behaviourRef: menuBehaviourRef,
2594
+ onRun: (item) => {
2595
+ item.run();
2596
+ },
2597
+ onClose: close
2598
+ }
2599
+ )
2600
+ }
2601
+ )
2602
+ ] });
2603
+ }
2604
+
2605
+ // src/DataGrid/filterMenu/SimpleFilterEditor.tsx
2606
+ import {
2607
+ Fragment as Fragment5,
2608
+ useId,
2609
+ useImperativeHandle,
2610
+ useLayoutEffect as useLayoutEffect7,
2611
+ useRef as useRef8,
2612
+ useState as useState8
2613
+ } from "react";
2614
+ import {
2615
+ conditionRowsFrom,
2616
+ conditionsFromRows,
2617
+ describeColumnFilter,
2618
+ draftToFilter,
2619
+ filterEditorReducer,
2620
+ initialFilterEditorDraft
2621
+ } from "@gridkitjs/core";
2622
+
2623
+ // src/DataGrid/filterMenu/ConditionRow.tsx
2624
+ import {
2625
+ filterOperandArity,
2626
+ parseFilterOperand
2627
+ } from "@gridkitjs/core";
2628
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
2629
+ function inputType(family, type) {
2630
+ switch (family) {
2631
+ case "text":
2632
+ return "text";
2633
+ case "number":
2634
+ return "number";
2635
+ case "date":
2636
+ return type === "date" ? "date" : "datetime-local";
2637
+ }
2638
+ }
2639
+ function OperandField({
2640
+ family,
2641
+ type,
2642
+ text,
2643
+ label,
2644
+ invalidMessageId,
2645
+ fieldRef,
2646
+ messages,
2647
+ onChange
2648
+ }) {
2649
+ const field = {
2650
+ ref: fieldRef,
2651
+ className: "filter-menu-value",
2652
+ "aria-label": label,
2653
+ "aria-invalid": invalidMessageId === void 0 ? void 0 : true,
2654
+ "aria-describedby": invalidMessageId,
2655
+ value: text,
2656
+ onChange: (event) => {
2657
+ onChange(event.currentTarget.value);
2658
+ }
2659
+ };
2660
+ if (family === "boolean") {
2661
+ return /* @__PURE__ */ jsxs12("select", { ...field, children: [
2662
+ /* @__PURE__ */ jsx17("option", { value: "" }),
2663
+ /* @__PURE__ */ jsx17("option", { value: "true", children: messages.booleanTrue }),
2664
+ /* @__PURE__ */ jsx17("option", { value: "false", children: messages.booleanFalse })
2665
+ ] });
2666
+ }
2667
+ return /* @__PURE__ */ jsx17("input", { ...field, type: inputType(family, type) });
2668
+ }
2669
+ function ConditionRow({
2670
+ row,
2671
+ position,
2672
+ menu,
2673
+ type,
2674
+ messages,
2675
+ invalidMessageId,
2676
+ focusRef,
2677
+ onChange,
2678
+ ref
2679
+ }) {
2680
+ const arity = filterOperandArity(row.operator);
2681
+ const operators = menu.operators.includes(row.operator) ? menu.operators : [row.operator, ...menu.operators];
2682
+ const operand = { family: menu.family, type, messages };
2683
+ function messageIdFor(text) {
2684
+ return parseFilterOperand(text, type) === void 0 ? invalidMessageId : void 0;
2685
+ }
2686
+ return /* @__PURE__ */ jsxs12(
2687
+ "div",
2688
+ {
2689
+ ref,
2690
+ role: "group",
2691
+ "aria-label": messages.filterConditionLabel(position),
2692
+ className: "filter-menu-condition",
2693
+ children: [
2694
+ /* @__PURE__ */ jsx17(
2695
+ "select",
2696
+ {
2697
+ ref: arity === 0 ? focusRef : void 0,
2698
+ className: "filter-menu-operator",
2699
+ "aria-label": messages.filterOperatorFieldLabel,
2700
+ value: row.operator,
2701
+ onChange: (event) => {
2702
+ const operator = operators[event.currentTarget.selectedIndex];
2703
+ if (operator !== void 0) {
2704
+ onChange({ ...row, operator });
2705
+ }
2706
+ },
2707
+ children: operators.map((operator) => /* @__PURE__ */ jsx17("option", { value: operator, children: messages.filterOperatorLabel(operator, menu.family) }, operator))
2708
+ }
2709
+ ),
2710
+ arity > 0 && /* @__PURE__ */ jsx17(
2711
+ OperandField,
2712
+ {
2713
+ ...operand,
2714
+ text: row.text,
2715
+ label: arity === 2 ? messages.filterFromLabel : messages.filterValueLabel,
2716
+ invalidMessageId: messageIdFor(row.text),
2717
+ fieldRef: focusRef,
2718
+ onChange: (text) => {
2719
+ onChange({ ...row, text });
2720
+ }
2721
+ }
2722
+ ),
2723
+ arity === 2 && /* @__PURE__ */ jsx17(
2724
+ OperandField,
2725
+ {
2726
+ ...operand,
2727
+ text: row.textTo,
2728
+ label: messages.filterToLabel,
2729
+ invalidMessageId: messageIdFor(row.textTo),
2730
+ fieldRef: void 0,
2731
+ onChange: (textTo) => {
2732
+ onChange({ ...row, textTo });
2733
+ }
2734
+ }
2735
+ )
2736
+ ]
2737
+ }
2738
+ );
2739
+ }
2740
+
2741
+ // src/DataGrid/filterMenu/SimpleFilterEditor.tsx
2742
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2743
+ var COMBINATORS = ["and", "or"];
2744
+ var NO_ROWS = [];
2745
+ var NO_VALUES = {
2746
+ options: [],
2747
+ total: 0,
2748
+ truncated: false,
2749
+ blanks: 0
2750
+ };
2751
+ function holdsUnreadable(row) {
2752
+ return Array.from(row?.querySelectorAll("input") ?? []).some(
2753
+ (input) => input.validity.badInput
2754
+ );
2755
+ }
2756
+ function CombinatorChoice({
2757
+ name,
2758
+ combinator,
2759
+ messages,
2760
+ onChange
2761
+ }) {
2762
+ return /* @__PURE__ */ jsx18(
2763
+ "div",
2764
+ {
2765
+ role: "radiogroup",
2766
+ "aria-label": messages.filterCombinatorLabel,
2767
+ className: "filter-menu-combinator",
2768
+ children: COMBINATORS.map((choice) => /* @__PURE__ */ jsxs13("label", { className: "filter-menu-combinator-option", children: [
2769
+ /* @__PURE__ */ jsx18(
2770
+ "input",
2771
+ {
2772
+ type: "radio",
2773
+ name,
2774
+ checked: choice === combinator,
2775
+ onChange: () => {
2776
+ onChange(choice);
2777
+ }
2778
+ }
2779
+ ),
2780
+ choice === "and" ? messages.filterAnd : messages.filterOr
2781
+ ] }, choice))
2782
+ }
2783
+ );
2784
+ }
2785
+ function SimpleFilterEditor({
2786
+ column,
2787
+ menu,
2788
+ controller,
2789
+ messages,
2790
+ initialFocusRef,
2791
+ applyFilter,
2792
+ operator,
2793
+ ref
2794
+ }) {
2795
+ const type = column.column.type;
2796
+ const [opened] = useState8(() => {
2797
+ const shape = describeColumnFilter(controller.getState().filter, column.id);
2798
+ const rows = conditionRowsFrom(shape, menu.operators, type);
2799
+ const draft2 = {
2800
+ // Its typed values kept, as picking the operator in the row keeps them.
2801
+ rows: operator === void 0 ? rows : rows.map(
2802
+ (row, index) => index === 0 ? { ...row, operator } : row
2803
+ ),
2804
+ combinator: shape.kind === "conditions" ? shape.combinator : "and"
2805
+ };
2806
+ return {
2807
+ custom: shape.kind === "custom" || shape.kind === "values" && operator === void 0,
2808
+ draft: draft2
2809
+ };
2810
+ });
2811
+ const [draft, setDraft] = useState8(opened.draft);
2812
+ const [invalid, setInvalid] = useState8(NO_ROWS);
2813
+ const rowElements = useRef8([]);
2814
+ const invalidMessageId = useId();
2815
+ const combinatorName = useId();
2816
+ function update(next) {
2817
+ setDraft(next);
2818
+ setInvalid(NO_ROWS);
2819
+ }
2820
+ function applyConditions(conditions) {
2821
+ const applied = filterEditorReducer(
2822
+ initialFilterEditorDraft({ kind: "none" }, NO_VALUES),
2823
+ { type: "setConditions", conditions, combinator: draft.combinator }
2824
+ );
2825
+ applyFilter(
2826
+ draftToFilter(applied, column.id, controller.getState().filter)
2827
+ );
2828
+ }
2829
+ useImperativeHandle(ref, () => ({
2830
+ apply: () => {
2831
+ const parsed = conditionsFromRows(draft.rows, column.id, type);
2832
+ const unreadable = draft.rows.map(
2833
+ (_, index) => holdsUnreadable(rowElements.current[index])
2834
+ );
2835
+ const marked = draft.rows.flatMap(
2836
+ (_, index) => parsed.invalid.includes(index) || unreadable[index] === true ? [index] : []
2837
+ );
2838
+ if (marked.length === 0) {
2839
+ applyConditions(parsed.conditions);
2840
+ return true;
2841
+ }
2842
+ if (draft === opened.draft && !unreadable.includes(true)) {
2843
+ return true;
2844
+ }
2845
+ setInvalid(marked);
2846
+ return false;
2847
+ }
2848
+ }));
2849
+ useLayoutEffect7(() => {
2850
+ const [first] = invalid;
2851
+ if (first !== void 0) {
2852
+ rowElements.current[first]?.querySelector('[aria-invalid="true"]')?.focus();
2853
+ }
2854
+ }, [invalid]);
2855
+ return /* @__PURE__ */ jsxs13("div", { className: "filter-menu-conditions", children: [
2856
+ opened.custom && /* @__PURE__ */ jsx18("p", { className: "filter-menu-custom", children: messages.customFilter }),
2857
+ draft.rows.map((row, index) => (
2858
+ // A row is its place: the first condition, or the second.
2859
+ /* @__PURE__ */ jsxs13(Fragment5, { children: [
2860
+ index > 0 && /* @__PURE__ */ jsx18(
2861
+ CombinatorChoice,
2862
+ {
2863
+ name: combinatorName,
2864
+ combinator: draft.combinator,
2865
+ messages,
2866
+ onChange: (combinator) => {
2867
+ update({ ...draft, combinator });
2868
+ }
2869
+ }
2870
+ ),
2871
+ /* @__PURE__ */ jsx18(
2872
+ ConditionRow,
2873
+ {
2874
+ ref: (element) => {
2875
+ rowElements.current[index] = element;
2876
+ },
2877
+ row,
2878
+ position: index + 1,
2879
+ menu,
2880
+ type,
2881
+ messages,
2882
+ invalidMessageId: invalid.includes(index) ? invalidMessageId : void 0,
2883
+ focusRef: index === 0 ? initialFocusRef : void 0,
2884
+ onChange: (next) => {
2885
+ update({
2886
+ ...draft,
2887
+ rows: draft.rows.map(
2888
+ (current, at) => at === index ? next : current
2889
+ )
2890
+ });
2891
+ }
2892
+ }
2893
+ )
2894
+ ] }, index)
2895
+ )),
2896
+ invalid.length > 0 && /* @__PURE__ */ jsx18("p", { id: invalidMessageId, className: "filter-menu-invalid", children: messages.filterValueInvalid(menu.family) })
2897
+ ] });
2898
+ }
2899
+
2900
+ // src/DataGrid/filterMenu/ValueChecklist.tsx
2901
+ import { useLayoutEffect as useLayoutEffect8, useRef as useRef9, useState as useState9 } from "react";
2902
+ import {
2903
+ attachChecklist,
2904
+ createRowVirtualizer
2905
+ } from "@gridkitjs/dom";
2906
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2907
+ var ESTIMATED_ITEM_HEIGHT = 28;
2908
+ var OVERSCAN = 4;
2909
+ var NO_RANGE = {
2910
+ startIndex: 0,
2911
+ endIndex: -1,
2912
+ offsetBefore: 0,
2913
+ offsetAfter: 0
2914
+ };
2915
+ function ValueChecklist({
2916
+ items,
2917
+ showCounts,
2918
+ label,
2919
+ onToggle
2920
+ }) {
2921
+ const listRef = useRef9(null);
2922
+ const checklistRef = useRef9(null);
2923
+ const [virtualizer, setVirtualizer] = useState9(null);
2924
+ const [range, setRange] = useState9(NO_RANGE);
2925
+ const latest = useRef9({ items, onToggle });
2926
+ useLayoutEffect8(() => {
2927
+ latest.current = { items, onToggle };
2928
+ });
2929
+ useLayoutEffect8(() => {
2930
+ const list = listRef.current;
2931
+ if (list === null) {
2932
+ return void 0;
2933
+ }
2934
+ const rows = createRowVirtualizer(list, {
2935
+ estimatedRowHeight: ESTIMATED_ITEM_HEIGHT,
2936
+ overscan: OVERSCAN,
2937
+ onRange: setRange
2938
+ });
2939
+ const checklist = attachChecklist(list, {
2940
+ onToggle: (index) => {
2941
+ const item = latest.current.items[index];
2942
+ if (item !== void 0) {
2943
+ latest.current.onToggle(item);
2944
+ }
2945
+ },
2946
+ scrollToIndex: (index) => {
2947
+ rows.scrollToIndex(index, { block: "nearest" });
2948
+ }
2949
+ });
2950
+ checklistRef.current = checklist;
2951
+ setVirtualizer(rows);
2952
+ return () => {
2953
+ checklistRef.current = null;
2954
+ checklist.detach();
2955
+ rows.destroy();
2956
+ };
2957
+ }, []);
2958
+ useLayoutEffect8(() => {
2959
+ virtualizer?.setRowCount(items.length);
2960
+ checklistRef.current?.setCount(items.length);
2961
+ }, [virtualizer, items.length]);
2962
+ const shown = items.slice(range.startIndex, range.endIndex + 1);
2963
+ return /* @__PURE__ */ jsxs14(
2964
+ "div",
2965
+ {
2966
+ ref: listRef,
2967
+ role: "listbox",
2968
+ "aria-multiselectable": "true",
2969
+ "aria-label": label,
2970
+ className: "filter-menu-checklist",
2971
+ children: [
2972
+ /* @__PURE__ */ jsx19("div", { role: "presentation", style: { height: range.offsetBefore } }),
2973
+ shown.map((item, offset) => {
2974
+ const index = range.startIndex + offset;
2975
+ return /* @__PURE__ */ jsxs14(
2976
+ "div",
2977
+ {
2978
+ ref: virtualizer?.measureRow(index),
2979
+ role: "option",
2980
+ "data-gridkit-checklist-item": "",
2981
+ "data-gridkit-checklist-index": index,
2982
+ "aria-checked": item.checked,
2983
+ "aria-setsize": items.length,
2984
+ "aria-posinset": index + 1,
2985
+ className: "filter-menu-option",
2986
+ children: [
2987
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-check", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(CheckIcon, {}) }),
2988
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-label", children: item.label }),
2989
+ showCounts && /* @__PURE__ */ jsxs14(Fragment6, { children: [
2990
+ " ",
2991
+ /* @__PURE__ */ jsx19("span", { className: "filter-menu-option-count", children: String(item.count) })
2992
+ ] })
2993
+ ]
2994
+ },
2995
+ item.key
2996
+ );
2997
+ }),
2998
+ /* @__PURE__ */ jsx19("div", { role: "presentation", style: { height: range.offsetAfter } })
2999
+ ]
3000
+ }
3001
+ );
3002
+ }
3003
+
3004
+ // src/DataGrid/filterMenu/ExcelFilterEditor.tsx
3005
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
3006
+ function toggleOf(item) {
3007
+ return item.kind === "blanks" ? { type: "setIncludeBlanks", includeBlanks: !item.checked } : { type: "toggleValue", value: item.value };
3008
+ }
3009
+ function ExcelFilterEditor({
3010
+ column,
3011
+ menu,
3012
+ controller,
3013
+ messages,
3014
+ initialFocusRef,
3015
+ applyFilter,
3016
+ columnValues,
3017
+ ref
3018
+ }) {
3019
+ const [opened] = useState10(() => {
3020
+ const shape2 = describeColumnFilter2(controller.getState().filter, column.id);
3021
+ return {
3022
+ shape: shape2,
3023
+ draft: initialFilterEditorDraft2(shape2, columnValues(column.id))
3024
+ };
3025
+ });
3026
+ const [draft, dispatch] = useReducer(filterEditorReducer2, opened.draft);
3027
+ const [operator, setOperator] = useState10(null);
3028
+ const conditions = useRef10(null);
3029
+ const conditionsFocus = useRef10(null);
3030
+ const operatorButton = useRef10(null);
3031
+ const switched = useRef10(false);
3032
+ const selectAllRef = useRef10(null);
3033
+ const selectAll = useMemo4(() => selectAllState(draft), [draft]);
3034
+ const checklist = useMemo4(
3035
+ () => filterChecklist(draft, {
3036
+ valueLimit: menu.valueLimit,
3037
+ blanksLabel: messages.filterBlanks
3038
+ }),
3039
+ [draft, menu.valueLimit, messages.filterBlanks]
3040
+ );
3041
+ useLayoutEffect9(() => {
3042
+ if (selectAllRef.current !== null) {
3043
+ selectAllRef.current.indeterminate = selectAll === "mixed";
3044
+ }
3045
+ });
3046
+ useLayoutEffect9(() => {
3047
+ if (switched.current) {
3048
+ switched.current = false;
3049
+ (operator === null ? operatorButton.current : conditionsFocus.current)?.focus();
3050
+ }
3051
+ }, [operator]);
3052
+ function switchTo(chosen) {
3053
+ switched.current = true;
3054
+ setOperator(chosen);
3055
+ }
3056
+ useImperativeHandle2(ref, () => ({
3057
+ apply: () => {
3058
+ if (operator !== null) {
3059
+ return conditions.current?.apply() ?? true;
3060
+ }
3061
+ const picked = draft.checked !== opened.draft.checked || draft.includeBlanks !== opened.draft.includeBlanks;
3062
+ if (picked) {
3063
+ applyFilter(
3064
+ draftToFilter2(draft, column.id, controller.getState().filter)
3065
+ );
3066
+ }
3067
+ return true;
3068
+ }
3069
+ }));
3070
+ if (operator !== null) {
3071
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
3072
+ /* @__PURE__ */ jsxs15(
3073
+ "button",
3074
+ {
3075
+ type: "button",
3076
+ className: "filter-menu-back",
3077
+ onClick: () => {
3078
+ switchTo(null);
3079
+ },
3080
+ children: [
3081
+ /* @__PURE__ */ jsx20("span", { className: "filter-menu-back-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx20(ChevronLeftIcon, {}) }),
3082
+ messages.filterBackToValues
3083
+ ]
3084
+ }
3085
+ ),
3086
+ /* @__PURE__ */ jsx20(
3087
+ SimpleFilterEditor,
3088
+ {
3089
+ ref: conditions,
3090
+ column,
3091
+ menu,
3092
+ controller,
3093
+ messages,
3094
+ initialFocusRef: (element) => {
3095
+ conditionsFocus.current = element;
3096
+ },
3097
+ applyFilter,
3098
+ operator
3099
+ }
3100
+ )
3101
+ ] });
3102
+ }
3103
+ const { shape } = opened;
3104
+ return /* @__PURE__ */ jsxs15("div", { className: "filter-menu-values", children: [
3105
+ menu.operators.length > 0 && /* @__PURE__ */ jsx20(
3106
+ OperatorMenuButton,
3107
+ {
3108
+ ref: operatorButton,
3109
+ menu,
3110
+ active: shape.kind === "conditions" ? shape.conditions.map((condition) => condition.operator) : void 0,
3111
+ messages,
3112
+ onChoose: switchTo
3113
+ }
3114
+ ),
3115
+ (shape.kind === "conditions" || shape.kind === "custom") && /* @__PURE__ */ jsx20("p", { className: "filter-menu-custom", children: shape.kind === "conditions" ? messages.filterByCondition : messages.customFilter }),
3116
+ /* @__PURE__ */ jsx20(
3117
+ "input",
3118
+ {
3119
+ ref: initialFocusRef,
3120
+ type: "search",
3121
+ className: "filter-menu-search",
3122
+ "aria-label": messages.filterSearchLabel,
3123
+ placeholder: messages.searchPlaceholder,
3124
+ value: draft.search,
3125
+ onChange: (event) => {
3126
+ dispatch({ type: "search", search: event.currentTarget.value });
3127
+ }
3128
+ }
3129
+ ),
3130
+ /* @__PURE__ */ jsxs15("label", { className: "filter-menu-select-all", children: [
3131
+ /* @__PURE__ */ jsx20(
3132
+ "input",
3133
+ {
3134
+ ref: selectAllRef,
3135
+ type: "checkbox",
3136
+ checked: selectAll === "checked",
3137
+ disabled: !checklist.needsSearch && checklist.items.length === 0,
3138
+ onChange: () => {
3139
+ dispatch({ type: "toggleAll" });
3140
+ }
3141
+ }
3142
+ ),
3143
+ messages.filterSelectAll
3144
+ ] }),
3145
+ checklist.needsSearch ? /* @__PURE__ */ jsx20("p", { className: "filter-menu-search-first", children: messages.filterSearchFirst(checklist.total) }) : /* @__PURE__ */ jsx20(
3146
+ ValueChecklist,
3147
+ {
3148
+ items: checklist.items,
3149
+ showCounts: menu.showCounts,
3150
+ label: messages.filterValuesLabel,
3151
+ onToggle: (item) => {
3152
+ dispatch(toggleOf(item));
3153
+ }
3154
+ }
3155
+ ),
3156
+ checklist.hiddenCount > 0 && /* @__PURE__ */ jsx20("p", { role: "status", className: "filter-menu-more", children: messages.filterMoreValues(checklist.hiddenCount) })
3157
+ ] });
3158
+ }
3159
+
3160
+ // src/DataGrid/filterMenu/FilterMenu.tsx
3161
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
3162
+ var SORT_COMMANDS = [
3163
+ "sort.ascending",
3164
+ "sort.descending",
3165
+ "sort.clear"
3166
+ ];
3167
+ function isItem(entry) {
3168
+ return entry.type === "item";
3169
+ }
3170
+ function FilterMenu({
3171
+ target,
3172
+ controller,
3173
+ commands,
3174
+ messages,
3175
+ close,
3176
+ initialFocusRef,
3177
+ applyFilter,
3178
+ columnValues
3179
+ }) {
3180
+ const editor = useRef11(null);
3181
+ const column = target.area === "header" ? controller.getView().dataColumns.find((entry) => entry.id === target.columnId) : void 0;
3182
+ const menu = column === void 0 ? null : resolveColumnFilterMenu2(controller.getOptions(), column.column);
3183
+ useEffect3(() => {
3184
+ if (menu === null) {
3185
+ close();
3186
+ }
3187
+ });
3188
+ if (column === void 0 || menu === null) {
3189
+ return null;
3190
+ }
3191
+ const resolveOptions = {
3192
+ commands: commands.commands,
3193
+ context: commands.contextFor(target)
3194
+ };
3195
+ const sortItems = resolveMenu2(SORT_COMMANDS, resolveOptions).filter(isItem);
3196
+ const [clearItem] = resolveMenu2(["filter.clear"], resolveOptions).filter(
3197
+ isItem
3198
+ );
3199
+ function run(item) {
3200
+ commands.run(item.id, target, "filterMenu");
3201
+ close();
3202
+ }
3203
+ const editorProps = {
3204
+ ref: editor,
3205
+ column,
3206
+ menu,
3207
+ controller,
3208
+ messages,
3209
+ initialFocusRef,
3210
+ applyFilter
3211
+ };
3212
+ function apply(event) {
3213
+ event.preventDefault();
3214
+ if (editor.current?.apply() ?? true) {
3215
+ close();
3216
+ }
3217
+ }
3218
+ return /* @__PURE__ */ jsx21(
3219
+ "div",
3220
+ {
3221
+ role: "dialog",
3222
+ "aria-label": messages.filterMenuLabel(
3223
+ resolveColumnHeaderText(column.column)
3224
+ ),
3225
+ className: "gridkit-filter-menu",
3226
+ children: /* @__PURE__ */ jsxs16(
3227
+ "form",
3228
+ {
3229
+ className: "filter-menu-form",
3230
+ noValidate: true,
3231
+ onSubmit: apply,
3232
+ onKeyDown: (event) => {
3233
+ if (event.key === "Enter" && event.target instanceof Element && event.target.closest("select, [role=listbox]") !== null) {
3234
+ event.preventDefault();
3235
+ event.currentTarget.requestSubmit();
3236
+ }
3237
+ },
3238
+ children: [
3239
+ sortItems.length > 0 && /* @__PURE__ */ jsx21("div", { className: "filter-menu-sort", children: sortItems.map((item) => /* @__PURE__ */ jsxs16(
3240
+ "button",
3241
+ {
3242
+ type: "button",
3243
+ className: "filter-menu-sort-item",
3244
+ disabled: item.disabled,
3245
+ ...ariaAttr(
3246
+ item.checked !== void 0,
3247
+ "aria-pressed",
3248
+ item.checked
3249
+ ),
3250
+ onClick: () => {
3251
+ run(item);
3252
+ },
3253
+ children: [
3254
+ /* @__PURE__ */ jsx21("span", { className: "filter-menu-sort-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx21(MenuIcon, { name: item.icon }) }),
3255
+ item.label
3256
+ ]
3257
+ },
3258
+ item.id
3259
+ )) }),
3260
+ menu.ui === "excel" ? /* @__PURE__ */ jsx21(ExcelFilterEditor, { ...editorProps, columnValues }) : /* @__PURE__ */ jsx21(SimpleFilterEditor, { ...editorProps }),
3261
+ /* @__PURE__ */ jsxs16("div", { className: "filter-menu-actions", children: [
3262
+ clearItem !== void 0 && /* @__PURE__ */ jsx21(
3263
+ "button",
3264
+ {
3265
+ type: "button",
3266
+ className: "filter-menu-clear",
3267
+ disabled: clearItem.disabled,
3268
+ onClick: () => {
3269
+ run(clearItem);
3270
+ },
3271
+ children: clearItem.label
3272
+ }
3273
+ ),
3274
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "filter-menu-cancel", onClick: close, children: messages.cancelFilter }),
3275
+ /* @__PURE__ */ jsx21("button", { type: "submit", className: "filter-menu-apply", children: messages.applyFilter })
3276
+ ] })
3277
+ ]
3278
+ }
3279
+ )
3280
+ }
3281
+ );
3282
+ }
3283
+
3284
+ // src/DataGrid/filterMenu/useApplyFilter.ts
3285
+ import { useEffect as useEffect4, useRef as useRef12 } from "react";
3286
+ function useApplyFilter(controller, messages, announce) {
3287
+ const unannounced = useRef12(null);
3288
+ useEffect4(() => {
3289
+ if (unannounced.current === controller.getState().filter) {
3290
+ unannounced.current = null;
3291
+ announce(
3292
+ messages.filterResults(
3293
+ controller.getView().rows.length,
3294
+ controller.getOptions().dataSource?.length ?? 0
3295
+ )
3296
+ );
3297
+ }
3298
+ });
3299
+ return (filter) => {
3300
+ unannounced.current = filter;
3301
+ controller.setFilter(filter);
3302
+ };
3303
+ }
3304
+
3305
+ // src/DataGrid/filterMenu/useFilterValueCache.ts
3306
+ import { useState as useState11 } from "react";
3307
+ import {
3308
+ createColumnValueOptionsCache
3309
+ } from "@gridkitjs/core";
3310
+ var NO_ROWS2 = [];
3311
+ function useFilterValueCache(controller, messages) {
3312
+ const [cache] = useState11(createColumnValueOptionsCache);
3313
+ return (columnId) => {
3314
+ const { dataSource = NO_ROWS2, locale } = controller.getOptions();
3315
+ return cache.get(
3316
+ dataSource,
3317
+ controller.getView().dataColumns,
3318
+ controller.getState().filter,
3319
+ columnId,
3320
+ locale === void 0 ? { messages } : { locale, messages }
3321
+ );
3322
+ };
3323
+ }
3324
+
2256
3325
  // src/DataGrid/panels/GridPanels.tsx
2257
- import { jsx as jsx16 } from "react/jsx-runtime";
2258
- var panelComponents = {
2259
- columnChooser: ColumnChooser
3326
+ import { jsx as jsx22 } from "react/jsx-runtime";
3327
+ var panelKinds = {
3328
+ columnChooser: { component: ColumnChooser, trapFocus: false },
3329
+ filter: { component: FilterMenu, trapFocus: true }
2260
3330
  };
2261
3331
  function GridPanels({
2262
3332
  panels,
2263
3333
  controller,
3334
+ commands,
2264
3335
  messages
2265
3336
  }) {
3337
+ const initialFocus = useRef13(null);
3338
+ const applyFilter = useApplyFilter(
3339
+ controller,
3340
+ messages,
3341
+ commands.host.announce
3342
+ );
3343
+ const columnValues = useFilterValueCache(controller, messages);
2266
3344
  const { panel, close } = panels;
2267
3345
  if (panel === null) {
2268
3346
  return null;
2269
3347
  }
2270
- const Panel = panelComponents[panel.kind];
2271
- return /* @__PURE__ */ jsx16(
3348
+ const { component: Panel, trapFocus } = panelKinds[panel.kind];
3349
+ return /* @__PURE__ */ jsx22(
2272
3350
  Overlay,
2273
3351
  {
2274
3352
  anchor: panel.anchor,
2275
3353
  placement: panel.placement,
2276
3354
  gap: panel.anchor instanceof Element ? ANCHOR_GAP : 0,
3355
+ initialFocus,
2277
3356
  returnFocusTo: panel.returnFocusTo,
2278
3357
  ignoreScrollAfterInput: true,
3358
+ trapFocus,
2279
3359
  themeClassName: panel.themeClassName,
2280
3360
  onDismiss: close,
2281
- children: /* @__PURE__ */ jsx16(
3361
+ children: /* @__PURE__ */ jsx22(
2282
3362
  Panel,
2283
3363
  {
2284
3364
  target: panel.target,
2285
3365
  controller,
2286
- messages
3366
+ commands,
3367
+ messages,
3368
+ close,
3369
+ initialFocusRef: (element) => {
3370
+ initialFocus.current = element;
3371
+ },
3372
+ applyFilter,
3373
+ columnValues
2287
3374
  }
2288
3375
  )
2289
3376
  },
@@ -2291,8 +3378,31 @@ function GridPanels({
2291
3378
  );
2292
3379
  }
2293
3380
 
3381
+ // src/DataGrid/panels/panelApi.ts
3382
+ import { resolveColumnFilterMenu as resolveColumnFilterMenu3 } from "@gridkitjs/core";
3383
+ function panelApi(panels, controller) {
3384
+ return {
3385
+ openColumnChooser: () => {
3386
+ panels.open("columnChooser", { area: "grid" });
3387
+ },
3388
+ openFilterMenu: (columnId) => {
3389
+ const shown = controller.getView().columns.find((entry) => entry.id === columnId);
3390
+ if (shown === void 0 || resolveColumnFilterMenu3(controller.getOptions(), shown.column) === null) {
3391
+ return false;
3392
+ }
3393
+ panels.open("filter", { area: "header", columnId });
3394
+ return true;
3395
+ },
3396
+ closeFilterMenu: () => {
3397
+ if (panels.panel?.kind === "filter") {
3398
+ panels.close();
3399
+ }
3400
+ }
3401
+ };
3402
+ }
3403
+
2294
3404
  // src/DataGrid/panels/useGridPanels.ts
2295
- import { useLayoutEffect as useLayoutEffect6, useRef as useRef6, useState as useState7 } from "react";
3405
+ import { useLayoutEffect as useLayoutEffect10, useRef as useRef14, useState as useState12 } from "react";
2296
3406
  function toolbarItem(root, itemId) {
2297
3407
  const items = root.querySelectorAll(
2298
3408
  ":scope > .gridkit-toolbar > [data-gridkit-toolbar-item]"
@@ -2315,8 +3425,8 @@ function useGridPanels({
2315
3425
  headerTableRef,
2316
3426
  nav
2317
3427
  }) {
2318
- const [panel, setPanel] = useState7(null);
2319
- const openings = useRef6(0);
3428
+ const [panel, setPanel] = useState12(null);
3429
+ const openings = useRef14(0);
2320
3430
  function placeFor(root, target) {
2321
3431
  const element = target.area === "toolbar" ? toolbarItem(root, target.itemId) : target.area === "header" ? headerCell2(headerTableRef.current, target.columnId) : null;
2322
3432
  if (element !== null) {
@@ -2334,8 +3444,8 @@ function useGridPanels({
2334
3444
  returnFocusTo: void 0
2335
3445
  };
2336
3446
  }
2337
- const shown = useRef6(null);
2338
- useLayoutEffect6(() => {
3447
+ const shown = useRef14(null);
3448
+ useLayoutEffect10(() => {
2339
3449
  const closed = shown.current;
2340
3450
  shown.current = panel;
2341
3451
  if (panel === null && closed?.returnFocusTo?.isConnected === false && document.activeElement === document.body) {
@@ -2365,7 +3475,7 @@ function useGridPanels({
2365
3475
  }
2366
3476
 
2367
3477
  // src/DataGrid/toolbar/GridToolbar.tsx
2368
- import { useLayoutEffect as useLayoutEffect9, useRef as useRef10 } from "react";
3478
+ import { useLayoutEffect as useLayoutEffect13, useRef as useRef18 } from "react";
2369
3479
  import {
2370
3480
  resolveToolbar,
2371
3481
  splitToolbarItems,
@@ -2374,8 +3484,8 @@ import {
2374
3484
  import { attachToolbar } from "@gridkitjs/dom";
2375
3485
 
2376
3486
  // src/DataGrid/toolbar/ToolbarMenuButton.tsx
2377
- import { useLayoutEffect as useLayoutEffect7, useRef as useRef7, useState as useState8 } from "react";
2378
- import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
3487
+ import { useLayoutEffect as useLayoutEffect11, useRef as useRef15, useState as useState13 } from "react";
3488
+ import { Fragment as Fragment8, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2379
3489
  function ToolbarMenuButton({
2380
3490
  itemKey,
2381
3491
  label,
@@ -2384,9 +3494,9 @@ function ToolbarMenuButton({
2384
3494
  more = false,
2385
3495
  onMenuClose
2386
3496
  }) {
2387
- const [menu, setMenu] = useState8(null);
2388
- const menuBehaviourRef = useRef7(null);
2389
- useLayoutEffect7(() => {
3497
+ const [menu, setMenu] = useState13(null);
3498
+ const menuBehaviourRef = useRef15(null);
3499
+ useLayoutEffect11(() => {
2390
3500
  if (menu !== null && menu.focusItem > 0) {
2391
3501
  menuBehaviourRef.current?.focusItem(menu.focusItem);
2392
3502
  }
@@ -2402,8 +3512,8 @@ function ToolbarMenuButton({
2402
3512
  setMenu(null);
2403
3513
  onMenuClose();
2404
3514
  }
2405
- return /* @__PURE__ */ jsxs11(Fragment4, { children: [
2406
- /* @__PURE__ */ jsxs11(
3515
+ return /* @__PURE__ */ jsxs17(Fragment8, { children: [
3516
+ /* @__PURE__ */ jsxs17(
2407
3517
  "button",
2408
3518
  {
2409
3519
  type: "button",
@@ -2426,13 +3536,13 @@ function ToolbarMenuButton({
2426
3536
  }
2427
3537
  },
2428
3538
  children: [
2429
- icon !== void 0 && /* @__PURE__ */ jsx17("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(MenuIcon, { name: icon }) }),
3539
+ icon !== void 0 && /* @__PURE__ */ jsx23("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx23(MenuIcon, { name: icon }) }),
2430
3540
  label,
2431
- /* @__PURE__ */ jsx17("span", { className: "toolbar-button-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx17(ChevronDownIcon, {}) })
3541
+ /* @__PURE__ */ jsx23("span", { className: "toolbar-button-indicator", "aria-hidden": "true", children: /* @__PURE__ */ jsx23(ChevronDownIcon, {}) })
2432
3542
  ]
2433
3543
  }
2434
3544
  ),
2435
- menu !== null && /* @__PURE__ */ jsx17(
3545
+ menu !== null && /* @__PURE__ */ jsx23(
2436
3546
  Overlay,
2437
3547
  {
2438
3548
  anchor: menu.anchor,
@@ -2441,7 +3551,7 @@ function ToolbarMenuButton({
2441
3551
  returnFocusTo: menu.anchor,
2442
3552
  themeClassName: menu.themeClassName,
2443
3553
  onDismiss: close,
2444
- children: /* @__PURE__ */ jsx17(
3554
+ children: /* @__PURE__ */ jsx23(
2445
3555
  GridMenu,
2446
3556
  {
2447
3557
  entries,
@@ -2461,12 +3571,12 @@ function ToolbarMenuButton({
2461
3571
  }
2462
3572
 
2463
3573
  // src/DataGrid/toolbar/ToolbarSearch.tsx
2464
- import { useEffect as useEffect3, useRef as useRef8, useState as useState9 } from "react";
3574
+ import { useEffect as useEffect5, useRef as useRef16, useState as useState14 } from "react";
2465
3575
  import {
2466
3576
  searchTextOf,
2467
3577
  setSearchText
2468
3578
  } from "@gridkitjs/core";
2469
- import { jsx as jsx18 } from "react/jsx-runtime";
3579
+ import { jsx as jsx24 } from "react/jsx-runtime";
2470
3580
  var SEARCH_DELAY_MS = 250;
2471
3581
  function ToolbarSearch({
2472
3582
  itemKey,
@@ -2475,9 +3585,9 @@ function ToolbarSearch({
2475
3585
  announce
2476
3586
  }) {
2477
3587
  const applied = searchTextOf(controller.getState().filter);
2478
- const [draft, setDraft] = useState9(null);
2479
- const timer = useRef8(void 0);
2480
- const unannounced = useRef8(null);
3588
+ const [draft, setDraft] = useState14(null);
3589
+ const timer = useRef16(void 0);
3590
+ const unannounced = useRef16(null);
2481
3591
  const text = draft !== null && draft.over === applied ? draft.text : applied;
2482
3592
  function apply(next, over) {
2483
3593
  clearTimeout(timer.current);
@@ -2492,19 +3602,19 @@ function ToolbarSearch({
2492
3602
  controller.setFilter(searched);
2493
3603
  }
2494
3604
  }
2495
- useEffect3(() => {
3605
+ useEffect5(() => {
2496
3606
  if (unannounced.current === applied) {
2497
3607
  unannounced.current = null;
2498
3608
  announce(messages.searchResults(controller.getView().rows.length));
2499
3609
  }
2500
3610
  });
2501
- useEffect3(
3611
+ useEffect5(
2502
3612
  () => () => {
2503
3613
  clearTimeout(timer.current);
2504
3614
  },
2505
3615
  []
2506
3616
  );
2507
- return /* @__PURE__ */ jsx18(
3617
+ return /* @__PURE__ */ jsx24(
2508
3618
  "input",
2509
3619
  {
2510
3620
  type: "search",
@@ -2534,12 +3644,12 @@ function ToolbarSearch({
2534
3644
  }
2535
3645
 
2536
3646
  // src/DataGrid/toolbar/useToolbarOverflow.ts
2537
- import { useLayoutEffect as useLayoutEffect8, useRef as useRef9, useState as useState10 } from "react";
3647
+ import { useLayoutEffect as useLayoutEffect12, useRef as useRef17, useState as useState15 } from "react";
2538
3648
  import { observeToolbarOverflow } from "@gridkitjs/dom";
2539
3649
  function useToolbarOverflow(toolbarRef, items) {
2540
- const [fit, setFit] = useState10(items.length);
2541
- const overflow = useRef9(null);
2542
- const measuredLayout = useRef9("");
3650
+ const [fit, setFit] = useState15(items.length);
3651
+ const overflow = useRef17(null);
3652
+ const measuredLayout = useRef17("");
2543
3653
  const layout = JSON.stringify(
2544
3654
  items.map((item) => [
2545
3655
  item.type,
@@ -2547,7 +3657,7 @@ function useToolbarOverflow(toolbarRef, items) {
2547
3657
  "label" in item ? item.label : null
2548
3658
  ])
2549
3659
  );
2550
- useLayoutEffect8(() => {
3660
+ useLayoutEffect12(() => {
2551
3661
  const toolbar = toolbarRef.current;
2552
3662
  if (toolbar === null || layout === measuredLayout.current) {
2553
3663
  return;
@@ -2560,7 +3670,7 @@ function useToolbarOverflow(toolbarRef, items) {
2560
3670
  overflow.current.update(options);
2561
3671
  }
2562
3672
  }, [toolbarRef, layout, items]);
2563
- useLayoutEffect8(
3673
+ useLayoutEffect12(
2564
3674
  () => () => {
2565
3675
  overflow.current?.detach();
2566
3676
  overflow.current = null;
@@ -2572,7 +3682,7 @@ function useToolbarOverflow(toolbarRef, items) {
2572
3682
  }
2573
3683
 
2574
3684
  // src/DataGrid/toolbar/GridToolbar.tsx
2575
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
3685
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
2576
3686
  function reporting(run, report) {
2577
3687
  return () => {
2578
3688
  const ran = run();
@@ -2625,7 +3735,7 @@ function GridToolbar(props) {
2625
3735
  return null;
2626
3736
  }
2627
3737
  const label = (typeof props.toolbar === "object" ? props.toolbar.label : void 0) ?? props.messages.toolbarLabel;
2628
- return /* @__PURE__ */ jsx19(Toolbar, { ...props, entries, label });
3738
+ return /* @__PURE__ */ jsx25(Toolbar, { ...props, entries, label });
2629
3739
  }
2630
3740
  function Toolbar({
2631
3741
  entries,
@@ -2637,8 +3747,8 @@ function Toolbar({
2637
3747
  messages,
2638
3748
  announce
2639
3749
  }) {
2640
- const toolbarRef = useRef10(null);
2641
- const menuClosed = useRef10(false);
3750
+ const toolbarRef = useRef18(null);
3751
+ const menuClosed = useRef18(false);
2642
3752
  const items = resolveToolbar(entries, {
2643
3753
  commands: commands.commands,
2644
3754
  contextFor: commands.contextFor
@@ -2649,11 +3759,11 @@ function Toolbar({
2649
3759
  );
2650
3760
  const fit = useToolbarOverflow(toolbarRef, items);
2651
3761
  const { shown, overflow } = splitToolbarItems(items, fit);
2652
- useLayoutEffect9(() => {
3762
+ useLayoutEffect13(() => {
2653
3763
  const toolbar = toolbarRef.current;
2654
3764
  return toolbar === null ? void 0 : attachToolbar(toolbar);
2655
3765
  }, []);
2656
- useLayoutEffect9(() => {
3766
+ useLayoutEffect13(() => {
2657
3767
  if (!menuClosed.current) {
2658
3768
  return;
2659
3769
  }
@@ -2669,7 +3779,7 @@ function Toolbar({
2669
3779
  function draw(item) {
2670
3780
  switch (item.type) {
2671
3781
  case "button":
2672
- return /* @__PURE__ */ jsxs12(
3782
+ return /* @__PURE__ */ jsxs18(
2673
3783
  "button",
2674
3784
  {
2675
3785
  type: "button",
@@ -2690,14 +3800,14 @@ function Toolbar({
2690
3800
  item.run();
2691
3801
  },
2692
3802
  children: [
2693
- item.icon !== void 0 && /* @__PURE__ */ jsx19("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(MenuIcon, { name: item.icon }) }),
3803
+ item.icon !== void 0 && /* @__PURE__ */ jsx25("span", { className: "toolbar-button-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx25(MenuIcon, { name: item.icon }) }),
2694
3804
  item.label
2695
3805
  ]
2696
3806
  },
2697
3807
  item.key
2698
3808
  );
2699
3809
  case "menu":
2700
- return /* @__PURE__ */ jsx19(
3810
+ return /* @__PURE__ */ jsx25(
2701
3811
  ToolbarMenuButton,
2702
3812
  {
2703
3813
  itemKey: item.key,
@@ -2709,7 +3819,7 @@ function Toolbar({
2709
3819
  item.key
2710
3820
  );
2711
3821
  case "search":
2712
- return /* @__PURE__ */ jsx19(
3822
+ return /* @__PURE__ */ jsx25(
2713
3823
  ToolbarSearch,
2714
3824
  {
2715
3825
  itemKey: item.key,
@@ -2720,7 +3830,7 @@ function Toolbar({
2720
3830
  item.key
2721
3831
  );
2722
3832
  case "separator":
2723
- return /* @__PURE__ */ jsx19(
3833
+ return /* @__PURE__ */ jsx25(
2724
3834
  "div",
2725
3835
  {
2726
3836
  role: "separator",
@@ -2731,7 +3841,7 @@ function Toolbar({
2731
3841
  item.key
2732
3842
  );
2733
3843
  case "spacer":
2734
- return /* @__PURE__ */ jsx19(
3844
+ return /* @__PURE__ */ jsx25(
2735
3845
  "div",
2736
3846
  {
2737
3847
  className: "toolbar-spacer",
@@ -2740,7 +3850,7 @@ function Toolbar({
2740
3850
  item.key
2741
3851
  );
2742
3852
  case "custom":
2743
- return /* @__PURE__ */ jsx19(
3853
+ return /* @__PURE__ */ jsx25(
2744
3854
  "div",
2745
3855
  {
2746
3856
  className: "toolbar-custom",
@@ -2751,7 +3861,7 @@ function Toolbar({
2751
3861
  );
2752
3862
  }
2753
3863
  }
2754
- return /* @__PURE__ */ jsxs12(
3864
+ return /* @__PURE__ */ jsxs18(
2755
3865
  "div",
2756
3866
  {
2757
3867
  ref: toolbarRef,
@@ -2761,7 +3871,7 @@ function Toolbar({
2761
3871
  className: "gridkit-toolbar",
2762
3872
  children: [
2763
3873
  shown.map(draw),
2764
- overflow.length > 0 && /* @__PURE__ */ jsx19(
3874
+ overflow.length > 0 && /* @__PURE__ */ jsx25(
2765
3875
  ToolbarMenuButton,
2766
3876
  {
2767
3877
  itemKey: "more",
@@ -2817,7 +3927,7 @@ function useColumnDrag({
2817
3927
 
2818
3928
  // src/DataGrid/useColumnResize.ts
2819
3929
  import {
2820
- useState as useState11
3930
+ useState as useState16
2821
3931
  } from "react";
2822
3932
  import {
2823
3933
  beginColumnResize,
@@ -2831,7 +3941,7 @@ function useColumnResize({
2831
3941
  controller,
2832
3942
  columnSizeDefaults
2833
3943
  }) {
2834
- const [activeColumnId, setActiveColumnId] = useState11(null);
3944
+ const [activeColumnId, setActiveColumnId] = useState16(null);
2835
3945
  function startResize(entry, event) {
2836
3946
  event.stopPropagation();
2837
3947
  const session = beginColumnResize(
@@ -2909,11 +4019,11 @@ function useColumnResize({
2909
4019
  }
2910
4020
 
2911
4021
  // src/DataGrid/useElementWidth.ts
2912
- import { useLayoutEffect as useLayoutEffect10, useState as useState12 } from "react";
4022
+ import { useLayoutEffect as useLayoutEffect14, useState as useState17 } from "react";
2913
4023
  import { observeElementWidth } from "@gridkitjs/dom";
2914
4024
  function useElementWidth(ref, enabled) {
2915
- const [width, setWidth] = useState12(null);
2916
- useLayoutEffect10(() => {
4025
+ const [width, setWidth] = useState17(null);
4026
+ useLayoutEffect14(() => {
2917
4027
  const element = ref.current;
2918
4028
  if (element === null || !enabled) {
2919
4029
  return void 0;
@@ -2924,12 +4034,12 @@ function useElementWidth(ref, enabled) {
2924
4034
  }
2925
4035
 
2926
4036
  // src/DataGrid/useGridController.ts
2927
- import { useState as useState13, useSyncExternalStore as useSyncExternalStore2 } from "react";
4037
+ import { useState as useState18, useSyncExternalStore as useSyncExternalStore2 } from "react";
2928
4038
  import {
2929
4039
  createGridController
2930
4040
  } from "@gridkitjs/core";
2931
4041
  function useGridController(options) {
2932
- const [controller] = useState13(
4042
+ const [controller] = useState18(
2933
4043
  () => createGridController(options)
2934
4044
  );
2935
4045
  controller.setOptions(options);
@@ -2938,15 +4048,15 @@ function useGridController(options) {
2938
4048
  }
2939
4049
 
2940
4050
  // src/DataGrid/useInfiniteScroll.ts
2941
- import { useEffect as useEffect4, useRef as useRef11 } from "react";
4051
+ import { useEffect as useEffect6, useRef as useRef19 } from "react";
2942
4052
  import { attachInfiniteScroll } from "@gridkitjs/dom";
2943
4053
  function useInfiniteScroll({
2944
4054
  rootRef,
2945
4055
  sentinelRef,
2946
4056
  config
2947
4057
  }) {
2948
- const scroll = useRef11(null);
2949
- useEffect4(() => {
4058
+ const scroll = useRef19(null);
4059
+ useEffect6(() => {
2950
4060
  const root = rootRef.current;
2951
4061
  if (root === null) {
2952
4062
  return void 0;
@@ -2961,7 +4071,7 @@ function useInfiniteScroll({
2961
4071
  scroll.current = null;
2962
4072
  };
2963
4073
  }, [rootRef]);
2964
- useEffect4(() => {
4074
+ useEffect6(() => {
2965
4075
  scroll.current?.update({
2966
4076
  sentinel: sentinelRef.current,
2967
4077
  hasMore: config?.hasMore ?? false,
@@ -2973,11 +4083,11 @@ function useInfiniteScroll({
2973
4083
  }
2974
4084
 
2975
4085
  // src/DataGrid/useLiveAnnouncer.ts
2976
- import { useCallback, useLayoutEffect as useLayoutEffect11, useRef as useRef12 } from "react";
4086
+ import { useCallback, useLayoutEffect as useLayoutEffect15, useRef as useRef20 } from "react";
2977
4087
  import { createLiveAnnouncer } from "@gridkitjs/dom";
2978
4088
  function useLiveAnnouncer(containerRef) {
2979
- const announcer = useRef12(null);
2980
- useLayoutEffect11(() => {
4089
+ const announcer = useRef20(null);
4090
+ useLayoutEffect15(() => {
2981
4091
  const container = containerRef.current;
2982
4092
  if (container === null) {
2983
4093
  return void 0;
@@ -2995,8 +4105,8 @@ function useLiveAnnouncer(containerRef) {
2995
4105
  }
2996
4106
 
2997
4107
  // src/DataGrid/useRowVirtualizer.ts
2998
- import { useLayoutEffect as useLayoutEffect12, useState as useState14 } from "react";
2999
- import { createRowVirtualizer } from "@gridkitjs/dom";
4108
+ import { useLayoutEffect as useLayoutEffect16, useState as useState19 } from "react";
4109
+ import { createRowVirtualizer as createRowVirtualizer2 } from "@gridkitjs/dom";
3000
4110
  var EMPTY_RANGE = {
3001
4111
  startIndex: 0,
3002
4112
  endIndex: -1,
@@ -3013,14 +4123,14 @@ function useRowVirtualizer({
3013
4123
  estimatedRowHeight,
3014
4124
  overscan
3015
4125
  }) {
3016
- const [virtualizer, setVirtualizer] = useState14(null);
3017
- const [range, setRange] = useState14(EMPTY_RANGE);
3018
- useLayoutEffect12(() => {
4126
+ const [virtualizer, setVirtualizer] = useState19(null);
4127
+ const [range, setRange] = useState19(EMPTY_RANGE);
4128
+ useLayoutEffect16(() => {
3019
4129
  const element = bodyScrollRef.current;
3020
4130
  if (!enabled || element === null) {
3021
4131
  return void 0;
3022
4132
  }
3023
- const created = createRowVirtualizer(element, {
4133
+ const created = createRowVirtualizer2(element, {
3024
4134
  estimatedRowHeight,
3025
4135
  overscan,
3026
4136
  onRange: setRange
@@ -3030,7 +4140,7 @@ function useRowVirtualizer({
3030
4140
  created.destroy();
3031
4141
  };
3032
4142
  }, [enabled, bodyScrollRef, estimatedRowHeight, overscan]);
3033
- useLayoutEffect12(() => {
4143
+ useLayoutEffect16(() => {
3034
4144
  virtualizer?.setRowCount(rows.length);
3035
4145
  virtualizer?.resetHeights();
3036
4146
  }, [virtualizer, rows]);
@@ -3047,7 +4157,7 @@ function useRowVirtualizer({
3047
4157
  }
3048
4158
 
3049
4159
  // src/DataGrid/DataGrid.tsx
3050
- import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
4160
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
3051
4161
  var NO_AGGREGATES = [];
3052
4162
  function paginationReader(controller) {
3053
4163
  let last;
@@ -3104,16 +4214,16 @@ function DataGrid(props) {
3104
4214
  const hoverRows = hoverable?.rows ?? true;
3105
4215
  const hoverColumns = hoverable?.columns ?? true;
3106
4216
  const hoverCells = hoverable?.cells ?? true;
3107
- const viewportRef = useRef13(null);
3108
- const headerTableRef = useRef13(null);
3109
- const bodyTableRef = useRef13(null);
3110
- const bodyScrollRef = useRef13(null);
3111
- const sentinelRef = useRef13(null);
4217
+ const viewportRef = useRef21(null);
4218
+ const headerTableRef = useRef21(null);
4219
+ const bodyTableRef = useRef21(null);
4220
+ const bodyScrollRef = useRef21(null);
4221
+ const sentinelRef = useRef21(null);
3112
4222
  const viewportWidth = useElementWidth(viewportRef, resizeMode === "fit");
3113
- const subscribersRef = useRef13(/* @__PURE__ */ new Set());
3114
- const gridId = useId();
4223
+ const subscribersRef = useRef21(/* @__PURE__ */ new Set());
4224
+ const gridId = useId2();
3115
4225
  const announce = useLiveAnnouncer(viewportRef);
3116
- const messages = useMemo4(
4226
+ const messages = useMemo5(
3117
4227
  () => resolveMessages(messageOverrides),
3118
4228
  [messageOverrides]
3119
4229
  );
@@ -3128,8 +4238,8 @@ function DataGrid(props) {
3128
4238
  columnOf
3129
4239
  })
3130
4240
  });
3131
- const [readPagination] = useState15(() => paginationReader(controller));
3132
- const [readSelectedRows] = useState15(() => selectedRowsReader(controller));
4241
+ const [readPagination] = useState20(() => paginationReader(controller));
4242
+ const [readSelectedRows] = useState20(() => selectedRowsReader(controller));
3133
4243
  const virtualizer = useRowVirtualizer({
3134
4244
  enabled: virtualized,
3135
4245
  rows: view.pageRows,
@@ -3137,7 +4247,7 @@ function DataGrid(props) {
3137
4247
  estimatedRowHeight,
3138
4248
  overscan
3139
4249
  });
3140
- useEffect5(() => {
4250
+ useEffect7(() => {
3141
4251
  if (virtualized && height === void 0) {
3142
4252
  console.error(
3143
4253
  "DataGrid: `virtualized` has no effect without `height` \u2014 a body with no bounded height has no viewport to window rows against."
@@ -3145,7 +4255,7 @@ function DataGrid(props) {
3145
4255
  }
3146
4256
  }, [virtualized, height]);
3147
4257
  const hasInfiniteScroll = infiniteScroll !== void 0;
3148
- useEffect5(() => {
4258
+ useEffect7(() => {
3149
4259
  if (paginated && hasInfiniteScroll) {
3150
4260
  console.error(
3151
4261
  "DataGrid: `paginated` and `infiniteScroll` are mutually exclusive \u2014 only `paginated` takes effect while both are set."
@@ -3162,7 +4272,7 @@ function DataGrid(props) {
3162
4272
  }
3163
4273
  function columnName(columnId) {
3164
4274
  const column = columnOf(columnId);
3165
- return column === void 0 ? columnId : resolveColumnHeaderText(column);
4275
+ return column === void 0 ? columnId : resolveColumnHeaderText2(column);
3166
4276
  }
3167
4277
  const resize = useColumnResize({
3168
4278
  // `@gridkitjs/dom`'s `measureColumnContent` only ever matches
@@ -3206,19 +4316,20 @@ function DataGrid(props) {
3206
4316
  resize,
3207
4317
  openPanel: panels.open
3208
4318
  });
3209
- const orderedIds = useMemo4(
4319
+ const handleCopyKey = useGridCopy({ controller, commands, nav });
4320
+ const orderedIds = useMemo5(
3210
4321
  () => view.dataColumns.map((entry) => entry.id),
3211
4322
  [view.dataColumns]
3212
4323
  );
3213
- const displayedIds = useMemo4(
4324
+ const displayedIds = useMemo5(
3214
4325
  () => view.columns.map((entry) => entry.id),
3215
4326
  [view.columns]
3216
4327
  );
3217
- const definitions = useMemo4(
4328
+ const definitions = useMemo5(
3218
4329
  () => new Map(view.dataColumns.map((entry) => [entry.id, entry.column])),
3219
4330
  [view.dataColumns]
3220
4331
  );
3221
- const groupByDraggableIds = useMemo4(
4332
+ const groupByDraggableIds = useMemo5(
3222
4333
  () => new Set(
3223
4334
  view.columns.filter((entry) => entry.groupByDraggable).map((entry) => entry.id)
3224
4335
  ),
@@ -3277,12 +4388,12 @@ function DataGrid(props) {
3277
4388
  messages,
3278
4389
  columnName
3279
4390
  });
3280
- useEffect5(() => {
4391
+ useEffect7(() => {
3281
4392
  for (const listener of subscribersRef.current) {
3282
4393
  listener();
3283
4394
  }
3284
4395
  }, [view, state, nav.focus]);
3285
- useImperativeHandle(ref, () => ({
4396
+ useImperativeHandle3(ref, () => ({
3286
4397
  get element() {
3287
4398
  return viewportRef.current;
3288
4399
  },
@@ -3347,9 +4458,9 @@ function DataGrid(props) {
3347
4458
  },
3348
4459
  openContextMenu: contextMenu.open,
3349
4460
  closeContextMenu: contextMenu.close,
3350
- openColumnChooser: () => {
3351
- panels.open("columnChooser", { area: "grid" });
3352
- },
4461
+ ...exportApi(commands),
4462
+ ...printApi(commands),
4463
+ ...panelApi(panels, controller),
3353
4464
  runCommand: (commandId, target = { area: "grid" }) => commands.run(commandId, target, "api"),
3354
4465
  scrollToRow: (rowId, options) => {
3355
4466
  const index = controller.getView().pageRows.findIndex(
@@ -3391,8 +4502,8 @@ function DataGrid(props) {
3391
4502
  selection.columnMode === false ? "" : "selectable-columns",
3392
4503
  selection.cellMode === false ? "" : "selectable-cells"
3393
4504
  );
3394
- return /* @__PURE__ */ jsxs13("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
3395
- /* @__PURE__ */ jsx20(
4505
+ return /* @__PURE__ */ jsxs19("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
4506
+ /* @__PURE__ */ jsx26(
3396
4507
  GridToolbar,
3397
4508
  {
3398
4509
  toolbar: props.toolbar,
@@ -3404,7 +4515,7 @@ function DataGrid(props) {
3404
4515
  announce
3405
4516
  }
3406
4517
  ),
3407
- showGroupByBar && /* @__PURE__ */ jsx20(
4518
+ showGroupByBar && /* @__PURE__ */ jsx26(
3408
4519
  GroupByBar,
3409
4520
  {
3410
4521
  columns: view.columns,
@@ -3418,7 +4529,7 @@ function DataGrid(props) {
3418
4529
  messages
3419
4530
  }
3420
4531
  ),
3421
- /* @__PURE__ */ jsxs13(
4532
+ /* @__PURE__ */ jsxs19(
3422
4533
  "div",
3423
4534
  {
3424
4535
  id: gridId,
@@ -3435,6 +4546,7 @@ function DataGrid(props) {
3435
4546
  ),
3436
4547
  onKeyDown: (event) => {
3437
4548
  if (!isOwnEvent(event)) return;
4549
+ handleCopyKey(event);
3438
4550
  if (event.key === "Escape") {
3439
4551
  if (drag.draggedColumnId !== null || resize.activeColumnId !== null)
3440
4552
  return;
@@ -3448,27 +4560,28 @@ function DataGrid(props) {
3448
4560
  }
3449
4561
  },
3450
4562
  children: [
3451
- /* @__PURE__ */ jsxs13(
4563
+ /* @__PURE__ */ jsxs19(
3452
4564
  "table",
3453
4565
  {
3454
4566
  ref: headerTableRef,
3455
4567
  style: { width: totalColumnWidth(view.columns) },
3456
4568
  className: classNames(gridTableClassName, "gridkit-data-grid-header"),
3457
4569
  children: [
3458
- /* @__PURE__ */ jsx20(GridColgroup, { columns: view.columns }),
3459
- /* @__PURE__ */ jsx20(
4570
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4571
+ /* @__PURE__ */ jsx26(
3460
4572
  GridHeader,
3461
4573
  {
3462
4574
  columns: view.columns,
3463
4575
  resize,
3464
4576
  drag,
3465
4577
  controller,
3466
- columnSort: state.columnSort,
4578
+ state,
3467
4579
  sortableColumns,
3468
- groupBy: state.groupBy,
3469
4580
  groupableColumns,
3470
4581
  groupToggleIconColumns,
3471
4582
  headerMenu: contextMenu.opens("header"),
4583
+ panels,
4584
+ messages,
3472
4585
  nav,
3473
4586
  selection
3474
4587
  }
@@ -3476,7 +4589,7 @@ function DataGrid(props) {
3476
4589
  ]
3477
4590
  }
3478
4591
  ),
3479
- /* @__PURE__ */ jsx20(
4592
+ /* @__PURE__ */ jsx26(
3480
4593
  "div",
3481
4594
  {
3482
4595
  className: "gridkit-data-grid-body",
@@ -3484,15 +4597,15 @@ function DataGrid(props) {
3484
4597
  style: height === void 0 ? void 0 : {
3485
4598
  "--gridkit-body-height": typeof height === "number" ? `${String(height)}px` : height
3486
4599
  },
3487
- children: /* @__PURE__ */ jsxs13(
4600
+ children: /* @__PURE__ */ jsxs19(
3488
4601
  "table",
3489
4602
  {
3490
4603
  ref: bodyTableRef,
3491
4604
  style: { width: totalColumnWidth(view.columns) },
3492
4605
  className: gridTableClassName,
3493
4606
  children: [
3494
- /* @__PURE__ */ jsx20(GridColgroup, { columns: view.columns }),
3495
- /* @__PURE__ */ jsx20(
4607
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4608
+ /* @__PURE__ */ jsx26(
3496
4609
  GridBody,
3497
4610
  {
3498
4611
  columns: view.columns,
@@ -3521,7 +4634,7 @@ function DataGrid(props) {
3521
4634
  )
3522
4635
  }
3523
4636
  ),
3524
- aggregates.length > 0 && /* @__PURE__ */ jsxs13(
4637
+ aggregates.length > 0 && /* @__PURE__ */ jsxs19(
3525
4638
  "table",
3526
4639
  {
3527
4640
  style: { width: totalColumnWidth(view.columns) },
@@ -3530,8 +4643,8 @@ function DataGrid(props) {
3530
4643
  "gridkit-data-grid-footer"
3531
4644
  ),
3532
4645
  children: [
3533
- /* @__PURE__ */ jsx20(GridColgroup, { columns: view.columns }),
3534
- /* @__PURE__ */ jsx20(
4646
+ /* @__PURE__ */ jsx26(GridColgroup, { columns: view.columns }),
4647
+ /* @__PURE__ */ jsx26(
3535
4648
  GridFooter,
3536
4649
  {
3537
4650
  columns: view.columns,
@@ -3547,9 +4660,17 @@ function DataGrid(props) {
3547
4660
  ]
3548
4661
  }
3549
4662
  ),
3550
- /* @__PURE__ */ jsx20(GridContextMenu, { contextMenu }),
3551
- /* @__PURE__ */ jsx20(GridPanels, { panels, controller, messages }),
3552
- paginated && (pager?.template ? pager.template(pagerContext) : /* @__PURE__ */ jsx20(GridPager, { pager: pagerContext, config: pager, messages }))
4663
+ /* @__PURE__ */ jsx26(GridContextMenu, { contextMenu }),
4664
+ /* @__PURE__ */ jsx26(
4665
+ GridPanels,
4666
+ {
4667
+ panels,
4668
+ controller,
4669
+ commands,
4670
+ messages
4671
+ }
4672
+ ),
4673
+ paginated && (pager?.template ? pager.template(pagerContext) : /* @__PURE__ */ jsx26(GridPager, { pager: pagerContext, config: pager, messages }))
3553
4674
  ] });
3554
4675
  }
3555
4676
 
@@ -3726,9 +4847,9 @@ function useColumnVisibilityState(gridRef) {
3726
4847
  }
3727
4848
 
3728
4849
  // src/DataGrid/hooks/useFilterState.ts
3729
- import { useCallback as useCallback11, useState as useState16 } from "react";
4850
+ import { useCallback as useCallback11, useState as useState21 } from "react";
3730
4851
  function useFilterState(gridRef) {
3731
- const [fallback] = useState16(() => []);
4852
+ const [fallback] = useState21(() => []);
3732
4853
  const filter = useGridSnapshot(
3733
4854
  gridRef,
3734
4855
  useCallback11((api) => api.getFilter(), []),
@@ -3741,8 +4862,12 @@ function useFilterState(gridRef) {
3741
4862
  }
3742
4863
  };
3743
4864
  }
4865
+
4866
+ // src/index.ts
4867
+ import { excelFilter } from "@gridkitjs/core";
3744
4868
  export {
3745
4869
  DataGrid,
4870
+ excelFilter,
3746
4871
  serializeGridState,
3747
4872
  useAggregateState,
3748
4873
  useColumnOrderState,