@opencorestack/opengridx 3.2.2 → 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.
- package/CHANGELOG.md +53 -0
- package/README.md +16 -2
- package/dist/ai.cjs +1 -0
- package/dist/ai.cjs.map +1 -0
- package/dist/ai.d.ts +166 -0
- package/dist/ai.es.js +1 -0
- package/dist/ai.es.js.map +1 -0
- package/dist/index.d.ts +321 -3
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +6483 -5291
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -13
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +132 -3
- package/docs/README.md +4 -1
- package/docs/architecture/datagrid-orchestration.md +23 -6
- package/docs/components/header.md +25 -0
- package/docs/customization/theming.md +12 -1
- package/docs/features/ai-toolkit.md +119 -0
- package/docs/features/cell-selection.md +131 -0
- package/docs/features/clipboard.md +90 -0
- package/docs/features/editing-reordering.md +50 -0
- package/docs/features/keyboard-navigation.md +25 -2
- package/docs/features/undo-redo.md +79 -0
- package/docs/roadmap.md +64 -7
- package/lib/ai/coerce.ts +90 -0
- package/lib/ai/columns.ts +90 -0
- package/lib/ai/index.ts +16 -0
- package/lib/ai/schema.ts +214 -0
- package/lib/ai/types.ts +88 -0
- package/lib/ai/validate.ts +286 -0
- package/lib/components/Cell/Cell.css +35 -0
- package/lib/components/Cell/Cell.tsx +25 -13
- package/lib/components/Cell/CellErrorBoundary.tsx +3 -0
- package/lib/components/Cell/GridEditInputCell.css +12 -4
- package/lib/components/Cell/GridEditInputCell.tsx +19 -12
- package/lib/components/CellSelectionStats/CellSelectionStats.css +31 -0
- package/lib/components/CellSelectionStats/CellSelectionStats.tsx +32 -0
- package/lib/components/CellSelectionStats/GridCellSelectionStatsArea.tsx +24 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +14 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +3 -2
- package/lib/components/DataGrid/DataGrid.tsx +123 -3
- package/lib/components/DataGrid/GridLiveRegion.tsx +8 -2
- package/lib/components/DataGrid/GridPinnedRows.tsx +9 -0
- package/lib/components/DataGrid/GridToolbarSlot.tsx +5 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +9 -0
- package/lib/components/FilterPanel/FilterPanel.css +14 -0
- package/lib/components/FilterPanel/FilterPanel.tsx +5 -3
- package/lib/components/Header/Header.css +12 -0
- package/lib/components/Header/Header.tsx +17 -4
- package/lib/components/ListView/ListView.css +2 -4
- package/lib/components/ListView/ListViewRow.tsx +2 -2
- package/lib/components/Row/Row.tsx +38 -1
- package/lib/components/Toolbar/GlobalSearch.css +16 -0
- package/lib/components/Toolbar/GlobalSearch.tsx +3 -2
- package/lib/components/Toolbar/GridToolbar.tsx +10 -4
- package/lib/components/ui/Checkbox.tsx +26 -10
- package/lib/components/ui/Input.css +43 -0
- package/lib/components/ui/Input.tsx +24 -5
- package/lib/hooks/core/gridApiPlaceholder.ts +14 -1
- package/lib/hooks/core/useDataGrid.ts +27 -0
- package/lib/hooks/core/useGridCellSelection.ts +631 -0
- package/lib/hooks/core/useGridCellSelectionApi.ts +97 -0
- package/lib/hooks/core/useGridClipboardApi.ts +8 -2
- package/lib/hooks/core/useGridClipboardPaste.ts +283 -0
- package/lib/hooks/core/useGridControlledState.ts +38 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +32 -0
- package/lib/hooks/core/useGridRowInteractions.ts +8 -4
- package/lib/hooks/core/useGridUndoRedo.ts +220 -0
- package/lib/hooks/features/useAggregation.ts +8 -6
- package/lib/hooks/features/useGridBatchEdit.ts +217 -0
- package/lib/hooks/features/useGridClipboard.ts +47 -2
- package/lib/hooks/features/useGridEditing.ts +39 -51
- package/lib/index.ts +16 -0
- package/lib/styles/base/variables.css +6 -1
- package/lib/styles/opengridx.css +3 -1
- package/lib/theme/DataGridThemeProvider.tsx +4 -0
- package/lib/theme/presets.ts +8 -0
- package/lib/theme/types.ts +4 -0
- package/lib/types/index.ts +254 -0
- package/lib/utils/attempt.ts +14 -0
- package/lib/utils/cellSelection.ts +238 -0
- package/lib/utils/editing/commit.ts +69 -0
- package/lib/utils/editing/targets.ts +109 -0
- package/lib/utils/headerWrap.ts +23 -0
- package/lib/utils/parsing.ts +252 -0
- package/llms.txt +71 -2
- package/package.json +14 -4
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { useState, useCallback, useRef, useEffect, useLayoutEffect, useMemo } from 'react';
|
|
2
2
|
import type { GridColDef, 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';
|
|
3
13
|
|
|
4
14
|
export interface GridEditingState {
|
|
5
15
|
editingCell: {
|
|
@@ -22,6 +32,12 @@ export interface UseGridEditingParams<R extends GridRowModel> {
|
|
|
22
32
|
* grid resolved with getRowId), not on a field of the returned row.
|
|
23
33
|
*/
|
|
24
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;
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
export interface StopCellEditParams {
|
|
@@ -45,18 +61,6 @@ interface PendingCommit {
|
|
|
45
61
|
|
|
46
62
|
const RESOLVED: Promise<void> = Promise.resolve();
|
|
47
63
|
|
|
48
|
-
/** A misuse of the editing contract (e.g. processRowUpdate returned nothing), as opposed to a consumer rejection. */
|
|
49
|
-
class OpenGridXEditError extends Error {}
|
|
50
|
-
|
|
51
|
-
function isSameValue(a: unknown, b: unknown): boolean {
|
|
52
|
-
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
53
|
-
return Object.is(a, b);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
57
|
-
return typeof value === 'object' && value !== null && typeof (value as { then?: unknown }).then === 'function';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
64
|
/**
|
|
61
65
|
* Cell edit sessions and their commits.
|
|
62
66
|
*
|
|
@@ -95,6 +99,10 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
95
99
|
setEditingCellState(next);
|
|
96
100
|
}, []);
|
|
97
101
|
|
|
102
|
+
// The in-flight branch below re-runs the commit through this ref: a callback that refers to
|
|
103
|
+
// itself is read before its declaration, which React Compiler rejects.
|
|
104
|
+
const commitRef = useRef<((token: number, cell: EditingCell) => Promise<void>) | null>(null);
|
|
105
|
+
|
|
98
106
|
const commit = useCallback((token: number, cell: EditingCell): Promise<void> => {
|
|
99
107
|
const inFlight = pendingRef.current.get(token);
|
|
100
108
|
if (inFlight) {
|
|
@@ -103,7 +111,7 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
103
111
|
return inFlight.promise.then(() => {
|
|
104
112
|
const now = editingCellRef.current;
|
|
105
113
|
if (sessionRef.current !== token || !now || isSameValue(now.value, inFlight.value)) return;
|
|
106
|
-
return
|
|
114
|
+
return commitRef.current?.(token, now);
|
|
107
115
|
});
|
|
108
116
|
}
|
|
109
117
|
|
|
@@ -121,24 +129,21 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
121
129
|
return RESOLVED;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
|
-
const report = (error: unknown) =>
|
|
125
|
-
|
|
126
|
-
if (onError) {
|
|
127
|
-
onError(error);
|
|
128
|
-
} else if (process.env.NODE_ENV !== 'production' && error instanceof OpenGridXEditError) {
|
|
129
|
-
console.error(error.message);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
+
const report = (error: unknown) => reportEditError(error, latestRef.current.onProcessRowUpdateError);
|
|
133
|
+
const colDef = columns?.find(c => c.field === cell.field);
|
|
132
134
|
|
|
133
135
|
const apply = (processed: unknown) => {
|
|
134
136
|
if (typeof processed !== 'object' || processed === null) {
|
|
135
|
-
report(
|
|
136
|
-
`[OpenGridX] processRowUpdate must return the updated row object (or a Promise of it); ` +
|
|
137
|
-
`it returned ${processed === null ? 'null' : typeof processed}. The edit was not saved and the editor stays open.`
|
|
138
|
-
));
|
|
137
|
+
report(invalidProcessedRowError(processed, 'The edit was not saved and the editor stays open.'));
|
|
139
138
|
return;
|
|
140
139
|
}
|
|
141
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
|
+
}
|
|
142
147
|
const now = editingCellRef.current;
|
|
143
148
|
if (sessionRef.current !== token || !now) return;
|
|
144
149
|
if (isSameValue(now.value, cell.value)) {
|
|
@@ -159,18 +164,18 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
159
164
|
if (pendingRef.current.get(token) === entry) pendingRef.current.delete(token);
|
|
160
165
|
};
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const colDef = columns?.find(c => c.field === cell.field);
|
|
167
|
+
const outcome = attempt<unknown>(() => {
|
|
165
168
|
const newRow = buildEditedRow(existingRow, cell, colDef, warnedFieldsRef.current);
|
|
166
|
-
|
|
167
|
-
}
|
|
169
|
+
return processRowUpdate ? processRowUpdate(newRow, existingRow) : newRow;
|
|
170
|
+
});
|
|
171
|
+
if (!outcome.ok) {
|
|
168
172
|
// Consumer code (valueSetter / processRowUpdate) threw: report it, keep the editor open.
|
|
169
173
|
finish();
|
|
170
|
-
report(error);
|
|
174
|
+
report(outcome.error);
|
|
171
175
|
settle();
|
|
172
176
|
return promise;
|
|
173
177
|
}
|
|
178
|
+
const result = outcome.value;
|
|
174
179
|
|
|
175
180
|
const complete = (processed: unknown) => {
|
|
176
181
|
finish();
|
|
@@ -194,6 +199,9 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
194
199
|
}
|
|
195
200
|
return promise;
|
|
196
201
|
}, [setEditingCell]);
|
|
202
|
+
useLayoutEffect(() => {
|
|
203
|
+
commitRef.current = commit;
|
|
204
|
+
}, [commit]);
|
|
197
205
|
|
|
198
206
|
const startCellEdit = useCallback((editParams: { id: GridRowId; field: string; value: unknown }) => {
|
|
199
207
|
const current = editingCellRef.current;
|
|
@@ -237,23 +245,3 @@ export function useGridEditing<R extends GridRowModel>(params: UseGridEditingPar
|
|
|
237
245
|
setEditCellValue
|
|
238
246
|
}), [editingCell, startCellEdit, stopCellEdit, setEditCellValue]);
|
|
239
247
|
}
|
|
240
|
-
|
|
241
|
-
function buildEditedRow<R extends GridRowModel>(
|
|
242
|
-
row: R,
|
|
243
|
-
cell: EditingCell,
|
|
244
|
-
colDef: GridColDef<R> | undefined,
|
|
245
|
-
warnedFields: Set<string>
|
|
246
|
-
): R {
|
|
247
|
-
if (colDef?.valueSetter) {
|
|
248
|
-
return colDef.valueSetter({ value: cell.value, row, field: cell.field });
|
|
249
|
-
}
|
|
250
|
-
if (colDef?.valueGetter && process.env.NODE_ENV !== 'production' && !warnedFields.has(cell.field)) {
|
|
251
|
-
warnedFields.add(cell.field);
|
|
252
|
-
console.warn(
|
|
253
|
-
`[OpenGridX] Column "${cell.field}" is editable and has a valueGetter but no valueSetter. ` +
|
|
254
|
-
`The edited value was written to row["${cell.field}"], which the valueGetter may ignore, so the cell ` +
|
|
255
|
-
'can keep showing the old value. Add a valueSetter that maps the value back onto the row.'
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
return { ...row, [cell.field]: cell.value };
|
|
259
|
-
}
|
package/lib/index.ts
CHANGED
|
@@ -21,6 +21,8 @@ export type { GridToolbarProps, ToolbarButtonRenderProps, ToolbarQuickFilterRend
|
|
|
21
21
|
export { GridTooltip } from './components/Tooltip/Tooltip';
|
|
22
22
|
export type { GridTooltipProps } from './components/Tooltip/Tooltip';
|
|
23
23
|
export { ColumnVisibilityPanel } from './components/ColumnVisibilityPanel/ColumnVisibilityPanel';
|
|
24
|
+
export { CellSelectionStats } from './components/CellSelectionStats/CellSelectionStats';
|
|
25
|
+
export type { CellSelectionStatsProps } from './components/CellSelectionStats/CellSelectionStats';
|
|
24
26
|
export type { ColumnVisibilityPanelProps } from './components/ColumnVisibilityPanel/ColumnVisibilityPanel';
|
|
25
27
|
|
|
26
28
|
export { useGridApiRef } from './hooks/core/useGridApiRef';
|
|
@@ -91,6 +93,7 @@ export type {
|
|
|
91
93
|
GridValueGetterParams,
|
|
92
94
|
GridSortCellParams,
|
|
93
95
|
GridValueSetterParams,
|
|
96
|
+
GridValueParserParams,
|
|
94
97
|
GridValueFormatterParams,
|
|
95
98
|
GridRowParams,
|
|
96
99
|
GridCellParams,
|
|
@@ -99,6 +102,19 @@ export type {
|
|
|
99
102
|
GridPinnedPosition,
|
|
100
103
|
GridColumnVisibilityModel,
|
|
101
104
|
GridRowSelectionModel,
|
|
105
|
+
GridCellCoordinates,
|
|
106
|
+
GridCellRange,
|
|
107
|
+
GridCellSelectionModel,
|
|
108
|
+
GridCellSelectionReason,
|
|
109
|
+
GridCellSelectionChangeDetails,
|
|
110
|
+
GridSelectedCell,
|
|
111
|
+
GridCellSelectionStatsSlotProps,
|
|
112
|
+
GridEditSkipReason,
|
|
113
|
+
GridBatchEditResult,
|
|
114
|
+
GridClipboardPasteResult,
|
|
115
|
+
GridBeforeClipboardPasteParams,
|
|
116
|
+
GridHistoryChangeParams,
|
|
117
|
+
GridUndoRedoOptions,
|
|
102
118
|
GridColumnOrder,
|
|
103
119
|
GridColumnOrderChangeParams,
|
|
104
120
|
GridRowOrderChangeParams,
|
|
@@ -93,7 +93,10 @@
|
|
|
93
93
|
--ogx-grid-row-selected-background: var(--ogx-color-primary-light);
|
|
94
94
|
--ogx-grid-row-selected-hover-background: #dbeafe;
|
|
95
95
|
--ogx-grid-cell-focus-border: var(--ogx-color-primary);
|
|
96
|
-
|
|
96
|
+
/* Cell range selection (cellSelection, v3.3): tint over the range and its outline. */
|
|
97
|
+
--ogx-range-background: rgba(59, 130, 246, 0.12);
|
|
98
|
+
--ogx-range-border: var(--ogx-color-primary);
|
|
99
|
+
--ogx-grid-pinned-left-shadow: 4px 0 24px -4px rgba(0, 0, 0, 0.05);
|
|
97
100
|
--ogx-grid-pinned-right-shadow: -4px 0 24px -4px rgba(0, 0, 0, 0.05);
|
|
98
101
|
--ogx-skeleton-base: #f0f0f0;
|
|
99
102
|
--ogx-skeleton-highlight: #f8f8f8;
|
|
@@ -142,6 +145,8 @@
|
|
|
142
145
|
--ogx-grid-row-selected-background: #1e3a5f;
|
|
143
146
|
--ogx-grid-row-selected-hover-background: #1d4ed8;
|
|
144
147
|
--ogx-grid-cell-focus-border: #60a5fa;
|
|
148
|
+
--ogx-range-background: rgba(96, 165, 250, 0.18);
|
|
149
|
+
--ogx-range-border: #60a5fa;
|
|
145
150
|
--ogx-checkbox-bg: #1e293b;
|
|
146
151
|
--ogx-checkbox-border: #64748b;
|
|
147
152
|
--ogx-grid-pinned-left-shadow: 4px 0 24px -4px rgba(0, 0, 0, 0.3);
|
package/lib/styles/opengridx.css
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
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';
|
|
5
7
|
@import '../components/Row/Row.css';
|
|
@@ -16,7 +18,6 @@
|
|
|
16
18
|
@import '../components/ui/Checkbox.css';
|
|
17
19
|
@import '../components/ui/DragHandleIcon.css';
|
|
18
20
|
@import '../components/ui/ExpandIcon.css';
|
|
19
|
-
@import '../components/ui/Input.css';
|
|
20
21
|
@import '../components/Toolbar/GridToolbar.css';
|
|
21
22
|
@import '../components/Toolbar/PivotPanel.css';
|
|
22
23
|
@import '../components/FilterPanel/FilterPanel.css';
|
|
@@ -25,3 +26,4 @@
|
|
|
25
26
|
@import '../components/ListView/ListView.css';
|
|
26
27
|
@import '../components/ColumnGroupHeader/ColumnGroupHeader.css';
|
|
27
28
|
@import '../components/Tooltip/Tooltip.css';
|
|
29
|
+
@import '../components/CellSelectionStats/CellSelectionStats.css';
|
|
@@ -9,6 +9,8 @@ const primaryDerived: Record<string, string> = {
|
|
|
9
9
|
'--ogx-grid-row-selected-background': 'var(--ogx-color-primary-light)',
|
|
10
10
|
'--ogx-grid-row-selected-hover-background': 'color-mix(in srgb, var(--ogx-color-primary) 14%, var(--ogx-color-primary-light))',
|
|
11
11
|
'--ogx-grid-cell-focus-border': 'var(--ogx-color-primary)',
|
|
12
|
+
'--ogx-range-background': 'color-mix(in srgb, var(--ogx-color-primary) 12%, transparent)',
|
|
13
|
+
'--ogx-range-border': 'var(--ogx-color-primary)',
|
|
12
14
|
'--ogx-toolbar-btn-primary-bg': 'var(--ogx-color-primary)',
|
|
13
15
|
'--ogx-toolbar-btn-primary-hover': 'var(--ogx-color-primary-dark)',
|
|
14
16
|
'--ogx-toolbar-input-focus-border': 'var(--ogx-color-primary)',
|
|
@@ -281,6 +283,8 @@ function themeToCSS(theme: GridTheme): Record<string, string> {
|
|
|
281
283
|
set('--ogx-grid-row-selected-background', g.rowSelectedBackground);
|
|
282
284
|
set('--ogx-grid-row-selected-hover-background', g.rowSelectedHoverBackground);
|
|
283
285
|
set('--ogx-grid-cell-focus-border', g.cellFocusBorder);
|
|
286
|
+
set('--ogx-range-background', g.rangeBackground);
|
|
287
|
+
set('--ogx-range-border', g.rangeBorder);
|
|
284
288
|
set('--ogx-grid-pinned-left-shadow', g.pinnedLeftShadow);
|
|
285
289
|
set('--ogx-grid-pinned-right-shadow', g.pinnedRightShadow);
|
|
286
290
|
set('--ogx-checkbox-bg', g.checkboxBg);
|
package/lib/theme/presets.ts
CHANGED
|
@@ -21,6 +21,8 @@ export const darkTheme: GridTheme = {
|
|
|
21
21
|
rowSelectedBackground: '#1e3a5f',
|
|
22
22
|
rowSelectedHoverBackground: '#1d4ed8',
|
|
23
23
|
cellFocusBorder: '#60a5fa',
|
|
24
|
+
rangeBackground: 'rgba(96, 165, 250, 0.18)',
|
|
25
|
+
rangeBorder: '#60a5fa',
|
|
24
26
|
checkboxBg: '#1e293b',
|
|
25
27
|
checkboxBorder: '#64748b',
|
|
26
28
|
},
|
|
@@ -92,6 +94,8 @@ export const roseTheme: GridTheme = {
|
|
|
92
94
|
rowSelectedBackground: '#ffe4e6',
|
|
93
95
|
rowSelectedHoverBackground: '#fecdd3',
|
|
94
96
|
cellFocusBorder: '#e11d48',
|
|
97
|
+
rangeBackground: 'rgba(225, 29, 72, 0.10)',
|
|
98
|
+
rangeBorder: '#e11d48',
|
|
95
99
|
},
|
|
96
100
|
};
|
|
97
101
|
|
|
@@ -110,6 +114,8 @@ export const emeraldTheme: GridTheme = {
|
|
|
110
114
|
rowSelectedBackground: '#d1fae5',
|
|
111
115
|
rowSelectedHoverBackground: '#a7f3d0',
|
|
112
116
|
cellFocusBorder: '#059669',
|
|
117
|
+
rangeBackground: 'rgba(5, 150, 105, 0.12)',
|
|
118
|
+
rangeBorder: '#059669',
|
|
113
119
|
},
|
|
114
120
|
};
|
|
115
121
|
|
|
@@ -128,6 +134,8 @@ export const amberTheme: GridTheme = {
|
|
|
128
134
|
rowSelectedBackground: '#fef3c7',
|
|
129
135
|
rowSelectedHoverBackground: '#fde68a',
|
|
130
136
|
cellFocusBorder: '#d97706',
|
|
137
|
+
rangeBackground: 'rgba(217, 119, 6, 0.12)',
|
|
138
|
+
rangeBorder: '#d97706',
|
|
131
139
|
},
|
|
132
140
|
};
|
|
133
141
|
|
package/lib/theme/types.ts
CHANGED
|
@@ -159,6 +159,10 @@ export interface GridThemeGrid {
|
|
|
159
159
|
rowSelectedBackground?: string;
|
|
160
160
|
rowSelectedHoverBackground?: string;
|
|
161
161
|
cellFocusBorder?: string;
|
|
162
|
+
/** Tint over a selected cell range (`--ogx-range-background`). Default: the primary colour at 12%. @since v3.3 */
|
|
163
|
+
rangeBackground?: string;
|
|
164
|
+
/** Outline of a selected cell range (`--ogx-range-border`). Default: the primary colour. @since v3.3 */
|
|
165
|
+
rangeBorder?: string;
|
|
162
166
|
pinnedLeftShadow?: string;
|
|
163
167
|
pinnedRightShadow?: string;
|
|
164
168
|
checkboxBg?: string;
|
package/lib/types/index.ts
CHANGED
|
@@ -144,6 +144,12 @@ export interface GridColDef<R extends GridValidRowModel = GridRowModel> {
|
|
|
144
144
|
align?: GridAlignment;
|
|
145
145
|
/** Content alignment within the header cell. */
|
|
146
146
|
headerAlign?: GridAlignment;
|
|
147
|
+
/**
|
|
148
|
+
* Wraps this column's header title onto several lines instead of cutting it with an ellipsis.
|
|
149
|
+
* Overrides the grid's `wrapHeaderText`. The header keeps `headerHeight`: the title shows as many
|
|
150
|
+
* lines as fit and ends in an ellipsis if it is still longer. Raise `headerHeight` for more lines.
|
|
151
|
+
*/
|
|
152
|
+
wrapHeaderText?: boolean;
|
|
147
153
|
/** If false, sorting is disabled for this column. */
|
|
148
154
|
sortable?: boolean;
|
|
149
155
|
/** If false, filtering is disabled for this column. */
|
|
@@ -172,6 +178,14 @@ export interface GridColDef<R extends GridValidRowModel = GridRowModel> {
|
|
|
172
178
|
* @since v3.0
|
|
173
179
|
*/
|
|
174
180
|
valueSetter?(params: GridValueSetterParams<R>): R;
|
|
181
|
+
/**
|
|
182
|
+
* Turns pasted text into this column's value (clipboard paste), instead of the default parser for
|
|
183
|
+
* the column `type` (see `docs/features/clipboard.md`). Use it for locales such as `1.234,5` or for
|
|
184
|
+
* custom codes. Throw (or return `undefined`) to reject the text: the cell is skipped with reason
|
|
185
|
+
* `'invalidValue'`. The value then goes through `valueSetter` and `processRowUpdate` like an edit.
|
|
186
|
+
* @since v3.4
|
|
187
|
+
*/
|
|
188
|
+
valueParser?(text: string, params: GridValueParserParams<R>): unknown;
|
|
175
189
|
/**
|
|
176
190
|
* Custom ascending comparator for client-side sorting by this column, used instead of the built-in
|
|
177
191
|
* type-aware comparison. `v1` / `v2` are the cell values (`valueGetter` applied) and **include
|
|
@@ -234,6 +248,16 @@ export interface GridColDef<R extends GridValidRowModel = GridRowModel> {
|
|
|
234
248
|
* @default true
|
|
235
249
|
*/
|
|
236
250
|
exportable?: boolean;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Example values for this column, put into the schema from `getGridAiSchema`
|
|
254
|
+
* (`@opencorestack/opengridx/ai`) to help a language model write filters ("Acme" → `customer`).
|
|
255
|
+
* **These values are sent to the model.** The grid never reads row data for the schema; only what
|
|
256
|
+
* you list here leaves the app, so list nothing private. Strings, numbers and booleans are used
|
|
257
|
+
* (at most 10); other values are ignored. Not used by the grid itself.
|
|
258
|
+
* @since v3.4
|
|
259
|
+
*/
|
|
260
|
+
aiExamples?: unknown[];
|
|
237
261
|
}
|
|
238
262
|
|
|
239
263
|
/** Column definition used exclusively in List View mode. */
|
|
@@ -294,6 +318,14 @@ export interface GridValueGetterParams<R extends GridValidRowModel = GridRowMode
|
|
|
294
318
|
value: unknown;
|
|
295
319
|
}
|
|
296
320
|
|
|
321
|
+
/** Parameters passed to `GridColDef.valueParser`. @since v3.4 */
|
|
322
|
+
export interface GridValueParserParams<R extends GridValidRowModel = GridRowModel> {
|
|
323
|
+
/** The row the text is pasted into, as stored now. */
|
|
324
|
+
row: R;
|
|
325
|
+
field: string;
|
|
326
|
+
colDef: GridColDef<R>;
|
|
327
|
+
}
|
|
328
|
+
|
|
297
329
|
/**
|
|
298
330
|
* Parameters passed to the `valueSetter` function.
|
|
299
331
|
* @since v3.0
|
|
@@ -658,6 +690,68 @@ export interface GridPaginationModel {
|
|
|
658
690
|
|
|
659
691
|
export type GridRowSelectionModel = GridRowId[];
|
|
660
692
|
|
|
693
|
+
/** One cell, addressed by its row id and column field. @since v3.3 */
|
|
694
|
+
export interface GridCellCoordinates {
|
|
695
|
+
id: GridRowId;
|
|
696
|
+
field: string;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* A rectangle of cells between two corners. `anchor` is the active cell: it keeps focus, is the cell
|
|
701
|
+
* an edit starts in, and stays put while the range is extended. `head` is the corner that moves with
|
|
702
|
+
* Shift+arrows or the pointer, and the one scrolled into view. A single cell has `anchor` equal to `head`.
|
|
703
|
+
* @since v3.3
|
|
704
|
+
*/
|
|
705
|
+
export interface GridCellRange {
|
|
706
|
+
anchor: GridCellCoordinates;
|
|
707
|
+
head: GridCellCoordinates;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* The cell selection: zero or one range in 3.3 (the array form leaves room for several ranges later).
|
|
712
|
+
* Corners are ids and fields, so the rectangle follows its corner rows through sorting and filtering.
|
|
713
|
+
* @since v3.3
|
|
714
|
+
*/
|
|
715
|
+
export type GridCellSelectionModel = readonly GridCellRange[];
|
|
716
|
+
|
|
717
|
+
/** What changed the cell selection. @since v3.3 */
|
|
718
|
+
export type GridCellSelectionReason = 'pointer' | 'keyboard' | 'selectAll' | 'api' | 'clear' | 'dataChange';
|
|
719
|
+
|
|
720
|
+
/** Second argument of `onCellSelectionModelChange`. @since v3.3 */
|
|
721
|
+
export interface GridCellSelectionChangeDetails {
|
|
722
|
+
reason: GridCellSelectionReason;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/** One cell of the selected range, as returned by `apiRef.getSelectedCells()`. @since v3.3 */
|
|
726
|
+
export interface GridSelectedCell {
|
|
727
|
+
id: GridRowId;
|
|
728
|
+
field: string;
|
|
729
|
+
/** The cell's value, read through the column's `valueGetter`. */
|
|
730
|
+
value: unknown;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Props the grid passes to `slots.cellSelectionStats` (the status bar of `showCellSelectionStats`),
|
|
735
|
+
* before `slotProps.cellSelectionStats` is merged over them. Rendered only while more than one cell
|
|
736
|
+
* is selected.
|
|
737
|
+
* @since v3.3
|
|
738
|
+
*/
|
|
739
|
+
export interface GridCellSelectionStatsSlotProps {
|
|
740
|
+
apiRef: React.MutableRefObject<GridApi>;
|
|
741
|
+
/** Cells in the rectangle (rows × columns), synthetic rows and span-covered positions included. */
|
|
742
|
+
cellCount: number;
|
|
743
|
+
rowCount: number;
|
|
744
|
+
columnCount: number;
|
|
745
|
+
/** Non-empty cells of data rows. */
|
|
746
|
+
count: number;
|
|
747
|
+
/** Cells holding a number. */
|
|
748
|
+
numericCount: number;
|
|
749
|
+
/** Sum of the numeric cells, or `null` when there are none. */
|
|
750
|
+
sum: number | null;
|
|
751
|
+
/** Average of the numeric cells, or `null` when there are none. */
|
|
752
|
+
average: number | null;
|
|
753
|
+
}
|
|
754
|
+
|
|
661
755
|
export interface GridPinnedColumns {
|
|
662
756
|
left?: string[];
|
|
663
757
|
right?: string[];
|
|
@@ -735,6 +829,54 @@ export interface GridRowScrollEndParams {
|
|
|
735
829
|
viewportHeight: number;
|
|
736
830
|
}
|
|
737
831
|
|
|
832
|
+
/**
|
|
833
|
+
* Why a cell of a batch edit (paste, range clear, undo, redo) was not written:
|
|
834
|
+
* - `'notEditable'`: the column is not `editable`, `isCellEditable` returned false, or the row is a
|
|
835
|
+
* synthetic row (group header, subtotal, tree auto-parent, pivot Grand Total);
|
|
836
|
+
* - `'invalidValue'`: the pasted text does not parse for the column type (or `valueParser` rejected it);
|
|
837
|
+
* - `'changed'`: undo/redo only, the cell no longer holds the value the history recorded;
|
|
838
|
+
* - `'missing'`: undo/redo only, the row is gone.
|
|
839
|
+
* @since v3.4
|
|
840
|
+
*/
|
|
841
|
+
export type GridEditSkipReason = 'notEditable' | 'invalidValue' | 'changed' | 'missing';
|
|
842
|
+
|
|
843
|
+
/** What a batch edit (paste, range clear, undo, redo) did. @since v3.4 */
|
|
844
|
+
export interface GridBatchEditResult {
|
|
845
|
+
/** Rows whose `processRowUpdate` succeeded and that were stored, in the order they were first touched. */
|
|
846
|
+
updated: GridRowId[];
|
|
847
|
+
/** Rows whose `valueSetter` or `processRowUpdate` threw or rejected (also reported to `onProcessRowUpdateError`). */
|
|
848
|
+
failed: { id: GridRowId; error: unknown }[];
|
|
849
|
+
/** Cells that were not written, with the reason. */
|
|
850
|
+
skipped: { id: GridRowId; field: string; reason: GridEditSkipReason }[];
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/** `onClipboardPaste` details: the batch edit result plus the text that was pasted. @since v3.4 */
|
|
854
|
+
export interface GridClipboardPasteResult extends GridBatchEditResult {
|
|
855
|
+
text: string;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/** `onBeforeClipboardPaste` params. @since v3.4 */
|
|
859
|
+
export interface GridBeforeClipboardPasteParams {
|
|
860
|
+
/** The clipboard text (`text/plain`). */
|
|
861
|
+
text: string;
|
|
862
|
+
/** The cell the paste starts from (the range's top-left cell, or the focused cell). */
|
|
863
|
+
anchor: GridCellCoordinates | null;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/** `onHistoryChange` params. @since v3.4 */
|
|
867
|
+
export interface GridHistoryChangeParams {
|
|
868
|
+
canUndo: boolean;
|
|
869
|
+
canRedo: boolean;
|
|
870
|
+
/** Number of actions that can be undone. */
|
|
871
|
+
size: number;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/** `undoRedo` options. @since v3.4 */
|
|
875
|
+
export interface GridUndoRedoOptions {
|
|
876
|
+
/** How many actions are kept; the oldest is dropped beyond it. Default: 100. */
|
|
877
|
+
limit?: number;
|
|
878
|
+
}
|
|
879
|
+
|
|
738
880
|
/** Overrideable user-visible strings for internationalisation. All fields are optional; defaults match the built-in English strings. */
|
|
739
881
|
export interface GridLocaleText {
|
|
740
882
|
/** Label before the rows-per-page select. Default: `"Rows per page:"` */
|
|
@@ -745,6 +887,12 @@ export interface GridLocaleText {
|
|
|
745
887
|
paginationPage?: (page: number, pageCount: number) => string;
|
|
746
888
|
/** Empty-state label. When set, overrides the `noRowsLabel` prop on DataGrid. Default: `"No Data"` */
|
|
747
889
|
noRowsLabel?: string;
|
|
890
|
+
/**
|
|
891
|
+
* Screen-reader announcement after the cell range changes (`cellSelection`). Receives (cells, rows,
|
|
892
|
+
* columns). Default: `` (c, r, col) => `${c} cells selected, ${r} rows by ${col} columns` `` (singular for 1).
|
|
893
|
+
* @since v3.3
|
|
894
|
+
*/
|
|
895
|
+
cellSelectionAnnouncement?: (cellCount: number, rowCount: number, columnCount: number) => string;
|
|
748
896
|
}
|
|
749
897
|
|
|
750
898
|
/**
|
|
@@ -763,6 +911,13 @@ export interface DataGridProps<R extends GridValidRowModel = GridRowModel> {
|
|
|
763
911
|
rowHeight?: number;
|
|
764
912
|
/** Height of the header row in pixels. Defaults to 56. */
|
|
765
913
|
headerHeight?: number;
|
|
914
|
+
/**
|
|
915
|
+
* Wraps header titles onto several lines instead of cutting them with an ellipsis. The header
|
|
916
|
+
* keeps `headerHeight` (2 lines at the default 56px, 3 at 72px, 4 at 88px); a title that still
|
|
917
|
+
* does not fit ends in an ellipsis. `GridColDef.wrapHeaderText` overrides it per column.
|
|
918
|
+
* Defaults to `false`.
|
|
919
|
+
*/
|
|
920
|
+
wrapHeaderText?: boolean;
|
|
766
921
|
/** If true, the grid height will adjust to match the total height of its rows. */
|
|
767
922
|
autoHeight?: boolean;
|
|
768
923
|
/**
|
|
@@ -840,6 +995,57 @@ export interface DataGridProps<R extends GridValidRowModel = GridRowModel> {
|
|
|
840
995
|
*/
|
|
841
996
|
disableClipboardCopy?: boolean;
|
|
842
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Lets users select a rectangle of cells (drag, Shift+click, Shift+arrows, Ctrl/Cmd+A) and copy it
|
|
1000
|
+
* with Ctrl/Cmd+C as tab-separated text. While on, Ctrl/Cmd+C copies the cell range (the focused
|
|
1001
|
+
* cell at minimum) instead of the selected rows; `apiRef.copySelectedRows()` still copies rows.
|
|
1002
|
+
* Default: false. @since v3.3
|
|
1003
|
+
*/
|
|
1004
|
+
cellSelection?: boolean;
|
|
1005
|
+
/** Controlled cell selection (zero or one range). Needs `cellSelection`. @since v3.3 */
|
|
1006
|
+
cellSelectionModel?: GridCellSelectionModel;
|
|
1007
|
+
/** Fired when the cell selection changes, with what changed it. @since v3.3 */
|
|
1008
|
+
onCellSelectionModelChange?: (model: GridCellSelectionModel, details: GridCellSelectionChangeDetails) => void;
|
|
1009
|
+
/**
|
|
1010
|
+
* Shows a status bar under the grid with the count of non-empty cells and the sum and average of the
|
|
1011
|
+
* numeric cells in the range, while more than one cell is selected. Replaceable through
|
|
1012
|
+
* `slots.cellSelectionStats`. Needs `cellSelection`. Default: false. @since v3.3
|
|
1013
|
+
*/
|
|
1014
|
+
showCellSelectionStats?: boolean;
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* With `cellSelection` on, Ctrl/Cmd+V pastes tab-separated text (Excel, Google Sheets) into the
|
|
1018
|
+
* editable cells from the range's top-left cell (or the focused cell), through `valueSetter` and
|
|
1019
|
+
* `processRowUpdate`. `true` turns the shortcut off; `apiRef.pasteText()` still works. Default: false.
|
|
1020
|
+
* @since v3.4
|
|
1021
|
+
*/
|
|
1022
|
+
disableClipboardPaste?: boolean;
|
|
1023
|
+
/**
|
|
1024
|
+
* Fired after every paste with the result (`updated` rows, `failed` rows, `skipped` cells and why)
|
|
1025
|
+
* and the pasted text. @since v3.4
|
|
1026
|
+
*/
|
|
1027
|
+
onClipboardPaste?: (result: GridClipboardPasteResult) => void;
|
|
1028
|
+
/**
|
|
1029
|
+
* Called before a paste is applied. Return `false` to cancel it, or a string to paste instead of the
|
|
1030
|
+
* clipboard text. A throw cancels the paste. @since v3.4
|
|
1031
|
+
*/
|
|
1032
|
+
onBeforeClipboardPaste?: (params: GridBeforeClipboardPasteParams) => boolean | string;
|
|
1033
|
+
/**
|
|
1034
|
+
* With `cellSelection` on, Delete / Backspace on a range of more than one cell empties its editable
|
|
1035
|
+
* cells (`null`, or `''` in string columns) as one edit. `true` turns that off. Default: false.
|
|
1036
|
+
* @since v3.4
|
|
1037
|
+
*/
|
|
1038
|
+
disableRangeClear?: boolean;
|
|
1039
|
+
/**
|
|
1040
|
+
* Undo and redo of cell edits, pastes and range clears: Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z and Ctrl+Y while
|
|
1041
|
+
* focus is in the grid and no editor is open, and `apiRef.undo()` / `redo()`. Undo and redo write
|
|
1042
|
+
* through `valueSetter` and `processRowUpdate`, like an edit. `{ limit }` sets how many actions are
|
|
1043
|
+
* kept (default 100). Default: false. @since v3.4
|
|
1044
|
+
*/
|
|
1045
|
+
undoRedo?: boolean | GridUndoRedoOptions;
|
|
1046
|
+
/** Fired when what can be undone or redone changes (`undoRedo`). @since v3.4 */
|
|
1047
|
+
onHistoryChange?: (params: GridHistoryChangeParams) => void;
|
|
1048
|
+
|
|
843
1049
|
/** Loading state. With no rows the body shows skeleton rows (or `slots.loadingOverlay`); with rows, they stay and a progress bar (or `slots.loadingOverlay`) is shown over them. */
|
|
844
1050
|
loading?: boolean;
|
|
845
1051
|
/** Visual density of the grid. */
|
|
@@ -992,6 +1198,11 @@ export interface DataGridProps<R extends GridValidRowModel = GridRowModel> {
|
|
|
992
1198
|
* Receives `GridFooterSlotProps` (plus `slotProps.footer`).
|
|
993
1199
|
*/
|
|
994
1200
|
footer?: React.ComponentType<GridFooterSlotProps & Record<string, unknown>>;
|
|
1201
|
+
/**
|
|
1202
|
+
* Replaces the status bar of `showCellSelectionStats`. Receives `GridCellSelectionStatsSlotProps`
|
|
1203
|
+
* (plus `slotProps.cellSelectionStats`). @since v3.3
|
|
1204
|
+
*/
|
|
1205
|
+
cellSelectionStats?: React.ComponentType<GridCellSelectionStatsSlotProps & Record<string, unknown>>;
|
|
995
1206
|
};
|
|
996
1207
|
/** Properties passed directly to custom slots. */
|
|
997
1208
|
slotProps?: {
|
|
@@ -1007,6 +1218,8 @@ export interface DataGridProps<R extends GridValidRowModel = GridRowModel> {
|
|
|
1007
1218
|
loadingOverlay?: GridOverlaySlotProps;
|
|
1008
1219
|
/** Merged over the `GridFooterSlotProps` the grid passes to the footer slot. */
|
|
1009
1220
|
footer?: Partial<GridFooterSlotProps> & Record<string, unknown>;
|
|
1221
|
+
/** Merged over the `GridCellSelectionStatsSlotProps` the grid passes to the status bar. */
|
|
1222
|
+
cellSelectionStats?: Partial<GridCellSelectionStatsSlotProps> & Record<string, unknown>;
|
|
1010
1223
|
};
|
|
1011
1224
|
|
|
1012
1225
|
/** When true, renders the grid as a single-column list of cards. Perfect for mobile/responsive views. */
|
|
@@ -1198,4 +1411,45 @@ export interface GridApi<R extends GridValidRowModel = GridRowModel> {
|
|
|
1198
1411
|
* in which case nothing is written) and rejects when the clipboard write fails.
|
|
1199
1412
|
*/
|
|
1200
1413
|
copySelectedRows: () => Promise<void>;
|
|
1414
|
+
|
|
1415
|
+
/** The cell selection (`[]` when `cellSelection` is off or nothing is selected). @since v3.3 */
|
|
1416
|
+
getCellSelectionModel: () => GridCellSelectionModel;
|
|
1417
|
+
/**
|
|
1418
|
+
* Replaces the cell selection (only the first range is kept) and moves focus to its anchor.
|
|
1419
|
+
* Does nothing while `cellSelection` is off. @since v3.3
|
|
1420
|
+
*/
|
|
1421
|
+
setCellSelectionModel: (model: GridCellSelectionModel) => void;
|
|
1422
|
+
/** Selects the rectangle between two cells; `anchor` becomes the active cell. @since v3.3 */
|
|
1423
|
+
selectCellRange: (anchor: GridCellCoordinates, head: GridCellCoordinates) => void;
|
|
1424
|
+
/** Empties the cell selection. Focus stays where it is. @since v3.3 */
|
|
1425
|
+
clearCellSelection: () => void;
|
|
1426
|
+
/**
|
|
1427
|
+
* Cells of the current range in display order (row by row, columns in screen order), values read
|
|
1428
|
+
* through the column's `valueGetter`. Synthetic rows (group headers, subtotals, tree auto-parents, the
|
|
1429
|
+
* pivot Grand Total) and positions covered by a span are left out. @since v3.3
|
|
1430
|
+
*/
|
|
1431
|
+
getSelectedCells: () => GridSelectedCell[];
|
|
1432
|
+
/**
|
|
1433
|
+
* Copies the cell range (the focused cell when nothing is selected) as tab-separated text, formatted
|
|
1434
|
+
* the way the cells show it, with no header line. Resolves without writing when there is nothing to
|
|
1435
|
+
* copy; rejects when the clipboard write fails. @since v3.3
|
|
1436
|
+
*/
|
|
1437
|
+
copySelectedCells: () => Promise<void>;
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Pastes tab-separated text as Ctrl/Cmd+V does: from `anchor` (default: the range's top-left cell,
|
|
1441
|
+
* else the focused cell), with `onBeforeClipboardPaste` and `onClipboardPaste`. Works with
|
|
1442
|
+
* `disableClipboardPaste`. Resolves once every row's `processRowUpdate` has settled. @since v3.4
|
|
1443
|
+
*/
|
|
1444
|
+
pasteText: (text: string, anchor?: GridCellCoordinates) => Promise<GridBatchEditResult>;
|
|
1445
|
+
/** Undoes the last action (`undoRedo`). Resolves with what was written. @since v3.4 */
|
|
1446
|
+
undo: () => Promise<GridBatchEditResult>;
|
|
1447
|
+
/** Redoes the last undone action (`undoRedo`). @since v3.4 */
|
|
1448
|
+
redo: () => Promise<GridBatchEditResult>;
|
|
1449
|
+
/** Whether there is an action to undo. @since v3.4 */
|
|
1450
|
+
canUndo: () => boolean;
|
|
1451
|
+
/** Whether there is an undone action to redo. @since v3.4 */
|
|
1452
|
+
canRedo: () => boolean;
|
|
1453
|
+
/** Forgets every action (undo and redo). @since v3.4 */
|
|
1454
|
+
clearHistory: () => void;
|
|
1201
1455
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AttemptResult<T> = { ok: true; value: T } | { ok: false; error: unknown };
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calls `fn` and contains a throw. Components and hooks use it instead of an inline `try` / `catch`
|
|
5
|
+
* around expressions with `?.`, `??`, `||` or `? :`: React Compiler cannot compile a function that has
|
|
6
|
+
* those inside a `try` statement yet and skips the whole component or hook.
|
|
7
|
+
*/
|
|
8
|
+
export function attempt<T>(fn: () => T): AttemptResult<T> {
|
|
9
|
+
try {
|
|
10
|
+
return { ok: true, value: fn() };
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return { ok: false, error };
|
|
13
|
+
}
|
|
14
|
+
}
|