@opencorestack/opengridx 3.3.0 → 3.5.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 (101) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +10 -1
  3. package/dist/ai.cjs +2 -0
  4. package/dist/ai.cjs.map +1 -0
  5. package/dist/ai.d.ts +366 -0
  6. package/dist/ai.es.js +2 -0
  7. package/dist/ai.es.js.map +1 -0
  8. package/dist/index.d.ts +396 -6
  9. package/dist/opengridx.css +1 -1
  10. package/dist/opengridx.es.js +8029 -6090
  11. package/dist/opengridx.es.js.map +1 -1
  12. package/dist/opengridx.umd.js +16 -14
  13. package/dist/opengridx.umd.js.map +1 -1
  14. package/docs/API_REFERENCE.md +182 -4
  15. package/docs/README.md +5 -1
  16. package/docs/architecture/datagrid-orchestration.md +19 -5
  17. package/docs/components/filter-panel.md +1 -0
  18. package/docs/components/header.md +4 -1
  19. package/docs/components/toolbar.md +7 -0
  20. package/docs/customization/theming.md +12 -1
  21. package/docs/features/ai-toolkit.md +213 -0
  22. package/docs/features/cell-selection.md +8 -2
  23. package/docs/features/clipboard.md +88 -0
  24. package/docs/features/editing-reordering.md +50 -0
  25. package/docs/features/fill-handle.md +125 -0
  26. package/docs/features/filtering.md +3 -0
  27. package/docs/features/header-filters.md +138 -0
  28. package/docs/features/keyboard-navigation.md +31 -2
  29. package/docs/features/undo-redo.md +79 -0
  30. package/docs/roadmap.md +3 -4
  31. package/lib/ai/agentTools.ts +160 -0
  32. package/lib/ai/coerce.ts +90 -0
  33. package/lib/ai/columns.ts +90 -0
  34. package/lib/ai/index.ts +33 -0
  35. package/lib/ai/promptHandler.ts +109 -0
  36. package/lib/ai/schema.ts +214 -0
  37. package/lib/ai/types.ts +152 -0
  38. package/lib/ai/validate.ts +296 -0
  39. package/lib/components/AiAssistant/GridAiAssistantArea.tsx +26 -0
  40. package/lib/components/AiAssistant/GridAiAssistantPanel.css +171 -0
  41. package/lib/components/AiAssistant/GridAiAssistantPanel.tsx +145 -0
  42. package/lib/components/AiAssistant/useSpeechInput.ts +74 -0
  43. package/lib/components/Cell/Cell.css +33 -0
  44. package/lib/components/Cell/Cell.tsx +6 -2
  45. package/lib/components/Cell/GridEditInputCell.css +12 -4
  46. package/lib/components/Cell/GridEditInputCell.tsx +19 -12
  47. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +14 -0
  48. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +3 -2
  49. package/lib/components/DataGrid/DataGrid.tsx +138 -5
  50. package/lib/components/DataGrid/GridLiveRegion.tsx +5 -2
  51. package/lib/components/DataGrid/GridPinnedRows.tsx +5 -1
  52. package/lib/components/DataGrid/GridVirtualRows.tsx +5 -1
  53. package/lib/components/FilterPanel/FilterPanel.css +14 -0
  54. package/lib/components/FilterPanel/FilterPanel.tsx +5 -11
  55. package/lib/components/Header/Header.tsx +44 -23
  56. package/lib/components/Header/headerColumnStyle.ts +28 -0
  57. package/lib/components/HeaderFilter/HeaderFilter.css +160 -0
  58. package/lib/components/HeaderFilter/HeaderFilterCell.tsx +370 -0
  59. package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +124 -0
  60. package/lib/components/HeaderFilter/HeaderFilterRow.tsx +157 -0
  61. package/lib/components/ListView/ListView.css +2 -4
  62. package/lib/components/ListView/ListViewRow.tsx +2 -2
  63. package/lib/components/Toolbar/GlobalSearch.css +16 -0
  64. package/lib/components/Toolbar/GlobalSearch.tsx +3 -2
  65. package/lib/components/Toolbar/GridToolbar.tsx +51 -0
  66. package/lib/components/ui/Checkbox.tsx +16 -5
  67. package/lib/components/ui/Input.css +43 -0
  68. package/lib/components/ui/Input.tsx +24 -5
  69. package/lib/hooks/core/gridApiPlaceholder.ts +16 -1
  70. package/lib/hooks/core/gridToolbarHostContext.ts +5 -0
  71. package/lib/hooks/core/useDataGrid.ts +27 -0
  72. package/lib/hooks/core/useGridApiMethods.ts +39 -0
  73. package/lib/hooks/core/useGridAriaRows.ts +5 -3
  74. package/lib/hooks/core/useGridCellSelection.ts +107 -13
  75. package/lib/hooks/core/useGridClipboardPaste.ts +283 -0
  76. package/lib/hooks/core/useGridColumnsPanel.ts +24 -1
  77. package/lib/hooks/core/useGridControlledState.ts +27 -0
  78. package/lib/hooks/core/useGridFillHandle.ts +323 -0
  79. package/lib/hooks/core/useGridKeyboardNavigation.ts +29 -10
  80. package/lib/hooks/core/useGridToolbarProps.ts +19 -0
  81. package/lib/hooks/core/useGridUndoRedo.ts +220 -0
  82. package/lib/hooks/features/useGridAiAssistant.ts +284 -0
  83. package/lib/hooks/features/useGridBatchEdit.ts +217 -0
  84. package/lib/hooks/features/useGridEditing.ts +24 -45
  85. package/lib/index.ts +10 -0
  86. package/lib/styles/opengridx.css +4 -1
  87. package/lib/theme/DataGridThemeProvider.tsx +2 -0
  88. package/lib/theme/types.ts +2 -0
  89. package/lib/types/index.ts +343 -1
  90. package/lib/utils/aiAssistant.ts +205 -0
  91. package/lib/utils/aiBrand.ts +17 -0
  92. package/lib/utils/cellSelection.ts +25 -5
  93. package/lib/utils/editing/commit.ts +69 -0
  94. package/lib/utils/editing/fill.ts +238 -0
  95. package/lib/utils/editing/targets.ts +109 -0
  96. package/lib/utils/filtering/index.ts +14 -0
  97. package/lib/utils/focus/index.ts +9 -3
  98. package/lib/utils/headerFilters.ts +189 -0
  99. package/lib/utils/parsing.ts +252 -0
  100. package/llms.txt +97 -3
  101. package/package.json +8 -2
