@opencorestack/opengridx 3.3.0 → 3.4.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +4 -1
  3. package/dist/ai.cjs +1 -0
  4. package/dist/ai.cjs.map +1 -0
  5. package/dist/ai.d.ts +166 -0
  6. package/dist/ai.es.js +1 -0
  7. package/dist/ai.es.js.map +1 -0
  8. package/dist/index.d.ts +148 -2
  9. package/dist/opengridx.css +1 -1
  10. package/dist/opengridx.es.js +6084 -5523
  11. package/dist/opengridx.es.js.map +1 -1
  12. package/dist/opengridx.umd.js +17 -15
  13. package/dist/opengridx.umd.js.map +1 -1
  14. package/docs/API_REFERENCE.md +98 -1
  15. package/docs/README.md +3 -1
  16. package/docs/architecture/datagrid-orchestration.md +10 -1
  17. package/docs/customization/theming.md +9 -0
  18. package/docs/features/ai-toolkit.md +119 -0
  19. package/docs/features/clipboard.md +88 -0
  20. package/docs/features/editing-reordering.md +50 -0
  21. package/docs/features/keyboard-navigation.md +11 -0
  22. package/docs/features/undo-redo.md +79 -0
  23. package/docs/roadmap.md +1 -1
  24. package/lib/ai/coerce.ts +90 -0
  25. package/lib/ai/columns.ts +90 -0
  26. package/lib/ai/index.ts +16 -0
  27. package/lib/ai/schema.ts +214 -0
  28. package/lib/ai/types.ts +88 -0
  29. package/lib/ai/validate.ts +286 -0
  30. package/lib/components/Cell/Cell.css +5 -0
  31. package/lib/components/Cell/GridEditInputCell.css +12 -4
  32. package/lib/components/Cell/GridEditInputCell.tsx +19 -12
  33. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +14 -0
  34. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +3 -2
  35. package/lib/components/DataGrid/DataGrid.tsx +62 -2
  36. package/lib/components/FilterPanel/FilterPanel.css +14 -0
  37. package/lib/components/FilterPanel/FilterPanel.tsx +3 -2
  38. package/lib/components/ListView/ListView.css +2 -4
  39. package/lib/components/ListView/ListViewRow.tsx +2 -2
  40. package/lib/components/Toolbar/GlobalSearch.css +16 -0
  41. package/lib/components/Toolbar/GlobalSearch.tsx +3 -2
  42. package/lib/components/ui/Checkbox.tsx +16 -5
  43. package/lib/components/ui/Input.css +43 -0
  44. package/lib/components/ui/Input.tsx +24 -5
  45. package/lib/hooks/core/gridApiPlaceholder.ts +8 -1
  46. package/lib/hooks/core/useDataGrid.ts +27 -0
  47. package/lib/hooks/core/useGridCellSelection.ts +30 -2
  48. package/lib/hooks/core/useGridClipboardPaste.ts +283 -0
  49. package/lib/hooks/core/useGridUndoRedo.ts +220 -0
  50. package/lib/hooks/features/useGridBatchEdit.ts +217 -0
  51. package/lib/hooks/features/useGridEditing.ts +24 -45
  52. package/lib/index.ts +7 -0
  53. package/lib/styles/opengridx.css +2 -1
  54. package/lib/types/index.ts +124 -0
  55. package/lib/utils/editing/commit.ts +69 -0
  56. package/lib/utils/editing/targets.ts +109 -0
  57. package/lib/utils/parsing.ts +252 -0
  58. package/llms.txt +55 -1
  59. package/package.json +8 -2
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # OpenGridX — AI Agent Context
2
2
  # Package: @opencorestack/opengridx
3
- # Version: 3.3.0
3
+ # Version: 3.4.0
4
4
  # Docs: https://opencorestack.github.io/OpenGridX/
5
5
  # This file is machine-readable. AI developers: start here.
6
6
 
