@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
@@ -0,0 +1,90 @@
1
+ // Value coercion for the AI validator. Text goes through the same parsers as clipboard paste
2
+ // (`lib/utils/parsing.ts`), so a value the grid accepts from Excel is accepted from a model and the
3
+ // other way round. Model output is JSON, so typed values (a number, a boolean, a Date) are taken as
4
+ // they are. Empty input is valid: null, or '' for text columns.
5
+ import {
6
+ parseBooleanText,
7
+ parseDateText,
8
+ parseNumberText,
9
+ parseSingleSelectText,
10
+ type GridParsedValue,
11
+ } from '../utils/parsing';
12
+
13
+ export type CoerceResult<T = unknown> = { ok: true; value: T } | { ok: false; reason: 'invalidValue' };
14
+
15
+ /** The column properties coercion reads. */
16
+ export interface CoerceColumn {
17
+ type?: string;
18
+ valueOptions?: readonly (string | number | { value: unknown; label: string })[];
19
+ }
20
+
21
+ const ok = <T>(value: T): CoerceResult<T> => ({ ok: true, value });
22
+ const INVALID: CoerceResult<never> = { ok: false, reason: 'invalidValue' };
23
+
24
+ const fromParsed = <T>(parsed: GridParsedValue): CoerceResult<T> =>
25
+ parsed.ok ? ok(parsed.value as T) : INVALID;
26
+
27
+ function isBlank(text: unknown): boolean {
28
+ return text == null || (typeof text === 'string' && text.trim() === '');
29
+ }
30
+
31
+ /** `string` and `image` columns: text as is. Numbers and booleans become their text. */
32
+ export function coerceString(text: unknown): CoerceResult<string> {
33
+ if (text == null) return ok('');
34
+ if (typeof text === 'string') return ok(text);
35
+ if (typeof text === 'number' || typeof text === 'boolean') return ok(String(text));
36
+ return INVALID;
37
+ }
38
+
39
+ /** `number` columns: a finite number, or text in the paste format (`1,234.5`, `12%`, `$40`). */
40
+ export function coerceNumber(text: unknown): CoerceResult<number | null> {
41
+ if (isBlank(text)) return ok(null);
42
+ if (typeof text === 'number') return Number.isFinite(text) ? ok(text) : INVALID;
43
+ if (typeof text !== 'string') return INVALID;
44
+ return fromParsed(parseNumberText(text));
45
+ }
46
+
47
+ /** `date` columns: a Date, epoch milliseconds, or text in the paste format (ISO `2026-10-07` …). */
48
+ export function coerceDate(text: unknown): CoerceResult<Date | null> {
49
+ if (isBlank(text)) return ok(null);
50
+ if (text instanceof Date) return Number.isNaN(text.getTime()) ? INVALID : ok(text);
51
+ if (typeof text === 'number') return Number.isFinite(text) ? ok(new Date(text)) : INVALID;
52
+ if (typeof text !== 'string') return INVALID;
53
+ return fromParsed(parseDateText(text));
54
+ }
55
+
56
+ /** `boolean` columns: a boolean, or `true`/`false`, `yes`/`no`, `1`/`0` (case-insensitive). */
57
+ export function coerceBoolean(text: unknown): CoerceResult<boolean | null> {
58
+ if (isBlank(text)) return ok(null);
59
+ if (typeof text === 'boolean') return ok(text);
60
+ if (typeof text !== 'string' && typeof text !== 'number') return INVALID;
61
+ return fromParsed(parseBooleanText(String(text)));
62
+ }
63
+
64
+ /**
65
+ * `singleSelect` columns: an option value or label, case-insensitive; returns the option's value.
66
+ * Without `valueOptions` the model has nothing to choose from, so any scalar is kept as it is.
67
+ */
68
+ export function coerceSingleSelect(text: unknown, colDef: CoerceColumn): CoerceResult<unknown> {
69
+ if (isBlank(text)) return ok(null);
70
+ if (typeof text !== 'string' && typeof text !== 'number' && typeof text !== 'boolean') return INVALID;
71
+ const options = colDef.valueOptions;
72
+ if (!options || options.length === 0) return ok(text);
73
+ return fromParsed(parseSingleSelectText(String(text), options));
74
+ }
75
+
76
+ /** Coerces `text` for the column's `type`. */
77
+ export function coerceValue(text: unknown, colDef: CoerceColumn): CoerceResult<unknown> {
78
+ switch (colDef.type) {
79
+ case 'number':
80
+ return coerceNumber(text);
81
+ case 'date':
82
+ return coerceDate(text);
83
+ case 'boolean':
84
+ return coerceBoolean(text);
85
+ case 'singleSelect':
86
+ return coerceSingleSelect(text, colDef);
87
+ default:
88
+ return coerceString(text);
89
+ }
90
+ }
@@ -0,0 +1,90 @@
1
+ // Which columns may take part in each part of the state. The schema and the validator both read
2
+ // these, so the model is never offered a field the validator would drop.
3
+ import { getOperatorsForType } from '../utils/filtering';
4
+ import type { GridFilterOperator, GridPivotAggFn } from '../types';
5
+ import type { GridAiColumn, GridAiPart, GridAiPartOptions } from './types';
6
+
7
+ /** The built-in aggregation functions (`AGGREGATION_FUNCTIONS` in lib/utils/aggregation). */
8
+ export const AGGREGATION_FNS: readonly string[] = ['sum', 'avg', 'count', 'min', 'max', 'unique'];
9
+ /** The functions a pivot value field takes (`GridPivotAggFn`). */
10
+ export const PIVOT_FNS: readonly GridPivotAggFn[] = ['sum', 'avg', 'count', 'min', 'max'];
11
+ /** Operators without a value. */
12
+ export const NO_VALUE_OPS: readonly string[] = ['isEmpty', 'isNotEmpty'];
13
+ /** Deepest nesting of filter groups. */
14
+ export const MAX_FILTER_DEPTH = 3;
15
+
16
+ export const ALL_PARTS: readonly GridAiPart[] = ['filter', 'sort', 'grouping', 'aggregation', 'pivot', 'columnVisibility'];
17
+
18
+ /** State key of each part, in output order. */
19
+ export const PART_KEYS: Record<GridAiPart, string> = {
20
+ filter: 'filterModel',
21
+ sort: 'sortModel',
22
+ grouping: 'rowGroupingModel',
23
+ aggregation: 'aggregationModel',
24
+ pivot: 'pivotModel',
25
+ columnVisibility: 'columnVisibilityModel',
26
+ };
27
+
28
+ export function hasOwn(obj: object, key: string): boolean {
29
+ return Object.prototype.hasOwnProperty.call(obj, key);
30
+ }
31
+
32
+ export function isPlainObject(value: unknown): value is Record<string, unknown> {
33
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
34
+ }
35
+
36
+ export function selectedParts(options: GridAiPartOptions | undefined): GridAiPart[] {
37
+ const include = Array.isArray(options?.include) ? options.include : ALL_PARTS;
38
+ const exclude = Array.isArray(options?.exclude) ? options.exclude : [];
39
+ return ALL_PARTS.filter((part) => include.includes(part) && !exclude.includes(part));
40
+ }
41
+
42
+ /**
43
+ * The usable columns, first one per field. System columns (`__check__`, `__group__`, …), spacers
44
+ * and fields that are not plain strings are skipped.
45
+ */
46
+ export function usableColumns(columns: readonly GridAiColumn[] | unknown): GridAiColumn[] {
47
+ if (!Array.isArray(columns)) return [];
48
+ const seen = new Set<string>();
49
+ const out: GridAiColumn[] = [];
50
+ for (const col of columns as unknown[]) {
51
+ if (!isPlainObject(col)) continue;
52
+ const field = col.field;
53
+ if (typeof field !== 'string' || field === '' || field.startsWith('__') || col.isSpacer === true || seen.has(field)) continue;
54
+ seen.add(field);
55
+ out.push(col as unknown as GridAiColumn);
56
+ }
57
+ return out;
58
+ }
59
+
60
+ export function operatorsOf(col: GridAiColumn): GridFilterOperator[] {
61
+ return getOperatorsForType(col.type);
62
+ }
63
+
64
+ export const isFilterable = (col: GridAiColumn): boolean => col.filterable !== false;
65
+ export const isSortable = (col: GridAiColumn): boolean => col.sortable !== false;
66
+ export const isGroupable = (col: GridAiColumn): boolean => col.groupable !== false;
67
+ export const isHideable = (col: GridAiColumn): boolean => col.hideable !== false;
68
+
69
+ /**
70
+ * Aggregation functions for a column, as the Summaries panel offers them: every built-in function
71
+ * for number columns (or `aggregable: true`), `count` for the others, never with `aggregable: false`,
72
+ * and limited by `availableAggregationFunctions`.
73
+ */
74
+ export function aggregationFnsOf(col: GridAiColumn, fns: readonly string[] = AGGREGATION_FNS): string[] {
75
+ if (col.aggregable === false) return [];
76
+ const base = col.type === 'number' || col.aggregable === true ? fns : fns.filter((fn) => fn === 'count');
77
+ const allowed = col.availableAggregationFunctions;
78
+ return Array.isArray(allowed) ? base.filter((fn) => allowed.includes(fn)) : [...base];
79
+ }
80
+
81
+ /** JSON-safe option values of a singleSelect column, in order, without duplicates. */
82
+ export function optionValues(col: GridAiColumn): (string | number)[] {
83
+ const out: (string | number)[] = [];
84
+ if (!Array.isArray(col.valueOptions)) return out;
85
+ for (const option of col.valueOptions) {
86
+ const value: unknown = option !== null && typeof option === 'object' ? option.value : option;
87
+ if ((typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value))) && !out.includes(value)) out.push(value);
88
+ }
89
+ return out;
90
+ }
@@ -0,0 +1,16 @@
1
+ // @opencorestack/opengridx/ai: schema and validator for driving the grid from a language model.
2
+ // No React, no dependencies and no AI SDK; the app sends the schema to its own model.
3
+ export { getGridAiSchema } from './schema';
4
+ export { validateGridAiState } from './validate';
5
+ export type {
6
+ GridAiColumn,
7
+ GridAiJsonSchema,
8
+ GridAiPart,
9
+ GridAiPartOptions,
10
+ GridAiSchema,
11
+ GridAiSchemaOptions,
12
+ GridAiState,
13
+ GridAiValidateOptions,
14
+ GridAiValidationError,
15
+ GridAiValidationResult,
16
+ } from './types';
@@ -0,0 +1,214 @@
1
+ // getGridAiSchema: a JSON Schema (draft 2020-12) of the grid state a model may produce. Built from
2
+ // the column definitions only; no row data is read. Same columns and options, same schema.
3
+ import {
4
+ MAX_FILTER_DEPTH,
5
+ NO_VALUE_OPS,
6
+ PART_KEYS,
7
+ PIVOT_FNS,
8
+ aggregationFnsOf,
9
+ isFilterable,
10
+ isGroupable,
11
+ isHideable,
12
+ isSortable,
13
+ operatorsOf,
14
+ optionValues,
15
+ selectedParts,
16
+ usableColumns,
17
+ } from './columns';
18
+ import type { GridAiColumn, GridAiJsonSchema, GridAiPart, GridAiSchema, GridAiSchemaOptions } from './types';
19
+
20
+ /** Title of every schema. Also the marker `check-bundle` uses to keep this code out of the core bundle. */
21
+ export const GRID_AI_SCHEMA_TITLE = 'OpenGridX grid state';
22
+
23
+ const MAX_EXAMPLES = 10;
24
+
25
+ interface Ctx {
26
+ descriptions: boolean;
27
+ examples: boolean;
28
+ }
29
+
30
+ function label(col: GridAiColumn): string {
31
+ const header = typeof col.headerName === 'string' && col.headerName !== '' ? col.headerName : col.field;
32
+ const desc = typeof col.description === 'string' && col.description !== '' ? ` (${col.description})` : '';
33
+ return `${header}${desc}`;
34
+ }
35
+
36
+ function describe(node: GridAiJsonSchema, text: string, ctx: Ctx): GridAiJsonSchema {
37
+ return ctx.descriptions ? { ...node, description: text } : node;
38
+ }
39
+
40
+ function examplesOf(col: GridAiColumn, ctx: Ctx): unknown[] {
41
+ if (!ctx.examples || !Array.isArray(col.aiExamples)) return [];
42
+ return col.aiExamples
43
+ .filter((v) => typeof v === 'string' || typeof v === 'boolean' || (typeof v === 'number' && Number.isFinite(v)))
44
+ .slice(0, MAX_EXAMPLES);
45
+ }
46
+
47
+ /** Schema of one filter value for the column's type. */
48
+ function valueSchema(col: GridAiColumn, ctx: Ctx): GridAiJsonSchema {
49
+ let node: GridAiJsonSchema;
50
+ switch (col.type) {
51
+ case 'number':
52
+ node = { type: 'number' };
53
+ break;
54
+ case 'date':
55
+ node = { type: 'string', format: 'date' };
56
+ break;
57
+ case 'boolean':
58
+ node = { type: 'boolean' };
59
+ break;
60
+ case 'singleSelect': {
61
+ const values = optionValues(col);
62
+ const one: GridAiJsonSchema = values.length > 0 ? { enum: values } : { type: 'string' };
63
+ node = { anyOf: [one, { type: 'array', items: one }] };
64
+ break;
65
+ }
66
+ default:
67
+ node = { type: 'string' };
68
+ }
69
+ const examples = examplesOf(col, ctx);
70
+ return examples.length > 0 ? { ...node, examples } : node;
71
+ }
72
+
73
+ function filterItemSchema(col: GridAiColumn, ctx: Ctx): GridAiJsonSchema {
74
+ const operators = operatorsOf(col);
75
+ const noValue = operators.filter((op) => NO_VALUE_OPS.includes(op));
76
+ const hint = col.type === 'singleSelect' ? '; isAnyOf takes an array' : '';
77
+ const ops = noValue.length > 0 ? `${noValue.join(' and ')} take no value${hint}` : hint.slice(2);
78
+ return describe(
79
+ {
80
+ type: 'object',
81
+ properties: {
82
+ field: { const: col.field },
83
+ operator: { enum: operators },
84
+ value: valueSchema(col, ctx),
85
+ },
86
+ required: ['field', 'operator'],
87
+ additionalProperties: false,
88
+ },
89
+ ops ? `${label(col)}: ${ops}` : label(col),
90
+ ctx,
91
+ );
92
+ }
93
+
94
+ function filterSchema(cols: GridAiColumn[], ctx: Ctx, defs: Record<string, GridAiJsonSchema>): GridAiJsonSchema | null {
95
+ const filterable = cols.filter(isFilterable);
96
+ const properties: Record<string, GridAiJsonSchema> = {};
97
+ const logic = { enum: ['and', 'or'] };
98
+ if (filterable.length > 0) {
99
+ defs.filterItem = { oneOf: filterable.map((col) => filterItemSchema(col, ctx)) };
100
+ // Groups are unrolled to MAX_FILTER_DEPTH levels: the deepest one holds items only.
101
+ for (let depth = MAX_FILTER_DEPTH; depth >= 1; depth--) {
102
+ const child = depth === MAX_FILTER_DEPTH ? { $ref: '#/$defs/filterItem' } : { anyOf: [{ $ref: '#/$defs/filterItem' }, { $ref: `#/$defs/filterGroup${depth + 1}` }] };
103
+ defs[`filterGroup${depth}`] = {
104
+ type: 'object',
105
+ properties: { logicOperator: logic, items: { type: 'array', items: child } },
106
+ required: ['logicOperator', 'items'],
107
+ additionalProperties: false,
108
+ };
109
+ }
110
+ properties.items = { type: 'array', items: { anyOf: [{ $ref: '#/$defs/filterItem' }, { $ref: '#/$defs/filterGroup1' }] } };
111
+ properties.logicOperator = logic;
112
+ }
113
+ properties.quickFilterValues = describe({ type: 'array', items: { type: 'string' } }, 'Words a row must contain, in any column', ctx);
114
+ return { type: 'object', properties, additionalProperties: false };
115
+ }
116
+
117
+ function fieldEnum(cols: GridAiColumn[]): GridAiJsonSchema {
118
+ return { enum: cols.map((c) => c.field) };
119
+ }
120
+
121
+ function partSchema(part: GridAiPart, cols: GridAiColumn[], ctx: Ctx, defs: Record<string, GridAiJsonSchema>): GridAiJsonSchema | null {
122
+ switch (part) {
123
+ case 'filter':
124
+ return filterSchema(cols, ctx, defs);
125
+ case 'sort': {
126
+ const sortable = cols.filter(isSortable);
127
+ if (sortable.length === 0) return null;
128
+ return describe(
129
+ {
130
+ type: 'array',
131
+ items: {
132
+ type: 'object',
133
+ properties: { field: fieldEnum(sortable), sort: { enum: ['asc', 'desc'] } },
134
+ required: ['field', 'sort'],
135
+ additionalProperties: false,
136
+ },
137
+ },
138
+ 'Most significant first',
139
+ ctx,
140
+ );
141
+ }
142
+ case 'grouping': {
143
+ const groupable = cols.filter(isGroupable);
144
+ if (groupable.length === 0) return null;
145
+ return describe({ type: 'array', items: fieldEnum(groupable), uniqueItems: true }, 'Outermost first', ctx);
146
+ }
147
+ case 'aggregation': {
148
+ const properties: Record<string, GridAiJsonSchema> = {};
149
+ for (const col of cols) {
150
+ const fns = aggregationFnsOf(col);
151
+ if (fns.length > 0) properties[col.field] = describe({ enum: fns }, label(col), ctx);
152
+ }
153
+ if (Object.keys(properties).length === 0) return null;
154
+ return { type: 'object', properties, additionalProperties: false };
155
+ }
156
+ case 'pivot': {
157
+ const dims = cols.filter(isGroupable);
158
+ const values = cols
159
+ .map((col) => ({ col, fns: aggregationFnsOf(col, PIVOT_FNS) }))
160
+ .filter((v) => v.fns.length > 0)
161
+ .map(({ col, fns }) => describe({
162
+ type: 'object',
163
+ properties: { field: { const: col.field }, aggFn: { enum: fns } },
164
+ required: ['field', 'aggFn'],
165
+ additionalProperties: false,
166
+ }, label(col), ctx));
167
+ if (dims.length === 0 || values.length === 0) return null;
168
+ const dimArray = { type: 'array', items: fieldEnum(dims), uniqueItems: true };
169
+ return {
170
+ type: 'object',
171
+ properties: { rowFields: dimArray, columnFields: dimArray, valueFields: { type: 'array', items: { oneOf: values } } },
172
+ required: ['rowFields', 'columnFields', 'valueFields'],
173
+ additionalProperties: false,
174
+ };
175
+ }
176
+ case 'columnVisibility': {
177
+ const properties: Record<string, GridAiJsonSchema> = {};
178
+ for (const col of cols.filter(isHideable)) properties[col.field] = describe({ type: 'boolean' }, label(col), ctx);
179
+ if (Object.keys(properties).length === 0) return null;
180
+ return describe({ type: 'object', properties, additionalProperties: false }, 'false hides a column', ctx);
181
+ }
182
+ }
183
+ }
184
+
185
+ /**
186
+ * A JSON Schema (draft 2020-12) of the grid state a model may return for these columns: filter,
187
+ * sort, grouping, aggregation, pivot and column visibility, each limited to the fields, operators
188
+ * and functions the columns allow. Send it with the user's prompt to a model with structured
189
+ * output, then pass the reply to `validateGridAiState`. Reads column definitions only, never rows.
190
+ * @since v3.4
191
+ */
192
+ export function getGridAiSchema<C extends GridAiColumn>(columns: readonly C[], options: GridAiSchemaOptions = {}): GridAiSchema {
193
+ const ctx: Ctx = { descriptions: options.descriptions !== false, examples: options.examples !== false };
194
+ const cols = usableColumns(columns);
195
+ const defs: Record<string, GridAiJsonSchema> = {};
196
+ const properties: Record<string, GridAiJsonSchema> = {};
197
+ for (const part of selectedParts(options)) {
198
+ const node = partSchema(part, cols, ctx, defs);
199
+ if (node) properties[PART_KEYS[part]] = node;
200
+ }
201
+ const schema: GridAiSchema = {
202
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
203
+ schemaVersion: 1,
204
+ title: GRID_AI_SCHEMA_TITLE,
205
+ type: 'object',
206
+ properties,
207
+ additionalProperties: false,
208
+ };
209
+ if (ctx.descriptions) {
210
+ schema.description = 'Grid state to apply. Include only the parts the user asked to change.';
211
+ }
212
+ if (Object.keys(defs).length > 0) schema.$defs = defs;
213
+ return schema;
214
+ }
@@ -0,0 +1,88 @@
1
+ import type {
2
+ GridAggregationModel,
3
+ GridColumnVisibilityModel,
4
+ GridFilterModel,
5
+ GridPivotModel,
6
+ GridRowGroupingModel,
7
+ GridSortItem,
8
+ } from '../types';
9
+
10
+ /**
11
+ * The column properties the AI toolkit reads. A `GridColDef` (typed or untyped) is accepted as is;
12
+ * renderers, getters and formatters are never called, and no row data is read.
13
+ * @since v3.4
14
+ */
15
+ export interface GridAiColumn {
16
+ field: string;
17
+ headerName?: string;
18
+ description?: string;
19
+ type?: string;
20
+ filterable?: boolean;
21
+ sortable?: boolean;
22
+ groupable?: boolean;
23
+ hideable?: boolean;
24
+ aggregable?: boolean;
25
+ availableAggregationFunctions?: readonly string[];
26
+ valueOptions?: readonly (string | number | { value: unknown; label: string })[];
27
+ /** Example values sent to the model. Real data: see `GridColDef.aiExamples`. */
28
+ aiExamples?: readonly unknown[];
29
+ /** Internal spacer columns are skipped. */
30
+ isSpacer?: boolean;
31
+ }
32
+
33
+ /** A part of the grid state the model may change. */
34
+ export type GridAiPart = 'filter' | 'sort' | 'grouping' | 'aggregation' | 'pivot' | 'columnVisibility';
35
+
36
+ /** Chooses the parts of the state; `exclude` wins over `include`. Default: every part. */
37
+ export interface GridAiPartOptions {
38
+ include?: readonly GridAiPart[];
39
+ exclude?: readonly GridAiPart[];
40
+ }
41
+
42
+ export interface GridAiSchemaOptions extends GridAiPartOptions {
43
+ /**
44
+ * Put each column's `headerName` and `description` into the schema, so the model can map the
45
+ * user's words ("revenue") to a field (`amt_usd`).
46
+ * @default true
47
+ */
48
+ descriptions?: boolean;
49
+ /**
50
+ * Include the columns' `aiExamples`. Columns without `aiExamples` never send example values.
51
+ * @default true
52
+ */
53
+ examples?: boolean;
54
+ }
55
+
56
+ export type GridAiValidateOptions = GridAiPartOptions;
57
+
58
+ /** A JSON Schema (draft 2020-12) node. */
59
+ export type GridAiJsonSchema = { [keyword: string]: unknown };
60
+
61
+ /** The schema returned by `getGridAiSchema`. `schemaVersion` changes when its shape changes. */
62
+ export interface GridAiSchema extends GridAiJsonSchema {
63
+ $schema: 'https://json-schema.org/draft/2020-12/schema';
64
+ schemaVersion: 1;
65
+ type: 'object';
66
+ properties: Record<string, GridAiJsonSchema>;
67
+ }
68
+
69
+ /** The validated state: only parts, fields, operators and values the columns allow. */
70
+ export interface GridAiState {
71
+ filterModel?: GridFilterModel;
72
+ sortModel?: GridSortItem[];
73
+ rowGroupingModel?: GridRowGroupingModel;
74
+ aggregationModel?: GridAggregationModel;
75
+ pivotModel?: GridPivotModel;
76
+ columnVisibilityModel?: GridColumnVisibilityModel;
77
+ }
78
+
79
+ /** One thing the validator dropped or could not use. `path` points into the model output. */
80
+ export interface GridAiValidationError {
81
+ path: string;
82
+ message: string;
83
+ }
84
+
85
+ export interface GridAiValidationResult {
86
+ state: GridAiState;
87
+ errors: GridAiValidationError[];
88
+ }