@@ -0,0 +1,217 @@
1
+ import { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import type { GridBatchEditResult, GridColDef, GridEditSkipReason, GridRowId, GridRowModel } from '../../types';
3
+ import { attempt } from '../../utils/attempt';
4
+ import { getCellValue } from '../../utils/values';
5
+ import {
6
+ buildEditedRow,
7
+ invalidProcessedRowError,
8
+ isPromiseLike,
9
+ isSameValue,
10
+ reportEditError,
11
+ } from '../../utils/editing/commit';
12
+ import type { GridCellChange } from '../../utils/editing/commit';
13
+
14
+ /** What made a commit: history records `edit`, `paste`, `clear` and `fill`; `undo` / `redo` replay it. */
15
+ export type GridEditSource = 'edit' | 'paste' | 'clear' | 'fill' | 'undo' | 'redo';
16
+
17
+ /** One cell to write in a batch edit. */
18
+ export interface GridBatchCellEdit {
19
+ id: GridRowId;
20
+ field: string;
21
+ value: unknown;
22
+ /**
23
+ * Undo/redo: the value the cell must still hold. When it holds something else the cell is
24
+ * skipped with reason `'changed'` (values are compared, not row objects).
25
+ */
26
+ expect?: { value: unknown };
27
+ }
28
+
29
+ /** A batch edit's result, plus the cell changes that were stored (what history records). */
30
+ export interface GridBatchEditOutcome {
31
+ result: GridBatchEditResult;
32
+ committed: GridCellChange[];
33
+ }
34
+
35
+ export interface UseGridBatchEditParams<R extends GridRowModel> {
36
+ /** The rendered row store (`state.rows.idRowsLookup`). */
37
+ rowsLookup: ReadonlyMap<GridRowId, R>;
38
+ columns: GridColDef<R>[];
39
+ processRowUpdate?: (newRow: R, oldRow: R) => R | Promise<R>;
40
+ onProcessRowUpdateError?: (error: unknown) => void;
41
+ /** Stores rows in one update (`replaceRows` of the row store). */
42
+ replaceRows: (rows: ReadonlyMap<GridRowId, R>) => void;
43
+ /** Receives the stored changes of every commit that is not an undo or redo (history). */
44
+ onCommitted?: (changes: GridCellChange[], source: GridEditSource) => void;
45
+ }
46
+
47
+ export interface UseGridBatchEditReturn<R extends GridRowModel> {
48
+ /**
49
+ * Writes cells through `valueSetter` → `processRowUpdate` (once per row, rows in parallel) →
50
+ * the store (one update for every row that succeeded). A row that throws or rejects is reported
51
+ * to `onProcessRowUpdateError` and does not stop the others. Cells whose value does not change
52
+ * are left alone. Resolves when every row has settled.
53
+ */
54
+ applyEdits: (edits: readonly GridBatchCellEdit[], source: GridEditSource) => Promise<GridBatchEditOutcome>;
55
+ /** The latest row under `id`, including rows stored but not rendered yet. */
56
+ getRow: (id: GridRowId) => R | undefined;
57
+ /** Stores one committed row (a single-cell edit), so `getRow` sees it before the next render. */
58
+ storeRow: (id: GridRowId, row: R) => void;
59
+ }
60
+
61
+ interface RowCell<R extends GridRowModel> {
62
+ field: string;
63
+ value: unknown;
64
+ colDef: GridColDef<R> | undefined;
65
+ order: number;
66
+ }
67
+
68
+ interface RowGroup<R extends GridRowModel> {
69
+ id: GridRowId;
70
+ row: R;
71
+ cells: RowCell<R>[];
72
+ }
73
+
74
+ type Settled = { id: GridRowId; ok: true; processed: unknown } | { id: GridRowId; ok: false; error: unknown };
75
+
76
+ /**
77
+ * Batch edits: several cells committed together through the single-edit pipeline (paste, range clear,
78
+ * undo, redo). One `processRowUpdate` per row, one store update for the batch, failures isolated per
79
+ * row and reported through the same path as single edits. The returned functions are stable.
80
+ */
81
+ export function useGridBatchEdit<R extends GridRowModel>(params: UseGridBatchEditParams<R>): UseGridBatchEditReturn<R> {
82
+ const latestRef = useRef(params);
83
+ // Rows stored since the last render. The rendered lookup catches up on the next render, when
84
+ // this is emptied; until then an undo right after a paste still reads the pasted rows.
85
+ const overlayRef = useRef(new Map<GridRowId, R>());
86
+ const warnedFieldsRef = useRef(new Set<string>());
87
+ useLayoutEffect(() => {
88
+ latestRef.current = params;
89
+ overlayRef.current.clear();
90
+ });
91
+
92
+ const getRow = useCallback((id: GridRowId): R | undefined => {
93
+ const pending = overlayRef.current.get(id);
94
+ return pending === undefined ? latestRef.current.rowsLookup.get(id) : pending;
95
+ }, []);
96
+
97
+ const store = useCallback((rows: Map<GridRowId, R>) => {
98
+ if (rows.size === 0) return;
99
+ rows.forEach((row, id) => { overlayRef.current.set(id, row); });
100
+ latestRef.current.replaceRows(rows);
101
+ }, []);
102
+
103
+ const storeRow = useCallback((id: GridRowId, row: R) => {
104
+ store(new Map([[id, row]]));
105
+ }, [store]);
106
+
107
+ const applyEdits = useCallback((edits: readonly GridBatchCellEdit[], source: GridEditSource): Promise<GridBatchEditOutcome> => {
108
+ const { columns, processRowUpdate } = latestRef.current;
109
+ const report = (error: unknown) => reportEditError(error, latestRef.current.onProcessRowUpdateError);
110
+ const columnOrder = new Map<string, number>();
111
+ const columnByField = new Map<string, GridColDef<R>>();
112
+ columns.forEach((col, index) => {
113
+ columnOrder.set(col.field, index);
114
+ columnByField.set(col.field, col);
115
+ });
116
+
117
+ const skipped: { id: GridRowId; field: string; reason: GridEditSkipReason }[] = [];
118
+ const groups = new Map<GridRowId, RowGroup<R>>();
119
+ for (const edit of edits) {
120
+ const row = getRow(edit.id);
121
+ if (row === undefined) {
122
+ skipped.push({ id: edit.id, field: edit.field, reason: 'missing' });
123
+ continue;
124
+ }
125
+ const colDef = columnByField.get(edit.field);
126
+ if (edit.expect && !isSameValue(getCellValue(row, edit.field, colDef), edit.expect.value)) {
127
+ skipped.push({ id: edit.id, field: edit.field, reason: 'changed' });
128
+ continue;
129
+ }
130
+ let group = groups.get(edit.id);
131
+ if (!group) {
132
+ group = { id: edit.id, row, cells: [] };
133
+ groups.set(edit.id, group);
134
+ }
135
+ const order = columnOrder.get(edit.field) ?? Number.MAX_SAFE_INTEGER;
136
+ const existing = group.cells.findIndex(cell => cell.field === edit.field);
137
+ const cell = { field: edit.field, value: edit.value, colDef, order };
138
+ // The same cell twice: the last value wins.
139
+ if (existing === -1) group.cells.push(cell);
140
+ else group.cells[existing] = cell;
141
+ }
142
+
143
+ const settled: Settled[] = [];
144
+ const pending: Promise<Settled>[] = [];
145
+ const warned = warnedFieldsRef.current;
146
+ groups.forEach(group => {
147
+ const { id, row } = group;
148
+ // Cells that already hold the value are not written; a row with none left is left alone.
149
+ const cells = group.cells
150
+ .filter(cell => !isSameValue(getCellValue(row, cell.field, cell.colDef), cell.value))
151
+ .sort((a, b) => a.order - b.order);
152
+ group.cells = cells;
153
+ if (cells.length === 0) return;
154
+ const outcome = attempt<unknown>(() => {
155
+ let newRow = row;
156
+ for (const cell of cells) newRow = buildEditedRow(newRow, cell, cell.colDef, warned);
157
+ return processRowUpdate ? processRowUpdate(newRow, row) : newRow;
158
+ });
159
+ if (!outcome.ok) {
160
+ settled.push({ id, ok: false, error: outcome.error });
161
+ return;
162
+ }
163
+ const value = outcome.value;
164
+ if (isPromiseLike(value)) {
165
+ pending.push(Promise.resolve(value).then(
166
+ (processed): Settled => ({ id, ok: true, processed }),
167
+ (error: unknown): Settled => ({ id, ok: false, error }),
168
+ ));
169
+ } else {
170
+ settled.push({ id, ok: true, processed: value });
171
+ }
172
+ });
173
+
174
+ const finish = (all: Settled[]): GridBatchEditOutcome => {
175
+ const byId = new Map<GridRowId, Settled>();
176
+ all.forEach(entry => { byId.set(entry.id, entry); });
177
+ const updated: GridRowId[] = [];
178
+ const failed: { id: GridRowId; error: unknown }[] = [];
179
+ const committed: GridCellChange[] = [];
180
+ const stored = new Map<GridRowId, R>();
181
+ groups.forEach(group => {
182
+ const entry = byId.get(group.id);
183
+ if (!entry) return;
184
+ if (!entry.ok) {
185
+ failed.push({ id: group.id, error: entry.error });
186
+ report(entry.error);
187
+ return;
188
+ }
189
+ const processed = entry.processed;
190
+ if (typeof processed !== 'object' || processed === null) {
191
+ const error = invalidProcessedRowError(processed, 'The row was not saved.');
192
+ failed.push({ id: group.id, error });
193
+ report(error);
194
+ return;
195
+ }
196
+ const storedRow = processed as R;
197
+ stored.set(group.id, storedRow);
198
+ updated.push(group.id);
199
+ for (const cell of group.cells) {
200
+ const before = getCellValue(group.row, cell.field, cell.colDef);
201
+ const after = getCellValue(storedRow, cell.field, cell.colDef);
202
+ if (!isSameValue(before, after)) committed.push({ id: group.id, field: cell.field, before, after });
203
+ }
204
+ });
205
+ store(stored);
206
+ if (committed.length > 0 && source !== 'undo' && source !== 'redo') {
207
+ latestRef.current.onCommitted?.(committed, source);
208
+ }
209
+ return { result: { updated, failed, skipped }, committed };
210
+ };
211
+
212
+ if (pending.length === 0) return Promise.resolve(finish(settled));
213
+ return Promise.all(pending).then(results => finish([...settled, ...results]));
214
+ }, [getRow, store]);
215
+
216
+ return useMemo(() => ({ applyEdits, getRow, storeRow }), [applyEdits, getRow, storeRow]);
217
+ }
@@ -1,6 +1,15 @@
1
1
  import { useState, useCallback, useRef, useEffect, useLayoutEffect, useMemo } from 'react';
2
2
  import type { GridColDef, GridRowId, GridRowModel } from '../../types';
3
3
  import { attempt } from '../../utils/attempt';
4
+ import { getCellValue } from '../../utils/values';
5
+ import {
6
+ buildEditedRow,
7
+ invalidProcessedRowError,
8
+ isPromiseLike,
9
+ isSameValue,
10
+ reportEditError,
11
+ } from '../../utils/editing/commit';
12
+ import type { GridCellChange } from '../../utils/editing/commit';
4
13
 
5
14
  export interface GridEditingState {
6
15
  editingCell: {
@@ -23,6 +32,12 @@ export interface UseGridEditingParams<R extends GridRowModel> {
23
32
  * grid resolved with getRowId), not on a field of the returned row.
24
33
  */
25
34
  onRowChange?: (newRow: R, id: GridRowId) => void;
35
+ /**
36
+ * Receives the cell change once `processRowUpdate` succeeded and the row was stored (undo/redo
37
+ * history): the value before and the stored value, both read through `getCellValue`. Not called
38
+ * when the stored value equals the previous one.
39
+ */
40
+ onCommitted?: (changes: GridCellChange[]) => void;
26
41
  }
27
42
 
28
43
  export interface StopCellEditParams {
@@ -46,18 +61,6 @@ interface PendingCommit {
46
61
 
47
62
  const RESOLVED: Promise<void> = Promise.resolve();
48
63
 
49
- /** A misuse of the editing contract (e.g. processRowUpdate returned nothing), as opposed to a consumer rejection. */
50
- class OpenGridXEditError extends Error {}
51
-
52
- function isSameValue(a: unknown, b: unknown): boolean {
53
- if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
54
- return Object.is(a, b);
55
- }
56
-
57
- function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
58
- return typeof value === 'object' && value !== null && typeof (value as { then?: unknown }).then === 'function';
59
- }
60
-
61
64
  /**
62
65
  * Cell edit sessions and their commits.
63
66
  *
@@ -126,24 +129,21 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
126
129
  return RESOLVED;
127
130
  }
128
131
 
129
- const report = (error: unknown) => {
130
- const onError = latestRef.current.onProcessRowUpdateError;
131
- if (onError) {
132
- onError(error);
133
- } else if (process.env.NODE_ENV !== 'production' && error instanceof OpenGridXEditError) {
134
- console.error(error.message);
135
- }
136
- };
132
+ const report = (error: unknown) => reportEditError(error, latestRef.current.onProcessRowUpdateError);
133
+ const colDef = columns?.find(c => c.field === cell.field);
137
134
 
138
135
  const apply = (processed: unknown) => {
139
136
  if (typeof processed !== 'object' || processed === null) {
140
- report(new OpenGridXEditError(
141
- `[OpenGridX] processRowUpdate must return the updated row object (or a Promise of it); ` +
142
- `it returned ${processed === null ? 'null' : typeof processed}. The edit was not saved and the editor stays open.`
143
- ));
137
+ report(invalidProcessedRowError(processed, 'The edit was not saved and the editor stays open.'));
144
138
  return;
145
139
  }
146
140
  latestRef.current.onRowChange?.(processed as R, cell.id);
141
+ const { onCommitted } = latestRef.current;
142
+ if (onCommitted) {
143
+ const before = getCellValue(existingRow, cell.field, colDef);
144
+ const after = getCellValue(processed as R, cell.field, colDef);
145
+ if (!isSameValue(before, after)) onCommitted([{ id: cell.id, field: cell.field, before, after }]);
146
+ }
147
147
  const now = editingCellRef.current;
148
148
  if (sessionRef.current !== token || !now) return;
149
149
  if (isSameValue(now.value, cell.value)) {
@@ -164,7 +164,6 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
164
164
  if (pendingRef.current.get(token) === entry) pendingRef.current.delete(token);
165
165
  };
166
166
 
167
- const colDef = columns?.find(c => c.field === cell.field);
168
167
  const outcome = attempt<unknown>(() => {
169
168
  const newRow = buildEditedRow(existingRow, cell, colDef, warnedFieldsRef.current);
170
169
  return processRowUpdate ? processRowUpdate(newRow, existingRow) : newRow;
@@ -246,23 +245,3 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
246
245
  setEditCellValue
247
246
  }), [editingCell, startCellEdit, stopCellEdit, setEditCellValue]);
248
247
  }
249
-
250
- function buildEditedRow<R extends GridRowModel>(
251
- row: R,
252
- cell: EditingCell,
253
- colDef: GridColDef<R> | undefined,
254
- warnedFields: Set<string>
255
- ): R {
256
- if (colDef?.valueSetter) {
257
- return colDef.valueSetter({ value: cell.value, row, field: cell.field });
258
- }
259
- if (colDef?.valueGetter && process.env.NODE_ENV !== 'production' && !warnedFields.has(cell.field)) {
260
- warnedFields.add(cell.field);
261
- console.warn(
262
- `[OpenGridX] Column "${cell.field}" is editable and has a valueGetter but no valueSetter. ` +
263
- `The edited value was written to row["${cell.field}"], which the valueGetter may ignore, so the cell ` +
264
- 'can keep showing the old value. Add a valueSetter that maps the value back onto the row.'
265
- );
266
- }
267
- return { ...row, [cell.field]: cell.value };
268
- }
package/lib/index.ts CHANGED
@@ -93,6 +93,7 @@ export type {
93
93
  GridValueGetterParams,
94
94
  GridSortCellParams,
95
95
  GridValueSetterParams,
96
+ GridValueParserParams,
96
97
  GridValueFormatterParams,
97
98
  GridRowParams,
98
99
  GridCellParams,
@@ -108,6 +109,15 @@ export type {
108
109
  GridCellSelectionChangeDetails,
109
110
  GridSelectedCell,
110
111
  GridCellSelectionStatsSlotProps,
112
+ GridEditSkipReason,
113
+ GridBatchEditResult,
114
+ GridClipboardPasteResult,
115
+ GridFillDirection,
116
+ GridFillResult,
117
+ GridFillValueParams,
118
+ GridBeforeClipboardPasteParams,
119
+ GridHistoryChangeParams,
120
+ GridUndoRedoOptions,
111
121
  GridColumnOrder,
112
122
  GridColumnOrderChangeParams,
113
123
  GridRowOrderChangeParams,
@@ -1,7 +1,10 @@
1
1
  @import './base/variables.css';
2
2
  @import './base/reset.css';
3
+ /* Shared primitives first, so component styles that restyle them win on equal specificity. */
4
+ @import '../components/ui/Input.css';
3
5
  @import '../components/DataGrid/DataGrid.css';
4
6
  @import '../components/Header/Header.css';
7
+ @import '../components/HeaderFilter/HeaderFilter.css';
5
8
  @import '../components/Row/Row.css';
6
9
  @import '../components/Cell/Cell.css';
7
10
  @import '../components/Cell/GridEditInputCell.css';
@@ -16,7 +19,6 @@
16
19
  @import '../components/ui/Checkbox.css';
17
20
  @import '../components/ui/DragHandleIcon.css';
18
21
  @import '../components/ui/ExpandIcon.css';
19
- @import '../components/ui/Input.css';
20
22
  @import '../components/Toolbar/GridToolbar.css';
21
23
  @import '../components/Toolbar/PivotPanel.css';
22
24
  @import '../components/FilterPanel/FilterPanel.css';
@@ -26,3 +28,4 @@
26
28
  @import '../components/ColumnGroupHeader/ColumnGroupHeader.css';
27
29
  @import '../components/Tooltip/Tooltip.css';
28
30
  @import '../components/CellSelectionStats/CellSelectionStats.css';
31
+ @import '../components/AiAssistant/GridAiAssistantPanel.css';
@@ -11,6 +11,7 @@ const primaryDerived: Record<string, string> = {
11
11
  '--ogx-grid-cell-focus-border': 'var(--ogx-color-primary)',
12
12
  '--ogx-range-background': 'color-mix(in srgb, var(--ogx-color-primary) 12%, transparent)',
13
13
  '--ogx-range-border': 'var(--ogx-color-primary)',
14
+ '--ogx-fill-handle-color': 'var(--ogx-range-border)',
14
15
  '--ogx-toolbar-btn-primary-bg': 'var(--ogx-color-primary)',
15
16
  '--ogx-toolbar-btn-primary-hover': 'var(--ogx-color-primary-dark)',
16
17
  '--ogx-toolbar-input-focus-border': 'var(--ogx-color-primary)',
@@ -285,6 +286,7 @@ function themeToCSS(theme: GridTheme): Record<string, string> {
285
286
  set('--ogx-grid-cell-focus-border', g.cellFocusBorder);
286
287
  set('--ogx-range-background', g.rangeBackground);
287
288
  set('--ogx-range-border', g.rangeBorder);
289
+ set('--ogx-fill-handle-color', g.fillHandleColor);
288
290
  set('--ogx-grid-pinned-left-shadow', g.pinnedLeftShadow);
289
291
  set('--ogx-grid-pinned-right-shadow', g.pinnedRightShadow);
290
292
  set('--ogx-checkbox-bg', g.checkboxBg);
@@ -163,6 +163,8 @@ export interface GridThemeGrid {
163
163
  rangeBackground?: string;
164
164
  /** Outline of a selected cell range (`--ogx-range-border`). Default: the primary colour. @since v3.3 */
165
165
  rangeBorder?: string;
166
+ /** The fill handle on the range's corner (`--ogx-fill-handle-color`). Default: the range border colour. @since v3.5 */
167
+ fillHandleColor?: string;
166
168
  pinnedLeftShadow?: string;
167
169
  pinnedRightShadow?: string;
168
170
  checkboxBg?: string;