@@ -97,6 +97,12 @@ export default function App() {
97
97
  | cellSelectionModel | GridCellSelectionModel | Controlled range: readonly { anchor: { id, field }, head: { id, field } }[] (0 or 1 range). Corners are ids/fields: the range follows sorting; cleared ('dataChange') when a corner is no longer displayed |
98
98
  | onCellSelectionModelChange | (model, { reason }) => void | reason: 'pointer' | 'keyboard' | 'selectAll' | 'api' | 'clear' | 'dataChange' |
99
99
  | showCellSelectionStats | boolean | Status bar: Count (non-empty), Sum and Average (numeric) of the range; slots.cellSelectionStats replaces it |
100
+ | disableClipboardPaste | boolean (3.4.0+) | With cellSelection, Ctrl/Cmd+V pastes TSV (Excel/Sheets) into editable cells: one value fills a range; a block goes from the range's top-left (or focused) cell, cut to the range and grid edges; no rows created. Parsed per column type, then valueSetter + processRowUpdate once per row. true = shortcut off (apiRef.pasteText still works) |
101
+ | onClipboardPaste | (result) => void (3.4.0+) | After every paste: { updated: id[], failed: {id, error}[], skipped: {id, field, reason: 'notEditable'/'invalidValue'}[], text } |
102
+ | onBeforeClipboardPaste | ({ text, anchor }) => boolean / string | Return false to cancel, a string to paste instead; a throw cancels |
103
+ | disableRangeClear | boolean (3.4.0+) | With cellSelection, Delete/Backspace on a multi-cell range empties editable cells ('' strings, null otherwise) as one edit; true = off |
104
+ | undoRedo | boolean / { limit } (3.4.0+) | Undo/redo of edits, pastes, range clears (default off, 100 actions). Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z, Ctrl+Y on a cell with no editor open. Recorded only after processRowUpdate succeeds; replayed through valueSetter + processRowUpdate; cells changed since are skipped ('changed', compared by value), removed rows ('missing') |
105
+ | onHistoryChange | ({ canUndo, canRedo, size }) => void | History change (size = undoable actions) |
100
106
 
101
107
  ### Sorting
102
108
  | Prop | Type | Description |
@@ -245,6 +251,7 @@ There are no getRowClassName / getCellClassName / theme props: use GridColDef.ce
245
251
  | renderEditCell | (params: GridRenderEditCellParams<R>) => ReactNode | Custom editor: call params.onValueChange(v), params.onCommit(), params.onCancel() |
246
252
  | valueGetter | ({row, field, value}) => unknown | Computed value; used by sort, filter, quick search, aggregation, copy, export. Must be pure (quick filter caches per row object); a throw reads as undefined + dev warning |
247
253
  | valueSetter | ({value, row, field}) => R | Write an edited value back onto the row (editable valueGetter columns) |
254
+ | valueParser | (text, { row, field, colDef }) => unknown | Paste parser for this column (3.4.0+), e.g. "1.234,5" → 1234.5. Throw/undefined skips the cell ('invalidValue'). Default by type: number "1,234.5"/"12%"/"$5"; date ISO or Date.parse (keeps the cell's kind); boolean true/false/yes/no/1/0; singleSelect value or label; empty → null ('' for strings) |
248
255
  | valueFormatter | ({value, row, field}) => string | Format display value (value is `unknown`: cast before calling methods) |
249
256
  | cellClassName | string / (params) => string | CSS class on cells |
250
257
  | headerClassName | string | CSS class on header cell |
@@ -265,6 +272,7 @@ There are no getRowClassName / getCellClassName / theme props: use GridColDef.ce
265
272
  | groupingValueFormatter | ({field, value}) => string | Group row label (default "field: value") |
266
273
  | aggregable | boolean | false = no aggregation. Summaries panel offers number columns + aggregable: true |
267
274
  | availableAggregationFunctions | string[] | Restrict aggregation functions for this column |
275
+ | aiExamples | unknown[] | Example values put into getGridAiSchema's schema (3.4.0+). SENT TO THE MODEL: real data, opt-in. Primitives only, max 10. Unused by the grid |
268
276
 
269
277
  ---
270
278
 
@@ -321,6 +329,14 @@ apiRef.current.getSelectedCells() // → { id, field, value }[]; synthetic
321
329
  apiRef.current.copySelectedCells() // → Promise<void>; TSV, no header line
322
330
  apiRef.current.clearCellSelection()
323
331
 
332
+ // Paste and undo/redo (3.4.0+)
333
+ apiRef.current.pasteText(text, anchor?) // → Promise<GridBatchEditResult>; anchor default: range top-left, else focused cell
334
+ apiRef.current.undo() // → Promise<GridBatchEditResult> (undoRedo)
335
+ apiRef.current.redo()
336
+ apiRef.current.canUndo() // → boolean
337
+ apiRef.current.canRedo()
338
+ apiRef.current.clearHistory()
339
+
324
340
  // Aggregation
325
341
  apiRef.current.getAggregationResult() // → Record<string, unknown> | null (null in pivot mode)
326
342
  apiRef.current.getAggregationModel() // → GridAggregationModel | null
@@ -356,6 +372,22 @@ const processRowUpdate = (newRow: Person) => {
356
372
 
357
373
  Double-click or Enter opens the editor; Enter / Tab / blur commits; Escape cancels. An edit whose row scrolls, pages or filters away is committed, not dropped.
358
374
 
375
+ Custom editors (3.3.1+): build them on the exported `Input` with `variant="cell"` — it fills the cell with no border/background/focus ring, like the built-in editors. `ref` reaches the `<input>` (forwardRef, React 18 + 19), `inputClassName` styles the `<input>` (`className` the wrapper), `startAdornment` / `endAdornment` add a unit, `error` adds an inset error edge and `aria-invalid`. Stop propagation of the Enter/Escape you handle.
376
+
377
+ ```tsx
378
+ import { Input } from '@opencorestack/opengridx';
379
+ function PriceEditor({ value, onValueChange, onCommit, onCancel }: GridRenderEditCellParams) {
380
+ const [text, setText] = useState(String(value ?? ''));
381
+ const ref = useRef<HTMLInputElement>(null);
382
+ useEffect(() => { ref.current?.focus(); ref.current?.select(); }, []);
383
+ const valid = text.trim() !== '' && Number(text) >= 0;
384
+ return <Input ref={ref} variant="cell" startAdornment="$" error={!valid} value={text}
385
+ onChange={e => { setText(e.target.value); const n = Number(e.target.value); if (e.target.value.trim() !== '' && n >= 0) onValueChange(n); }}
386
+ onKeyDown={e => { if (e.key === 'Enter') { e.stopPropagation(); if (valid) onCommit(); } if (e.key === 'Escape') { e.stopPropagation(); onCancel(); } }}
387
+ onBlur={() => (valid ? onCommit() : onCancel())} />;
388
+ }
389
+ ```
390
+
359
391
  ---
360
392
 
361
393
  ## SERVER-SIDE DATA SOURCE
@@ -524,6 +556,27 @@ await printGrid(rows, columns, 'My Report Title');
524
556
 
525
557
  ---
526
558
 
559
+ ## AI TOOLKIT (@opencorestack/opengridx/ai, 3.4.0+)
560
+
561
+ Separate entry point: no React, no dependencies, ~4.5 kB gzip; never loaded by the core import. OpenGridX calls no AI service: the app sends { prompt, schema, currentState } to its own model (structured output) and validates the reply.
562
+
563
+ ```ts
564
+ import { getGridAiSchema, validateGridAiState } from '@opencorestack/opengridx/ai';
565
+
566
+ const schema = getGridAiSchema(columns, { include: ['filter', 'sort', 'grouping', 'aggregation'] });
567
+ const reply = await fetch('/api/grid-assistant', { method: 'POST', body: JSON.stringify({ prompt, schema, currentState }) });
568
+ const { state, errors } = validateGridAiState(await reply.text(), columns);
569
+ if (state.filterModel) setFilterModel(state.filterModel); // apply only the parts present
570
+ if (state.sortModel) setSortModel(state.sortModel);
571
+ ```
572
+
573
+ - getGridAiSchema(columns, { include?, exclude?, descriptions = true, examples = true }) → JSON Schema draft 2020-12 with `schemaVersion: 1`. Parts (option name → key): filter → filterModel (items as oneOf per field, AND/OR groups ≤ 3 levels, quickFilterValues), sort → sortModel, grouping → rowGroupingModel, aggregation → aggregationModel, pivot → pivotModel, columnVisibility → columnVisibilityModel. Fields from filterable / sortable / groupable / hideable / aggregable !== false; operators = the column type's filter operators; aggregation: number (or aggregable: true) columns get sum/avg/count/min/max/unique, others count, limited by availableAggregationFunctions; singleSelect valueOptions become enums. Fields starting with `__` are skipped. Deterministic; reads no rows.
574
+ - validateGridAiState(json | jsonString, columns, { include?, exclude? }) → { state: GridAiState, errors: { path, message }[] }. Drops unknown parts, fields, disallowed fields, wrong operators, enum values outside the list; coerces values like clipboard paste ("1,200"/"$1,200" → 1200, "12%" → 0.12, yes/no/1/0 → boolean, dates → "YYYY-MM-DD", singleSelect by value or label, case-insensitive); removes empty groups; lists capped at 200 entries, errors at 100. Never throws; never reads `__proto__`.
575
+ - Privacy: no row data by default; `aiExamples` and `valueOptions` ARE sent; always apply `state`, never the raw reply.
576
+ - Types (from /ai): GridAiColumn, GridAiPart, GridAiSchema, GridAiSchemaOptions, GridAiState, GridAiValidateOptions, GridAiValidationError, GridAiValidationResult.
577
+
578
+ ---
579
+
527
580
  ## CSS CUSTOMIZATION
528
581
 
529
582
  All CSS variables use the `--ogx-` prefix and are declared on `:root` by the stylesheet. Override them on a wrapper (or use DataGridThemeProvider, which sets the same variables):
@@ -547,6 +600,7 @@ Class names use the `ogx__` BEM prefix, e.g.:
547
600
  - Cell ranges (3.3.0+): `ogx__cell--range`, `ogx__cell--range-top|bottom|left|right` (inset box-shadow outline), `ogx--range-dragging` on the root during a drag; variables `--ogx-range-background`, `--ogx-range-border` (theme keys `grid.rangeBackground`, `grid.rangeBorder`)
548
601
  - `ogx__header-cell`, `ogx__header-cell--sorted`, `ogx__header-cell--wrap` (wrapHeaderText, 3.3.0+)
549
602
  - Sticky wrappers: `ogx__sticky-top` (header + top-pinned rows), `ogx__sticky-bottom` (bottom-pinned rows + aggregation footer)
603
+ - Form controls (3.3.1+): the toolbar search, column-panel search, filter value box and text/number/date editors render through `Input`: the `<input>` keeps its class (`ogx-global-search__input`, `ogx-column-visibility-panel__search-input`, `ogx-filter__value-input`, `ogx__edit-input`) inside a `div.ogx-input-wrapper` (`ogx-input-wrapper--cell` for editors) — use descendant selectors (`.ogx-global-search input`), not `> input`. The boolean editor and list-view row checkbox are the shared `Checkbox` (`.ogx-checkbox__box`; hidden `<input>`)
550
604
 
551
605
  ---
552
606
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencorestack/opengridx",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "OpenGridX: High-performance React data infrastructure. Unlock advanced Row Grouping, Excel Export, and Column Pinning without the usual \"Pro\" gatekeeping. Built for speed, scale, and complete architectural freedom. Fully open, virtualization-ready, and feature-complete.",
5
5
  "type": "module",
6
6
  "main": "./dist/opengridx.umd.js",
@@ -12,6 +12,11 @@
12
12
  "import": "./dist/opengridx.es.js",
13
13
  "require": "./dist/opengridx.umd.js"
14
14
  },
15
+ "./ai": {
16
+ "types": "./dist/ai.d.ts",
17
+ "import": "./dist/ai.es.js",
18
+ "require": "./dist/ai.cjs"
19
+ },
15
20
  "./styles": {
16
21
  "types": "./dist/opengridx.css.d.ts",
17
22
  "default": "./dist/opengridx.css"
@@ -23,6 +28,7 @@
23
28
  "!lib/**/*.test.ts",
24
29
  "!lib/**/*.test.tsx",
25
30
  "!lib/**/__screenshots__",
31
+ "!lib/**/__snapshots__",
26
32
  "docs/architecture",
27
33
  "docs/assets",
28
34
  "docs/components",
@@ -45,7 +51,7 @@
45
51
  "scripts": {
46
52
  "dev": "vite",
47
53
  "build": "tsc --project tsconfig.build.json && vite build",
48
- "build:lib": "tsc --project tsconfig.build.json && vite build --config vite.config.lib.js && cp lib/styles/opengridx.css.d.ts dist/opengridx.css.d.ts && node scripts/check-bundle.mjs",
54
+ "build:lib": "tsc --project tsconfig.build.json && vite build --config vite.config.lib.js && vite build --config vite.config.lib.js --mode ai && cp lib/styles/opengridx.css.d.ts dist/opengridx.css.d.ts && node scripts/check-bundle.mjs",
49
55
  "lint": "eslint .",
50
56
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit && tsc -p e2e/tsconfig.json",
51
57
  "preview": "vite preview",