@opencorestack/opengridx 3.5.1 → 3.7.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/CHANGELOG.md +53 -0
- package/README.md +2 -0
- package/dist/ai.cjs +4 -2
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.es.js +4 -2
- package/dist/ai.es.js.map +1 -1
- package/dist/index.d.ts +62 -0
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +9643 -9024
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -16
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +17 -3
- package/docs/README.md +2 -0
- package/docs/architecture/datagrid-orchestration.md +46 -29
- package/docs/architecture/grid-row-meta.md +1 -1
- package/docs/customization/slots-api.md +1 -1
- package/docs/customization/theming.md +43 -0
- package/docs/features/ai-toolkit.md +2 -2
- package/docs/features/export-guide.md +2 -0
- package/docs/features/header-filters.md +34 -0
- package/docs/features/keyboard-navigation.md +2 -0
- package/docs/features/nextjs.md +241 -0
- package/docs/features/pdf-export.md +2 -0
- package/docs/features/pinning.md +2 -0
- package/docs/features/rtl.md +58 -0
- package/docs/features/virtualization.md +16 -2
- package/docs/performance.md +89 -119
- package/docs/roadmap.md +4 -2
- package/lib/ai/promptHandler.ts +12 -8
- package/lib/ai/validate.ts +5 -2
- package/lib/components/AiAssistant/GridAiAssistantPanel.css +4 -2
- package/lib/components/Cell/Cell.css +31 -9
- package/lib/components/Cell/Cell.tsx +2 -2
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +4 -4
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +3 -3
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +18 -2
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +3 -1
- package/lib/components/DataGrid/DataGrid.css +19 -13
- package/lib/components/DataGrid/DataGrid.tsx +163 -670
- package/lib/components/DataGrid/GridAggregationFooter.tsx +3 -3
- package/lib/components/DataGrid/GridListView.tsx +3 -1
- package/lib/components/DataGrid/GridRoot.tsx +239 -0
- package/lib/components/DataGrid/GridViewport.tsx +314 -0
- package/lib/components/FilterPanel/FilterPanel.css +9 -8
- package/lib/components/Header/ColumnMenu.css +5 -4
- package/lib/components/Header/ColumnMenu.tsx +14 -6
- package/lib/components/Header/Header.css +23 -14
- package/lib/components/Header/Header.tsx +16 -9
- package/lib/components/Header/headerColumnStyle.ts +3 -2
- package/lib/components/HeaderFilter/HeaderFilter.css +9 -3
- package/lib/components/HeaderFilter/HeaderFilterCell.tsx +21 -18
- package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +5 -4
- package/lib/components/HeaderFilter/HeaderFilterRow.tsx +9 -3
- package/lib/components/ListView/ListViewRow.tsx +1 -1
- package/lib/components/Pagination/Pagination.css +5 -1
- package/lib/components/Pagination/Pagination.tsx +4 -4
- package/lib/components/Row/Row.css +2 -2
- package/lib/components/Row/Row.tsx +3 -3
- package/lib/components/SkeletonRow/SkeletonRow.tsx +2 -2
- package/lib/components/Toolbar/GridToolbar.css +8 -7
- package/lib/components/Toolbar/GridToolbar.tsx +8 -28
- package/lib/components/Toolbar/PivotPanel.css +3 -3
- package/lib/components/Toolbar/PivotPanel.tsx +4 -7
- package/lib/components/ui/ExpandIcon.css +13 -0
- package/lib/components/ui/ExpandIcon.tsx +2 -4
- package/lib/hooks/core/useGridCellSelection.ts +20 -5
- package/lib/hooks/core/useGridColumns.tsx +2 -2
- package/lib/hooks/core/useGridDevWarnings.ts +3 -1
- package/lib/hooks/core/useGridEditPipeline.ts +55 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +7 -4
- package/lib/hooks/core/useGridRangeEditing.ts +129 -0
- package/lib/hooks/core/useGridRenderWindow.ts +66 -0
- package/lib/hooks/core/useGridScrollSync.ts +28 -5
- package/lib/hooks/core/useGridScrollViewport.ts +42 -0
- package/lib/hooks/core/useGridSelectionAndStateApi.ts +61 -0
- package/lib/hooks/core/useGridToolbarStack.ts +49 -0
- package/lib/hooks/core/useGridViewApis.ts +46 -0
- package/lib/hooks/core/useLayout.ts +13 -6
- package/lib/hooks/useRowGrouping.ts +9 -9
- package/lib/styles/base/variables.css +12 -0
- package/lib/theme/DataGridThemeProvider.tsx +15 -0
- package/lib/theme/__baseline__/default-theme.darwin.chromium.json +1503 -0
- package/lib/theme/__baseline__/default-theme.darwin.firefox.json +1503 -0
- package/lib/theme/__baseline__/default-theme.darwin.webkit.json +1503 -0
- package/lib/theme/types.ts +31 -0
- package/lib/types/ai.ts +109 -0
- package/lib/types/api.ts +169 -0
- package/lib/types/columns.ts +341 -0
- package/lib/types/dataSource.ts +40 -0
- package/lib/types/editing.ts +77 -0
- package/lib/types/export.ts +164 -0
- package/lib/types/filtering.ts +52 -0
- package/lib/types/grouping.ts +27 -0
- package/lib/types/index.ts +18 -1672
- package/lib/types/locale.ts +51 -0
- package/lib/types/props.ts +405 -0
- package/lib/types/rows.ts +132 -0
- package/lib/types/selection.ts +66 -0
- package/lib/types/slots.ts +56 -0
- package/lib/types/sorting.ts +25 -0
- package/lib/types/state.ts +35 -0
- package/lib/utils/aggregation/index.ts +56 -1
- package/lib/utils/direction.ts +52 -0
- package/lib/utils/export/exportToExcelAdvanced.ts +9 -2
- package/lib/utils/export/exportToPdf.ts +2 -1
- package/lib/utils/filtering/index.ts +58 -6
- package/lib/utils/headerFilterLocale.ts +37 -0
- package/lib/utils/scroll/index.ts +5 -3
- package/lib/utils/sorting/index.ts +141 -26
- package/lib/utils/values.ts +10 -6
- package/llms.txt +4 -2
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [3.7.0] — 2026-10-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **16 theme keys for fonts, dividers and corners** (additive; every one defaults to exactly what the grid drew before). `grid.headerFontFamily` / `headerFontWeight` / `cellFontFamily` / `cellFontWeight` (`--ogx-grid-header-font-family`, `--ogx-grid-header-font-weight` default 600, `--ogx-grid-cell-font-family`, `--ogx-grid-cell-font-weight`), `grid.headerPaddingX` (`--ogx-grid-header-padding-x`, defaults to the cell padding), `grid.borderWidth` (`--ogx-grid-border-width`, 1px) and `grid.borderRadius` (`--ogx-grid-border-radius`, defaults to `--ogx-border-radius-lg`), `grid.columnDividerWidth` / `headerColumnDividerWidth` / `rowDividerWidth` (`--ogx-grid-column-divider-width`, `--ogx-grid-header-column-divider-width`, `--ogx-grid-row-divider-width`, 1px; `0` hides them) and `grid.columnDividerColor` / `rowDividerColor` (`--ogx-grid-column-divider-color`, `--ogx-grid-row-divider-color`, default the grid border colour), and `overlays.fontFamily` / `fontWeight` / `fontSize` (`--ogx-overlay-font-*`) for the column menu, header filter menu, toolbar panels and AI panel. The hard-coded 1px divider borders of cells, rows, header rows, column-group rows, the header filter row, aggregation footer cells and the right-pinned edge now read these variables (logical `border-inline-*` properties kept). A new browser test (`lib/theme/defaultThemeBaseline.browser.test.tsx`) compares computed styles and element boxes of eleven grids (plain, pinned with column groups and selection, dark and compact presets, header filters and their menu, aggregation footer, toolbar panels, column menu) with a baseline captured before the change, in Chromium, Firefox and WebKit: no differences. Guide: *Fonts, dividers and corners* and the *hide the dividers* recipe in `docs/customization/theming.md`.
|
|
13
|
+
- **Theme Builder demo page** (`/theme-builder`, under Customization, linked from the Theming page). Grouped, collapsible controls with a reset each: colour picker plus text field, font family (presets or custom), weight 300–800 and size, sliders for heights, padding, widths and radius, and show/hide switches for body column dividers, header dividers and row dividers (they set the width to 0). A live preview grid (toolbar, pinned column, selected row, row grouping, density switch, a button that opens the column menu) in `DataGridThemeProvider`. A Light / Dark switch picks which theme is edited and previewed; "Start from" loads a built-in preset. Copy theme (TypeScript, only values that differ from the defaults, light, dark or both; clipboard API with a fallback), Download `.ts`, Share via URL (`#theme=…`, restored on load). WCAG AA contrast hints for seven text/background pairs (warnings only) and a collapsed Advanced JSON editor for every theme key. Controls move into a drawer when the page is narrower than about 1,060 px. The demo smoke test also runs it in Firefox and WebKit.
|
|
14
|
+
|
|
15
|
+
### Internal
|
|
16
|
+
|
|
17
|
+
- **`DataGrid.tsx` split up** (1,128 → 620 lines), with no change to the public API, the rendered DOM, attributes, class names, event order or hook order. The JSX moved into `GridRoot` (root element, toolbar, panels, list view, footer, live region, overlays) and `GridViewport` (the `role="grid"` viewport and the shared row renderer props). Seven composite hooks each wrap a contiguous run of the documented hook order and call it unchanged: `useGridEditPipeline` (12a–13), `useGridSelectionAndStateApi` (23–24), `useGridViewApis` (30–31a), `useGridScrollViewport` (32–33), `useGridRangeEditing` (36a–36d), `useGridRenderWindow` (38–41) and `useGridToolbarStack` (44–46). `docs/architecture/datagrid-orchestration.md` lists each hook with its composite. React DevTools shows the two new components (`GridRoot`, `GridViewport`) under `DataGrid`; each renders once per grid render.
|
|
18
|
+
- **Demo-site smoke test** (`npm run test:demo`, `e2e/demo.spec.ts`, `e2e/playwright.demo.config.ts`). Builds the demo and serves it with `vite preview` on port 4322 under `/OpenGridX/`, then loads every route of `demo/App.tsx` (read from the source; 55 routes, checked against the built sidebar) in Chromium, and seven heavy pages (cell selection, AI assistant, full feature test, RTL, theming, pivot, infinite scroll) also in Firefox and WebKit. Each page must log no console error or warning and no page error, replace the loading skeleton, show text, and every grid must show a row or an intentional empty / loading / error state. Runs in `ci.yml` after the package smoke suite and in `deploy.yml` in place of the bare demo build, so a broken demo page is never deployed. No demo page was broken.
|
|
19
|
+
- **Height-limit guidance.** The development warning for content taller than the browser lays out now names `dataSource` (server-side pages or infinite scroll) with `docs/features/data-source.md`, then `pagination` and compact density, and links the new *The browser height limit* section of `docs/features/virtualization.md`. That section opens with a decision guide: up to about 300,000 rows at 52 px fit in every browser, up to about 600,000 in Chromium and WebKit only, beyond that use a `dataSource` or pagination; `density="compact"` stretches each limit about 1.6×. `docs/performance.md` links it. New unit test `useGridDevWarnings.test.ts` covers the warning text.
|
|
20
|
+
- **`source-map-js` pinned to 1.2.2** through `overrides` in the root and in the e2e and bench fixtures (GHSA-68fv-2mgg-jv7q, high, dev toolchain only; the root lockfile had 1.2.1). The `sprintf-js` advisory (GHSA-hp3w-g68c-fv3c, no patched release) stays open: it comes from `@microsoft/api-extractor` → `@rushstack/ts-command-line` → `argparse@1`, which `vite-plugin-dts` v5 still needs to bundle `dist/index.d.ts` and `dist/ai.d.ts` (api-extractor 7.59.4 still depends on `argparse ~1.0.9`), so upgrading to v5 would not remove it. It only runs at build time on this repository's own sources.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [3.6.0] — 2026-10-12
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Translatable header filter row.** New `GridLocaleText` keys for every string the header filter row shows or announces: `headerFilterCustom` / `headerFilterCustomTooltip` ("Custom filter"), `headerFilterCellLabel`, `headerFilterValueLabel`, `headerFilterOperatorButtonLabel`, `headerFilterOperatorMenuLabel`, `headerFilterClearLabel` (aria-labels, functions of the column header), `headerFilterClearTooltip`, `headerFilterAny` / `headerFilterTrue` / `headerFilterFalse` (the selects) and `headerFilterOperators` (operator names). Each defaults to the English text it replaces. The *Locale Text* demo shows them in French.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- The column resize handle now releases its pointer capture when a resize ends. A capture left behind (for example by a press that never got a real `pointerup`) could keep the pointer's later events from reaching the rest of the page.
|
|
33
|
+
- **Apps without the optional export peers failed to build in webpack and Next.js** (`Module not found: Can't resolve 'exceljs'` from `next build` on Next.js 15 and 16, and from plain webpack 5). The `exceljs` import in `exportToExcelAdvanced` now sits inside a `try` block, as the `jspdf` imports already did, which webpack and Turbopack treat as an optional dependency: apps without `exceljs`, `jspdf` and `jspdf-autotable` build (webpack shows a warning) and `exportToExcelAdvanced` rejects with `exportToExcelAdvanced requires 'exceljs'. Run: npm install exceljs`. Apps that install the peers are unchanged. The `next.config.ts` aliases in the Next.js guide are no longer needed. The package smoke suite has a third fixture, `e2e/fixtures/nopeers` (webpack 5, no peers), that must build, render the grid and get the install hints in Chromium, Firefox and WebKit.
|
|
34
|
+
- **`validateGridAiState` reported a top-level `message` as "Unknown part"** when given a JSON string, and also for an object passed directly. A string `message` (the model's reply text) is now skipped in both forms; a non-string `message` is reported as `Expected text, got …`. `createGridAiPromptHandler` now also returns a `message` key found inside a JSON string reply or a fenced JSON block as `result.message` (joined with the text around the block when both exist), as it did for object replies. The Next.js guide no longer strips `message` before validating.
|
|
35
|
+
- **Importing the grid from a React Server Component crashed the build** (`TypeError: createContext is not a function` in Next.js App Router "collecting page data"). `dist/opengridx.es.js` now starts with `'use client'`, so a Server Component gets a client reference: `<DataGrid>` with serialisable props can be rendered from a server file, and the grid hydrates in the browser. Wrapping it in your own `'use client'` file (needed for column functions) works as before. `scripts/check-bundle.mjs` checks the directive. The UMD build is minified and does not carry it; Next.js and other RSC bundlers load the ES build.
|
|
36
|
+
- **Right-to-left grids** (`dir="rtl"` on an ancestor or `<html>`). Unpinned RTL grids now work: horizontal scrolling no longer leaves the body blank (column virtualization read the negative RTL `scrollLeft` as 0; offsets are now measured from the start edge, also for keyboard scroll-into-view and `scrollToIndexes`); ArrowLeft / ArrowRight (and Shift+Arrow ranges, Alt+Arrow expand / collapse and column resize) follow the screen direction; cell text uses `text-align: start` / `end` (`align: 'left'` / `'right'` mean start / end); the resize handle sits on the column's left edge and dragging left widens it; the fill handle sits on the range's bottom-left corner; the range outline is drawn on its outer edges; dragging a range past the left edge auto-scrolls; toolbar panels open rightwards from their button. Left-to-right grids are unchanged (logical CSS properties, or code that runs only when the computed direction is `rtl`; a browser test compared 415 element positions in Chromium, Firefox and WebKit before and after).
|
|
37
|
+
- **Pinned columns in right-to-left grids.** `pinnedColumns.left` / `.right` now mean the inline start / end: in RTL left-pinned columns (and the checkbox, expand and reorder columns pinned with `pinCheckboxColumn` / `pinExpandColumn`) stick to the right edge and right-pinned ones to the left edge, in the header, column group rows, header filter row, body, skeleton rows and aggregation footer. They used physical `left` / `right` sticky offsets and scrolled away. Pinned shadows and section borders face the scrolling columns, range drag auto-scroll stops at the pinned sections on either side, and the column menu's *Pin to Left* / *Pin to Right* pin by screen side (in RTL *Pin to Left* adds to `pinnedColumns.right`). Left-to-right is pixel-identical: a browser test compared 2,211 element boxes, shadows, borders and pseudo-element offsets (pinned columns, system columns, column groups, header filters, footer; before and after a scroll and with a range) in Chromium, Firefox and WebKit before and after.
|
|
38
|
+
- **Mirrored icons in RTL.** Pagination first / previous / next / last arrows and the tree / group expand chevron mirror in RTL; tree indentation is on the start side. Sort arrows and other non-directional icons are unchanged.
|
|
39
|
+
- **Switching `dir` on a mounted, scrolled grid** left the body blank in Firefox (it resets `scrollLeft` without a scroll event); the grid now re-reads the scroll position when a `dir` attribute changes.
|
|
40
|
+
- **RTL browser tests failed on Linux CI by 10 px.** Test-only: Playwright's headless Chromium hides scrollbars, but with classic scrollbars `scrollbar-gutter: stable` still reserves the gutter and in RTL Chromium then cannot scroll to the start (`scrollLeft` stops at minus the gutter; a plain scroller does the same). The tests now measure against the browser's scroll origin and scrollport.
|
|
41
|
+
|
|
42
|
+
### Docs
|
|
43
|
+
|
|
44
|
+
- **Right-to-left guide** — `docs/features/rtl.md`: what works in RTL, what was fixed, and the known limitations with workarounds (do not pin columns in RTL grids). New demo page *Right-to-Left* (`/rtl`) with Arabic and Hebrew data and an RTL toggle.
|
|
45
|
+
- **Next.js (App Router) guide** — `docs/features/nextjs.md`: the `'use client'` grid file, the stylesheet in `app/layout.tsx`, server-fetched rows as props (columns with functions stay in the client file), `dataSource` with a Route Handler, `@opencorestack/opengridx/ai` on the server and in the browser (the validation brand does not survive JSON, so the client validates), the Pages Router, and the common errors. Verified with `next build` + `next start` on Next.js 16.4 (Turbopack) and 15.5 (webpack), React 19.3, in Chromium, Firefox and WebKit. It also covers the optional export peers (not needed to build; see Fixed).
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **Faster sort, quick filter and grouping on large data; results unchanged.** Row order, filtered rows and group contents are the same as before (randomized tests compare them with the previous implementation). Medians at 1M rows (`npm run bench`, Apple M1, before → after, `docs/performance.md` → Benchmarks):
|
|
50
|
+
- Sort by a number column: Chromium 1,716 → 467 ms, Firefox 1,935 → 468 ms, WebKit 1,126 → 340 ms.
|
|
51
|
+
- Sort by a string column: Chromium 1,510 → 467 ms, Firefox 2,338 → 560 ms, WebKit 2,939 → 562 ms. Strings are ranked by sorting only their distinct values with the collator instead of comparing every row pair with it.
|
|
52
|
+
- Quick filter, first keystroke: Chromium 2,127 → 1,813 ms, Firefox 3,283 → 2,789 ms, WebKit 3,646 → 3,166 ms.
|
|
53
|
+
- Group by a column + `sum`: Chromium 924 → 693 ms, Firefox 1,879 → 1,776 ms, WebKit 818 → 675 ms.
|
|
54
|
+
- Also faster: number filters (144 → 97 ms in Chromium) and mount (804 → 621 ms), from reading plain cells without allocating and computing row heights in one pass.
|
|
55
|
+
|
|
56
|
+
### Internal
|
|
57
|
+
|
|
58
|
+
- `docs/superpowers/specs/2026-10-12-nonblocking-pipeline-proposal.md`: a proposal for time-sliced sorting, filtering and grouping (not implemented).
|
|
59
|
+
- `lib/types/index.ts` is split into one file per domain (`rows`, `columns`, `sorting`, `filtering`, `editing`, `export`, `dataSource`, `selection`, `grouping`, `state`, `locale`, `props`, `slots`, `api`, `ai`); `index.ts` re-exports them. A move only: the published `index.d.ts` and `ai.d.ts` declare the same types with the same doc comments.
|
|
60
|
+
|
|
8
61
|
## [3.5.1] — 2026-10-12
|
|
9
62
|
|
|
10
63
|
### Fixed
|
package/README.md
CHANGED
|
@@ -532,7 +532,9 @@ Full documentation at 👉 **[opencorestack.github.io/OpenGridX](https://opencor
|
|
|
532
532
|
- **[Fill Handle](docs/features/fill-handle.md)** — Drag the range corner to fill a series or copy; Ctrl+D / Ctrl+R
|
|
533
533
|
- **[AI Toolkit](docs/features/ai-toolkit.md)** — Schema and validator for driving the grid from your own model
|
|
534
534
|
- **[Keyboard & Accessibility](docs/features/keyboard-navigation.md)** — Keys, focus and ARIA
|
|
535
|
+
- **[Right-to-Left (RTL)](docs/features/rtl.md)** — `dir="rtl"` support and its limits
|
|
535
536
|
- **[Data Source](docs/features/data-source.md)** — Server-side integration
|
|
537
|
+
- **[Next.js (App Router)](docs/features/nextjs.md)** — Client components, server-fetched rows and Route Handlers
|
|
536
538
|
- **[Loading States](docs/features/loading-states.md)** — Skeleton and shimmer overlays
|
|
537
539
|
|
|
538
540
|
### 📊 Advanced Data
|
package/dist/ai.cjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||Q.has(e)||(Q.add(e),console.warn(`[OpenGridX] ${t}.`,r))}function pe(e,t,r){const o=e[t];if(!r?.valueGetter)return{value:o};try{return{value:r.valueGetter({row:e,field:t,value:o})}}catch(n){return{value:void 0,error:n??new Error("valueGetter failed")}}}function ge(e,t,r){const{value:o,error:n}=pe(e,t,r);return n!==void 0&&de(`valueGetter:${t}`,`valueGetter for column "${t}" threw; the value is read as undefined`,n),o}const me=/^(\d{4})-(\d{2})-(\d{2})$/;function ee(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const r=e.trim();if(r==="")return null;const o=me.exec(r);if(o){const n=Number(o[1]),i=Number(o[2])-1,s=Number(o[3]);if(t=new Date(n,i,s),t.getMonth()!==i||t.getDate()!==s)return null}else t=new Date(r)}else return null;return Number.isNaN(t.getTime())?null:t}function ye(e){const t=ee(e);if(!t)return"";const r=String(t.getFullYear()).padStart(4,"0"),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${r}-${o}-${n}`}function be(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const he=["sum","avg","count","min","max","unique"],B=["sum","avg","count","min","max"],te=["isEmpty","isNotEmpty"],D=3,U=["filter","sort","grouping","aggregation","pivot","columnVisibility"],H={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function re(e){const t=Array.isArray(e?.include)?e.include:U,r=Array.isArray(e?.exclude)?e.exclude:[];return U.filter(o=>t.includes(o)&&!r.includes(o))}function J(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const o of e){if(!$(o))continue;const n=o.field;typeof n!="string"||n===""||n.startsWith("__")||o.isSpacer===!0||t.has(n)||(t.add(n),r.push(o))}return r}function ne(e){return be(e.type)}const ie=e=>e.filterable!==!1,oe=e=>e.sortable!==!1,T=e=>e.groupable!==!1,se=e=>e.hideable!==!1;function F(e,t=he){if(e.aggregable===!1)return[];const r=e.type==="number"||e.aggregable===!0?t:t.filter(n=>n==="count"),o=e.availableAggregationFunctions;return Array.isArray(o)?r.filter(n=>o.includes(n)):[...r]}function $e(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const r of e.valueOptions){const o=r!==null&&typeof r=="object"?r.value:r;(typeof o=="string"||typeof o=="number"&&Number.isFinite(o))&&!t.includes(o)&&t.push(o)}return t}const Ae="OpenGridX grid state",Se=10;function j(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,r=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${r}`}function O(e,t,r){return r.descriptions?{...e,description:t}:e}function Oe(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(r=>typeof r=="string"||typeof r=="boolean"||typeof r=="number"&&Number.isFinite(r)).slice(0,Se)}function ve(e,t){let r;switch(e.type){case"number":r={type:"number"};break;case"date":r={type:"string",format:"date"};break;case"boolean":r={type:"boolean"};break;case"singleSelect":{const n=$e(e),i=n.length>0?{enum:n}:{type:"string"};r={anyOf:[i,{type:"array",items:i}]};break}default:r={type:"string"}}const o=Oe(e,t);return o.length>0?{...r,examples:o}:r}function Me(e,t){const r=ne(e),o=r.filter(s=>te.includes(s)),n=e.type==="singleSelect"?"; isAnyOf takes an array":"",i=o.length>0?`${o.join(" and ")} take no value${n}`:n.slice(2);return O({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:ve(e,t)},required:["field","operator"],additionalProperties:!1},i?`${j(e)}: ${i}`:j(e),t)}function Ee(e,t,r){const o=e.filter(ie),n={},i={enum:["and","or"]};if(o.length>0){r.filterItem={oneOf:o.map(s=>Me(s,t))};for(let s=D;s>=1;s--){const l=s===D?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${s+1}`}]};r[`filterGroup${s}`]={type:"object",properties:{logicOperator:i,items:{type:"array",items:l}},required:["logicOperator","items"],additionalProperties:!1}}n.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},n.logicOperator=i}return n.quickFilterValues=O({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:n,additionalProperties:!1}}function q(e){return{enum:e.map(t=>t.field)}}function we(e,t,r,o){switch(e){case"filter":return Ee(t,r,o);case"sort":{const n=t.filter(oe);return n.length===0?null:O({type:"array",items:{type:"object",properties:{field:q(n),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const n=t.filter(T);return n.length===0?null:O({type:"array",items:q(n),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const n={};for(const i of t){const s=F(i);s.length>0&&(n[i.field]=O({enum:s},j(i),r))}return Object.keys(n).length===0?null:{type:"object",properties:n,additionalProperties:!1}}case"pivot":{const n=t.filter(T),i=t.map(l=>({col:l,fns:F(l,B)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>O({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},j(l),r));if(n.length===0||i.length===0)return null;const s={type:"array",items:q(n),uniqueItems:!0};return{type:"object",properties:{rowFields:s,columnFields:s,valueFields:{type:"array",items:{oneOf:i}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const n={};for(const i of t.filter(se))n[i.field]=O({type:"boolean"},j(i),r);return Object.keys(n).length===0?null:O({type:"object",properties:n,additionalProperties:!1},"false hides a column",r)}}}function Y(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},o=J(e),n={},i={};for(const l of re(t)){const a=we(l,o,r,n);a&&(i[H[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:Ae,type:"object",properties:i,additionalProperties:!1};return r.descriptions&&(s.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(n).length>0&&(s.$defs=n),s}const Fe=Symbol.for("opengridx.ai.validated");function I(e){return Object.defineProperty(e,Fe,{value:!0,enumerable:!1}),e}const M={ok:!1},h=e=>({ok:!0,value:e}),Ne=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,_e=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,je=/[\p{Sc}\s]/gu;function Te(e){let t=e.replace(je,"");if(t==="")return e.trim()===""?h(null):M;let r=!1;t.startsWith("(")&&t.endsWith(")")&&(r=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(r=!r),t=t.slice(1));let o=!1;if(t.endsWith("%")&&(o=!0,t=t.slice(0,-1)),ke.test(t))t=t.split(",").join("");else if(!Ne.test(t)&&!_e.test(t))return M;let n=Number(t);return Number.isFinite(n)?(o&&(n/=100),r&&(n=-n),h(n)):M}function Ge(e,t){const r=e.trim();if(r==="")return h(null);const o=ee(r);return o?h(o):M}const Ve=new Set(["true","yes","1"]),Pe=new Set(["false","no","0"]);function Re(e,t){const r=e.trim().toLowerCase();return r===""?h(null):Ve.has(r)?h(!0):Pe.has(r)?h(!1):M}function De(e,t){const r=e.trim();if(r==="")return h(null);if(!t||t.length===0)return M;const o=r.toLowerCase();for(const n of t){const i=typeof n=="object"?n.value:n;if(String(i).toLowerCase()===o)return h(i)}for(const n of t)if(typeof n=="object"&&n.label.toLowerCase()===o)return h(n.value);return M}const d=e=>({ok:!0,value:e}),A={ok:!1,reason:"invalidValue"},L=e=>e.ok?d(e.value):A;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function Ie(e){return e==null?d(""):typeof e=="string"?d(e):typeof e=="number"||typeof e=="boolean"?d(String(e)):A}function Le(e){return C(e)?d(null):typeof e=="number"?Number.isFinite(e)?d(e):A:typeof e!="string"?A:L(Te(e))}function Ce(e){return C(e)?d(null):e instanceof Date?Number.isNaN(e.getTime())?A:d(e):typeof e=="number"?Number.isFinite(e)?d(new Date(e)):A:typeof e!="string"?A:L(Ge(e))}function le(e){return C(e)?d(null):typeof e=="boolean"?d(e):typeof e!="string"&&typeof e!="number"?A:L(Re(String(e)))}function qe(e,t){if(C(e))return d(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return A;const r=t.valueOptions;return!r||r.length===0?d(e):L(De(String(e),r))}function We(e,t){switch(t.type){case"number":return Le(e);case"date":return Ce(e);case"boolean":return le(e);case"singleSelect":return qe(e,t);default:return Ie(e)}}const W=200,Ue=100;function u(e,t,r){return e.errors.length<Ue&&e.errors.push({path:t,message:r}),null}function p(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function ae(e){return typeof e=="string"?e.trim().toLowerCase():null}function K(e){return Object.keys(e).filter(t=>t!=="__proto__")}function N(e,t,r,o,n){const i=typeof t=="string"?e.byField.get(t):void 0;return i?o(i)?i:u(e,r,`Field ${p(t)} cannot be ${n}`):u(e,r,`Unknown field ${p(t)}`)}function _(e,t,r){if(t==null)return[];const o=Array.isArray(t)?t:[t];return o.length>W&&u(e,r,`Only the first ${W} entries are read`),o.slice(0,W)}function G(e,t,r,o){const n=ae(t);return o.find(i=>i.toLowerCase()===n)??u(e,r,`${p(t)} is not one of ${o.join(", ")}`)}function Z(e,t,r,o){const n=We(t,r);if(!n.ok)return u(e,o,`${p(t)} is not a valid ${r.type??"string"} value`);const i=n.value instanceof Date?ye(n.value):n.value;return i==null||i===""?u(e,o,"Missing value"):{v:i}}function Xe(e,t,r){const o=N(e,t.field,`${r}.field`,ie,"filtered");if(!o)return null;const n=G(e,t.operator,`${r}.operator`,ne(o));if(n===null)return null;const i={field:o.field,operator:n};if(te.includes(i.operator))return i;const s=S(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return _(e,s,`${r}.value`).forEach((c,v)=>{const g=Z(e,c,o,`${r}.value[${v}]`);g&&!a.includes(g.v)&&a.push(g.v)}),a.length===0?u(e,`${r}.value`,"No valid values"):(i.value=a,i)}if(Array.isArray(s))return u(e,`${r}.value`,"Expected one value, not a list");const l=Z(e,s,o,`${r}.value`);return l?(i.value=l.v,i):null}function ue(e,t,r){return t===void 0?null:G(e,t,r,["and","or"])}function fe(e,t,r,o){const n=[];return _(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!$(i))u(e,l,`Expected a filter, got ${p(i)}`);else if(S(i,"items")||S(i,"logicOperator")){if(o>=D){u(e,l,`Groups nest at most ${D} levels`);return}const a=fe(e,i.items,`${l}.items`,o+1);a.length===0?u(e,l,"Empty group removed"):n.push({logicOperator:ue(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Xe(e,i,l);a&&n.push(a)}}),n}function Be(e,t,r){const o={};S(t,"items")&&(o.items=fe(e,t.items,`${r}.items`,0));const n=ue(e,t.logicOperator,`${r}.logicOperator`);return n&&(o.logicOperator=n),S(t,"quickFilterValues")&&(o.quickFilterValues=[],_(e,t.quickFilterValues,`${r}.quickFilterValues`).forEach((i,s)=>{const l=typeof i=="string"||typeof i=="number"?String(i).trim():null;l===null?u(e,`${r}.quickFilterValues[${s}]`,`Expected text, got ${p(i)}`):l!==""&&o.quickFilterValues?.push(l)})),o}function He(e,t,r){const o=[];return _(e,t,r).forEach((n,i)=>{const s=`${r}[${i}]`;if(!$(n))return u(e,s,`Expected { field, sort }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,oe,"sorted"),a=l&&G(e,n.sort,`${s}.sort`,["asc","desc"]);!l||!a||(o.some(c=>c.field===l.field)?u(e,s,`${p(l.field)} is sorted twice`):o.push({field:l.field,sort:a}))}),o}function X(e,t,r,o,n){const i=[];return _(e,t,r).forEach((s,l)=>{const a=N(e,s,`${r}[${l}]`,o,n);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Je=e=>F(e).length>0,Ye=e=>F(e,B).length>0;function Ke(e,t,r){const o={};for(const n of K(t)){if(ae(t[n])==="none")continue;const i=N(e,n,`${r}.${n}`,Je,"aggregated"),s=i&&G(e,t[n],`${r}.${n}`,F(i));i&&s&&(o[i.field]=s)}return o}function ze(e,t,r){const o=[];return _(e,t.valueFields,`${r}.valueFields`).forEach((n,i)=>{const s=`${r}.valueFields[${i}]`;if(!$(n))return u(e,s,`Expected { field, aggFn }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,Ye,"a pivot value"),a=l&&G(e,n.aggFn,`${s}.aggFn`,F(l,B));l&&a&&!o.some(c=>c.field===l.field&&c.aggFn===a)&&o.push({field:l.field,aggFn:a})}),{rowFields:X(e,t.rowFields,`${r}.rowFields`,T,"a pivot label"),columnFields:X(e,t.columnFields,`${r}.columnFields`,T,"a pivot label"),valueFields:o}}function Qe(e,t,r){const o={};for(const n of K(t)){const i=N(e,n,`${r}.${n}`,se,"hidden"),s=le(t[n]);i&&(s.ok&&s.value!==null?o[i.field]=s.value:u(e,`${r}.${n}`,`Expected true or false, got ${p(t[n])}`))}return o}function Ze(e,t,r,o,n){t==="sort"?n.sortModel=He(e,r,o):t==="grouping"?n.rowGroupingModel=X(e,r,o,T,"grouped"):$(r)?t==="filter"?n.filterModel=Be(e,r,o):t==="aggregation"?n.aggregationModel=Ke(e,r,o):t==="pivot"?n.pivotModel=ze(e,r,o):n.columnVisibilityModel=Qe(e,r,o):u(e,o,`Expected an object, got ${p(r)}`)}function z(e,t,r={}){const o=xe(e,t,r);return I(o.state),I(o)}function xe(e,t,r){const o={errors:[],byField:new Map},n={};try{for(const l of J(t))o.byField.set(l.field,l);let i=e;if(typeof e=="string")try{i=JSON.parse(e)}catch{return u(o,"","Not valid JSON"),{state:n,errors:o.errors}}if(!$(i))return u(o,"",`Expected an object, got ${p(i)}`),{state:n,errors:o.errors};const s=re(r);for(const l of K(i)){const a=U.find(c=>H[c]===l);l==="schemaVersion"?i[l]!==1&&u(o,l,`Unsupported schemaVersion ${p(i[l])}`):a?s.includes(a)?Ze(o,a,i[l],l,n):u(o,l,"Part not allowed"):u(o,l,"Unknown part")}}catch{return u(o,"","Could not read the input"),{state:{},errors:o.errors}}return{state:n,errors:o.errors}}const k="```";function V(){return typeof DOMException=="function"?new DOMException("The operation was aborted.","AbortError"):Object.assign(new Error("The operation was aborted."),{name:"AbortError"})}function et(e){const t=e.trim();if(t.startsWith("{"))return{json:t,message:""};const r=e.indexOf(k),o=r<0?-1:e.indexOf(`
|
|
2
|
-
`,r+k.length),n
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||Q.has(e)||(Q.add(e),console.warn(`[OpenGridX] ${t}.`,r))}function pe(e,t,r){const n=e[t];if(!r?.valueGetter)return{value:n};try{return{value:r.valueGetter({row:e,field:t,value:n})}}catch(o){return{value:void 0,error:o??new Error("valueGetter failed")}}}function ge(e,t,r){if(!r?.valueGetter)return e[t];const{value:n,error:o}=pe(e,t,r);return o!==void 0&&de(`valueGetter:${t}`,`valueGetter for column "${t}" threw; the value is read as undefined`,o),n}const me=/^(\d{4})-(\d{2})-(\d{2})$/;function ee(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const r=e.trim();if(r==="")return null;const n=me.exec(r);if(n){const o=Number(n[1]),i=Number(n[2])-1,s=Number(n[3]);if(t=new Date(o,i,s),t.getMonth()!==i||t.getDate()!==s)return null}else t=new Date(r)}else return null;return Number.isNaN(t.getTime())?null:t}function ye(e){const t=ee(e);if(!t)return"";const r=String(t.getFullYear()).padStart(4,"0"),n=String(t.getMonth()+1).padStart(2,"0"),o=String(t.getDate()).padStart(2,"0");return`${r}-${n}-${o}`}function be(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const he=["sum","avg","count","min","max","unique"],X=["sum","avg","count","min","max"],te=["isEmpty","isNotEmpty"],I=3,U=["filter","sort","grouping","aggregation","pivot","columnVisibility"],H={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function re(e){const t=Array.isArray(e?.include)?e.include:U,r=Array.isArray(e?.exclude)?e.exclude:[];return U.filter(n=>t.includes(n)&&!r.includes(n))}function J(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const n of e){if(!$(n))continue;const o=n.field;typeof o!="string"||o===""||o.startsWith("__")||n.isSpacer===!0||t.has(o)||(t.add(o),r.push(n))}return r}function ne(e){return be(e.type)}const ie=e=>e.filterable!==!1,oe=e=>e.sortable!==!1,G=e=>e.groupable!==!1,se=e=>e.hideable!==!1;function F(e,t=he){if(e.aggregable===!1)return[];const r=e.type==="number"||e.aggregable===!0?t:t.filter(o=>o==="count"),n=e.availableAggregationFunctions;return Array.isArray(n)?r.filter(o=>n.includes(o)):[...r]}function $e(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const r of e.valueOptions){const n=r!==null&&typeof r=="object"?r.value:r;(typeof n=="string"||typeof n=="number"&&Number.isFinite(n))&&!t.includes(n)&&t.push(n)}return t}const Ae="OpenGridX grid state",Se=10;function j(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,r=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${r}`}function O(e,t,r){return r.descriptions?{...e,description:t}:e}function Oe(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(r=>typeof r=="string"||typeof r=="boolean"||typeof r=="number"&&Number.isFinite(r)).slice(0,Se)}function ve(e,t){let r;switch(e.type){case"number":r={type:"number"};break;case"date":r={type:"string",format:"date"};break;case"boolean":r={type:"boolean"};break;case"singleSelect":{const o=$e(e),i=o.length>0?{enum:o}:{type:"string"};r={anyOf:[i,{type:"array",items:i}]};break}default:r={type:"string"}}const n=Oe(e,t);return n.length>0?{...r,examples:n}:r}function Me(e,t){const r=ne(e),n=r.filter(s=>te.includes(s)),o=e.type==="singleSelect"?"; isAnyOf takes an array":"",i=n.length>0?`${n.join(" and ")} take no value${o}`:o.slice(2);return O({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:ve(e,t)},required:["field","operator"],additionalProperties:!1},i?`${j(e)}: ${i}`:j(e),t)}function Ee(e,t,r){const n=e.filter(ie),o={},i={enum:["and","or"]};if(n.length>0){r.filterItem={oneOf:n.map(s=>Me(s,t))};for(let s=I;s>=1;s--){const l=s===I?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${s+1}`}]};r[`filterGroup${s}`]={type:"object",properties:{logicOperator:i,items:{type:"array",items:l}},required:["logicOperator","items"],additionalProperties:!1}}o.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},o.logicOperator=i}return o.quickFilterValues=O({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:o,additionalProperties:!1}}function q(e){return{enum:e.map(t=>t.field)}}function we(e,t,r,n){switch(e){case"filter":return Ee(t,r,n);case"sort":{const o=t.filter(oe);return o.length===0?null:O({type:"array",items:{type:"object",properties:{field:q(o),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const o=t.filter(G);return o.length===0?null:O({type:"array",items:q(o),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const o={};for(const i of t){const s=F(i);s.length>0&&(o[i.field]=O({enum:s},j(i),r))}return Object.keys(o).length===0?null:{type:"object",properties:o,additionalProperties:!1}}case"pivot":{const o=t.filter(G),i=t.map(l=>({col:l,fns:F(l,X)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>O({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},j(l),r));if(o.length===0||i.length===0)return null;const s={type:"array",items:q(o),uniqueItems:!0};return{type:"object",properties:{rowFields:s,columnFields:s,valueFields:{type:"array",items:{oneOf:i}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const o={};for(const i of t.filter(se))o[i.field]=O({type:"boolean"},j(i),r);return Object.keys(o).length===0?null:O({type:"object",properties:o,additionalProperties:!1},"false hides a column",r)}}}function Y(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},n=J(e),o={},i={};for(const l of re(t)){const a=we(l,n,r,o);a&&(i[H[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:Ae,type:"object",properties:i,additionalProperties:!1};return r.descriptions&&(s.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(o).length>0&&(s.$defs=o),s}const Fe=Symbol.for("opengridx.ai.validated");function D(e){return Object.defineProperty(e,Fe,{value:!0,enumerable:!1}),e}const M={ok:!1},h=e=>({ok:!0,value:e}),Ne=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,_e=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,je=/[\p{Sc}\s]/gu;function Ge(e){let t=e.replace(je,"");if(t==="")return e.trim()===""?h(null):M;let r=!1;t.startsWith("(")&&t.endsWith(")")&&(r=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(r=!r),t=t.slice(1));let n=!1;if(t.endsWith("%")&&(n=!0,t=t.slice(0,-1)),ke.test(t))t=t.split(",").join("");else if(!Ne.test(t)&&!_e.test(t))return M;let o=Number(t);return Number.isFinite(o)?(n&&(o/=100),r&&(o=-o),h(o)):M}function Te(e,t){const r=e.trim();if(r==="")return h(null);const n=ee(r);return n?h(n):M}const Ve=new Set(["true","yes","1"]),Pe=new Set(["false","no","0"]);function Re(e,t){const r=e.trim().toLowerCase();return r===""?h(null):Ve.has(r)?h(!0):Pe.has(r)?h(!1):M}function Ie(e,t){const r=e.trim();if(r==="")return h(null);if(!t||t.length===0)return M;const n=r.toLowerCase();for(const o of t){const i=typeof o=="object"?o.value:o;if(String(i).toLowerCase()===n)return h(i)}for(const o of t)if(typeof o=="object"&&o.label.toLowerCase()===n)return h(o.value);return M}const p=e=>({ok:!0,value:e}),A={ok:!1,reason:"invalidValue"},L=e=>e.ok?p(e.value):A;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function De(e){return e==null?p(""):typeof e=="string"?p(e):typeof e=="number"||typeof e=="boolean"?p(String(e)):A}function Le(e){return C(e)?p(null):typeof e=="number"?Number.isFinite(e)?p(e):A:typeof e!="string"?A:L(Ge(e))}function Ce(e){return C(e)?p(null):e instanceof Date?Number.isNaN(e.getTime())?A:p(e):typeof e=="number"?Number.isFinite(e)?p(new Date(e)):A:typeof e!="string"?A:L(Te(e))}function le(e){return C(e)?p(null):typeof e=="boolean"?p(e):typeof e!="string"&&typeof e!="number"?A:L(Re(String(e)))}function qe(e,t){if(C(e))return p(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return A;const r=t.valueOptions;return!r||r.length===0?p(e):L(Ie(String(e),r))}function We(e,t){switch(t.type){case"number":return Le(e);case"date":return Ce(e);case"boolean":return le(e);case"singleSelect":return qe(e,t);default:return De(e)}}const W=200,Ue=100;function u(e,t,r){return e.errors.length<Ue&&e.errors.push({path:t,message:r}),null}function c(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function ae(e){return typeof e=="string"?e.trim().toLowerCase():null}function K(e){return Object.keys(e).filter(t=>t!=="__proto__")}function N(e,t,r,n,o){const i=typeof t=="string"?e.byField.get(t):void 0;return i?n(i)?i:u(e,r,`Field ${c(t)} cannot be ${o}`):u(e,r,`Unknown field ${c(t)}`)}function _(e,t,r){if(t==null)return[];const n=Array.isArray(t)?t:[t];return n.length>W&&u(e,r,`Only the first ${W} entries are read`),n.slice(0,W)}function T(e,t,r,n){const o=ae(t);return n.find(i=>i.toLowerCase()===o)??u(e,r,`${c(t)} is not one of ${n.join(", ")}`)}function Z(e,t,r,n){const o=We(t,r);if(!o.ok)return u(e,n,`${c(t)} is not a valid ${r.type??"string"} value`);const i=o.value instanceof Date?ye(o.value):o.value;return i==null||i===""?u(e,n,"Missing value"):{v:i}}function Be(e,t,r){const n=N(e,t.field,`${r}.field`,ie,"filtered");if(!n)return null;const o=T(e,t.operator,`${r}.operator`,ne(n));if(o===null)return null;const i={field:n.field,operator:o};if(te.includes(i.operator))return i;const s=S(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return _(e,s,`${r}.value`).forEach((f,v)=>{const g=Z(e,f,n,`${r}.value[${v}]`);g&&!a.includes(g.v)&&a.push(g.v)}),a.length===0?u(e,`${r}.value`,"No valid values"):(i.value=a,i)}if(Array.isArray(s))return u(e,`${r}.value`,"Expected one value, not a list");const l=Z(e,s,n,`${r}.value`);return l?(i.value=l.v,i):null}function ue(e,t,r){return t===void 0?null:T(e,t,r,["and","or"])}function fe(e,t,r,n){const o=[];return _(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!$(i))u(e,l,`Expected a filter, got ${c(i)}`);else if(S(i,"items")||S(i,"logicOperator")){if(n>=I){u(e,l,`Groups nest at most ${I} levels`);return}const a=fe(e,i.items,`${l}.items`,n+1);a.length===0?u(e,l,"Empty group removed"):o.push({logicOperator:ue(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Be(e,i,l);a&&o.push(a)}}),o}function Xe(e,t,r){const n={};S(t,"items")&&(n.items=fe(e,t.items,`${r}.items`,0));const o=ue(e,t.logicOperator,`${r}.logicOperator`);return o&&(n.logicOperator=o),S(t,"quickFilterValues")&&(n.quickFilterValues=[],_(e,t.quickFilterValues,`${r}.quickFilterValues`).forEach((i,s)=>{const l=typeof i=="string"||typeof i=="number"?String(i).trim():null;l===null?u(e,`${r}.quickFilterValues[${s}]`,`Expected text, got ${c(i)}`):l!==""&&n.quickFilterValues?.push(l)})),n}function He(e,t,r){const n=[];return _(e,t,r).forEach((o,i)=>{const s=`${r}[${i}]`;if(!$(o))return u(e,s,`Expected { field, sort }, got ${c(o)}`);const l=N(e,o.field,`${s}.field`,oe,"sorted"),a=l&&T(e,o.sort,`${s}.sort`,["asc","desc"]);!l||!a||(n.some(f=>f.field===l.field)?u(e,s,`${c(l.field)} is sorted twice`):n.push({field:l.field,sort:a}))}),n}function B(e,t,r,n,o){const i=[];return _(e,t,r).forEach((s,l)=>{const a=N(e,s,`${r}[${l}]`,n,o);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Je=e=>F(e).length>0,Ye=e=>F(e,X).length>0;function Ke(e,t,r){const n={};for(const o of K(t)){if(ae(t[o])==="none")continue;const i=N(e,o,`${r}.${o}`,Je,"aggregated"),s=i&&T(e,t[o],`${r}.${o}`,F(i));i&&s&&(n[i.field]=s)}return n}function ze(e,t,r){const n=[];return _(e,t.valueFields,`${r}.valueFields`).forEach((o,i)=>{const s=`${r}.valueFields[${i}]`;if(!$(o))return u(e,s,`Expected { field, aggFn }, got ${c(o)}`);const l=N(e,o.field,`${s}.field`,Ye,"a pivot value"),a=l&&T(e,o.aggFn,`${s}.aggFn`,F(l,X));l&&a&&!n.some(f=>f.field===l.field&&f.aggFn===a)&&n.push({field:l.field,aggFn:a})}),{rowFields:B(e,t.rowFields,`${r}.rowFields`,G,"a pivot label"),columnFields:B(e,t.columnFields,`${r}.columnFields`,G,"a pivot label"),valueFields:n}}function Qe(e,t,r){const n={};for(const o of K(t)){const i=N(e,o,`${r}.${o}`,se,"hidden"),s=le(t[o]);i&&(s.ok&&s.value!==null?n[i.field]=s.value:u(e,`${r}.${o}`,`Expected true or false, got ${c(t[o])}`))}return n}function Ze(e,t,r,n,o){t==="sort"?o.sortModel=He(e,r,n):t==="grouping"?o.rowGroupingModel=B(e,r,n,G,"grouped"):$(r)?t==="filter"?o.filterModel=Xe(e,r,n):t==="aggregation"?o.aggregationModel=Ke(e,r,n):t==="pivot"?o.pivotModel=ze(e,r,n):o.columnVisibilityModel=Qe(e,r,n):u(e,n,`Expected an object, got ${c(r)}`)}function z(e,t,r={}){const n=xe(e,t,r);return D(n.state),D(n)}function xe(e,t,r){const n={errors:[],byField:new Map},o={};try{for(const l of J(t))n.byField.set(l.field,l);let i=e;if(typeof e=="string")try{i=JSON.parse(e)}catch{return u(n,"","Not valid JSON"),{state:o,errors:n.errors}}if(!$(i))return u(n,"",`Expected an object, got ${c(i)}`),{state:o,errors:n.errors};const s=re(r);for(const l of K(i)){const a=U.find(f=>H[f]===l);l==="schemaVersion"?i[l]!==1&&u(n,l,`Unsupported schemaVersion ${c(i[l])}`):l==="message"?typeof i[l]!="string"&&u(n,l,`Expected text, got ${c(i[l])}`):a?s.includes(a)?Ze(n,a,i[l],l,o):u(n,l,"Part not allowed"):u(n,l,"Unknown part")}}catch{return u(n,"","Could not read the input"),{state:{},errors:n.errors}}return{state:o,errors:n.errors}}const k="```";function V(){return typeof DOMException=="function"?new DOMException("The operation was aborted.","AbortError"):Object.assign(new Error("The operation was aborted."),{name:"AbortError"})}function et(e){const t=e.trim();if(t.startsWith("{"))return{json:t,message:""};const r=e.indexOf(k),n=r<0?-1:e.indexOf(`
|
|
2
|
+
`,r+k.length),o=n<0?-1:e.indexOf(k,n+1),i=o<0?"":e.slice(r+k.length,n).trim().toLowerCase();return o<0||i!==""&&i!=="json"?{json:null,message:t}:{json:e.slice(n+1,o),message:`${e.slice(0,r)}${e.slice(o+k.length)}`.trim()}}function tt(e){return e instanceof Error&&e.message?e.message:typeof e=="string"&&e?e:"Unknown error"}function ce(e,t){const r={state:D({}),errors:e};return t&&(r.message=t),D(r)}function rt(e,t,r){let n=e,o="";if(typeof e=="string"){const a=et(e);if(a.json===null)return ce([],a.message||void 0);o=a.message;try{n=JSON.parse(a.json)}catch{n=a.json}}const i=$(n)&&S(n,"message")&&typeof n.message=="string"?n.message.trim():"",s=[o,i].filter(Boolean).join(`
|
|
3
|
+
|
|
4
|
+
`),l=z(n,t,r);return s&&(l.message=s),l}function nt(e){const{columns:t,callModel:r,parts:n,schemaOptions:o={}}=e,i={include:n??o.include,exclude:o.exclude},s=Y(t,{...o,...i});return async(l,a)=>{const f=a?.signal??new AbortController().signal;if(f.aborted)throw V();const v=new Promise((m,y)=>{f.addEventListener("abort",()=>y(V()),{once:!0})});v.catch(()=>{});let g;try{const m={prompt:String(l),schema:s,currentState:a?.currentState??{},history:Array.isArray(a?.history)?a.history:[]};g=await Promise.race([r(m,{signal:f}),v])}catch(m){if(f.aborted)throw V();return ce([{path:"",message:`The model call failed: ${tt(m)}`}])}if(f.aborted)throw V();return rt(g,t,i)}}const it=20,ot=1e3,st=[{name:"set_filter",part:"filter",description:"Replace the filter model of the grid (filters and quick-filter words)."},{name:"set_sort",part:"sort",description:"Replace the sort model of the grid, most significant field first. An empty list clears sorting."},{name:"set_grouping",part:"grouping",description:"Group the rows by these fields, outermost first. An empty list removes grouping."},{name:"set_aggregation",part:"aggregation",description:"Replace the summaries: field to aggregation function. An empty object removes them."},{name:"set_column_visibility",part:"columnVisibility",description:"Show (true) or hide (false) columns. Columns not listed keep their visibility."}];function w(e,t=""){return{ok:!1,errors:[{path:t,message:e}]}}async function P(e){try{return e()}catch(t){return w(t instanceof Error&&t.message?t.message:"The tool failed")}}function R(e){const t=e?.current;return t&&typeof t.getGridAiState=="function"?t:null}function lt(e){return e instanceof Date?Number.isNaN(e.getTime())?null:e.toISOString():typeof e=="number"?Number.isFinite(e)?e:null:typeof e=="string"||typeof e=="boolean"?e:null}function at(e,t,r,n,o){const i=H[n],s=Y(t,{include:[n]});if(!S(s.properties,i))return null;const{$schema:l,schemaVersion:a,title:f,description:v,...g}=s;return{name:r,description:o,inputSchema:{...g,required:[i]},execute:m=>P(()=>{const y=R(e);if(!y)return w("The grid is not ready");if(!$(m)||!S(m,i))return w(`Expected an object with "${i}"`,i);const{state:d,errors:E}=z({[i]:m[i]},t,{include:[n]});if(E.length>0)return{ok:!1,errors:E};const b={};return d.filterModel&&y.setFilterModel(b.filterModel=d.filterModel),d.sortModel&&y.setSortModel(b.sortModel=d.sortModel),d.rowGroupingModel&&y.setRowGroupingModel(b.rowGroupingModel=d.rowGroupingModel),d.aggregationModel&&y.setAggregationModel(b.aggregationModel=d.aggregationModel),d.columnVisibilityModel&&(b.columnVisibilityModel={...y.getGridAiState().columnVisibilityModel,...d.columnVisibilityModel},y.setColumnVisibilityModel(b.columnVisibilityModel)),{ok:!0,applied:b}})}}const x={type:"object",properties:{},additionalProperties:!1};function ut(e,t,r={}){const n=J(t),o=[{name:"get_grid_state",description:"Read the current filter, sort, grouping, aggregation, pivot and column visibility of the grid, and its columns.",inputSchema:x,execute:()=>P(()=>{const i=R(e);return i?{ok:!0,state:i.getGridAiState(),columns:n.map(s=>({field:s.field,headerName:typeof s.headerName=="string"?s.headerName:s.field,type:typeof s.type=="string"?s.type:"string"}))}:w("The grid is not ready")})}];for(const{name:i,part:s,description:l}of st){const a=at(e,n,i,s,l);a&&o.push(a)}if(o.push({name:"clear_filters",description:"Remove every filter and the quick-filter words.",inputSchema:x,execute:()=>P(()=>{const i=R(e);if(!i)return w("The grid is not ready");const s={items:[]};return i.setFilterModel(s),{ok:!0,applied:{filterModel:s}}})}),r.allowRowAccess===!0){const i=typeof r.maxRows=="number"&&r.maxRows>=1?Math.min(Math.floor(r.maxRows),ot):it,s=Array.isArray(r.fields)?n.filter(l=>r.fields?.includes(l.field)):n;o.push({name:"get_row_summary",description:`Count the rows that pass the current filter and read the first ones (at most ${i}, fields: ${s.map(l=>l.field).join(", ")}).`,inputSchema:{type:"object",properties:{limit:{type:"integer",minimum:1,maximum:i}},additionalProperties:!1},execute:l=>P(()=>{const a=R(e);if(!a)return w("The grid is not ready");const f=$(l)&&S(l,"limit")?l.limit:i,v=typeof f=="number"&&f>=1?Math.min(Math.floor(f),i):i,g=a.getAllFilteredRows(),m=Array.isArray(g)?g:[],y=m.slice(0,v).map(d=>{const E={};if(!$(d))return E;for(const b of s)E[b.field]=lt(ge(d,b.field,b));return E});return{ok:!0,rowCount:m.length,rows:y}})})}return o}exports.createGridAgentTools=ut,exports.createGridAiPromptHandler=nt,exports.getGridAiSchema=Y,exports.validateGridAiState=z;
|