@updog/data-editor 0.1.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 +15 -0
- package/LICENSE +259 -0
- package/README.md +219 -0
- package/SECURITY.md +24 -0
- package/THIRD_PARTY_NOTICES.txt +297 -0
- package/assets/chatTransform.worker-BuuMWuTX.js +1 -0
- package/assets/filter.worker-Dtii7NzO.js +1 -0
- package/index.css +2 -0
- package/index.d.ts +2866 -0
- package/index.js +25230 -0
- package/package.json +69 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,2866 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
declare var export_default = {
|
|
4
|
+
ui: {
|
|
5
|
+
button: {
|
|
6
|
+
defaultAriaLabel: "Button",
|
|
7
|
+
},
|
|
8
|
+
calendar: {
|
|
9
|
+
previousMonth: "Previous month",
|
|
10
|
+
nextMonth: "Next month",
|
|
11
|
+
months: {
|
|
12
|
+
january: "January",
|
|
13
|
+
february: "February",
|
|
14
|
+
march: "March",
|
|
15
|
+
april: "April",
|
|
16
|
+
may: "May",
|
|
17
|
+
june: "June",
|
|
18
|
+
july: "July",
|
|
19
|
+
august: "August",
|
|
20
|
+
september: "September",
|
|
21
|
+
october: "October",
|
|
22
|
+
november: "November",
|
|
23
|
+
december: "December",
|
|
24
|
+
},
|
|
25
|
+
monthsShort: {
|
|
26
|
+
january: "Jan",
|
|
27
|
+
february: "Feb",
|
|
28
|
+
march: "Mar",
|
|
29
|
+
april: "Apr",
|
|
30
|
+
may: "May",
|
|
31
|
+
june: "Jun",
|
|
32
|
+
july: "Jul",
|
|
33
|
+
august: "Aug",
|
|
34
|
+
september: "Sep",
|
|
35
|
+
october: "Oct",
|
|
36
|
+
november: "Nov",
|
|
37
|
+
december: "Dec",
|
|
38
|
+
},
|
|
39
|
+
weekDays: {
|
|
40
|
+
mo: "Mo",
|
|
41
|
+
tu: "Tu",
|
|
42
|
+
we: "We",
|
|
43
|
+
th: "Th",
|
|
44
|
+
fr: "Fr",
|
|
45
|
+
sa: "Sa",
|
|
46
|
+
su: "Su",
|
|
47
|
+
},
|
|
48
|
+
weekDaysFull: {
|
|
49
|
+
mo: "Monday",
|
|
50
|
+
tu: "Tuesday",
|
|
51
|
+
we: "Wednesday",
|
|
52
|
+
th: "Thursday",
|
|
53
|
+
fr: "Friday",
|
|
54
|
+
sa: "Saturday",
|
|
55
|
+
su: "Sunday",
|
|
56
|
+
},
|
|
57
|
+
label: "Calendar",
|
|
58
|
+
selectMonth: "{{month}}, select month",
|
|
59
|
+
selectYear: "{{year}}, select year",
|
|
60
|
+
},
|
|
61
|
+
datePicker: {
|
|
62
|
+
placeholder: "DD/MM/YYYY",
|
|
63
|
+
openCalendar: "Open calendar",
|
|
64
|
+
clearSelection: "Clear selection",
|
|
65
|
+
},
|
|
66
|
+
input: {
|
|
67
|
+
clearField: "Clear field",
|
|
68
|
+
showPassword: "Show password",
|
|
69
|
+
hidePassword: "Hide password",
|
|
70
|
+
},
|
|
71
|
+
select: {
|
|
72
|
+
noResults: "No results",
|
|
73
|
+
clearSelection: "Clear selection",
|
|
74
|
+
},
|
|
75
|
+
comboBox: {
|
|
76
|
+
noResults: "No results",
|
|
77
|
+
clearSelection: "Clear selection",
|
|
78
|
+
},
|
|
79
|
+
emptyState: {
|
|
80
|
+
illustrationAlt: "No data illustration",
|
|
81
|
+
},
|
|
82
|
+
tag: {
|
|
83
|
+
remove: "Remove",
|
|
84
|
+
},
|
|
85
|
+
modal: {
|
|
86
|
+
closeLabel: "Close",
|
|
87
|
+
},
|
|
88
|
+
drawer: {
|
|
89
|
+
closeLabel: "Close",
|
|
90
|
+
},
|
|
91
|
+
toast: {
|
|
92
|
+
closeNotification: "Close notification",
|
|
93
|
+
},
|
|
94
|
+
confirmDialog: {
|
|
95
|
+
cancel: "Cancel",
|
|
96
|
+
},
|
|
97
|
+
menu: {
|
|
98
|
+
submenuAriaLabel: "{{text}} submenu",
|
|
99
|
+
},
|
|
100
|
+
formField: {
|
|
101
|
+
optional: "Optional",
|
|
102
|
+
},
|
|
103
|
+
segment: {
|
|
104
|
+
segmentControl: "Segment control",
|
|
105
|
+
},
|
|
106
|
+
stepper: {
|
|
107
|
+
progress: "Progress",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
dataEditor: {
|
|
111
|
+
modal: {
|
|
112
|
+
importTitle: "Import Data",
|
|
113
|
+
editTitle: "Edit Data",
|
|
114
|
+
},
|
|
115
|
+
confirmClose: {
|
|
116
|
+
title: "Discard changes?",
|
|
117
|
+
text: "You have unsaved changes. Are you sure you want to close?",
|
|
118
|
+
action: "Discard",
|
|
119
|
+
},
|
|
120
|
+
confirmSubmit: {
|
|
121
|
+
title: "Confirm submission",
|
|
122
|
+
text: "You are about to submit the following changes:",
|
|
123
|
+
createRows_one: "{{count}} new row will be created",
|
|
124
|
+
createRows_other: "{{count}} new rows will be created",
|
|
125
|
+
updateRows_one: "{{count}} row will be updated",
|
|
126
|
+
updateRows_other: "{{count}} rows will be updated",
|
|
127
|
+
deleteRows_one: "{{count}} row will be deleted",
|
|
128
|
+
deleteRows_other: "{{count}} rows will be deleted",
|
|
129
|
+
bannerTitle_one: "{{count}} row has errors",
|
|
130
|
+
bannerTitle_other: "{{count}} rows have errors",
|
|
131
|
+
action: "Submit",
|
|
132
|
+
},
|
|
133
|
+
footer: {
|
|
134
|
+
submit: "Submit",
|
|
135
|
+
import: "Import",
|
|
136
|
+
next: "Next",
|
|
137
|
+
cancel: "Cancel",
|
|
138
|
+
back: "Back",
|
|
139
|
+
export: "Export",
|
|
140
|
+
exportAll: "Export all",
|
|
141
|
+
exportFiltered: "Export filtered",
|
|
142
|
+
csvFormat: "Comma-separated values (.csv)",
|
|
143
|
+
tsvFormat: "Tab-separated values (.tsv)",
|
|
144
|
+
excelFormat: "Microsoft Excel (.xlsx)",
|
|
145
|
+
jsonFormat: "JSON (.json)",
|
|
146
|
+
xmlFormat: "XML (.xml)",
|
|
147
|
+
},
|
|
148
|
+
statusBar: {
|
|
149
|
+
totalRows: "{{total}} rows",
|
|
150
|
+
filteredRows: "{{filtered}} of {{total}} rows",
|
|
151
|
+
},
|
|
152
|
+
toast: {
|
|
153
|
+
dismiss: "Dismiss",
|
|
154
|
+
rowMayNotBeVisible:
|
|
155
|
+
"Row has been added but may not be visible due to active filters or sorting",
|
|
156
|
+
},
|
|
157
|
+
grid: {
|
|
158
|
+
emptyTitle: "No data",
|
|
159
|
+
emptyText: "Import data or add rows manually",
|
|
160
|
+
loading: "Loading data...",
|
|
161
|
+
emptyCell: "Empty",
|
|
162
|
+
a11y: {
|
|
163
|
+
gridLabel: "Data editor",
|
|
164
|
+
cellFocus: "Row {{row}}, {{column}}: {{value}}",
|
|
165
|
+
cellFocusEmpty: "Row {{row}}, {{column}}: empty",
|
|
166
|
+
selectAll: "All cells selected",
|
|
167
|
+
rowsSelected: "{{count}} rows selected",
|
|
168
|
+
columnsSelected: "{{count}} columns selected",
|
|
169
|
+
},
|
|
170
|
+
contextMenu: {
|
|
171
|
+
cut: "Cut",
|
|
172
|
+
cutCells: "Cut ({{count}})",
|
|
173
|
+
copy: "Copy",
|
|
174
|
+
copyCells: "Copy ({{count}})",
|
|
175
|
+
paste: "Paste",
|
|
176
|
+
deleteRow: "Delete row",
|
|
177
|
+
deleteRows: "Delete rows ({{count}})",
|
|
178
|
+
restoreRow: "Restore row",
|
|
179
|
+
restoreRows: "Restore rows ({{count}})",
|
|
180
|
+
sortAsc: "Sort A to Z",
|
|
181
|
+
sortDesc: "Sort Z to A",
|
|
182
|
+
sortRemove: "Remove sorting",
|
|
183
|
+
insertAbove: "Insert row above",
|
|
184
|
+
insertBelow: "Insert row below",
|
|
185
|
+
duplicateRow: "Duplicate row",
|
|
186
|
+
cutRow: "Cut row",
|
|
187
|
+
cutRows: "Cut rows ({{count}})",
|
|
188
|
+
copyRow: "Copy row",
|
|
189
|
+
copyRows: "Copy rows ({{count}})",
|
|
190
|
+
clearRow: "Clear row",
|
|
191
|
+
clearRows: "Clear rows ({{count}})",
|
|
192
|
+
clearAll: "Clear all",
|
|
193
|
+
pin: "Pin column",
|
|
194
|
+
pinColumns: "Pin columns",
|
|
195
|
+
unpin: "Unpin column",
|
|
196
|
+
unpinColumns: "Unpin columns",
|
|
197
|
+
cutColumn: "Cut column",
|
|
198
|
+
copyColumn: "Copy column",
|
|
199
|
+
pasteColumn: "Paste column",
|
|
200
|
+
lockColumn: "Lock column",
|
|
201
|
+
lockColumns: "Lock columns",
|
|
202
|
+
lockedCannotClear: "Can't clear a locked column",
|
|
203
|
+
lockedCannotDelete: "Can't delete a locked column",
|
|
204
|
+
lockedCannotRevert: "Can't revert a locked column",
|
|
205
|
+
lockedCannotUnlock: "This column was locked by configuration",
|
|
206
|
+
unlockColumn: "Unlock column",
|
|
207
|
+
unlockColumns: "Unlock columns",
|
|
208
|
+
clearColumn: "Clear column",
|
|
209
|
+
clearColumns: "Clear columns",
|
|
210
|
+
deleteColumn: "Delete column",
|
|
211
|
+
deleteColumns: "Delete columns",
|
|
212
|
+
editColumn: "Edit column",
|
|
213
|
+
transform: "Transform",
|
|
214
|
+
transformMerge: "Merge",
|
|
215
|
+
transformSplit: "Split",
|
|
216
|
+
formula: "Formula",
|
|
217
|
+
lockedCannotFormula: "Can't apply a formula to a locked column",
|
|
218
|
+
revertChanges: "Revert changes",
|
|
219
|
+
},
|
|
220
|
+
formulaModal: {
|
|
221
|
+
title: "Apply formula",
|
|
222
|
+
apply: "Apply",
|
|
223
|
+
applyToRows_one: "Apply to {{count}} row",
|
|
224
|
+
applyToRows_other: "Apply to {{count}} rows",
|
|
225
|
+
inputLabel: "Formula",
|
|
226
|
+
placeholder: "=UPPER(value) · Type @ to browse",
|
|
227
|
+
groupFunctions: "Functions",
|
|
228
|
+
groupColumns: "Columns",
|
|
229
|
+
groupKeywords: "Keywords",
|
|
230
|
+
keywordValue: "Current cell value",
|
|
231
|
+
preview: "Preview",
|
|
232
|
+
errors: {
|
|
233
|
+
"unexpected-character": "'{{char}}' isn't valid here",
|
|
234
|
+
"unexpected-escape":
|
|
235
|
+
"Invalid escape '\\{{char}}' — only \\} and \\\\ are allowed inside @{}",
|
|
236
|
+
"unterminated-column-ref": "@{...} isn't closed — add a closing }",
|
|
237
|
+
"expected-equals": "Formulas must start with =",
|
|
238
|
+
"expected-expression": "Add a function, value, or @column after =",
|
|
239
|
+
"expected-open-paren": "Missing '(' after {{name}}",
|
|
240
|
+
"expected-close-paren": "Missing closing ')'",
|
|
241
|
+
"expected-column-name":
|
|
242
|
+
"Add a column name after @ — e.g. @Name or @{First Name}",
|
|
243
|
+
"unexpected-token": "'{{token}}' isn't expected here",
|
|
244
|
+
"unknown-function": "No function named '{{name}}'",
|
|
245
|
+
"unknown-column": "No column named '{{name}}'",
|
|
246
|
+
"arity-mismatch-too-few":
|
|
247
|
+
"{{name}} expects at least {{min}} arguments — got {{actual}}",
|
|
248
|
+
"arity-mismatch-too-many":
|
|
249
|
+
"{{name}} takes at most {{max}} arguments — got {{actual}}",
|
|
250
|
+
"function-not-in-expression-mode":
|
|
251
|
+
"'{{name}}' can't be used here — use Merge or Split from the Transform menu",
|
|
252
|
+
"column-not-numeric":
|
|
253
|
+
"'{{name}}' is not a number column — arithmetic requires number columns",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
formulas: {
|
|
257
|
+
UPPER: { label: "Uppercase" },
|
|
258
|
+
LOWER: { label: "Lowercase" },
|
|
259
|
+
TRIM: { label: "Trim whitespace" },
|
|
260
|
+
CAPITALIZE: { label: "Capitalize" },
|
|
261
|
+
ROUND: { label: "Round" },
|
|
262
|
+
ROUND_UP: { label: "Round up" },
|
|
263
|
+
ROUND_DOWN: { label: "Round down" },
|
|
264
|
+
ABS: { label: "Absolute value" },
|
|
265
|
+
CLEAR: { label: "Clear" },
|
|
266
|
+
CONCAT: { label: "Concatenate" },
|
|
267
|
+
LEFT: { label: "Left" },
|
|
268
|
+
RIGHT: { label: "Right" },
|
|
269
|
+
MID: { label: "Mid" },
|
|
270
|
+
SUBSTITUTE: { label: "Substitute" },
|
|
271
|
+
MERGE: { label: "Merge columns" },
|
|
272
|
+
SPLIT: { label: "Split column" },
|
|
273
|
+
},
|
|
274
|
+
transformMerge: {
|
|
275
|
+
addColumn: "Add column",
|
|
276
|
+
apply: "Apply",
|
|
277
|
+
deleteSource: "Delete source columns",
|
|
278
|
+
deleteSourceDescription:
|
|
279
|
+
"Columns added during import can be deleted. Schema columns will remain.",
|
|
280
|
+
from: "From",
|
|
281
|
+
into: "Into",
|
|
282
|
+
joinWith: "Join with",
|
|
283
|
+
joinWithPlaceholder: "e.g. space, comma",
|
|
284
|
+
preview: "Preview",
|
|
285
|
+
removeColumn: "Remove column",
|
|
286
|
+
applyToRows_one: "Apply to {{count}} row",
|
|
287
|
+
applyToRows_other: "Apply to {{count}} rows",
|
|
288
|
+
selectColumn: "Select column",
|
|
289
|
+
title: "Merge",
|
|
290
|
+
},
|
|
291
|
+
transformSplit: {
|
|
292
|
+
addColumn: "Add column",
|
|
293
|
+
apply: "Apply",
|
|
294
|
+
applyToRows_one: "Apply to {{count}} row",
|
|
295
|
+
applyToRows_other: "Apply to {{count}} rows",
|
|
296
|
+
deleteSource: "Delete source column",
|
|
297
|
+
deleteSourceDescription:
|
|
298
|
+
"Columns added during import can be deleted. Schema columns will remain.",
|
|
299
|
+
from: "From",
|
|
300
|
+
into: "Into",
|
|
301
|
+
preview: "Preview",
|
|
302
|
+
removeColumn: "Remove column",
|
|
303
|
+
selectColumn: "Select column",
|
|
304
|
+
splitBy: "Split by",
|
|
305
|
+
splitByPlaceholder: "e.g. space, comma",
|
|
306
|
+
title: "Split",
|
|
307
|
+
},
|
|
308
|
+
phase: {
|
|
309
|
+
splitting: "Splitting columns...",
|
|
310
|
+
merging: "Merging columns...",
|
|
311
|
+
pasting: "Pasting data...",
|
|
312
|
+
clearing: "Clearing cells...",
|
|
313
|
+
filling: "Filling cells...",
|
|
314
|
+
sorting: "Sorting rows...",
|
|
315
|
+
undoing: "Reverting changes...",
|
|
316
|
+
redoing: "Reapplying changes...",
|
|
317
|
+
deleting: "Deleting rows...",
|
|
318
|
+
deletingSource: "Removing data source...",
|
|
319
|
+
importing: "Importing data...",
|
|
320
|
+
transforming: "Transforming...",
|
|
321
|
+
default: "Processing...",
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
dataSources: {
|
|
325
|
+
title: "Data Sources",
|
|
326
|
+
importData: "Import data",
|
|
327
|
+
addRow: "Add row",
|
|
328
|
+
manuallyAdded: "Manually added",
|
|
329
|
+
existingData: "Existing data",
|
|
330
|
+
addDataSource: "Add data source",
|
|
331
|
+
show: "Show",
|
|
332
|
+
hide: "Hide",
|
|
333
|
+
remove: "Remove",
|
|
334
|
+
confirmRemove: {
|
|
335
|
+
title: "Remove data source?",
|
|
336
|
+
text: "All data imported from this source will be removed.",
|
|
337
|
+
action: "Remove",
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
filters: {
|
|
341
|
+
title: "Filters",
|
|
342
|
+
search: "Search",
|
|
343
|
+
replace: "Replace",
|
|
344
|
+
findAndReplace: "Find & Replace",
|
|
345
|
+
searchMode: "Search mode",
|
|
346
|
+
matchCase: "Match case",
|
|
347
|
+
matchEntireCell: "Match entire cell",
|
|
348
|
+
rows: "Rows",
|
|
349
|
+
columns: "Columns",
|
|
350
|
+
searchInColumns: "Search in columns",
|
|
351
|
+
allColumns: "All columns",
|
|
352
|
+
rowsWithErrors: "Rows with errors",
|
|
353
|
+
editedRows: "Changed rows",
|
|
354
|
+
newRows: "New rows",
|
|
355
|
+
rowsWithEmptyCells: "Rows with empty cells",
|
|
356
|
+
deletedRows: "Deleted rows",
|
|
357
|
+
previousOccurrence: "Previous occurrence",
|
|
358
|
+
nextOccurrence: "Next occurrence",
|
|
359
|
+
zeroResults: "No results found",
|
|
360
|
+
allValues: "All values",
|
|
361
|
+
min: "Min",
|
|
362
|
+
max: "Max",
|
|
363
|
+
from: "From",
|
|
364
|
+
to: "To",
|
|
365
|
+
replaceAll: "Replace all",
|
|
366
|
+
resetFilters: "Reset filters",
|
|
367
|
+
showMore: "View error details",
|
|
368
|
+
showLess: "Hide error details",
|
|
369
|
+
},
|
|
370
|
+
undoRedo: {
|
|
371
|
+
undo: "Undo",
|
|
372
|
+
redo: "Redo",
|
|
373
|
+
},
|
|
374
|
+
validation: {
|
|
375
|
+
required: "This field is required",
|
|
376
|
+
invalidNumber: "Invalid number",
|
|
377
|
+
invalidEmail: "Invalid email address",
|
|
378
|
+
invalidDate: "Invalid date",
|
|
379
|
+
endDateBeforeStart: "End date must be after start date",
|
|
380
|
+
},
|
|
381
|
+
license: {
|
|
382
|
+
loading: "Loading data editor",
|
|
383
|
+
errors: {
|
|
384
|
+
LICENSE_INVALID: {
|
|
385
|
+
title: "Invalid license",
|
|
386
|
+
text: "Please contact support for assistance.",
|
|
387
|
+
},
|
|
388
|
+
MISSING_API_KEY: {
|
|
389
|
+
title: "Missing API key",
|
|
390
|
+
text: "No API key was provided. Please configure a valid API key.",
|
|
391
|
+
},
|
|
392
|
+
DOMAIN_NOT_ALLOWED: {
|
|
393
|
+
title: "Domain not allowed",
|
|
394
|
+
text: "This domain is not authorized for this license. Please check your domain settings.",
|
|
395
|
+
},
|
|
396
|
+
SUBSCRIPTION_INACTIVE: {
|
|
397
|
+
title: "Subscription inactive",
|
|
398
|
+
text: "Your subscription is no longer active. Please renew to continue.",
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
errorState: {
|
|
403
|
+
title: "Something went wrong",
|
|
404
|
+
text: "Please close this window and try again",
|
|
405
|
+
},
|
|
406
|
+
uploader: {
|
|
407
|
+
steps: {
|
|
408
|
+
selectFiles: "Select files",
|
|
409
|
+
sheetSelection: "Select sheet",
|
|
410
|
+
matchColumns: "Match columns",
|
|
411
|
+
matchValues: "Match values",
|
|
412
|
+
primaryKey: "Primary key",
|
|
413
|
+
},
|
|
414
|
+
uploadFile: {
|
|
415
|
+
title: "Upload file",
|
|
416
|
+
text: "Upload a CSV, TSV, Excel, JSON or XML file to import data",
|
|
417
|
+
textDynamic: "Upload a {{formats}} file to import data",
|
|
418
|
+
parseError: "Failed to parse file",
|
|
419
|
+
clickToUpload: "Click to upload",
|
|
420
|
+
orDragAndDrop: "or drag and drop",
|
|
421
|
+
formats: "CSV, TSV, XLSX, JSON, XML",
|
|
422
|
+
downloadExample: "Download sample file",
|
|
423
|
+
},
|
|
424
|
+
remoteSources: {
|
|
425
|
+
browseFile: "Browse file",
|
|
426
|
+
emptyData: "The source returned no data",
|
|
427
|
+
fetchError: "Failed to load data",
|
|
428
|
+
},
|
|
429
|
+
matchColumns: {
|
|
430
|
+
title: "Match columns",
|
|
431
|
+
text: "Map imported columns to existing columns",
|
|
432
|
+
banner: "Some columns could not be automatically matched",
|
|
433
|
+
importedColumns: "Imported columns",
|
|
434
|
+
targetColumns: "Target columns",
|
|
435
|
+
unmatchedCount: "{{count}} unmatched",
|
|
436
|
+
selectColumnPlaceholder: "Select column",
|
|
437
|
+
bestMatch: "Best match",
|
|
438
|
+
createColumn: "Create column",
|
|
439
|
+
createColumnNameLabel: "Column name",
|
|
440
|
+
createColumnNamePlaceholder: "Enter column name",
|
|
441
|
+
createColumnNameTaken: "A column with this name already exists",
|
|
442
|
+
createColumnSubmit: "Create",
|
|
443
|
+
createColumnTitle: "Create column",
|
|
444
|
+
editColumnSubmit: "Save",
|
|
445
|
+
editColumnTitle: "Edit column",
|
|
446
|
+
createColumnWarning: "A matching column already exists",
|
|
447
|
+
newColumnTag: "New",
|
|
448
|
+
unmatchedWarning:
|
|
449
|
+
"Unmatched columns won't be imported. Match this column to keep the data. You can transform columns after importing.",
|
|
450
|
+
},
|
|
451
|
+
sheetSelection: {
|
|
452
|
+
title: "Select sheet",
|
|
453
|
+
text: "This file contains multiple sheets. Choose which sheet to import.",
|
|
454
|
+
rowCount: "{{count}} rows",
|
|
455
|
+
emptySheet: "Empty sheet",
|
|
456
|
+
},
|
|
457
|
+
matchValues: {
|
|
458
|
+
title: "Match values",
|
|
459
|
+
text: "Review how imported values map to your column options. Adjust any mappings that need a different match.",
|
|
460
|
+
importedValues: "Imported values",
|
|
461
|
+
targetValue: "Target value",
|
|
462
|
+
fullyMatched: "All matched",
|
|
463
|
+
unmatchedCount: "{{matched}}/{{total}} matched",
|
|
464
|
+
selectValuePlaceholder: "Select value",
|
|
465
|
+
showMatched: "Show matched",
|
|
466
|
+
allMatched: "All values are matched",
|
|
467
|
+
unmatchedWarning: "Unmatched values won't be imported",
|
|
468
|
+
},
|
|
469
|
+
primaryKey: {
|
|
470
|
+
title: "Select primary key",
|
|
471
|
+
text: "Choose the column that uniquely identifies each row",
|
|
472
|
+
none: "No primary key",
|
|
473
|
+
noneHint: "All rows will be appended as new entries without deduplication.",
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
chat: {
|
|
477
|
+
emptyTitle: "What would you like to fix?",
|
|
478
|
+
header: "Chat with AI",
|
|
479
|
+
placeholder: "Ask AI...",
|
|
480
|
+
send: "Send",
|
|
481
|
+
},
|
|
482
|
+
common: {
|
|
483
|
+
cancel: "Cancel",
|
|
484
|
+
collapse: "Collapse",
|
|
485
|
+
expand: "Expand",
|
|
486
|
+
remove: "Remove",
|
|
487
|
+
search: "Search",
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
type PluralSuffixes = "_zero" | "_one" | "_two" | "_few" | "_many" | "_other";
|
|
493
|
+
|
|
494
|
+
/** Makes all properties optional recursively. */
|
|
495
|
+
type DeepPartial<T> = {
|
|
496
|
+
[P in keyof T]?: T[P] extends Record<string, unknown>
|
|
497
|
+
? DeepPartial<T[P]>
|
|
498
|
+
: T[P];
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Expands any key ending in a plural suffix to include all 6 CLDR plural suffixes.
|
|
503
|
+
* e.g. `{ createRows_one: string; createRows_other: string }` becomes
|
|
504
|
+
* `{ createRows_zero?: string; createRows_one?: string; createRows_two?: string; ... }`
|
|
505
|
+
*/
|
|
506
|
+
type ExpandPluralKeys<T> = T extends Record<string, unknown>
|
|
507
|
+
? {
|
|
508
|
+
[K in keyof T as K extends `${infer Base}${PluralSuffixes}`
|
|
509
|
+
? `${Base}_zero` | `${Base}_one` | `${Base}_two` | `${Base}_few` | `${Base}_many` | `${Base}_other`
|
|
510
|
+
: K]: T[K] extends Record<string, unknown> ? ExpandPluralKeys<T[K]> : T[K];
|
|
511
|
+
}
|
|
512
|
+
: T;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Consumer-facing translations type. All keys optional — consumers override only what they need.
|
|
516
|
+
* Plural keys are expanded to all 6 CLDR forms.
|
|
517
|
+
*/
|
|
518
|
+
type DataEditorTranslations = DeepPartial<ExpandPluralKeys<typeof export_default>>;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* ChunkedProcessor — Generic utility for processing items in prioritized chunks
|
|
522
|
+
* with cancellation support via generation counter.
|
|
523
|
+
*
|
|
524
|
+
* Used to break O(N) synchronous operations into non-blocking background work:
|
|
525
|
+
* 1. Priority items are processed synchronously first (e.g. viewport rows)
|
|
526
|
+
* 2. Remaining items are processed in chunks via MessageChannel
|
|
527
|
+
* 3. Between chunks the browser can paint frames and handle events
|
|
528
|
+
*
|
|
529
|
+
* Uses MessageChannel instead of setTimeout(0) to avoid the browser's ~4ms
|
|
530
|
+
* minimum delay imposed on nested setTimeouts, reducing scheduling overhead
|
|
531
|
+
* from ~6ms/chunk to ~1ms/chunk.
|
|
532
|
+
*
|
|
533
|
+
* Cancellation uses the same generation counter pattern as FilterEngine:
|
|
534
|
+
* calling cancel() or run() again increments the generation, and any
|
|
535
|
+
* in-flight MessageChannel callbacks silently exit when they detect
|
|
536
|
+
* their generation is stale.
|
|
537
|
+
*
|
|
538
|
+
* This class has zero domain knowledge — it works with any item type.
|
|
539
|
+
*/
|
|
540
|
+
type ChunkedProcessorCallbacks<T> = {
|
|
541
|
+
/** All items to process (excluding priority items). */
|
|
542
|
+
all: T[];
|
|
543
|
+
/** Items processed synchronously before any chunking begins. */
|
|
544
|
+
priority: T[];
|
|
545
|
+
/** Called for each batch of items (priority batch first, then chunks). */
|
|
546
|
+
onChunk: (items: T[]) => void;
|
|
547
|
+
/** Called after each chunk with (processed so far, total). */
|
|
548
|
+
onProgress: (processed: number, total: number) => void;
|
|
549
|
+
/** Called once when all items have been processed. */
|
|
550
|
+
onComplete: () => void;
|
|
551
|
+
/** Called when processing is cancelled via cancel(). */
|
|
552
|
+
onCancel: () => void;
|
|
553
|
+
};
|
|
554
|
+
declare class ChunkedProcessor<T> {
|
|
555
|
+
private _generation;
|
|
556
|
+
private _isRunning;
|
|
557
|
+
private _chunkSize;
|
|
558
|
+
private _activeOnCancel;
|
|
559
|
+
private _channel;
|
|
560
|
+
private _pendingChunkFn;
|
|
561
|
+
constructor(chunkSize?: number);
|
|
562
|
+
get isRunning(): boolean;
|
|
563
|
+
get chunkSize(): number;
|
|
564
|
+
run(params: ChunkedProcessorCallbacks<T>): void;
|
|
565
|
+
cancel(): void;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
type RegisterSourceOptions = {
|
|
569
|
+
name: string;
|
|
570
|
+
id?: DataSourceId;
|
|
571
|
+
isDeletable?: boolean;
|
|
572
|
+
isInitialData?: boolean;
|
|
573
|
+
};
|
|
574
|
+
type MergeEntry<TRow> = {
|
|
575
|
+
row: TRow;
|
|
576
|
+
sourceId: DataSourceId;
|
|
577
|
+
isNew: boolean;
|
|
578
|
+
isEdited: boolean;
|
|
579
|
+
};
|
|
580
|
+
type RemovalPlan<TRow> = {
|
|
581
|
+
rowsToDelete: Set<TRowId>;
|
|
582
|
+
rowsToRestore: Array<{
|
|
583
|
+
rowId: TRowId;
|
|
584
|
+
row: TRow;
|
|
585
|
+
originalSourceId: DataSourceId;
|
|
586
|
+
isNew: boolean;
|
|
587
|
+
isEdited: boolean;
|
|
588
|
+
}>;
|
|
589
|
+
};
|
|
590
|
+
type ExtendedRemovalPlan<TRow> = RemovalPlan<TRow> & {
|
|
591
|
+
sourceId: DataSourceId;
|
|
592
|
+
repairedEntries: Array<{
|
|
593
|
+
sourceId: DataSourceId;
|
|
594
|
+
rowId: TRowId;
|
|
595
|
+
before: MergeEntry<TRow>;
|
|
596
|
+
after: MergeEntry<TRow> | null;
|
|
597
|
+
}>;
|
|
598
|
+
};
|
|
599
|
+
declare class SourceManager<TRow extends DataEditorRow = DataEditorRow> {
|
|
600
|
+
private readonly _defaultSourceId;
|
|
601
|
+
private readonly overrides;
|
|
602
|
+
private readonly sources;
|
|
603
|
+
private readonly mergedRows;
|
|
604
|
+
getSourceId(rowId: TRowId): DataSourceId;
|
|
605
|
+
setSourceId(rowId: TRowId, sourceId: DataSourceId): void;
|
|
606
|
+
deleteSourceId(rowId: TRowId): void;
|
|
607
|
+
getOverrides(): ReadonlyMap<TRowId, DataSourceId>;
|
|
608
|
+
register(options: RegisterSourceOptions): DataSourceId;
|
|
609
|
+
/**
|
|
610
|
+
* Re-insert a source using a full captured state, preserving
|
|
611
|
+
* isVisible, isLoading, rowCount, etc. Used by SourceLifecycle.restore.
|
|
612
|
+
* If the source already exists, overwrites its state.
|
|
613
|
+
*/
|
|
614
|
+
restoreState(state: DataSourceState): void;
|
|
615
|
+
has(sourceId: DataSourceId): boolean;
|
|
616
|
+
get(sourceId: DataSourceId): DataSourceState | undefined;
|
|
617
|
+
delete(sourceId: DataSourceId): void;
|
|
618
|
+
setLoading(sourceId: DataSourceId, isLoading: boolean): void;
|
|
619
|
+
finalizeAllSources(): void;
|
|
620
|
+
values(): IterableIterator<DataSourceState>;
|
|
621
|
+
getHiddenSourceIds(): Set<DataSourceId>;
|
|
622
|
+
saveMergeSnapshot(sourceId: DataSourceId, rowId: TRowId, existingRow: TRow, previousSourceId: DataSourceId, isNew: boolean, isEdited: boolean): void;
|
|
623
|
+
/**
|
|
624
|
+
* Public so commands can re-install merge entries during undo of a remove.
|
|
625
|
+
*/
|
|
626
|
+
restoreMergeEntry(sourceId: DataSourceId, rowId: TRowId, entry: MergeEntry<TRow>): void;
|
|
627
|
+
/**
|
|
628
|
+
* Pure — computes the full removal plan without mutating any state.
|
|
629
|
+
* Callers run applyRemovalPlan(plan) to commit.
|
|
630
|
+
*/
|
|
631
|
+
planRemoval(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
|
|
632
|
+
/**
|
|
633
|
+
* Mutates internal state per plan produced by planRemoval.
|
|
634
|
+
*/
|
|
635
|
+
applyRemovalPlan(plan: ExtendedRemovalPlan<TRow>): void;
|
|
636
|
+
clear(): void;
|
|
637
|
+
private getUniqueName;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
type ServerDataManagerDeps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
641
|
+
clear(): void;
|
|
642
|
+
setLoading(isLoading: boolean): void;
|
|
643
|
+
registerSource(options: RegisterSourceOptions): DataSourceId;
|
|
644
|
+
setSourceLoading(sourceId: DataSourceId, isLoading: boolean): void;
|
|
645
|
+
getLocalRowCount(): number;
|
|
646
|
+
replaceServerRows(sourceId: DataSourceId, rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
|
|
647
|
+
appendServerRows(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
|
|
648
|
+
prependServerRows(rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
|
|
649
|
+
applyServerRowMeta(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
|
|
650
|
+
};
|
|
651
|
+
type FetchDirection = "forward" | "backward" | "jump";
|
|
652
|
+
declare class ServerDataManager<TRow extends DataEditorRow = DataEditorRow> {
|
|
653
|
+
private _offset;
|
|
654
|
+
private _totalCount;
|
|
655
|
+
private _isFetching;
|
|
656
|
+
private readonly _pageSize;
|
|
657
|
+
private readonly _maxBufferRows;
|
|
658
|
+
private _filters;
|
|
659
|
+
private _sources;
|
|
660
|
+
private _sort;
|
|
661
|
+
private _filterOptions;
|
|
662
|
+
private _filterOptionsFetched;
|
|
663
|
+
private _abortController;
|
|
664
|
+
private _syncAbort;
|
|
665
|
+
private _lastVisibleStart;
|
|
666
|
+
private _debouncedFetch;
|
|
667
|
+
private readonly _config;
|
|
668
|
+
private readonly _dataStoreRef;
|
|
669
|
+
private readonly _sourceLabel;
|
|
670
|
+
private _onChanged;
|
|
671
|
+
constructor(config: DataEditorServer<TRow>, dataStoreRef: ServerDataManagerDeps<TRow>, sourceLabel: string);
|
|
672
|
+
get offset(): number;
|
|
673
|
+
get totalCount(): number | null;
|
|
674
|
+
get isFetching(): boolean;
|
|
675
|
+
get pageSize(): number;
|
|
676
|
+
get maxBufferRows(): number;
|
|
677
|
+
setOnChanged(callback: () => void): void;
|
|
678
|
+
setOffset(offset: number): void;
|
|
679
|
+
setTotalCount(count: number): void;
|
|
680
|
+
private setFetching;
|
|
681
|
+
getExcess(currentCount: number, newCount: number): number;
|
|
682
|
+
shouldFetch(visibleStart: number, visibleEnd: number, loadedCount: number): FetchDirection | null;
|
|
683
|
+
/**
|
|
684
|
+
* Full reload — abort in-flight, clear store, fetch first page.
|
|
685
|
+
* Called on initial load, search/filter/sort changes, and resetFilters.
|
|
686
|
+
*/
|
|
687
|
+
reload(): void;
|
|
688
|
+
/**
|
|
689
|
+
* Scroll-driven pagination with velocity-based debouncing.
|
|
690
|
+
* Called from CanvasGrid on every scroll event.
|
|
691
|
+
*/
|
|
692
|
+
handleScroll(visibleStart: number, visibleEnd: number): void;
|
|
693
|
+
/**
|
|
694
|
+
* Fetch a single page based on scroll position and current window state.
|
|
695
|
+
*/
|
|
696
|
+
private fetchPage;
|
|
697
|
+
/**
|
|
698
|
+
* Re-query the current viewport and replace row data, metadata, and counts.
|
|
699
|
+
* Called after successful edits and find-and-replace mutations.
|
|
700
|
+
* Each call aborts the previous in-flight sync.
|
|
701
|
+
*/
|
|
702
|
+
syncCurrentView(): void;
|
|
703
|
+
/**
|
|
704
|
+
* Merge filter keys into server filter state and reload.
|
|
705
|
+
* Called by DataStore.setFilters() in server mode and by filter components.
|
|
706
|
+
*/
|
|
707
|
+
setFilters(filters: Partial<Filters>): void;
|
|
708
|
+
/**
|
|
709
|
+
* Set sort state and reload.
|
|
710
|
+
*/
|
|
711
|
+
setSort(sort: SortState): void;
|
|
712
|
+
/**
|
|
713
|
+
* Restrict query to visible sources and reload.
|
|
714
|
+
* Pass `undefined` to include all sources.
|
|
715
|
+
*/
|
|
716
|
+
setSources(sources: string[] | undefined): void;
|
|
717
|
+
/**
|
|
718
|
+
* Clear all filters and sort, then reload.
|
|
719
|
+
*/
|
|
720
|
+
resetFilters(): void;
|
|
721
|
+
/**
|
|
722
|
+
* One-time fetch of filter option dictionaries for sidebar filter controls.
|
|
723
|
+
*/
|
|
724
|
+
fetchFilterOptions(): void;
|
|
725
|
+
getFilterOptions(): FilterOptionsResponse | null;
|
|
726
|
+
get onEdit(): (params: EditParams, options?: ServerCallOptions) => Promise<EditResponse | void>;
|
|
727
|
+
get filters(): Record<string, unknown>;
|
|
728
|
+
get sort(): SortState;
|
|
729
|
+
get sources(): string[] | undefined;
|
|
730
|
+
get onSourceRemove(): ((params: SourceRemoveParams) => Promise<void>) | undefined;
|
|
731
|
+
get onColumnDelete(): ((params: ColumnDeleteParams) => Promise<EditResponse | void>) | undefined;
|
|
732
|
+
get onColumnEdit(): ((params: ColumnEditParams) => Promise<EditResponse | void>) | undefined;
|
|
733
|
+
get hasExport(): boolean;
|
|
734
|
+
private _exportAbortController;
|
|
735
|
+
export(format: DataEditorFormat, allRows: boolean, rtl: boolean): Promise<void>;
|
|
736
|
+
clear(): void;
|
|
737
|
+
destroy(): void;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* A post-resolution selection rectangle in stable coordinate space.
|
|
742
|
+
* Produced by grid-layer resolvers from CellRange[] in grid-index space.
|
|
743
|
+
* Consumed by DataStore operations and server sync.
|
|
744
|
+
*/
|
|
745
|
+
type SelectionRect = {
|
|
746
|
+
readonly fields: readonly string[];
|
|
747
|
+
readonly rowIds: readonly TRowId[];
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* ServerEditBuilder — Stateless coordinate translator for server-delegated edits.
|
|
752
|
+
*
|
|
753
|
+
* Converts frontend coordinates (TRowId, column index, grid ranges) into
|
|
754
|
+
* `EditParams` with `Region[]` that the server can interpret.
|
|
755
|
+
*
|
|
756
|
+
* Does NOT call `onEdit`. Only builds params.
|
|
757
|
+
* DataStore calls the builder, then sends the result to the server.
|
|
758
|
+
*
|
|
759
|
+
* Responsibilities:
|
|
760
|
+
* - TRowId → ServerRowId translation via primaryKey
|
|
761
|
+
* - Grid index → column ID translation via columns array
|
|
762
|
+
* - Filter/sort context attachment from ServerDataManager
|
|
763
|
+
*/
|
|
764
|
+
|
|
765
|
+
type ServerEditDeps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
766
|
+
getPrimaryKey: () => string;
|
|
767
|
+
getRowById: (id: TRowId) => TRow | undefined;
|
|
768
|
+
getColumnIds: () => string[];
|
|
769
|
+
getFilters: () => Record<string, unknown>;
|
|
770
|
+
getSort: () => SortState;
|
|
771
|
+
getLockedColumns: () => ReadonlyMap<string, ColumnLockMode>;
|
|
772
|
+
};
|
|
773
|
+
declare class ServerEditBuilder<TRow extends DataEditorRow = DataEditorRow> {
|
|
774
|
+
private readonly _deps;
|
|
775
|
+
constructor(deps: ServerEditDeps<TRow>);
|
|
776
|
+
resolveServerRowId(rowId: TRowId): ServerRowId | undefined;
|
|
777
|
+
buildRegion(rowIds: TRowId[], columnIds: string[]): Region;
|
|
778
|
+
/**
|
|
779
|
+
* Collapse rowIds × columnIds into minimal Region[].
|
|
780
|
+
* - All columns → omit column fields (row-only regions).
|
|
781
|
+
* - Contiguous columns in schema order → single fromColumn/toColumn span.
|
|
782
|
+
* - Non-contiguous → one region per contiguous column group.
|
|
783
|
+
* Rows are expressed as fromRow/toRow using first/last of the provided array.
|
|
784
|
+
*/
|
|
785
|
+
buildRegions(rowIds: TRowId[], columnIds: string[]): Region[];
|
|
786
|
+
/**
|
|
787
|
+
* Collapse columnIds into minimal column-only Region[] (all rows implied).
|
|
788
|
+
* - All columns → `{ allSelected: true }`.
|
|
789
|
+
* - Contiguous in schema order → single `{ fromColumn, toColumn }`.
|
|
790
|
+
* - Non-contiguous → one region per contiguous group.
|
|
791
|
+
*/
|
|
792
|
+
buildColumnRegions(columnIds: string[]): Region[];
|
|
793
|
+
/**
|
|
794
|
+
* Build minimal Region[] from multiple selection rectangles.
|
|
795
|
+
* Each rect is collapsed independently, preserving disjoint selections.
|
|
796
|
+
*/
|
|
797
|
+
buildRegionsFromRects(rects: SelectionRect[]): Region[];
|
|
798
|
+
buildAllSelectedRegion(): Region;
|
|
799
|
+
buildColumnRegion(columnId: string): Region;
|
|
800
|
+
buildRowRegion(fromRowId: TRowId, toRowId: TRowId): Region;
|
|
801
|
+
cellEdit(rowId: TRowId, field: string, value: unknown): EditParams;
|
|
802
|
+
clear(target: Region[]): EditParams;
|
|
803
|
+
paste(source: Region[], target: Region[], cut?: boolean): EditParams;
|
|
804
|
+
pasteExternal(target: Region[], values: unknown[][]): EditParams;
|
|
805
|
+
fill(source: Region[], target: Region[]): EditParams;
|
|
806
|
+
transform(target: Region[], transform: TransformParams): EditParams;
|
|
807
|
+
deleteRows(rowRanges: [TRowId, TRowId][]): EditParams;
|
|
808
|
+
restoreRows(rowRanges: [TRowId, TRowId][]): EditParams;
|
|
809
|
+
deleteAllRows(): EditParams;
|
|
810
|
+
restoreAllRows(): EditParams;
|
|
811
|
+
insertRow(anchorRowId: TRowId | undefined, position: InsertParams["position"], values: unknown[][], columnIds: string[]): EditParams;
|
|
812
|
+
/**
|
|
813
|
+
* Returns null when all columns are selected (caller decides representation).
|
|
814
|
+
* Otherwise returns contiguous column spans as `{ fromColumn, toColumn }` regions.
|
|
815
|
+
*/
|
|
816
|
+
private collapseColumns;
|
|
817
|
+
private viewContext;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
type UpdogErrorCode = "PARSE_ERROR" | "RENDER_ERROR" | "TRANSFORM_ERROR" | "VALIDATION_ERROR" | "WORKER_ERROR" | "COMMAND_ERROR" | "OPERATION_ERROR";
|
|
821
|
+
type UpdogError = {
|
|
822
|
+
code: UpdogErrorCode;
|
|
823
|
+
message: string;
|
|
824
|
+
source: string;
|
|
825
|
+
originalError?: unknown;
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
declare class ErrorHandler {
|
|
829
|
+
private onError?;
|
|
830
|
+
constructor(onError?: (error: UpdogError) => void);
|
|
831
|
+
handleError(error: UpdogError): void;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* DirtyTracker — Change classification and revert detection for rows.
|
|
836
|
+
*
|
|
837
|
+
* Every row falls into one of three categories:
|
|
838
|
+
* - Clean — matches the backend; not tracked here at all
|
|
839
|
+
* - New — created locally (CSV import or manual add), never existed on the backend
|
|
840
|
+
* - Edited — exists on the backend but has been modified locally
|
|
841
|
+
*
|
|
842
|
+
* Classification uses inverted tracking via `nonBackendRowIds`: only non-backend
|
|
843
|
+
* rows (CSV imports, manual adds) are stored in the set. Since backend rows are
|
|
844
|
+
* the vast majority (~1M), this inverted approach saves ~50MB by assuming any
|
|
845
|
+
* row NOT in the set is a backend row.
|
|
846
|
+
* - First edit of a backend row → "edited", original row is snapshotted
|
|
847
|
+
* - First edit of a non-backend row → "new"
|
|
848
|
+
*
|
|
849
|
+
* Smart revert: after every edit, the current row is compared field-by-field
|
|
850
|
+
* against the snapshot. If all fields match the original, the row silently
|
|
851
|
+
* reverts to "clean" — no dirty flag, no undo entry needed. This lets users
|
|
852
|
+
* fix typos by simply typing the original value back.
|
|
853
|
+
*
|
|
854
|
+
* Merge snapshots: when a CSV import overwrites an existing row, the original
|
|
855
|
+
* data + classification is saved so it can be restored if the source is removed.
|
|
856
|
+
*/
|
|
857
|
+
|
|
858
|
+
type IDirtyTracker<TRow extends DataEditorRow = DataEditorRow> = {
|
|
859
|
+
markDeleted(id: TRowId): void;
|
|
860
|
+
unmarkDeleted(id: TRowId): void;
|
|
861
|
+
isDeleted(id: TRowId): boolean;
|
|
862
|
+
getDeletedRowIds(): ReadonlySet<TRowId>;
|
|
863
|
+
deletedCount(): number;
|
|
864
|
+
isDefaultSourceRow(id: TRowId): boolean;
|
|
865
|
+
isNew(id: TRowId): boolean;
|
|
866
|
+
isEdited(id: TRowId): boolean;
|
|
867
|
+
isCellDirty(id: TRowId, field: string, currentRow: TRow | undefined): boolean;
|
|
868
|
+
getOriginalCellValue(id: TRowId, field: string): unknown | undefined;
|
|
869
|
+
getOriginalRow(id: TRowId): TRow | undefined;
|
|
870
|
+
hasOriginalRow(id: TRowId): boolean;
|
|
871
|
+
trackNonBackendRow(id: TRowId): void;
|
|
872
|
+
markNew(id: TRowId): void;
|
|
873
|
+
markEdited(id: TRowId): void;
|
|
874
|
+
snapshotOriginal(id: TRowId, row: TRow): void;
|
|
875
|
+
classifyOnFirstEdit(id: TRowId, row: TRow): void;
|
|
876
|
+
checkRevert(id: TRowId, currentRow: TRow): {
|
|
877
|
+
wasNew: boolean;
|
|
878
|
+
wasEdited: boolean;
|
|
879
|
+
};
|
|
880
|
+
removeTracking(id: TRowId): void;
|
|
881
|
+
getNewRowIds(): ReadonlySet<TRowId>;
|
|
882
|
+
getEditedRowIds(): ReadonlySet<TRowId>;
|
|
883
|
+
getMergeSnapshot(id: TRowId): {
|
|
884
|
+
isNew: boolean;
|
|
885
|
+
isEdited: boolean;
|
|
886
|
+
};
|
|
887
|
+
restoreMergeClassification(id: TRowId, isNew: boolean, isEdited: boolean): void;
|
|
888
|
+
clear(): void;
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
interface FlagReader {
|
|
892
|
+
hasError(id: TRowId): boolean;
|
|
893
|
+
isNew(id: TRowId): boolean;
|
|
894
|
+
isEdited(id: TRowId): boolean;
|
|
895
|
+
hasEmptyCells(id: TRowId): boolean;
|
|
896
|
+
isDeleted(id: TRowId): boolean;
|
|
897
|
+
hasDeletedRows(): boolean;
|
|
898
|
+
getSourceId(id: TRowId): string;
|
|
899
|
+
getErrorBitmask(id: TRowId, fieldOrder: string[], wordIdx: number): number;
|
|
900
|
+
getEditedBitmask(id: TRowId, fieldOrder: string[], wordIdx: number): number;
|
|
901
|
+
getErrorMessageToRows(): ReadonlyMap<string, ReadonlySet<TRowId>>;
|
|
902
|
+
getRowValidations(id: TRowId): Map<string, NonNullable<ValidationResult>> | undefined;
|
|
903
|
+
}
|
|
904
|
+
interface FilterRowReader<TRow extends DataEditorRow = DataEditorRow> {
|
|
905
|
+
getRowIds(): TRowId[];
|
|
906
|
+
getRowById(id: TRowId): TRow | undefined;
|
|
907
|
+
getHiddenSourceIds(): Set<DataSourceId>;
|
|
908
|
+
}
|
|
909
|
+
type IFilterEngine<TRow extends DataEditorRow = DataEditorRow> = {
|
|
910
|
+
getFilteredRowIds(): TRowId[] | null;
|
|
911
|
+
getBaseFilteredRowIds(): TRowId[] | null;
|
|
912
|
+
getFilterVersion(): number;
|
|
913
|
+
isFiltering(): boolean;
|
|
914
|
+
getFilters(): Filters;
|
|
915
|
+
getFieldOrder(): string[];
|
|
916
|
+
getWordsPerRow(): number;
|
|
917
|
+
getShowOnlyDeletedRows(): boolean;
|
|
918
|
+
getSortState(): SortState;
|
|
919
|
+
setReaders(rowReader: FilterRowReader<TRow>, flagReader: FlagReader): void;
|
|
920
|
+
setColumns(columns: DataEditorColumn[]): void;
|
|
921
|
+
setFilters(filters: Partial<Filters>): void;
|
|
922
|
+
setSortState(state: SortState, sortType?: SortType, locales?: string[]): Promise<void>;
|
|
923
|
+
updateRowText(rowId: TRowId, row: TRow): void;
|
|
924
|
+
updateRowsText(rows: {
|
|
925
|
+
id: TRowId;
|
|
926
|
+
row: TRow;
|
|
927
|
+
}[]): void;
|
|
928
|
+
deleteRowTextCache(rowId: TRowId): void;
|
|
929
|
+
clearRowTextCacheAll(): void;
|
|
930
|
+
setRowTextCache(rowId: TRowId, row: TRow): void;
|
|
931
|
+
rebuild(): void;
|
|
932
|
+
refilterAfterColumnsChange(): void;
|
|
933
|
+
notifyRowsAdded(ids: TRowId[]): void;
|
|
934
|
+
notifyRowsDeleted(deletedIds: TRowId[]): void;
|
|
935
|
+
notifyRowsInserted(restoredIds: TRowId[], positions: number[]): void;
|
|
936
|
+
refilterAfterSourceToggle(sourceId: DataSourceId, isVisible: boolean): void;
|
|
937
|
+
refilterAfterFlagChange(): void;
|
|
938
|
+
flushPendingFlags(): void;
|
|
939
|
+
markFlagsDirty(): void;
|
|
940
|
+
testRowAgainstFilters(rowId: TRowId, row: TRow, flagReader: FlagReader, hiddenSourceIds: Set<DataSourceId>): boolean;
|
|
941
|
+
getAllRowIdsSorted(rowReader: FilterRowReader<TRow>): Promise<TRowId[]>;
|
|
942
|
+
clear(): void;
|
|
943
|
+
destroy(): void;
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* RowStore — Row storage with dual-access pattern for the grid engine.
|
|
948
|
+
*
|
|
949
|
+
* Maintains two parallel structures:
|
|
950
|
+
* - (TRow | undefined)[] → O(1) lookup by stable internal ID (rows[id])
|
|
951
|
+
* - TRowId[] → O(1) lookup by visual index (required by the grid)
|
|
952
|
+
*
|
|
953
|
+
* The grid calls getRow(index) on every frame, so index-based access must be
|
|
954
|
+
* instant. Filtered views are supported by passing an alternate ID array
|
|
955
|
+
* (filteredIds) to the access methods — the store itself is unaware of filters.
|
|
956
|
+
*
|
|
957
|
+
* An inverted index (rowId → visual index) is lazily rebuilt on demand for
|
|
958
|
+
* O(1) reverse lookups (e.g., scrolling to a specific row after undo).
|
|
959
|
+
*
|
|
960
|
+
* Internal row IDs are auto-generated as sequential numbers (1, 2, 3, …).
|
|
961
|
+
* Using a plain array indexed by ID eliminates Map hash table overhead
|
|
962
|
+
* (~10MB saved at 1M rows). Deleted slots are set to undefined (not delete)
|
|
963
|
+
* to keep V8 in HOLEY_ELEMENTS mode.
|
|
964
|
+
*/
|
|
965
|
+
|
|
966
|
+
declare class RowStore<TRow extends DataEditorRow = DataEditorRow> {
|
|
967
|
+
private rows;
|
|
968
|
+
private rowIds;
|
|
969
|
+
private rowIdCounter;
|
|
970
|
+
private rowIdToIndex;
|
|
971
|
+
private _rowIndexDirty;
|
|
972
|
+
nextRowId(): TRowId;
|
|
973
|
+
getRow(index: number, filteredIds: TRowId[] | null): TRow | undefined;
|
|
974
|
+
getRowById(id: TRowId): TRow | undefined;
|
|
975
|
+
getRowId(index: number, filteredIds: TRowId[] | null): TRowId | undefined;
|
|
976
|
+
getRowIndex(rowId: TRowId, filteredIds: TRowId[] | null): number;
|
|
977
|
+
getRowIds(): TRowId[];
|
|
978
|
+
getRowCount(): number;
|
|
979
|
+
allRows(): IterableIterator<TRow>;
|
|
980
|
+
hasRow(id: TRowId): boolean;
|
|
981
|
+
setRow(id: TRowId, row: TRow): void;
|
|
982
|
+
deleteRow(id: TRowId): void;
|
|
983
|
+
pushRowId(id: TRowId): void;
|
|
984
|
+
spliceRowId(pos: number, id: TRowId): void;
|
|
985
|
+
removeRowId(id: TRowId): void;
|
|
986
|
+
filterRowIds(predicate: (id: TRowId) => boolean): void;
|
|
987
|
+
trimFromStart(count: number): TRowId[];
|
|
988
|
+
trimFromEnd(count: number): TRowId[];
|
|
989
|
+
unshiftRowIds(ids: TRowId[]): void;
|
|
990
|
+
invalidateIndex(): void;
|
|
991
|
+
clear(): void;
|
|
992
|
+
private rebuildRowIdToIndex;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* SnapshotManager — Immutable snapshot construction and listener notification
|
|
997
|
+
* for React's useSyncExternalStore integration.
|
|
998
|
+
*
|
|
999
|
+
* Builds a DataStoreSnapshot object that React components subscribe to via
|
|
1000
|
+
* useSyncExternalStore(subscribe, getSnapshot). A new snapshot object is
|
|
1001
|
+
* created on every notify() call, which triggers React's shallow comparison
|
|
1002
|
+
* and re-renders only when values actually change.
|
|
1003
|
+
*
|
|
1004
|
+
* Count tracking has two tiers:
|
|
1005
|
+
* - Visible counts: new/edited/error/empty rows from visible sources only.
|
|
1006
|
+
* Updated incrementally via adjust*() methods on single-row edits, or
|
|
1007
|
+
* recomputed in bulk when _countsDirty is set (after deletes, source changes, etc.).
|
|
1008
|
+
* - Filtered counts: subset of visible counts restricted to filteredRowIds.
|
|
1009
|
+
* Recomputed when _filteredCountsDirty is set (after filter changes).
|
|
1010
|
+
*
|
|
1011
|
+
* Batching: multiple operations can be grouped via beginBatch()/endBatch().
|
|
1012
|
+
* While batching, notify() calls are deferred — only the outermost endBatch()
|
|
1013
|
+
* triggers a single notification, preventing intermediate snapshots from
|
|
1014
|
+
* reaching React during multi-step operations (fill handle, batch delete, etc.).
|
|
1015
|
+
*
|
|
1016
|
+
* The SnapshotStateReader interface decouples this module from all other modules.
|
|
1017
|
+
* DataStore implements the interface by delegating to RowStore, DirtyTracker,
|
|
1018
|
+
* FilterEngine, and ValidationStore, so SnapshotManager never imports them directly.
|
|
1019
|
+
*/
|
|
1020
|
+
|
|
1021
|
+
type Listener = () => void;
|
|
1022
|
+
interface SnapshotStateReader {
|
|
1023
|
+
getRowCount(): number;
|
|
1024
|
+
getFilteredRowIds(): TRowId[] | null;
|
|
1025
|
+
getBaseFilteredRowIds(): TRowId[] | null;
|
|
1026
|
+
getNewRowIds(): ReadonlySet<TRowId>;
|
|
1027
|
+
getEditedRowIds(): ReadonlySet<TRowId>;
|
|
1028
|
+
getRowsWithErrors(): ReadonlySet<TRowId>;
|
|
1029
|
+
getRowsWithEmptyCells(): ReadonlySet<TRowId>;
|
|
1030
|
+
getDeletedCount(): number;
|
|
1031
|
+
isDeleted(id: TRowId): boolean;
|
|
1032
|
+
getSources(): DataSourceState[];
|
|
1033
|
+
isRowVisible(id: TRowId): boolean;
|
|
1034
|
+
canUndo(): boolean;
|
|
1035
|
+
canRedo(): boolean;
|
|
1036
|
+
isLoading(): boolean;
|
|
1037
|
+
isFiltering(): boolean;
|
|
1038
|
+
getVersion(): number;
|
|
1039
|
+
getFilteredNewCount(ids: TRowId[] | null): number;
|
|
1040
|
+
getFilteredEditedCount(ids: TRowId[] | null): number;
|
|
1041
|
+
getFilteredDirtyCount(ids: TRowId[] | null): number;
|
|
1042
|
+
getFilteredErrorCount(ids: TRowId[] | null): number;
|
|
1043
|
+
getFilteredEmptyCount(ids: TRowId[] | null): number;
|
|
1044
|
+
getErrorMessageCounts(filteredRowIds: TRowId[] | null): Record<string, number>;
|
|
1045
|
+
hasColumnScoping(): boolean;
|
|
1046
|
+
getSortState(): SortState;
|
|
1047
|
+
getShowOnlyDeletedRows(): boolean;
|
|
1048
|
+
/** Server-provided aggregate counts. Present only in server mode. */
|
|
1049
|
+
getServerEditedCount?(): number;
|
|
1050
|
+
getServerNewCount?(): number;
|
|
1051
|
+
getServerErrorCount?(): number;
|
|
1052
|
+
getServerEmptyCount?(): number;
|
|
1053
|
+
getServerDeletedCount?(): number;
|
|
1054
|
+
}
|
|
1055
|
+
declare class SnapshotManager {
|
|
1056
|
+
private _visibleNewCount;
|
|
1057
|
+
private _visibleEditedCount;
|
|
1058
|
+
private _visibleDirtyCount;
|
|
1059
|
+
private _visibleErrorCount;
|
|
1060
|
+
private _visibleEmptyCount;
|
|
1061
|
+
private _countsDirty;
|
|
1062
|
+
private _filteredDirtyCount;
|
|
1063
|
+
private _filteredNewCount;
|
|
1064
|
+
private _filteredEditedCount;
|
|
1065
|
+
private _filteredErrorCount;
|
|
1066
|
+
private _filteredEmptyCount;
|
|
1067
|
+
private _errorMessageCounts;
|
|
1068
|
+
private _filteredCountsDirty;
|
|
1069
|
+
private _phase;
|
|
1070
|
+
private _processingInfo;
|
|
1071
|
+
private snapshot;
|
|
1072
|
+
private listeners;
|
|
1073
|
+
private batchDepth;
|
|
1074
|
+
private pendingNotify;
|
|
1075
|
+
get countsDirty(): boolean;
|
|
1076
|
+
markCountsDirty(): void;
|
|
1077
|
+
markFilteredCountsDirty(): void;
|
|
1078
|
+
adjustVisibleErrorCount(delta: number): void;
|
|
1079
|
+
adjustVisibleNewCount(delta: number): void;
|
|
1080
|
+
adjustVisibleEditedCount(delta: number): void;
|
|
1081
|
+
adjustVisibleDirtyCount(delta: number): void;
|
|
1082
|
+
subscribe: (listener: Listener) => (() => void);
|
|
1083
|
+
getSnapshot: () => DataStoreSnapshot;
|
|
1084
|
+
isBatching(): boolean;
|
|
1085
|
+
beginBatch(): boolean;
|
|
1086
|
+
endBatch(): boolean;
|
|
1087
|
+
setPhase(phase: ProcessingPhase, info?: ProcessingInfo): void;
|
|
1088
|
+
getPhase(): ProcessingPhase;
|
|
1089
|
+
notify(reader: SnapshotStateReader): void;
|
|
1090
|
+
clear(): void;
|
|
1091
|
+
clearListeners(): void;
|
|
1092
|
+
private recomputeVisibleCounts;
|
|
1093
|
+
private recomputeFilteredCounts;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* ValidationStore — Cell-level validation state with incremental count tracking.
|
|
1098
|
+
*
|
|
1099
|
+
* Stores validation results in a two-level map: rowId → fieldId → ValidationResult[].
|
|
1100
|
+
* Maintains a running error count and a pre-computed row-level set
|
|
1101
|
+
* (_rowsWithErrors) for O(1) "does this row have errors?" checks.
|
|
1102
|
+
*
|
|
1103
|
+
* The key design decision is the ValidationDelta return type from setCellValidation().
|
|
1104
|
+
* Instead of directly triggering notifications, it returns a delta object describing
|
|
1105
|
+
* what changed (error count +/- 1, row-level error membership flipped).
|
|
1106
|
+
* The caller (DataStore) uses this delta for incremental snapshot count updates,
|
|
1107
|
+
* avoiding a full recount on every validation change.
|
|
1108
|
+
*
|
|
1109
|
+
* Empty cell tracking (_rowsWithEmptyCells) is separate from validation — it
|
|
1110
|
+
* tracks rows where any visible column has a null/empty value, used by the
|
|
1111
|
+
* "show only rows with empty cells" filter.
|
|
1112
|
+
*/
|
|
1113
|
+
|
|
1114
|
+
type ValidationDelta = {
|
|
1115
|
+
errorDelta: number;
|
|
1116
|
+
rowErrorChanged: boolean;
|
|
1117
|
+
};
|
|
1118
|
+
type IValidationStore = {
|
|
1119
|
+
setCellValidation(rowId: TRowId, field: string, result: ValidationResult): ValidationDelta;
|
|
1120
|
+
getCellValidation(rowId: TRowId, field: string): ValidationResult;
|
|
1121
|
+
clearRowValidations(rowId: TRowId): void;
|
|
1122
|
+
hasRowErrors(rowId: TRowId): boolean;
|
|
1123
|
+
getRowsWithErrors(): ReadonlySet<TRowId>;
|
|
1124
|
+
getRowsWithEmptyCells(): ReadonlySet<TRowId>;
|
|
1125
|
+
hasEmptyCells(rowId: TRowId): boolean;
|
|
1126
|
+
checkRowEmptyCells(rowId: TRowId, row: Record<string, unknown> | undefined, fieldOrder: string[]): boolean;
|
|
1127
|
+
deleteRowTracking(rowId: TRowId): void;
|
|
1128
|
+
getErrorCount(): number;
|
|
1129
|
+
getRowValidations(rowId: TRowId): Map<string, NonNullable<ValidationResult>> | undefined;
|
|
1130
|
+
getErrorMessageToRows(): ReadonlyMap<string, ReadonlySet<TRowId>>;
|
|
1131
|
+
clear(): void;
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
type IValidator<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1135
|
+
validateRow(rowId: TRowId): void;
|
|
1136
|
+
validateRows(rows: TRow[], rowIds: TRowId[]): void;
|
|
1137
|
+
validateUniqueness(): Promise<void>;
|
|
1138
|
+
validateCell(rowId: TRowId, field: string, oldValue?: unknown, _visited?: Set<string>): void;
|
|
1139
|
+
revalidateColumn(field: string): void;
|
|
1140
|
+
validateColumn(field: string, oldValues: ReadonlyMap<TRowId, unknown>): void;
|
|
1141
|
+
revalidateColumnChunked(field: string, oldValues: ReadonlyMap<TRowId, unknown>, newValues: ReadonlyMap<TRowId, unknown>, processor: ChunkedProcessor<TRowId>, onComplete: () => void): void;
|
|
1142
|
+
removeRow(rowId: TRowId): void;
|
|
1143
|
+
destroy(): void;
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
type IValueIndex<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1147
|
+
setTrackedFields(fields: Set<string>): void;
|
|
1148
|
+
addRow(row: TRow): void;
|
|
1149
|
+
removeRow(row: TRow): void;
|
|
1150
|
+
updateField(field: string, oldValue: unknown, newValue: unknown): void;
|
|
1151
|
+
rebuild(rows: Iterable<TRow>): void;
|
|
1152
|
+
getValues(field: string): ReadonlyMap<string, number>;
|
|
1153
|
+
getVersion(): number;
|
|
1154
|
+
isTracked(field: string): boolean;
|
|
1155
|
+
getMinMax(field: string): {
|
|
1156
|
+
min: number;
|
|
1157
|
+
max: number;
|
|
1158
|
+
} | null;
|
|
1159
|
+
getDateMinMax(field: string): {
|
|
1160
|
+
min: string;
|
|
1161
|
+
max: string;
|
|
1162
|
+
} | null;
|
|
1163
|
+
bumpVersion(): void;
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
type RowEntry<TRow extends DataEditorRow> = {
|
|
1167
|
+
rowId: TRowId;
|
|
1168
|
+
row: TRow;
|
|
1169
|
+
sourceId: DataSourceId;
|
|
1170
|
+
isNew: boolean;
|
|
1171
|
+
isEdited: boolean;
|
|
1172
|
+
isDeleted: boolean;
|
|
1173
|
+
originalRow?: TRow;
|
|
1174
|
+
};
|
|
1175
|
+
type OverlayEntry<TRow extends DataEditorRow> = {
|
|
1176
|
+
rowId: TRowId;
|
|
1177
|
+
displayValue: TRow;
|
|
1178
|
+
};
|
|
1179
|
+
type SourceSnapshot<TRow extends DataEditorRow> = {
|
|
1180
|
+
state: DataSourceState;
|
|
1181
|
+
ownedRows: RowEntry<TRow>[];
|
|
1182
|
+
overlayRows: OverlayEntry<TRow>[];
|
|
1183
|
+
rowIds: TRowId[];
|
|
1184
|
+
plan: ExtendedRemovalPlan<TRow>;
|
|
1185
|
+
};
|
|
1186
|
+
type SourceLifecycleHost<TRow extends DataEditorRow> = {
|
|
1187
|
+
getValidator: () => IValidator<TRow> | null;
|
|
1188
|
+
pushCommand: (cmd: Command<TRow>, cost?: number) => number;
|
|
1189
|
+
notify: () => void;
|
|
1190
|
+
isServerStrategy: () => boolean;
|
|
1191
|
+
checkRowEmptyCells: (rowId: TRowId) => void;
|
|
1192
|
+
clearRowValidations: (rowId: TRowId) => void;
|
|
1193
|
+
};
|
|
1194
|
+
declare class SourceLifecycle<TRow extends DataEditorRow = DataEditorRow> {
|
|
1195
|
+
private readonly sourceManager;
|
|
1196
|
+
private readonly rowStore;
|
|
1197
|
+
private readonly dirtyTracker;
|
|
1198
|
+
private readonly filterEngine;
|
|
1199
|
+
private readonly valueIndex;
|
|
1200
|
+
private readonly validationStore;
|
|
1201
|
+
private readonly snapshotManager;
|
|
1202
|
+
private readonly host;
|
|
1203
|
+
private readonly importTrackers;
|
|
1204
|
+
constructor(sourceManager: SourceManager<TRow>, rowStore: RowStore<TRow>, dirtyTracker: IDirtyTracker<TRow>, filterEngine: IFilterEngine<TRow>, valueIndex: IValueIndex<TRow>, validationStore: IValidationStore, snapshotManager: SnapshotManager, host: SourceLifecycleHost<TRow>);
|
|
1205
|
+
plan(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
|
|
1206
|
+
capture(sourceId: DataSourceId): SourceSnapshot<TRow> | null;
|
|
1207
|
+
apply(plan: ExtendedRemovalPlan<TRow>): void;
|
|
1208
|
+
private applyToStores;
|
|
1209
|
+
restore(snapshot: SourceSnapshot<TRow>): void;
|
|
1210
|
+
register(options: RegisterSourceOptions): DataSourceId;
|
|
1211
|
+
trackAppend(sourceId: DataSourceId, rowIds: TRowId[], rows: TRow[], usedIdMap: boolean): void;
|
|
1212
|
+
trackPrimaryKey(sourceId: DataSourceId, primaryKey: keyof TRow): void;
|
|
1213
|
+
finalize(sourceId: DataSourceId): Promise<void>;
|
|
1214
|
+
private pushImportCommandIfTracked;
|
|
1215
|
+
private removeInternal;
|
|
1216
|
+
remove(sourceId: DataSourceId): Promise<SourceSnapshot<TRow> | null>;
|
|
1217
|
+
finalizeAllSources(): void;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Fill-level delta computations.
|
|
1222
|
+
*
|
|
1223
|
+
* Pure functions that compute ColumnDelta[] for fill handle operations.
|
|
1224
|
+
* Zero side effects — take a spec + row reader, return deltas.
|
|
1225
|
+
*
|
|
1226
|
+
* buildFillSpec() — reads source values once, builds tiling index
|
|
1227
|
+
* computeFillDeltas() — processes a chunk of target rows against the spec
|
|
1228
|
+
*
|
|
1229
|
+
* Fill uses a tiling pattern: source values repeat cyclically.
|
|
1230
|
+
* For row r in fill range: srcRow = r % sourceHeight.
|
|
1231
|
+
* For col c in fill range: srcCol = c % sourceWidth.
|
|
1232
|
+
*/
|
|
1233
|
+
|
|
1234
|
+
type FillSpec = {
|
|
1235
|
+
/** 2D source grid: sourceValues[row][col]. Read once — source region is always small. */
|
|
1236
|
+
sourceValues: unknown[][];
|
|
1237
|
+
sourceHeight: number;
|
|
1238
|
+
sourceWidth: number;
|
|
1239
|
+
/** Column IDs for the fill target columns. */
|
|
1240
|
+
fields: string[];
|
|
1241
|
+
/** Target rowId → position within the fill range, for tiling modulo. */
|
|
1242
|
+
rowIdToFillIndex: ReadonlyMap<TRowId, number>;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
type FormulaCellContext = {
|
|
1246
|
+
value: unknown;
|
|
1247
|
+
field: string;
|
|
1248
|
+
rowId: TRowId;
|
|
1249
|
+
getField: (field: string) => unknown;
|
|
1250
|
+
/**
|
|
1251
|
+
* Positional arguments for expression-compiled formulas.
|
|
1252
|
+
* Populated by the expression evaluator when invoking multi-arg function
|
|
1253
|
+
* calls. Single-arg formulas (UPPER, TRIM, etc.) still read from `value`.
|
|
1254
|
+
* Undefined for all non-expression call sites — existing code is unaffected.
|
|
1255
|
+
*/
|
|
1256
|
+
args?: readonly unknown[];
|
|
1257
|
+
};
|
|
1258
|
+
type FormulaParamType = "string" | "number" | "boolean" | "select";
|
|
1259
|
+
type FormulaParam = {
|
|
1260
|
+
name: string;
|
|
1261
|
+
label: string;
|
|
1262
|
+
type: FormulaParamType;
|
|
1263
|
+
required?: boolean;
|
|
1264
|
+
defaultValue?: unknown;
|
|
1265
|
+
options?: Array<{
|
|
1266
|
+
id: string;
|
|
1267
|
+
text: string;
|
|
1268
|
+
}>;
|
|
1269
|
+
};
|
|
1270
|
+
type ColumnInputKind = "single" | "multiple";
|
|
1271
|
+
type ColumnInput = {
|
|
1272
|
+
name: string;
|
|
1273
|
+
label: string;
|
|
1274
|
+
kind: ColumnInputKind;
|
|
1275
|
+
required?: boolean;
|
|
1276
|
+
};
|
|
1277
|
+
type FormulaCategory = "text" | "number" | "logic" | "custom";
|
|
1278
|
+
type FormulaArity = {
|
|
1279
|
+
/** Minimum number of positional arguments. 0 means "callable with no args". */
|
|
1280
|
+
min: number;
|
|
1281
|
+
/** Maximum number of positional arguments. Use Number.POSITIVE_INFINITY for variadic. */
|
|
1282
|
+
max: number;
|
|
1283
|
+
};
|
|
1284
|
+
type FormulaBase = {
|
|
1285
|
+
name: string;
|
|
1286
|
+
label: string;
|
|
1287
|
+
category: FormulaCategory;
|
|
1288
|
+
description?: string;
|
|
1289
|
+
columns?: ColumnInput[];
|
|
1290
|
+
params: FormulaParam[];
|
|
1291
|
+
/**
|
|
1292
|
+
* The call signature of this formula when invoked from the expression
|
|
1293
|
+
* language. Required. For shortcut formulas (UPPER, TRIM, ...) use
|
|
1294
|
+
* { min: 1, max: 1 }. For CLEAR use { min: 0, max: 0 }. For MERGE use
|
|
1295
|
+
* { min: 2, max: Number.POSITIVE_INFINITY }.
|
|
1296
|
+
*/
|
|
1297
|
+
arity: FormulaArity;
|
|
1298
|
+
/** Parameter signature shown in autocomplete, without the function name. e.g. "(text, count)" */
|
|
1299
|
+
syntax?: string;
|
|
1300
|
+
/**
|
|
1301
|
+
* Whether this formula may be invoked from the expression parser.
|
|
1302
|
+
* Defaults to true when omitted. MERGE and SPLIT set this to false
|
|
1303
|
+
* because they have dedicated modals that supply their non-expression
|
|
1304
|
+
* params (column lists, separator, ...).
|
|
1305
|
+
*/
|
|
1306
|
+
expressionCallable?: boolean;
|
|
1307
|
+
};
|
|
1308
|
+
type CellFormula = FormulaBase & {
|
|
1309
|
+
kind: "cell";
|
|
1310
|
+
compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => unknown;
|
|
1311
|
+
};
|
|
1312
|
+
type RowFormula = FormulaBase & {
|
|
1313
|
+
kind: "row";
|
|
1314
|
+
targetFields: (params: Record<string, unknown>) => string[];
|
|
1315
|
+
compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => Record<string, unknown>;
|
|
1316
|
+
};
|
|
1317
|
+
type FormulaDefinition = CellFormula | RowFormula;
|
|
1318
|
+
|
|
1319
|
+
declare class FormulaRegistry {
|
|
1320
|
+
private readonly formulas;
|
|
1321
|
+
register(formula: FormulaDefinition): void;
|
|
1322
|
+
get(name: string): FormulaDefinition | undefined;
|
|
1323
|
+
getAll(): FormulaDefinition[];
|
|
1324
|
+
getByCategory(category: string): FormulaDefinition[];
|
|
1325
|
+
getExpressionCallable(): FormulaDefinition[];
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* Paste-level delta computations.
|
|
1330
|
+
*
|
|
1331
|
+
* Pure functions that compute ColumnDelta[] for paste operations.
|
|
1332
|
+
* Zero side effects — take a spec + row reader, return deltas.
|
|
1333
|
+
*
|
|
1334
|
+
* buildPasteSpec() — pre-resolves the source→target row mapping and column metadata
|
|
1335
|
+
* computePasteDeltas() — processes a chunk of target rows against the spec
|
|
1336
|
+
*
|
|
1337
|
+
* Designed for the same chunked execution pattern as columnTransforms:
|
|
1338
|
+
* - DataStore builds the spec once
|
|
1339
|
+
* - Small datasets: call computePasteDeltas() once with all target rows
|
|
1340
|
+
* - Large datasets: orchestrator calls computePasteDeltas() per chunk
|
|
1341
|
+
*/
|
|
1342
|
+
|
|
1343
|
+
type PasteSpec = {
|
|
1344
|
+
sourceColumnIds: string[];
|
|
1345
|
+
targetColumnIds: string[];
|
|
1346
|
+
targetToSource: ReadonlyMap<TRowId, TRowId>;
|
|
1347
|
+
selectOptionsMap: ReadonlyMap<string, ReadonlySet<string>>;
|
|
1348
|
+
skipColumnIndices: ReadonlySet<number>;
|
|
1349
|
+
isCut: boolean;
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
type ApplyFormulaOptions = {
|
|
1353
|
+
/**
|
|
1354
|
+
* Column IDs to delete AFTER the formula has been applied.
|
|
1355
|
+
* Only dynamic (user-added) columns are actually deleted; schema columns
|
|
1356
|
+
* in this list are silently ignored. All operations (formula + deletes)
|
|
1357
|
+
* are wrapped in a single CompoundCommand so that one Undo call restores
|
|
1358
|
+
* every change, regardless of how many columns were listed.
|
|
1359
|
+
*/
|
|
1360
|
+
readonly deleteColumnsAfter?: readonly string[];
|
|
1361
|
+
};
|
|
1362
|
+
declare class DataStore<TRow extends DataEditorRow = DataEditorRow> {
|
|
1363
|
+
private readonly _mode;
|
|
1364
|
+
isServer(): boolean;
|
|
1365
|
+
isClient(): boolean;
|
|
1366
|
+
private rowStore;
|
|
1367
|
+
readonly formulaRegistry: FormulaRegistry;
|
|
1368
|
+
private _isLoading;
|
|
1369
|
+
private sourceManager;
|
|
1370
|
+
readonly sourceLifecycle: SourceLifecycle<TRow>;
|
|
1371
|
+
private dirtyTracker;
|
|
1372
|
+
private filterEngine;
|
|
1373
|
+
private validationStore;
|
|
1374
|
+
private snapshotManager;
|
|
1375
|
+
private history;
|
|
1376
|
+
private validator;
|
|
1377
|
+
private valueIndex;
|
|
1378
|
+
private serverCounts;
|
|
1379
|
+
private editBuilder;
|
|
1380
|
+
private isUndoRedoing;
|
|
1381
|
+
private pendingBatchCommands;
|
|
1382
|
+
private _version;
|
|
1383
|
+
private _skipNotify;
|
|
1384
|
+
private _bulkMode;
|
|
1385
|
+
private _editedCells;
|
|
1386
|
+
private _primaryKey;
|
|
1387
|
+
/** Columns that are currently locked (pre-locked from schema + user-locked at runtime). */
|
|
1388
|
+
private _lockedColumns;
|
|
1389
|
+
/** Columns locked via schema definition — user cannot unlock these. */
|
|
1390
|
+
private _preLockedColumns;
|
|
1391
|
+
/** Last-known visible row range reported by the canvas scroll handler. */
|
|
1392
|
+
private _viewportStart;
|
|
1393
|
+
private _viewportEnd;
|
|
1394
|
+
private pendingMutations;
|
|
1395
|
+
private editParamsHistory;
|
|
1396
|
+
readonly errorHandler: ErrorHandler;
|
|
1397
|
+
readonly server: ServerDataManager<TRow> | null;
|
|
1398
|
+
private readonly strategy;
|
|
1399
|
+
private readonly serverStrategy;
|
|
1400
|
+
private snapshotReader;
|
|
1401
|
+
private flagReader;
|
|
1402
|
+
private buildErrorBitmask;
|
|
1403
|
+
private buildEditedBitmask;
|
|
1404
|
+
private buildColumnMasks;
|
|
1405
|
+
private countFilteredNew;
|
|
1406
|
+
private countFilteredEdited;
|
|
1407
|
+
private countFilteredDirty;
|
|
1408
|
+
private countFilteredErrors;
|
|
1409
|
+
private countErrorMessageRows;
|
|
1410
|
+
private countFilteredEmpty;
|
|
1411
|
+
private filterRowReader;
|
|
1412
|
+
private bulkMutationHost;
|
|
1413
|
+
private orchestrator;
|
|
1414
|
+
constructor(mode?: StoreMode, serverInit?: {
|
|
1415
|
+
config: DataEditorServer<TRow>;
|
|
1416
|
+
sourceLabel: string;
|
|
1417
|
+
}, errorHandler?: ErrorHandler);
|
|
1418
|
+
get mode(): StoreMode;
|
|
1419
|
+
getEditBuilder(): ServerEditBuilder<TRow> | null;
|
|
1420
|
+
getRowId(index: number): TRowId | undefined;
|
|
1421
|
+
getLocalRowCount(): number;
|
|
1422
|
+
setValidator(validator: IValidator<TRow>): void;
|
|
1423
|
+
setPrimaryKey(key: string): void;
|
|
1424
|
+
isColumnLocked(field: string): boolean;
|
|
1425
|
+
isColumnPreLocked(field: string): boolean;
|
|
1426
|
+
lockColumn(field: string, mode?: ColumnLockMode): void;
|
|
1427
|
+
unlockColumn(field: string): void;
|
|
1428
|
+
getLockedColumns(): ReadonlySet<string>;
|
|
1429
|
+
getColumnLockMode(field: string): ColumnLockMode | undefined;
|
|
1430
|
+
/** Sync pre-locked columns from schema definitions. */
|
|
1431
|
+
private syncPreLockedColumns;
|
|
1432
|
+
/** Returns true if the cell is locked for this specific row. */
|
|
1433
|
+
isCellLocked(field: string, rowId: TRowId): boolean;
|
|
1434
|
+
/** Filter out locked fields from an array. */
|
|
1435
|
+
private filterLockedFields;
|
|
1436
|
+
/** Build a new PasteSpec with locked target columns removed. */
|
|
1437
|
+
private filterLockedFieldsFromPasteSpec;
|
|
1438
|
+
private _schemaColumns;
|
|
1439
|
+
private _dynamicColumns;
|
|
1440
|
+
private _effectiveColumns;
|
|
1441
|
+
private _lastColumnsInput;
|
|
1442
|
+
setSchemaColumns(columns: DataEditorColumn[]): void;
|
|
1443
|
+
getDynamicColumns(): DataEditorColumn[];
|
|
1444
|
+
getEffectiveColumns: () => DataEditorColumn[];
|
|
1445
|
+
setDynamicColumns(fn: (prev: DataEditorColumn[]) => DataEditorColumn[]): void;
|
|
1446
|
+
addDynamicColumns(columns: DataEditorColumn[]): void;
|
|
1447
|
+
setColumns(columns: DataEditorColumn[]): void;
|
|
1448
|
+
subscribe: (listener: () => void) => (() => void);
|
|
1449
|
+
getSnapshot: () => DataStoreSnapshot;
|
|
1450
|
+
willRowBeVisible(rowId: TRowId): boolean;
|
|
1451
|
+
getColumnValues(field: string): ReadonlyMap<string, number>;
|
|
1452
|
+
getColumnMinMax(field: string): {
|
|
1453
|
+
min: number;
|
|
1454
|
+
max: number;
|
|
1455
|
+
} | null;
|
|
1456
|
+
getColumnDateMinMax(field: string): {
|
|
1457
|
+
min: string;
|
|
1458
|
+
max: string;
|
|
1459
|
+
} | null;
|
|
1460
|
+
getValueIndexVersion: () => number;
|
|
1461
|
+
getRow(index: number): TRow | undefined;
|
|
1462
|
+
getRowIds(): TRowId[];
|
|
1463
|
+
getFilterVersion(): number;
|
|
1464
|
+
consumeEditedCells(): Set<string>;
|
|
1465
|
+
getFilteredRowIds(): TRowId[] | null;
|
|
1466
|
+
getAllRowIdsSorted(): Promise<TRowId[]>;
|
|
1467
|
+
getRowById(id: TRowId): TRow | undefined;
|
|
1468
|
+
getRowIndex(rowId: TRowId): number;
|
|
1469
|
+
registerSource(options: RegisterSourceOptions): DataSourceId;
|
|
1470
|
+
setSourceLoading(sourceId: DataSourceId, isLoading: boolean): void;
|
|
1471
|
+
/**
|
|
1472
|
+
* Mark a source's load as complete. If no rows landed in it (e.g., every row
|
|
1473
|
+
* from the import matched an existing anchor and stayed in its original
|
|
1474
|
+
* source), the source is removed so users don't see an empty entry.
|
|
1475
|
+
*/
|
|
1476
|
+
finalizeSource(sourceId: DataSourceId): Promise<void>;
|
|
1477
|
+
hasSource(sourceId: DataSourceId): boolean;
|
|
1478
|
+
finalizeAllSources(): void;
|
|
1479
|
+
planSourceRemoval(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
|
|
1480
|
+
applySourceRemovalPlan(plan: ExtendedRemovalPlan<TRow>): void;
|
|
1481
|
+
captureSourceSnapshot(sourceId: DataSourceId): SourceSnapshot<TRow> | null;
|
|
1482
|
+
restoreSourceFromSnapshot(snapshot: SourceSnapshot<TRow>): void;
|
|
1483
|
+
removeSource(sourceId: DataSourceId): Promise<void>;
|
|
1484
|
+
toggleSourceVisibility(sourceId: DataSourceId): void;
|
|
1485
|
+
/**
|
|
1486
|
+
* Physically removes a row from all stores.
|
|
1487
|
+
* Used only by InsertRowCommand.undo() to reverse a row creation.
|
|
1488
|
+
* NOT for user-initiated deletion — that will use flag-based soft delete.
|
|
1489
|
+
*/
|
|
1490
|
+
removeRowPhysically(rowId: TRowId): void;
|
|
1491
|
+
isDefaultSourceRow(rowId: TRowId): boolean;
|
|
1492
|
+
isNew(rowId: TRowId): boolean;
|
|
1493
|
+
isEdited(rowId: TRowId): boolean;
|
|
1494
|
+
isDeleted(rowId: TRowId): boolean;
|
|
1495
|
+
getSourceId(rowId: TRowId): DataSourceId;
|
|
1496
|
+
getRowsWithErrors(): ReadonlySet<TRowId>;
|
|
1497
|
+
/**
|
|
1498
|
+
* Flag rows as deleted. Pushes a command for undo/redo.
|
|
1499
|
+
* Rows stay in all stores — they are just excluded from alive-mode filters.
|
|
1500
|
+
*/
|
|
1501
|
+
deleteRows(rowIds: TRowId[], rowRanges?: [TRowId, TRowId][]): Promise<void>;
|
|
1502
|
+
/**
|
|
1503
|
+
* Delete all visible rows at once. In server mode sends { allSelected: true }
|
|
1504
|
+
* instead of enumerating every row. Locally marks all loaded rows as deleted
|
|
1505
|
+
* and clears the RowStore display list.
|
|
1506
|
+
*/
|
|
1507
|
+
deleteAllRows(): Promise<void>;
|
|
1508
|
+
/**
|
|
1509
|
+
* Optimistic bulk delete for server mode. Marks all locally loaded rows
|
|
1510
|
+
* as deleted, clears the RowStore, and adjusts counts.
|
|
1511
|
+
*/
|
|
1512
|
+
private deleteAllServer;
|
|
1513
|
+
/**
|
|
1514
|
+
* Unflag rows (restore from deletion). Pushes a command for undo/redo.
|
|
1515
|
+
*/
|
|
1516
|
+
restoreRows(rowIds: TRowId[], rowRanges?: [TRowId, TRowId][]): Promise<void>;
|
|
1517
|
+
/**
|
|
1518
|
+
* Restore all deleted rows at once. In server mode sends { allSelected: true }
|
|
1519
|
+
* instead of enumerating every row. Locally clears all deleted flags and
|
|
1520
|
+
* empties the RowStore display list (bin view becomes empty).
|
|
1521
|
+
*/
|
|
1522
|
+
restoreAllRows(): Promise<void>;
|
|
1523
|
+
/**
|
|
1524
|
+
* Optimistic bulk restore for server mode. Clears all locally known deleted
|
|
1525
|
+
* flags and empties the RowStore display list (we are in deleted-only view,
|
|
1526
|
+
* so every visible row is being restored out). Server-provided counts will
|
|
1527
|
+
* be authoritative after the sync response arrives.
|
|
1528
|
+
*/
|
|
1529
|
+
private restoreAllDeletedServer;
|
|
1530
|
+
/**
|
|
1531
|
+
* Apply delete flags directly without cost-gating. Called by commands
|
|
1532
|
+
* (DeleteRowCommand.redo/undo, restore inline commands) where runHeavy
|
|
1533
|
+
* already gated the operation at the public method level.
|
|
1534
|
+
*/
|
|
1535
|
+
applyDeleteFlagsDirect(rowIds: TRowId[], deleted: boolean): void;
|
|
1536
|
+
private applyDeleteFlags;
|
|
1537
|
+
/**
|
|
1538
|
+
* Returns rows flagged for deletion. Used by onComplete.
|
|
1539
|
+
*/
|
|
1540
|
+
getDeletedRows(): TRow[];
|
|
1541
|
+
getRealRowPosition(rowId: TRowId): number;
|
|
1542
|
+
insertRow(sourceId: DataSourceId, row: TRow, position: number): Promise<TRowId>;
|
|
1543
|
+
insertRowDirect(rowId: TRowId, row: TRow, sourceId: DataSourceId, position: number): void;
|
|
1544
|
+
/**
|
|
1545
|
+
* Ingest a single ServerRow at a specific local position.
|
|
1546
|
+
* Used after the server confirms an insert and returns the full row.
|
|
1547
|
+
* Source counts come from the server via counts.sources — no local source tracking.
|
|
1548
|
+
*/
|
|
1549
|
+
insertServerRow(serverRow: ServerRow<TRow>, position: number): TRowId;
|
|
1550
|
+
/**
|
|
1551
|
+
* Server-mode insert: send insert request to server, wait for response,
|
|
1552
|
+
* then ingest the returned row locally. Non-optimistic.
|
|
1553
|
+
*/
|
|
1554
|
+
insertRowServer(row: TRow, localPosition: number, anchorRowId: TRowId | undefined, insertPosition: 'above' | 'below', columnIds: string[]): Promise<TRowId>;
|
|
1555
|
+
appendRows(sourceId: DataSourceId, newRows: TRow[], rowIdMap?: Map<number, TRowId>): TRowId[];
|
|
1556
|
+
upsertRows(sourceId: DataSourceId, newRows: TRow[], options?: {
|
|
1557
|
+
anchorKey?: keyof TRow;
|
|
1558
|
+
newRowIds?: TRowId[];
|
|
1559
|
+
}): UpsertResult<TRow>;
|
|
1560
|
+
setLoading(isLoading: boolean): void;
|
|
1561
|
+
setPhase(phase: ProcessingPhase, info?: ProcessingInfo): void;
|
|
1562
|
+
getPhase(): ProcessingPhase;
|
|
1563
|
+
cancelProcessing(): void;
|
|
1564
|
+
setBulkMode(enabled: boolean): void;
|
|
1565
|
+
/**
|
|
1566
|
+
* Called by the orchestrator after all chunks complete, before validation.
|
|
1567
|
+
* Deferred per-row work from updateColumnDirect is flushed here in bulk:
|
|
1568
|
+
* - rowTextCache cleared once instead of per-row
|
|
1569
|
+
* - checkRevert run for each affected row
|
|
1570
|
+
* - _editedCells populated for FindReplace incremental matching
|
|
1571
|
+
*/
|
|
1572
|
+
flushBulkMode(deltas: ColumnDelta[]): void;
|
|
1573
|
+
setViewportRange(start: number, end: number): void;
|
|
1574
|
+
clearViewportRange(): void;
|
|
1575
|
+
/**
|
|
1576
|
+
* Returns row IDs for the current viewport range +1 row above and below
|
|
1577
|
+
* to cover semi-scrolled rows at the edges. Returns empty array if the
|
|
1578
|
+
* viewport range has not been set.
|
|
1579
|
+
*/
|
|
1580
|
+
getViewportRowIds(): TRowId[];
|
|
1581
|
+
getAllFilteredRowIds(): TRowId[];
|
|
1582
|
+
replaceServerRows(sourceId: DataSourceId, serverRows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
|
|
1583
|
+
appendServerRows(serverRows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
|
|
1584
|
+
prependServerRows(serverRows: ServerRow<TRow>[], newOffset: number, counts?: ServerQueryCounts): void;
|
|
1585
|
+
/**
|
|
1586
|
+
* Seeds DirtyTracker from server-provided row metadata.
|
|
1587
|
+
* For rows with `meta.status.edited`, builds a synthetic original row
|
|
1588
|
+
* from `meta.changes` so that `isCellDirty()` and `getOriginalCellValue()`
|
|
1589
|
+
* work through the existing DirtyTracker comparison logic.
|
|
1590
|
+
*/
|
|
1591
|
+
private hydrateServerMeta;
|
|
1592
|
+
private hydrateRowMeta;
|
|
1593
|
+
applyServerRowMeta(serverRows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
|
|
1594
|
+
clear(): void;
|
|
1595
|
+
destroy(): void;
|
|
1596
|
+
setFilters(filters: Partial<Filters>): void;
|
|
1597
|
+
getFilters(): Filters;
|
|
1598
|
+
setSort(sortState: SortState, sortType?: SortType, locales?: string[]): Promise<void>;
|
|
1599
|
+
handleServerScroll(visibleStart: number, visibleEnd: number): void;
|
|
1600
|
+
reloadServerData(): void;
|
|
1601
|
+
resetFilters(): void;
|
|
1602
|
+
fetchFilterOptions(): void;
|
|
1603
|
+
getFilterOptions(): FilterOptionsResponse | null;
|
|
1604
|
+
get hasServerExport(): boolean;
|
|
1605
|
+
serverExport(format: DataEditorFormat, allRows: boolean, rtl: boolean): Promise<void>;
|
|
1606
|
+
syncWorkerFlags(): void;
|
|
1607
|
+
setCellValidation(rowId: TRowId, field: string, result: ValidationResult): void;
|
|
1608
|
+
getCellValidation(rowId: TRowId, field: string): ValidationResult;
|
|
1609
|
+
private clearRowValidations;
|
|
1610
|
+
updateRow(rowId: TRowId, field: string, value: unknown): void;
|
|
1611
|
+
updateRowDirect(rowId: TRowId, field: string, value: unknown): void;
|
|
1612
|
+
/**
|
|
1613
|
+
* Bulk-write a single column for many rows in one pass.
|
|
1614
|
+
* Skips per-row validation, notification, and snapshot adjustments.
|
|
1615
|
+
* Caller must handle validation (via Validator.revalidateColumn) and notification.
|
|
1616
|
+
*/
|
|
1617
|
+
updateColumnDirect(field: string, values: ReadonlyMap<TRowId, unknown>): void;
|
|
1618
|
+
batch(fn: () => void): number | undefined;
|
|
1619
|
+
canUndo(): boolean;
|
|
1620
|
+
getAllRowIds(): readonly TRowId[];
|
|
1621
|
+
undo(): Promise<UndoRedoResult>;
|
|
1622
|
+
private _undoSync;
|
|
1623
|
+
pushCommand(cmd: Command<TRow>, cost?: number): number;
|
|
1624
|
+
removeCommandById(id: number): void;
|
|
1625
|
+
/**
|
|
1626
|
+
* Delegate to ServerStrategy. Called by ActionsDispatcher and ClipboardManager
|
|
1627
|
+
* for non-cell edits (clear, paste, fill, transform).
|
|
1628
|
+
*/
|
|
1629
|
+
syncServerEdit(params: EditParams, cmdId: number, revertFn: () => void): void;
|
|
1630
|
+
fireServerEditParams(params: EditParams): void;
|
|
1631
|
+
syncColumnEdit(params: ColumnEditParams, cmdId: number, revertFn: () => void): void;
|
|
1632
|
+
redo(): Promise<UndoRedoResult>;
|
|
1633
|
+
private _redoSync;
|
|
1634
|
+
getOriginalCellValue(rowId: TRowId, field: string): unknown | undefined;
|
|
1635
|
+
isCellDirty(rowId: TRowId, field: string): boolean;
|
|
1636
|
+
hasRowErrors(rowId: TRowId): boolean;
|
|
1637
|
+
hasEmptyCells(rowId: TRowId): boolean;
|
|
1638
|
+
private checkRowEmptyCells;
|
|
1639
|
+
getValidAndInvalidRows(): {
|
|
1640
|
+
valid: TRow[];
|
|
1641
|
+
invalid: TRow[];
|
|
1642
|
+
};
|
|
1643
|
+
getValidRows(): TRow[];
|
|
1644
|
+
getInvalidRows(): TRow[];
|
|
1645
|
+
getResultBySource(): DataEditorResult<TRow>;
|
|
1646
|
+
private isRowVisible;
|
|
1647
|
+
applyFormula(formulaOrName: string | CellFormula, params: Record<string, unknown>, rects: SelectionRect[], options?: ApplyFormulaOptions): Promise<void>;
|
|
1648
|
+
private applyFormulaCompound;
|
|
1649
|
+
private runFormulaWithReturnCommand;
|
|
1650
|
+
private _runFormulaOperation;
|
|
1651
|
+
private captureDeleteColumnSnapshots;
|
|
1652
|
+
private applyDeleteColumnSnapshots;
|
|
1653
|
+
private syncFormulaToServer;
|
|
1654
|
+
private transformWorker;
|
|
1655
|
+
private initTransformWorker;
|
|
1656
|
+
private buildChatTransformCommand;
|
|
1657
|
+
applyChatTransform(fnString: string, ctx: {
|
|
1658
|
+
opts: Record<string, Set<string>>;
|
|
1659
|
+
}): Promise<void>;
|
|
1660
|
+
private _applyChatTransformViaWorker;
|
|
1661
|
+
private _applyChatTransformSync;
|
|
1662
|
+
applyChatRows(incomingRows: Record<string, unknown>[], primaryKey: string): Promise<void>;
|
|
1663
|
+
private syncChatTransformToServer;
|
|
1664
|
+
private get revertRowReader();
|
|
1665
|
+
revertColumns(fields: string[]): Promise<void>;
|
|
1666
|
+
revertRange(rects: SelectionRect[]): Promise<void>;
|
|
1667
|
+
private _revertInternal;
|
|
1668
|
+
private _buildRevertCommand;
|
|
1669
|
+
private syncRevertToServer;
|
|
1670
|
+
clearColumn(field: string): Promise<void>;
|
|
1671
|
+
clearColumns(fields: string[]): Promise<void>;
|
|
1672
|
+
private syncClearToServer;
|
|
1673
|
+
deleteColumn(columnId: string): Promise<void>;
|
|
1674
|
+
deleteColumns(columnIds: readonly string[]): Promise<void>;
|
|
1675
|
+
clearRange(rects: SelectionRect[]): Promise<void>;
|
|
1676
|
+
private syncRangeClearToServer;
|
|
1677
|
+
pasteChunked(spec: PasteSpec, targetRowIds: TRowId[], targetCell: {
|
|
1678
|
+
rowId: TRowId;
|
|
1679
|
+
field: string;
|
|
1680
|
+
}, onComplete?: (cmdId: number) => void): void;
|
|
1681
|
+
/**
|
|
1682
|
+
* Store-owned paste: cost-gated sync path or chunked orchestrator.
|
|
1683
|
+
* Replaces executePasteWithOverlay + pasteChunked for internal-clipboard paste.
|
|
1684
|
+
*/
|
|
1685
|
+
pasteInternal(spec: PasteSpec, targetRowIds: TRowId[], targetCell: {
|
|
1686
|
+
rowId: TRowId;
|
|
1687
|
+
field: string;
|
|
1688
|
+
}, validator?: IValidator<TRow>): Promise<{
|
|
1689
|
+
cmdId: number;
|
|
1690
|
+
} | null>;
|
|
1691
|
+
/**
|
|
1692
|
+
* Store-owned external paste from TSV text (OS clipboard).
|
|
1693
|
+
* Replaces the pasteFromText helper in pasteUtils.
|
|
1694
|
+
*/
|
|
1695
|
+
pasteFromText(text: string, pasteRow: number, pasteCol: number, validator?: IValidator<TRow>): Promise<{
|
|
1696
|
+
cmdId: number;
|
|
1697
|
+
} | null>;
|
|
1698
|
+
/**
|
|
1699
|
+
* Store-owned fill: cost-gated sync path or chunked orchestrator.
|
|
1700
|
+
* Replaces fillChunked + the per-caller isHeavy heuristic in ActionsDispatcher.
|
|
1701
|
+
*/
|
|
1702
|
+
fillInternal(spec: FillSpec, targetRowIds: TRowId[], targetCell: {
|
|
1703
|
+
rowId: TRowId;
|
|
1704
|
+
field: string;
|
|
1705
|
+
}, onComplete?: (cmdId: number) => void): Promise<{
|
|
1706
|
+
cmdId: number;
|
|
1707
|
+
} | null>;
|
|
1708
|
+
notify(): void;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* Command interface for undo/redo operations.
|
|
1713
|
+
* Each command knows how to apply and revert its changes.
|
|
1714
|
+
*/
|
|
1715
|
+
interface Command<TRow extends DataEditorRow = DataEditorRow> {
|
|
1716
|
+
/** Assigned by CommandHistory.push(). Used by removeById() to target specific commands. */
|
|
1717
|
+
id?: number;
|
|
1718
|
+
redo(store: DataStore<TRow>, validator: IValidator<TRow>): void;
|
|
1719
|
+
undo(store: DataStore<TRow>, validator: IValidator<TRow>): void;
|
|
1720
|
+
readonly description: string;
|
|
1721
|
+
/** Target cell for selection restoration on undo/redo */
|
|
1722
|
+
readonly targetCell: CellLocation;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* BulkMutationOrchestrator — Viewport-first chunked execution for heavy
|
|
1727
|
+
* column operations (split, merge, clear, paste, fill, backspace).
|
|
1728
|
+
*
|
|
1729
|
+
* When operation cost >= LARGE_OP_CELLS:
|
|
1730
|
+
* 1. Viewport rows are applied synchronously (instant visual result)
|
|
1731
|
+
* 2. Remaining rows are processed in chunks via requestIdleCallback
|
|
1732
|
+
* 3. Validation runs chunked after all mutations complete
|
|
1733
|
+
* 4. Command is pushed to history only after everything finishes
|
|
1734
|
+
*
|
|
1735
|
+
* Communicates with DataStore via BulkMutationHost interface to avoid
|
|
1736
|
+
* circular imports. DataStore constructs the host from its own methods.
|
|
1737
|
+
*/
|
|
1738
|
+
|
|
1739
|
+
type ColumnDelta = {
|
|
1740
|
+
field: string;
|
|
1741
|
+
oldValues: Map<TRowId, unknown>;
|
|
1742
|
+
newValues: Map<TRowId, unknown>;
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Base row shape. Each key is a column ID, each value is the cell data.
|
|
1747
|
+
* Extend this with your own type for type-safe column access.
|
|
1748
|
+
*
|
|
1749
|
+
* @example
|
|
1750
|
+
* ```ts
|
|
1751
|
+
* type Employee = { id: string; name: string; email: string };
|
|
1752
|
+
* <DataEditor<Employee> columns={...} primaryKey="id" />
|
|
1753
|
+
* ```
|
|
1754
|
+
*/
|
|
1755
|
+
type DataEditorRow = Record<string, unknown>;
|
|
1756
|
+
type SortDirection = "asc" | "desc";
|
|
1757
|
+
type SortState = {
|
|
1758
|
+
columnId: string;
|
|
1759
|
+
direction: SortDirection;
|
|
1760
|
+
} | null;
|
|
1761
|
+
type Filters = {
|
|
1762
|
+
search: string;
|
|
1763
|
+
matchCase: boolean;
|
|
1764
|
+
matchEntireCell: boolean;
|
|
1765
|
+
errorMessageFilters: string[];
|
|
1766
|
+
showOnlyNewRows: boolean;
|
|
1767
|
+
showOnlyEditedRows: boolean;
|
|
1768
|
+
showOnlyEmptyCells: boolean;
|
|
1769
|
+
/** When true, show only rows flagged for deletion (bin mode). All other filters are bypassed. */
|
|
1770
|
+
showOnlyDeletedRows: boolean;
|
|
1771
|
+
filterColumns: string[] | null;
|
|
1772
|
+
/** Per-column value filters. Key = column ID, value = allowed display-formatted strings. */
|
|
1773
|
+
columnValueFilters: Record<string, string[]>;
|
|
1774
|
+
/** Per-column numeric range filters. Key = column ID. */
|
|
1775
|
+
columnRangeFilters: Record<string, {
|
|
1776
|
+
min?: number;
|
|
1777
|
+
max?: number;
|
|
1778
|
+
}>;
|
|
1779
|
+
/** Per-column date range filters. Key = column ID, values are ISO date strings (YYYY-MM-DD). */
|
|
1780
|
+
columnDateRangeFilters: Record<string, {
|
|
1781
|
+
min?: string;
|
|
1782
|
+
max?: string;
|
|
1783
|
+
}>;
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1787
|
+
type: "status";
|
|
1788
|
+
content: string;
|
|
1789
|
+
} | {
|
|
1790
|
+
type: "message";
|
|
1791
|
+
content: string;
|
|
1792
|
+
} | {
|
|
1793
|
+
type: "rows";
|
|
1794
|
+
content: TRow[];
|
|
1795
|
+
} | {
|
|
1796
|
+
type: "transform";
|
|
1797
|
+
content: string;
|
|
1798
|
+
};
|
|
1799
|
+
type ChatRowStatus = "new" | "edited" | "original";
|
|
1800
|
+
type ChatRow<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1801
|
+
data: TRow;
|
|
1802
|
+
status: ChatRowStatus;
|
|
1803
|
+
errors: Record<string, string[]>;
|
|
1804
|
+
source: string;
|
|
1805
|
+
};
|
|
1806
|
+
type ChatErrorSummary = {
|
|
1807
|
+
field: string;
|
|
1808
|
+
message: string;
|
|
1809
|
+
count: number;
|
|
1810
|
+
examples: string[];
|
|
1811
|
+
};
|
|
1812
|
+
type ChatDataContext<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1813
|
+
columns: DataEditorColumn[];
|
|
1814
|
+
primaryKey: keyof TRow;
|
|
1815
|
+
totalRowCount: number;
|
|
1816
|
+
filteredRowCount: number;
|
|
1817
|
+
sample: ChatRow<TRow>[];
|
|
1818
|
+
errorSummary: ChatErrorSummary[];
|
|
1819
|
+
getRows: () => ChatRow<TRow>[];
|
|
1820
|
+
};
|
|
1821
|
+
type ChatContext<TRow extends DataEditorRow = DataEditorRow> = ChatDataContext<TRow> & {
|
|
1822
|
+
message: string;
|
|
1823
|
+
};
|
|
1824
|
+
type DataEditorChat<TRow extends DataEditorRow = DataEditorRow> = {
|
|
1825
|
+
sampleSize?: number;
|
|
1826
|
+
emptyTitle?: string;
|
|
1827
|
+
suggestionsCount?: number;
|
|
1828
|
+
loadSuggestions?: (context: ChatDataContext<TRow>) => Promise<string[]>;
|
|
1829
|
+
onMessage: (context: ChatContext<TRow>) => AsyncIterable<ChatResponseChunk<TRow>>;
|
|
1830
|
+
onCancel?: () => void;
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
/** Severity level for a validation message. */
|
|
1834
|
+
type ValidationLevel = "error";
|
|
1835
|
+
/**
|
|
1836
|
+
* A single validation message attached to a cell.
|
|
1837
|
+
* Return this from a `CellValidator` to flag a problem.
|
|
1838
|
+
*/
|
|
1839
|
+
type ValidationError = {
|
|
1840
|
+
level: ValidationLevel;
|
|
1841
|
+
/** Human-readable message shown in the cell tooltip. */
|
|
1842
|
+
message: string;
|
|
1843
|
+
};
|
|
1844
|
+
type ValidationResult = ValidationError[] | null;
|
|
1845
|
+
/**
|
|
1846
|
+
* A function that validates a single cell value.
|
|
1847
|
+
* Return a `ValidationError` to flag a problem, or `null` if the value is valid.
|
|
1848
|
+
*
|
|
1849
|
+
* Built-in validator factories: `required(msg)`, `numeric(msg)`, `email(msg)`,
|
|
1850
|
+
* `date(msg)`, `endDateAfterStart(startField, msg)`. Import them from the package root.
|
|
1851
|
+
*
|
|
1852
|
+
* @param value - The current cell value.
|
|
1853
|
+
* @param row - The full row, useful for cross-field checks.
|
|
1854
|
+
*/
|
|
1855
|
+
type CellValidator = (value: unknown, row: DataEditorRow) => ValidationError | null;
|
|
1856
|
+
/** Text input cell. This is the default editor when no `editor` is specified. */
|
|
1857
|
+
type TextEditorCell = {
|
|
1858
|
+
type: "text";
|
|
1859
|
+
};
|
|
1860
|
+
/** Date picker cell. Optionally restrict the selectable date range. */
|
|
1861
|
+
type DateEditorCell = {
|
|
1862
|
+
type: "date";
|
|
1863
|
+
/** Earliest selectable date. */
|
|
1864
|
+
minDate?: Date;
|
|
1865
|
+
/** Latest selectable date. */
|
|
1866
|
+
maxDate?: Date;
|
|
1867
|
+
};
|
|
1868
|
+
/** Dropdown select cell. The user picks from a fixed list of options. */
|
|
1869
|
+
type SelectEditorCell = {
|
|
1870
|
+
type: "select";
|
|
1871
|
+
/** The list of options shown in the dropdown. Each string is both the stored value and the display label. */
|
|
1872
|
+
options: string[];
|
|
1873
|
+
};
|
|
1874
|
+
/** Number input cell with locale-aware formatting. */
|
|
1875
|
+
type NumberEditorCell = {
|
|
1876
|
+
type: "number";
|
|
1877
|
+
/** Maximum number of decimal digits allowed. When omitted, decimals are unrestricted. */
|
|
1878
|
+
decimalPlaces?: number;
|
|
1879
|
+
/** Character used as the decimal point (e.g. `"."` or `","`). Defaults to the browser locale. */
|
|
1880
|
+
decimalSeparator?: string;
|
|
1881
|
+
/** Character inserted between groups of three digits (e.g. `","` or `"."`). Defaults to the browser locale. */
|
|
1882
|
+
thousandsSeparator?: string;
|
|
1883
|
+
/** Extra characters to allow beyond digits, decimal separator, and minus sign (e.g. `"%-"`). When defined, minus is only kept if explicitly included. */
|
|
1884
|
+
allowChars?: string;
|
|
1885
|
+
};
|
|
1886
|
+
/**
|
|
1887
|
+
* Controls how a cell is edited.
|
|
1888
|
+
*
|
|
1889
|
+
* - `"text"` — plain text input (default).
|
|
1890
|
+
* - `"date"` — date picker with optional min/max bounds.
|
|
1891
|
+
* - `"select"` — dropdown with a fixed list of options.
|
|
1892
|
+
* - `"number"` — number input with locale-aware formatting.
|
|
1893
|
+
*/
|
|
1894
|
+
type CellEditor = TextEditorCell | DateEditorCell | SelectEditorCell | NumberEditorCell;
|
|
1895
|
+
/** Dropdown filter shown in the sidebar Filters panel. */
|
|
1896
|
+
type SelectColumnFilter = {
|
|
1897
|
+
type: "select";
|
|
1898
|
+
/** Label displayed above the filter. */
|
|
1899
|
+
label?: string;
|
|
1900
|
+
/** Placeholder text when nothing is selected. */
|
|
1901
|
+
placeholder?: string;
|
|
1902
|
+
/** Fixed list of filter options. When omitted, options are derived from column values. */
|
|
1903
|
+
options?: string[];
|
|
1904
|
+
/** Allow selecting multiple values at once. */
|
|
1905
|
+
multiple?: boolean;
|
|
1906
|
+
};
|
|
1907
|
+
/** Numeric min/max range filter shown in the sidebar Filters panel. */
|
|
1908
|
+
type NumberRangeColumnFilter = {
|
|
1909
|
+
type: "number-range";
|
|
1910
|
+
/** Label displayed above the filter. */
|
|
1911
|
+
label?: string;
|
|
1912
|
+
};
|
|
1913
|
+
/** Date min/max range filter shown in the sidebar Filters panel. */
|
|
1914
|
+
type DateRangeColumnFilter = {
|
|
1915
|
+
type: "date-range";
|
|
1916
|
+
/** Label displayed above the filter. */
|
|
1917
|
+
label?: string;
|
|
1918
|
+
};
|
|
1919
|
+
/**
|
|
1920
|
+
* Filter control shown in the sidebar Filters panel for a column.
|
|
1921
|
+
*
|
|
1922
|
+
* - `"select"` — dropdown to pick one or more values.
|
|
1923
|
+
* - `"number-range"` — two inputs for min and max number.
|
|
1924
|
+
* - `"date-range"` — two date pickers for start and end date.
|
|
1925
|
+
*/
|
|
1926
|
+
type ColumnFilter = SelectColumnFilter | NumberRangeColumnFilter | DateRangeColumnFilter;
|
|
1927
|
+
/** Lock mode for a column. `"all"` locks for every row; `"default"` locks only default-source rows. */
|
|
1928
|
+
type ColumnLockMode = "all" | "default";
|
|
1929
|
+
/**
|
|
1930
|
+
* Defines a column in the editor grid.
|
|
1931
|
+
*
|
|
1932
|
+
* @example
|
|
1933
|
+
* ```ts
|
|
1934
|
+
* const columns: DataEditorColumn[] = [
|
|
1935
|
+
* { id: "name", title: "Full Name", width: 200, validate: required },
|
|
1936
|
+
* { id: "email", title: "Email", width: 250, validate: [required, email], unique: true },
|
|
1937
|
+
* { id: "role", title: "Role", editor: { type: "select", options: roleOptions } },
|
|
1938
|
+
* { id: "salary", title: "Salary", validate: numeric, formatter: (v) => `$${v}` },
|
|
1939
|
+
* ];
|
|
1940
|
+
* ```
|
|
1941
|
+
*/
|
|
1942
|
+
type DataEditorColumn = {
|
|
1943
|
+
/** Unique column identifier. Must match the keys in your row data. */
|
|
1944
|
+
id: string;
|
|
1945
|
+
/** Column header text. */
|
|
1946
|
+
title: string;
|
|
1947
|
+
/** One or more validators to run on every edit. */
|
|
1948
|
+
validate?: CellValidator | CellValidator[];
|
|
1949
|
+
/** When `true`, the editor flags duplicate values in this column as errors. */
|
|
1950
|
+
unique?: boolean;
|
|
1951
|
+
/** Column IDs to revalidate when this column changes. Use for cross-field rules like "end date must be after start date". */
|
|
1952
|
+
dependentFields?: string[];
|
|
1953
|
+
/** Format the display value without changing stored data. E.g. add `$` prefix. */
|
|
1954
|
+
formatter?: (value: string) => string;
|
|
1955
|
+
/** Transform the value before it enters the store. Runs on every edit and import. */
|
|
1956
|
+
transformer?: (value: unknown) => unknown;
|
|
1957
|
+
/** How the cell is edited. Defaults to text input. */
|
|
1958
|
+
editor?: CellEditor;
|
|
1959
|
+
/** Adds a filter control for this column in the sidebar Filters panel. */
|
|
1960
|
+
filter?: ColumnFilter;
|
|
1961
|
+
/** Whether this column can be pinned to the left via the header context menu. @default true */
|
|
1962
|
+
pinnable?: boolean;
|
|
1963
|
+
/** Column width in pixels. @default 150 */
|
|
1964
|
+
size?: number;
|
|
1965
|
+
/**
|
|
1966
|
+
* Controls whether cells in this column are locked.
|
|
1967
|
+
* - `true` | `"all"` — locked for all rows.
|
|
1968
|
+
* - `"default"` — locked only for default-source rows; non-default-source rows remain editable.
|
|
1969
|
+
* - `false` | `undefined` — not locked.
|
|
1970
|
+
*/
|
|
1971
|
+
locked?: boolean | ColumnLockMode;
|
|
1972
|
+
};
|
|
1973
|
+
|
|
1974
|
+
/** Params passed to `findAndReplace.onFind` when the user types a search query. */
|
|
1975
|
+
type FindParams = {
|
|
1976
|
+
/** The search string. */
|
|
1977
|
+
search: string;
|
|
1978
|
+
/** When `true`, matching is case-sensitive. */
|
|
1979
|
+
matchCase?: boolean;
|
|
1980
|
+
/** When `true`, the entire cell value must equal the search string. */
|
|
1981
|
+
matchEntireCell?: boolean;
|
|
1982
|
+
/** Restrict search to these columns. `null` or omitted = all columns. */
|
|
1983
|
+
columnIds?: string[] | null;
|
|
1984
|
+
/** Current view filters so the server can scope matches to the active filter set. */
|
|
1985
|
+
filters?: QueryFilters;
|
|
1986
|
+
/** Current sort state so match ordering follows the visual row order. */
|
|
1987
|
+
sort?: SortState;
|
|
1988
|
+
};
|
|
1989
|
+
/** A single match location returned by the server. */
|
|
1990
|
+
type FindMatch = {
|
|
1991
|
+
/** Row position in the current filtered+sorted view (for grid scrolling). */
|
|
1992
|
+
rowIndex: number;
|
|
1993
|
+
/** Column ID where the match occurs. */
|
|
1994
|
+
columnId: string;
|
|
1995
|
+
/** Character offset within the cell value (for inline highlight). */
|
|
1996
|
+
startIndex: number;
|
|
1997
|
+
/** 0-based position in the ordered match list (for counter display). */
|
|
1998
|
+
matchIndex: number;
|
|
1999
|
+
};
|
|
2000
|
+
/** Response from `findAndReplace.onFind`. */
|
|
2001
|
+
type FindResponse = {
|
|
2002
|
+
/** Total number of matches across all rows. */
|
|
2003
|
+
totalCount: number;
|
|
2004
|
+
/** The first match. Omit when `totalCount` is 0. */
|
|
2005
|
+
current?: FindMatch;
|
|
2006
|
+
};
|
|
2007
|
+
/** Params passed to `findAndReplace.onNavigate` when the user clicks prev/next. */
|
|
2008
|
+
type FindNavigateParams = FindParams & {
|
|
2009
|
+
/** Navigation direction. */
|
|
2010
|
+
direction: "next" | "prev";
|
|
2011
|
+
/** Current match position so the server knows where to navigate from. */
|
|
2012
|
+
currentMatchIndex: number;
|
|
2013
|
+
};
|
|
2014
|
+
/** Params passed to `findAndReplace.onReplace`. */
|
|
2015
|
+
type ReplaceParams = FindParams & {
|
|
2016
|
+
/** The replacement text. */
|
|
2017
|
+
replacement: string;
|
|
2018
|
+
/** When `true`, replace all matches. When omitted or `false`, replace only `target`. */
|
|
2019
|
+
all?: boolean;
|
|
2020
|
+
/** The specific match to replace. Required when `all` is not `true`. */
|
|
2021
|
+
target?: FindMatch;
|
|
2022
|
+
};
|
|
2023
|
+
/** Response from `findAndReplace.onReplace`. */
|
|
2024
|
+
type ReplaceResponse = {
|
|
2025
|
+
/** Remaining match count after replacement. */
|
|
2026
|
+
totalCount: number;
|
|
2027
|
+
/** Next match to navigate to after replacement. Omit when none left. */
|
|
2028
|
+
current?: FindMatch;
|
|
2029
|
+
};
|
|
2030
|
+
/** Server-side find and replace configuration. */
|
|
2031
|
+
type FindAndReplaceConfig = {
|
|
2032
|
+
/** Called when the user types a search query. Returns total count and first match. */
|
|
2033
|
+
onFind: (params: FindParams) => Promise<FindResponse>;
|
|
2034
|
+
/** Called when the user clicks prev/next arrows. Returns the target match. */
|
|
2035
|
+
onNavigate: (params: FindNavigateParams) => Promise<FindMatch>;
|
|
2036
|
+
/** Called when the user clicks Replace or Replace All. */
|
|
2037
|
+
onReplace: (params: ReplaceParams) => Promise<ReplaceResponse>;
|
|
2038
|
+
};
|
|
2039
|
+
type StoreMode = "client" | "server";
|
|
2040
|
+
type ServerRowId = string | number;
|
|
2041
|
+
/** Row-level status flags returned by the server. Drive row filters and sidebar counts. */
|
|
2042
|
+
type ServerRowStatus = {
|
|
2043
|
+
edited?: boolean;
|
|
2044
|
+
new?: boolean;
|
|
2045
|
+
deleted?: boolean;
|
|
2046
|
+
hasErrors?: boolean;
|
|
2047
|
+
hasEmptyCells?: boolean;
|
|
2048
|
+
};
|
|
2049
|
+
/** Server-reported change for a single cell. The current value lives in `fields`. */
|
|
2050
|
+
type ServerCellChange = {
|
|
2051
|
+
original: unknown;
|
|
2052
|
+
};
|
|
2053
|
+
/** Server-reported validation error for a single cell. */
|
|
2054
|
+
type ServerCellError = {
|
|
2055
|
+
message: string;
|
|
2056
|
+
code?: number | string;
|
|
2057
|
+
};
|
|
2058
|
+
/** Per-row metadata returned by the server in server-delegated mode. */
|
|
2059
|
+
type ServerRowMeta = {
|
|
2060
|
+
/** Row-level status flags — drive row filters and sidebar counts. */
|
|
2061
|
+
status?: ServerRowStatus;
|
|
2062
|
+
/** Cell-level change tracking. Key = field name. */
|
|
2063
|
+
changes?: Record<string, ServerCellChange>;
|
|
2064
|
+
/** Cell-level validation errors. Key = field name. */
|
|
2065
|
+
errors?: Record<string, ServerCellError[]>;
|
|
2066
|
+
};
|
|
2067
|
+
/** Per-source row count returned by the server. Drives the Data Sources sidebar. */
|
|
2068
|
+
type ServerSourceCount = {
|
|
2069
|
+
id: string;
|
|
2070
|
+
name: string;
|
|
2071
|
+
count: number;
|
|
2072
|
+
};
|
|
2073
|
+
/** Aggregate row counts returned alongside a query page. Drive sidebar indicators. */
|
|
2074
|
+
type ServerQueryCounts = {
|
|
2075
|
+
edited?: number;
|
|
2076
|
+
new?: number;
|
|
2077
|
+
deleted?: number;
|
|
2078
|
+
errors?: number;
|
|
2079
|
+
emptyCells?: number;
|
|
2080
|
+
sources?: ServerSourceCount[];
|
|
2081
|
+
};
|
|
2082
|
+
type ServerRow<T extends DataEditorRow = DataEditorRow> = {
|
|
2083
|
+
id: ServerRowId;
|
|
2084
|
+
fields: T;
|
|
2085
|
+
meta?: ServerRowMeta;
|
|
2086
|
+
};
|
|
2087
|
+
type ServerResponse<T> = {
|
|
2088
|
+
data: T;
|
|
2089
|
+
meta?: Record<string, unknown>;
|
|
2090
|
+
};
|
|
2091
|
+
type QueryFilters<F = Record<string, unknown>> = Partial<Filters> & F;
|
|
2092
|
+
type QueryParams<F = Record<string, unknown>> = {
|
|
2093
|
+
filters?: QueryFilters<F>;
|
|
2094
|
+
sort?: SortState;
|
|
2095
|
+
/** When present, only rows belonging to these source IDs are returned. Omit to include all sources. */
|
|
2096
|
+
sources?: string[];
|
|
2097
|
+
offset?: number;
|
|
2098
|
+
limit: number;
|
|
2099
|
+
signal?: AbortSignal;
|
|
2100
|
+
};
|
|
2101
|
+
type QueryResponse<T extends DataEditorRow = DataEditorRow> = ServerResponse<{
|
|
2102
|
+
rows: ServerRow<T>[];
|
|
2103
|
+
totalCount: number;
|
|
2104
|
+
filteredCount?: number;
|
|
2105
|
+
counts?: ServerQueryCounts;
|
|
2106
|
+
}>;
|
|
2107
|
+
/**
|
|
2108
|
+
* Filter options returned by `onFilterOptions` for populating sidebar filter controls in server mode.
|
|
2109
|
+
* Keys are column IDs. Only include columns that have a `filter` configured.
|
|
2110
|
+
*/
|
|
2111
|
+
type FilterOptionsResponse = {
|
|
2112
|
+
[columnId: string]: {
|
|
2113
|
+
/** Values for `"select"` filters. Raw display strings — no formatter is applied. */
|
|
2114
|
+
options?: string[];
|
|
2115
|
+
/** Bounds for `"number-range"` filters. */
|
|
2116
|
+
range?: {
|
|
2117
|
+
min: number;
|
|
2118
|
+
max: number;
|
|
2119
|
+
};
|
|
2120
|
+
/** Bounds for `"date-range"` filters. Values are ISO date strings (YYYY-MM-DD). */
|
|
2121
|
+
dateRange?: {
|
|
2122
|
+
min: string;
|
|
2123
|
+
max: string;
|
|
2124
|
+
};
|
|
2125
|
+
};
|
|
2126
|
+
};
|
|
2127
|
+
type ServerCallOptions = {
|
|
2128
|
+
signal: AbortSignal;
|
|
2129
|
+
};
|
|
2130
|
+
/**
|
|
2131
|
+
* Coordinate rectangle within the server's data view.
|
|
2132
|
+
* Uses ServerRowId (primary key) for rows and column ID strings for columns.
|
|
2133
|
+
*
|
|
2134
|
+
* Convention:
|
|
2135
|
+
* - Both row fields omitted → all rows.
|
|
2136
|
+
* - Both column fields omitted → all columns.
|
|
2137
|
+
* - Single row: `fromRow` AND `toRow` both set, `toRow === fromRow`.
|
|
2138
|
+
* - Single column: `fromColumn` AND `toColumn` both set, `toColumn === fromColumn`.
|
|
2139
|
+
* - Range: both `from` and `to` set, `to !== from`.
|
|
2140
|
+
* - `allSelected: true` → all rows and all columns.
|
|
2141
|
+
* - Empty `{}` → used only for insert operations.
|
|
2142
|
+
*
|
|
2143
|
+
* INVALID: `from` present without `to`, or vice versa.
|
|
2144
|
+
*/
|
|
2145
|
+
type Region = {
|
|
2146
|
+
fromRow?: ServerRowId;
|
|
2147
|
+
toRow?: ServerRowId;
|
|
2148
|
+
fromColumn?: string;
|
|
2149
|
+
toColumn?: string;
|
|
2150
|
+
allSelected?: boolean;
|
|
2151
|
+
};
|
|
2152
|
+
/** Describes where and how to insert a new row. */
|
|
2153
|
+
type InsertParams = {
|
|
2154
|
+
/** Existing row to anchor the insert relative to. Omitted when appending to the end. */
|
|
2155
|
+
anchorRow?: ServerRowId;
|
|
2156
|
+
/** Insert before or after the anchor row. */
|
|
2157
|
+
position: "above" | "below";
|
|
2158
|
+
/** Column IDs matching the order of `values` entries. */
|
|
2159
|
+
columns: string[];
|
|
2160
|
+
};
|
|
2161
|
+
/**
|
|
2162
|
+
* Unified edit params for all data mutations in server-delegated mode.
|
|
2163
|
+
*
|
|
2164
|
+
* The combination of fields determines the operation:
|
|
2165
|
+
* - `target` + `values` → cell edit, clear, or external paste
|
|
2166
|
+
* - `target` + `source` → internal paste (+ `cut` for cut-paste)
|
|
2167
|
+
* - `target` + `source` (fill) → fill handle
|
|
2168
|
+
* - `target` + `transform` → transform / revert
|
|
2169
|
+
* - `target` + `delete: true` → mark rows for deletion
|
|
2170
|
+
* - `target` + `delete: false` → restore rows (unmark deletion)
|
|
2171
|
+
* - `insert` + `values` → create a new row
|
|
2172
|
+
*
|
|
2173
|
+
* `values` is always a 2D array. For a single cell edit: `[["newValue"]]`.
|
|
2174
|
+
* For clearing: `[[""]]`. The server interprets dimensions relative to `target`:
|
|
2175
|
+
* a 1×1 `values` applied to a multi-cell target means "fill all cells with this value".
|
|
2176
|
+
*
|
|
2177
|
+
* `filters` and `sort` provide the view context so the server can resolve
|
|
2178
|
+
* which rows fall between `fromRow` and `toRow` in the current view.
|
|
2179
|
+
* Omitted for single-cell edits where `fromRow` is a direct row ID.
|
|
2180
|
+
*/
|
|
2181
|
+
type EditParams = {
|
|
2182
|
+
target: Region[];
|
|
2183
|
+
source?: Region[];
|
|
2184
|
+
values?: unknown[][];
|
|
2185
|
+
transform?: TransformParams;
|
|
2186
|
+
cut?: boolean;
|
|
2187
|
+
delete?: boolean;
|
|
2188
|
+
insert?: InsertParams;
|
|
2189
|
+
undo?: boolean;
|
|
2190
|
+
filters?: QueryFilters;
|
|
2191
|
+
sort?: SortState;
|
|
2192
|
+
lockedColumns?: Array<{
|
|
2193
|
+
columnId: string;
|
|
2194
|
+
mode: "all" | "default";
|
|
2195
|
+
}>;
|
|
2196
|
+
};
|
|
2197
|
+
type ExportParams$1<F = Record<string, unknown>> = {
|
|
2198
|
+
format: "csv" | "tsv" | "xlsx" | "json" | "xml";
|
|
2199
|
+
allRows: boolean;
|
|
2200
|
+
rtl?: boolean;
|
|
2201
|
+
filters?: QueryFilters<F>;
|
|
2202
|
+
sort?: SortState;
|
|
2203
|
+
signal?: AbortSignal;
|
|
2204
|
+
};
|
|
2205
|
+
/** Transform operation descriptor. `type` identifies the operation, optional fields carry parameters. */
|
|
2206
|
+
type TransformParams = {
|
|
2207
|
+
type: string;
|
|
2208
|
+
separator?: string;
|
|
2209
|
+
/** When `true`, the server should delete the dynamic source columns after applying the transform. */
|
|
2210
|
+
deleteSource?: boolean;
|
|
2211
|
+
};
|
|
2212
|
+
type FormulaParams = {
|
|
2213
|
+
formula: string;
|
|
2214
|
+
params: Record<string, unknown>;
|
|
2215
|
+
};
|
|
2216
|
+
/** Params passed to `onColumnDelete` when the user deletes a dynamic column. */
|
|
2217
|
+
type ColumnDeleteParams = {
|
|
2218
|
+
/** ID of the dynamic column to delete. */
|
|
2219
|
+
columnId: string;
|
|
2220
|
+
/** `true` when the server should reverse this operation (undo). Omitted on initial call and redo. */
|
|
2221
|
+
undo?: boolean;
|
|
2222
|
+
};
|
|
2223
|
+
/** Params passed to `onColumnEdit` when the user renames a dynamic column. */
|
|
2224
|
+
type ColumnEditParams = {
|
|
2225
|
+
/** ID of the dynamic column being edited. */
|
|
2226
|
+
columnId: string;
|
|
2227
|
+
/** New title for the column. */
|
|
2228
|
+
title: string;
|
|
2229
|
+
/** `true` when the server should reverse this operation (undo). Omitted on initial call and redo. */
|
|
2230
|
+
undo?: boolean;
|
|
2231
|
+
};
|
|
2232
|
+
/** Server's response after applying a mutation. */
|
|
2233
|
+
type EditResponse = {
|
|
2234
|
+
counts?: ServerQueryCounts;
|
|
2235
|
+
/** Business-logic rejection. SDK reverts the optimistic update and shows `reason` in a toast. */
|
|
2236
|
+
rejected?: boolean;
|
|
2237
|
+
/** Why the server rejected. Shown to the user as-is. */
|
|
2238
|
+
reason?: string;
|
|
2239
|
+
/** The full row created by the server. Returned for insert operations. */
|
|
2240
|
+
row?: ServerRow;
|
|
2241
|
+
/** Updated column list. When present, the SDK replaces its columns with this list. */
|
|
2242
|
+
columns?: Array<{
|
|
2243
|
+
id: string;
|
|
2244
|
+
title: string;
|
|
2245
|
+
}>;
|
|
2246
|
+
};
|
|
2247
|
+
/**
|
|
2248
|
+
* Every decision the user made during the import wizard, packed into one object.
|
|
2249
|
+
* You get the raw file and the full mapping config. Parse it however you want —
|
|
2250
|
+
* stream it, bulk-load it, hand it to a background job. Your call.
|
|
2251
|
+
*/
|
|
2252
|
+
type ImportMappings = {
|
|
2253
|
+
/** CSV header → column ID. Headers the user left unmatched are `undefined`. */
|
|
2254
|
+
columnMapping: Record<string, string | undefined>;
|
|
2255
|
+
/**
|
|
2256
|
+
* Value substitutions for select columns.
|
|
2257
|
+
* Outer key = column ID, inner key = imported value, inner value = target option.
|
|
2258
|
+
* Only present when at least one select column was matched.
|
|
2259
|
+
*/
|
|
2260
|
+
valueMapping: Record<string, Record<string, string | undefined>>;
|
|
2261
|
+
/** Column ID used to match imported rows against existing data. Same value as `DataEditorProps.primaryKey`. */
|
|
2262
|
+
primaryKey: string;
|
|
2263
|
+
/** Sheet name the user selected. Only present for multi-sheet XLSX files. */
|
|
2264
|
+
selectedSheet?: string;
|
|
2265
|
+
/** Zero-based index of the row the SDK detected as the header row. */
|
|
2266
|
+
headerRowIndex: number;
|
|
2267
|
+
/** Number format detected from the file contents. Affects how `"1.234,56"` vs `"1,234.56"` is read. */
|
|
2268
|
+
numberFormat: "EU" | "US";
|
|
2269
|
+
/**
|
|
2270
|
+
* Per-column date disambiguation. Key = CSV header (not column ID).
|
|
2271
|
+
* `"EU"` = day-first, `"US"` = month-first.
|
|
2272
|
+
* Only includes columns where the format was ambiguous and had to be resolved.
|
|
2273
|
+
*/
|
|
2274
|
+
dateFormats: Record<string, "EU" | "US">;
|
|
2275
|
+
/**
|
|
2276
|
+
* Columns the user created during import for unmatched headers.
|
|
2277
|
+
* These don't exist in your schema yet — you decide whether to persist them.
|
|
2278
|
+
*/
|
|
2279
|
+
newColumns: DataEditorColumn[];
|
|
2280
|
+
};
|
|
2281
|
+
/** Params passed to `onFileImport`. The original file, unchanged, plus everything the wizard collected. */
|
|
2282
|
+
type FileImportParams = {
|
|
2283
|
+
/** The original file. Same bytes the user dropped into the browser. */
|
|
2284
|
+
file: File;
|
|
2285
|
+
/** All mapping decisions from the import wizard. */
|
|
2286
|
+
mappings: ImportMappings;
|
|
2287
|
+
/** Display name for this import source. Typically the file name. */
|
|
2288
|
+
sourceName: string;
|
|
2289
|
+
/** Fires when the user cancels the import. */
|
|
2290
|
+
signal?: AbortSignal;
|
|
2291
|
+
};
|
|
2292
|
+
/**
|
|
2293
|
+
* Params passed to `onRowsImport` once per chunk.
|
|
2294
|
+
* The SDK already parsed the file, applied column mappings, normalized dates
|
|
2295
|
+
* and numbers, and resolved value mappings. You get clean, schema-conformant rows.
|
|
2296
|
+
*/
|
|
2297
|
+
type RowsImportParams = {
|
|
2298
|
+
/** Stable ID for this import session. Use it for idempotency or correlation. */
|
|
2299
|
+
importId: string;
|
|
2300
|
+
/** Display name for this import source. Typically the file name. */
|
|
2301
|
+
sourceName: string;
|
|
2302
|
+
/** Zero-based chunk index. Together with `importId`, uniquely identifies each chunk. */
|
|
2303
|
+
chunkIndex: number;
|
|
2304
|
+
/** `true` on the last chunk. Safe to finalize, run post-import hooks, or trigger validation. */
|
|
2305
|
+
isLastChunk: boolean;
|
|
2306
|
+
/** Transformed rows, keyed by column ID. Ready to store. */
|
|
2307
|
+
rows: Record<string, unknown>[];
|
|
2308
|
+
/** Columns the user created during import. Only present on the first chunk (`chunkIndex === 0`). */
|
|
2309
|
+
newColumns?: DataEditorColumn[];
|
|
2310
|
+
/** Column ID used to match imported rows against existing data. */
|
|
2311
|
+
primaryKey: string;
|
|
2312
|
+
/** Fires when the user cancels mid-import. Clean up any partial state. */
|
|
2313
|
+
signal?: AbortSignal;
|
|
2314
|
+
};
|
|
2315
|
+
/** Your response after processing a chunk. */
|
|
2316
|
+
type RowsImportResponse = {
|
|
2317
|
+
/** How many rows you accepted from this chunk. Drives progress reporting. */
|
|
2318
|
+
accepted: number;
|
|
2319
|
+
/** Per-row errors. `rowIndex` is relative to the chunk (0-based). Surfaced in the UI after import. */
|
|
2320
|
+
errors?: Array<{
|
|
2321
|
+
rowIndex: number;
|
|
2322
|
+
message: string;
|
|
2323
|
+
}>;
|
|
2324
|
+
};
|
|
2325
|
+
/** Params passed to `onSourceRemove` when the user deletes a data source. */
|
|
2326
|
+
type SourceRemoveParams = {
|
|
2327
|
+
/** The source ID to remove. Matches the `id` from `ServerQueryCounts.sources`. */
|
|
2328
|
+
sourceId: string;
|
|
2329
|
+
/** Fires when the user cancels. */
|
|
2330
|
+
signal?: AbortSignal;
|
|
2331
|
+
};
|
|
2332
|
+
type DataEditorServer<TRow extends DataEditorRow = DataEditorRow, TFilters = Record<string, unknown>> = {
|
|
2333
|
+
onQuery: (params: QueryParams<TFilters>) => Promise<QueryResponse<TRow>>;
|
|
2334
|
+
/** Fetches filter dictionaries for all filterable columns. Called once after license validation. */
|
|
2335
|
+
onFilterOptions?: () => Promise<FilterOptionsResponse>;
|
|
2336
|
+
/** Called when the user triggers an export. The consumer handles file generation and download. */
|
|
2337
|
+
onExport?: (params: ExportParams$1<TFilters>) => Promise<void>;
|
|
2338
|
+
/** Called after optimistic local apply. Return `{ rejected: true }` for business-logic errors; throw for infra failures. */
|
|
2339
|
+
onEdit: (params: EditParams, options?: ServerCallOptions) => Promise<EditResponse | void>;
|
|
2340
|
+
/**
|
|
2341
|
+
* File import strategy. You get the raw file and the mapping config.
|
|
2342
|
+
* You own parsing, transformation, and storage.
|
|
2343
|
+
* Mutually exclusive with `onRowsImport`.
|
|
2344
|
+
*/
|
|
2345
|
+
onFileImport?: (params: FileImportParams) => Promise<void>;
|
|
2346
|
+
/**
|
|
2347
|
+
* Rows import strategy. The SDK parses the file, applies all mappings,
|
|
2348
|
+
* normalizes dates and numbers, and sends you clean rows in chunks.
|
|
2349
|
+
* Called once per chunk. Awaited before sending the next chunk.
|
|
2350
|
+
* Mutually exclusive with `onFileImport`.
|
|
2351
|
+
*/
|
|
2352
|
+
onRowsImport?: (params: RowsImportParams) => Promise<RowsImportResponse | void>;
|
|
2353
|
+
/** Number of rows per import chunk when using `onRowsImport`. @default 5000 */
|
|
2354
|
+
importChunkSize?: number;
|
|
2355
|
+
/** Called when the user removes a data source. Delete all rows belonging to this source. */
|
|
2356
|
+
onSourceRemove?: (params: SourceRemoveParams) => Promise<void>;
|
|
2357
|
+
/** Called when the user deletes, undoes, or redoes a dynamic column deletion. */
|
|
2358
|
+
onColumnDelete?: (params: ColumnDeleteParams) => Promise<EditResponse | void>;
|
|
2359
|
+
/** Called when the user edits a dynamic column (e.g. renames it). */
|
|
2360
|
+
onColumnEdit?: (params: ColumnEditParams) => Promise<EditResponse | void>;
|
|
2361
|
+
/**
|
|
2362
|
+
* Server-side find and replace. When provided, enables Find & Replace UI
|
|
2363
|
+
* in server mode. The server owns all searching and replacing; the SDK
|
|
2364
|
+
* handles counts, navigation, and highlighting.
|
|
2365
|
+
*/
|
|
2366
|
+
findAndReplace?: FindAndReplaceConfig;
|
|
2367
|
+
/** Number of rows per page. @default 200 */
|
|
2368
|
+
pageSize?: number;
|
|
2369
|
+
/** Controls how eagerly the SDK fetches data while scrolling. 0 = always debounce, 1 = fetch immediately. @default 0.5 */
|
|
2370
|
+
scrollSensitivity?: number;
|
|
2371
|
+
};
|
|
2372
|
+
|
|
2373
|
+
/** Numeric row identifier. V8 stores small integers (Smi) inline — no heap allocation. */
|
|
2374
|
+
type TRowId = number;
|
|
2375
|
+
type SortType = "text" | "number" | "date";
|
|
2376
|
+
|
|
2377
|
+
/** A pointer to a specific cell in the grid. */
|
|
2378
|
+
type CellLocation = {
|
|
2379
|
+
/** Row identifier. */
|
|
2380
|
+
rowId: TRowId;
|
|
2381
|
+
/** Column ID (matches `DataEditorColumn.id`). */
|
|
2382
|
+
field: string;
|
|
2383
|
+
};
|
|
2384
|
+
type UndoRedoResult = {
|
|
2385
|
+
success: boolean;
|
|
2386
|
+
targetCell?: CellLocation;
|
|
2387
|
+
};
|
|
2388
|
+
/** Actions available inside the `onComplete` callback. */
|
|
2389
|
+
type DataEditorActions = {
|
|
2390
|
+
/** Discard all changes and reload the original data. */
|
|
2391
|
+
reset: () => void;
|
|
2392
|
+
};
|
|
2393
|
+
/**
|
|
2394
|
+
* A single row in the submit result, with orthogonal flags describing its state.
|
|
2395
|
+
*
|
|
2396
|
+
* - `isNew` — provenance: the row was imported or manually added this session.
|
|
2397
|
+
* Permanent from creation; does not flip on cell edits or reverts.
|
|
2398
|
+
* - `isChanged` — mutation: cell values differ from the row's origin snapshot.
|
|
2399
|
+
* Can flip back to false if the user types the original value back.
|
|
2400
|
+
* - `isDeleted` — the user marked the row for deletion via the UI.
|
|
2401
|
+
* - `isValid` — the row passes all validation rules.
|
|
2402
|
+
*
|
|
2403
|
+
* Clients route rows to INSERT/UPDATE/DELETE by filtering on these flags; the
|
|
2404
|
+
* SDK does not pre-group them because the routing rules are client-specific.
|
|
2405
|
+
*/
|
|
2406
|
+
type ResultRow<TRow extends DataEditorRow = DataEditorRow> = {
|
|
2407
|
+
row: TRow;
|
|
2408
|
+
isNew: boolean;
|
|
2409
|
+
isChanged: boolean;
|
|
2410
|
+
isDeleted: boolean;
|
|
2411
|
+
isValid: boolean;
|
|
2412
|
+
};
|
|
2413
|
+
/**
|
|
2414
|
+
* Per-source bucket of result rows. Pristine backend rows (isNew=false,
|
|
2415
|
+
* isChanged=false, isDeleted=false) are omitted — they are no-ops for the
|
|
2416
|
+
* client. Every other row state is emitted, including pristine imports
|
|
2417
|
+
* (isNew=true, isChanged=false) which the client will INSERT.
|
|
2418
|
+
*/
|
|
2419
|
+
type DataEditorSourceResult<TRow extends DataEditorRow = DataEditorRow> = {
|
|
2420
|
+
sourceId: DataSourceId;
|
|
2421
|
+
sourceName: string;
|
|
2422
|
+
rows: ResultRow<TRow>[];
|
|
2423
|
+
};
|
|
2424
|
+
/**
|
|
2425
|
+
* The result object passed to `onComplete` when the user submits changes.
|
|
2426
|
+
* Grouped by source; each row carries flags for client-side routing.
|
|
2427
|
+
*
|
|
2428
|
+
* @example
|
|
2429
|
+
* ```ts
|
|
2430
|
+
* onComplete={async (result, actions) => {
|
|
2431
|
+
* for (const source of result.sources) {
|
|
2432
|
+
* const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid);
|
|
2433
|
+
* const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
|
|
2434
|
+
* const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
|
|
2435
|
+
* await persist(source.sourceId, { inserts, updates, deletes });
|
|
2436
|
+
* }
|
|
2437
|
+
* actions.reset();
|
|
2438
|
+
* }}
|
|
2439
|
+
* ```
|
|
2440
|
+
*/
|
|
2441
|
+
type DataEditorResult<TRow extends DataEditorRow = DataEditorRow> = {
|
|
2442
|
+
sources: DataEditorSourceResult<TRow>[];
|
|
2443
|
+
counts: {
|
|
2444
|
+
new: number;
|
|
2445
|
+
changed: number;
|
|
2446
|
+
deleted: number;
|
|
2447
|
+
invalid: number;
|
|
2448
|
+
};
|
|
2449
|
+
};
|
|
2450
|
+
type ChunkSourceOptions = {
|
|
2451
|
+
/** Display name for this data source. */
|
|
2452
|
+
source: string;
|
|
2453
|
+
/** Stable identifier. Defaults to `source` value when omitted. */
|
|
2454
|
+
id?: string;
|
|
2455
|
+
/** Can the user delete this source? @default false */
|
|
2456
|
+
deletable?: boolean;
|
|
2457
|
+
/** Marks this source as finished loading. */
|
|
2458
|
+
done?: boolean;
|
|
2459
|
+
};
|
|
2460
|
+
type DataSourceId = string;
|
|
2461
|
+
type DataSourceState = {
|
|
2462
|
+
id: DataSourceId;
|
|
2463
|
+
name: string;
|
|
2464
|
+
rowCount: number;
|
|
2465
|
+
isLoading: boolean;
|
|
2466
|
+
isVisible: boolean;
|
|
2467
|
+
isDeletable: boolean;
|
|
2468
|
+
isInitialData: boolean;
|
|
2469
|
+
};
|
|
2470
|
+
|
|
2471
|
+
type UpsertResult<TRow extends DataEditorRow = DataEditorRow> = {
|
|
2472
|
+
rows: TRow[];
|
|
2473
|
+
rowIds: TRowId[];
|
|
2474
|
+
};
|
|
2475
|
+
type ProcessingPhase = "idle" | "loading" | "processing";
|
|
2476
|
+
type ProcessingCode = "pasting" | "clearing" | "filling" | "sorting" | "undoing" | "redoing" | "deleting" | "deletingSource" | "importing" | "transforming" | "reverting";
|
|
2477
|
+
type ProcessingInfo = {
|
|
2478
|
+
code: ProcessingCode;
|
|
2479
|
+
};
|
|
2480
|
+
type DataStoreSnapshot = {
|
|
2481
|
+
rowCount: number;
|
|
2482
|
+
filteredCount: number;
|
|
2483
|
+
newRowCount: number;
|
|
2484
|
+
editedRowCount: number;
|
|
2485
|
+
dirtyRowCount: number;
|
|
2486
|
+
deletedRowCount: number;
|
|
2487
|
+
filteredDirtyRowCount: number;
|
|
2488
|
+
filteredNewRowCount: number;
|
|
2489
|
+
filteredEditedRowCount: number;
|
|
2490
|
+
filteredErrorCount: number;
|
|
2491
|
+
errorCount: number;
|
|
2492
|
+
/** Per-error-message row counts. Key = distinct error message, value = visible non-deleted row count. */
|
|
2493
|
+
errorMessageCounts: Record<string, number>;
|
|
2494
|
+
emptyRowCount: number;
|
|
2495
|
+
filteredEmptyRowCount: number;
|
|
2496
|
+
isLoading: boolean;
|
|
2497
|
+
isFiltering: boolean;
|
|
2498
|
+
/** Unified phase state machine. Controls what actions are allowed. */
|
|
2499
|
+
phase: ProcessingPhase;
|
|
2500
|
+
/** Details about the in-progress operation. Null when phase is "idle". */
|
|
2501
|
+
processing: ProcessingInfo | null;
|
|
2502
|
+
sources: DataSourceState[];
|
|
2503
|
+
canUndo: boolean;
|
|
2504
|
+
canRedo: boolean;
|
|
2505
|
+
/** Increments on undo/redo to force canvas grid re-render */
|
|
2506
|
+
version: number;
|
|
2507
|
+
sortState: SortState;
|
|
2508
|
+
/** True when the "show deleted rows" filter is active (bin mode). */
|
|
2509
|
+
showOnlyDeletedRows: boolean;
|
|
2510
|
+
};
|
|
2511
|
+
/** Per-column data passed to `onValueMatch`: the unique imported values and the allowed select options. */
|
|
2512
|
+
type ValueMatchInput = {
|
|
2513
|
+
importedValues: string[];
|
|
2514
|
+
options: string[];
|
|
2515
|
+
};
|
|
2516
|
+
/** Per-column result from `onValueMatch`: maps each imported value to an option value, or `null` to skip auto-matching. */
|
|
2517
|
+
type ValueMatchOutput = Record<string, Record<string, string | null>>;
|
|
2518
|
+
/** File formats supported for import and export. */
|
|
2519
|
+
type DataEditorFormat = "csv" | "tsv" | "xlsx" | "json" | "xml";
|
|
2520
|
+
/**
|
|
2521
|
+
* A client-defined remote data source (e.g. Google Sheets, S3, Dropbox).
|
|
2522
|
+
*
|
|
2523
|
+
* The SDK renders a button per source and calls `fetch()` when clicked.
|
|
2524
|
+
* The client owns all integration complexity — auth, pickers, downloads.
|
|
2525
|
+
*
|
|
2526
|
+
* @example
|
|
2527
|
+
* ```ts
|
|
2528
|
+
* const googleSheets: RemoteSource = {
|
|
2529
|
+
* id: "google-sheets",
|
|
2530
|
+
* label: "Google Sheets",
|
|
2531
|
+
* icon: "<svg>...</svg>",
|
|
2532
|
+
* fetch: async () => {
|
|
2533
|
+
* const data = await myGoogleSheetsLib.pick();
|
|
2534
|
+
* return data.rows; // Record<string, unknown>[]
|
|
2535
|
+
* },
|
|
2536
|
+
* };
|
|
2537
|
+
* ```
|
|
2538
|
+
*/
|
|
2539
|
+
type RemoteSource = {
|
|
2540
|
+
/** Unique identifier for this source. */
|
|
2541
|
+
id: string;
|
|
2542
|
+
/** Button label shown to the user. */
|
|
2543
|
+
label: string;
|
|
2544
|
+
/** SVG markup or image URL for the button icon. */
|
|
2545
|
+
icon: string;
|
|
2546
|
+
/** Optional tooltip text. */
|
|
2547
|
+
description?: string;
|
|
2548
|
+
/** Returns a File (parsed via CSV/XLSX/JSON/XML pipeline) or structured records (used directly). */
|
|
2549
|
+
fetch: () => Promise<File | Record<string, unknown>[]>;
|
|
2550
|
+
};
|
|
2551
|
+
/**
|
|
2552
|
+
* Controls the initial view when the editor opens.
|
|
2553
|
+
*
|
|
2554
|
+
* - `"editor"` — opens directly into the spreadsheet grid.
|
|
2555
|
+
* - `"uploader"` — opens the file import wizard first, then transitions to the grid.
|
|
2556
|
+
*/
|
|
2557
|
+
type DataEditorVariant = "editor" | "uploader";
|
|
2558
|
+
/** Base configuration shared by the public `DataEditorProps` and the internal provider. */
|
|
2559
|
+
type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
2560
|
+
/** Column definitions. Each entry describes one column in the grid. */
|
|
2561
|
+
columns: DataEditorColumn[];
|
|
2562
|
+
/**
|
|
2563
|
+
* The column ID used to uniquely identify each row (e.g. `"id"` or `"employeeId"`).
|
|
2564
|
+
* Used to match imported rows to existing data and to track edits.
|
|
2565
|
+
*/
|
|
2566
|
+
primaryKey: keyof TRow;
|
|
2567
|
+
/**
|
|
2568
|
+
* Async function that feeds data into the editor. Called once when the editor opens.
|
|
2569
|
+
* Call `onChunk` one or more times to stream rows in batches.
|
|
2570
|
+
* The editor processes each chunk without blocking the UI.
|
|
2571
|
+
*
|
|
2572
|
+
* Pass `options` to tag chunks with a data source. Sources are auto-registered
|
|
2573
|
+
* on first encounter. Omit `options` to send rows to "Existing Data".
|
|
2574
|
+
*
|
|
2575
|
+
* @example
|
|
2576
|
+
* ```ts
|
|
2577
|
+
* // Single source
|
|
2578
|
+
* loadData={async (onChunk) => {
|
|
2579
|
+
* const rows = await fetch("/api/employees").then(r => r.json());
|
|
2580
|
+
* onChunk(rows);
|
|
2581
|
+
* }}
|
|
2582
|
+
*
|
|
2583
|
+
* // Multi-source
|
|
2584
|
+
* loadData={async (onChunk) => {
|
|
2585
|
+
* const sf = await fetchSalesforce();
|
|
2586
|
+
* onChunk(sf, { source: "Salesforce", done: true });
|
|
2587
|
+
*
|
|
2588
|
+
* const hs = await fetchHubSpot();
|
|
2589
|
+
* onChunk(hs, { source: "HubSpot", deletable: true, done: true });
|
|
2590
|
+
* }}
|
|
2591
|
+
* ```
|
|
2592
|
+
*/
|
|
2593
|
+
loadData?: (onChunk: (rows: TRow[], options?: ChunkSourceOptions) => void) => Promise<void>;
|
|
2594
|
+
/**
|
|
2595
|
+
* Server-delegated mode. When provided, the SDK acts as a rendering head
|
|
2596
|
+
* and delegates data operations (fetching, filtering, sorting, mutations)
|
|
2597
|
+
* to the client's backend via callbacks.
|
|
2598
|
+
*/
|
|
2599
|
+
server?: DataEditorServer<TRow>;
|
|
2600
|
+
/**
|
|
2601
|
+
* Called when the user clicks "Submit". Receives the edited data grouped
|
|
2602
|
+
* by source. Use `actions.reset()` to clear changes after a successful save.
|
|
2603
|
+
*
|
|
2604
|
+
* @example
|
|
2605
|
+
* ```ts
|
|
2606
|
+
* onComplete={async (result, actions) => {
|
|
2607
|
+
* for (const source of result.sources) {
|
|
2608
|
+
* const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid);
|
|
2609
|
+
* const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
|
|
2610
|
+
* const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
|
|
2611
|
+
* await persist(source.sourceId, { inserts, updates, deletes });
|
|
2612
|
+
* }
|
|
2613
|
+
* actions.reset();
|
|
2614
|
+
* }}
|
|
2615
|
+
* ```
|
|
2616
|
+
*/
|
|
2617
|
+
onComplete?: (result: DataEditorResult<TRow>, actions: DataEditorActions) => void;
|
|
2618
|
+
/** Controls the initial view. `"editor"` opens the grid, `"uploader"` opens the import wizard first. @default "editor" */
|
|
2619
|
+
variant?: DataEditorVariant;
|
|
2620
|
+
/** Override any UI string. Pass a partial object — only the keys you provide are replaced. */
|
|
2621
|
+
translations?: DataEditorTranslations;
|
|
2622
|
+
/** BCP 47 locale tag (e.g. `"en"`, `"fr"`, `"ar"`). Used for plural rules and locale-aware features. @default "en" */
|
|
2623
|
+
locale?: string;
|
|
2624
|
+
/**
|
|
2625
|
+
* Controls row deletion via the right-click context menu.
|
|
2626
|
+
* - `false` — deletion disabled (default).
|
|
2627
|
+
* - `"new"` — only manually added or imported rows can be deleted.
|
|
2628
|
+
* - `"all"` — any row can be deleted.
|
|
2629
|
+
* @default false
|
|
2630
|
+
*/
|
|
2631
|
+
enableDeleteRow?: "all" | "new" | false;
|
|
2632
|
+
/** Show the "Add row" button in the data sources panel. @default true */
|
|
2633
|
+
enableAddRow?: boolean;
|
|
2634
|
+
/** Which file formats the user can import. `undefined` allows all, `[]` disables import entirely. */
|
|
2635
|
+
importFormats?: DataEditorFormat[];
|
|
2636
|
+
/** Client-defined remote data sources rendered as buttons on the upload step. */
|
|
2637
|
+
remoteSources?: RemoteSource[];
|
|
2638
|
+
/** Which file formats the user can export. `undefined` allows all, `[]` disables export entirely. */
|
|
2639
|
+
exportFormats?: DataEditorFormat[];
|
|
2640
|
+
/** Row height in pixels. @default 34 */
|
|
2641
|
+
rowHeight?: number;
|
|
2642
|
+
/** Header row height in pixels. @default 36 */
|
|
2643
|
+
headerHeight?: number;
|
|
2644
|
+
/** When `true`, hides all editing UI (submit, add row, delete, import). The grid becomes view-only. */
|
|
2645
|
+
readonly?: boolean;
|
|
2646
|
+
/** Enable right-to-left layout for RTL languages. @default false */
|
|
2647
|
+
rtl?: boolean;
|
|
2648
|
+
/** Allow creating new columns for unmatched CSV headers during import. @default true */
|
|
2649
|
+
enableCreateColumn?: boolean;
|
|
2650
|
+
/** Override column matching during import. Return a map of `{ csvHeader: columnId | null }`. Unmapped or `null` entries fall back to built-in matching. */
|
|
2651
|
+
onColumnMatch?: (headers: string[], columns: DataEditorColumn[]) => Record<string, string | null> | Promise<Record<string, string | null>>;
|
|
2652
|
+
/**
|
|
2653
|
+
* Override value matching during import for select columns. Called once after column mapping
|
|
2654
|
+
* is established with all select columns' imported values and allowed options. Return a map
|
|
2655
|
+
* of `{ columnId: { importedValue: optionValue | null } }`. `null` entries skip auto-matching
|
|
2656
|
+
* for that value. Unmapped values fall back to built-in fuzzy matching.
|
|
2657
|
+
*
|
|
2658
|
+
* @example
|
|
2659
|
+
* ```ts
|
|
2660
|
+
* onValueMatch={async (valuesToMatch) => {
|
|
2661
|
+
* // valuesToMatch = { country: { importedValues: ["espana", "fr"], options: ["Spain", "France", ...] } }
|
|
2662
|
+
* return {
|
|
2663
|
+
* country: { espana: "Spain", fr: "France" },
|
|
2664
|
+
* };
|
|
2665
|
+
* }}
|
|
2666
|
+
* ```
|
|
2667
|
+
*/
|
|
2668
|
+
onValueMatch?: (valuesToMatch: Record<string, ValueMatchInput>) => ValueMatchOutput | Promise<ValueMatchOutput>;
|
|
2669
|
+
/** Extra synonyms for column auto-matching, e.g. `{ productsku: ["sku", "articleno"] }`. */
|
|
2670
|
+
synonyms?: Record<string, string[]>;
|
|
2671
|
+
/** Sample rows included in the "Download Example" file. When omitted, a generic row is auto-generated from column config. */
|
|
2672
|
+
sampleData?: Record<string, unknown>[];
|
|
2673
|
+
/** Bring Your Own AI chat. When provided, a chat panel is shown alongside the grid. */
|
|
2674
|
+
chat?: DataEditorChat<TRow>;
|
|
2675
|
+
};
|
|
2676
|
+
/**
|
|
2677
|
+
* Controls what the editor stores in `localStorage`.
|
|
2678
|
+
* Set to `false` to disable all local storage usage.
|
|
2679
|
+
*/
|
|
2680
|
+
type DataEditorLocalStorage = false | {
|
|
2681
|
+
/** Cache the license validation result to skip re-validation on reload. @default true */
|
|
2682
|
+
licenseGrant?: boolean;
|
|
2683
|
+
};
|
|
2684
|
+
/** Rendering mode: `"modal"` wraps in a dialog overlay, `"inline"` renders directly in the DOM. */
|
|
2685
|
+
type DataEditorMode = "modal" | "inline";
|
|
2686
|
+
/** Shared props present in both modal and inline modes. */
|
|
2687
|
+
type DataEditorCommonProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorBaseProps<TRow> & {
|
|
2688
|
+
/** Your Updog license key. Validates on each open. */
|
|
2689
|
+
apiKey: string;
|
|
2690
|
+
/** Controls what the editor stores in `localStorage`. Set to `false` to disable. */
|
|
2691
|
+
localStorage?: DataEditorLocalStorage;
|
|
2692
|
+
/** CSS class added to the wrapper element. */
|
|
2693
|
+
className?: string;
|
|
2694
|
+
/** Called when the SDK catches an internal error. Use for logging, Sentry, etc. Client mode only. */
|
|
2695
|
+
onError?: (error: UpdogError) => void;
|
|
2696
|
+
};
|
|
2697
|
+
/**
|
|
2698
|
+
* Props for modal mode (default). The editor renders as a full-screen dialog
|
|
2699
|
+
* controlled by `open` / `onClose`.
|
|
2700
|
+
*/
|
|
2701
|
+
type DataEditorModalProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorCommonProps<TRow> & {
|
|
2702
|
+
/** Rendering mode. @default "modal" */
|
|
2703
|
+
mode?: "modal";
|
|
2704
|
+
/** When `true`, the editor modal is visible. This is a controlled prop. Required in modal mode. */
|
|
2705
|
+
open: boolean;
|
|
2706
|
+
/** Called when the user closes the modal (clicks X or presses Escape). Required in modal mode. */
|
|
2707
|
+
onClose: () => void;
|
|
2708
|
+
};
|
|
2709
|
+
/**
|
|
2710
|
+
* Props for inline mode. The editor renders directly in the DOM with no
|
|
2711
|
+
* modal overlay or close button.
|
|
2712
|
+
*/
|
|
2713
|
+
type DataEditorInlineProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorCommonProps<TRow> & {
|
|
2714
|
+
/** Rendering mode. Must be `"inline"` to render without a modal. */
|
|
2715
|
+
mode: "inline";
|
|
2716
|
+
/** Not applicable in inline mode. */
|
|
2717
|
+
open?: never;
|
|
2718
|
+
/** Not applicable in inline mode. */
|
|
2719
|
+
onClose?: never;
|
|
2720
|
+
};
|
|
2721
|
+
/**
|
|
2722
|
+
* Props for the `<DataEditor />` component.
|
|
2723
|
+
*
|
|
2724
|
+
* Supports two rendering modes:
|
|
2725
|
+
* - **Modal** (default): full-screen dialog controlled by `open` / `onClose`.
|
|
2726
|
+
* - **Inline**: renders directly in the DOM, no overlay.
|
|
2727
|
+
*
|
|
2728
|
+
* @example
|
|
2729
|
+
* ```tsx
|
|
2730
|
+
* // Modal mode (default)
|
|
2731
|
+
* <DataEditor
|
|
2732
|
+
* apiKey="your-license-key"
|
|
2733
|
+
* open={isOpen}
|
|
2734
|
+
* onClose={() => setIsOpen(false)}
|
|
2735
|
+
* columns={columns}
|
|
2736
|
+
* primaryKey="id"
|
|
2737
|
+
* loadData={async (onChunk) => onChunk(await fetchRows())}
|
|
2738
|
+
* onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
|
|
2739
|
+
* />
|
|
2740
|
+
*
|
|
2741
|
+
* // Inline mode
|
|
2742
|
+
* <DataEditor
|
|
2743
|
+
* mode="inline"
|
|
2744
|
+
* apiKey="your-license-key"
|
|
2745
|
+
* columns={columns}
|
|
2746
|
+
* primaryKey="id"
|
|
2747
|
+
* loadData={async (onChunk) => onChunk(await fetchRows())}
|
|
2748
|
+
* onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
|
|
2749
|
+
* />
|
|
2750
|
+
* ```
|
|
2751
|
+
*/
|
|
2752
|
+
type DataEditorProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorModalProps<TRow> | DataEditorInlineProps<TRow>;
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Creates a validator that rejects empty values.
|
|
2756
|
+
*
|
|
2757
|
+
* @param message - Error message shown when the cell is empty.
|
|
2758
|
+
*
|
|
2759
|
+
* @example
|
|
2760
|
+
* ```ts
|
|
2761
|
+
* const columns = [
|
|
2762
|
+
* { id: "name", title: "Name", validate: [required("Name is required")] },
|
|
2763
|
+
* ];
|
|
2764
|
+
* ```
|
|
2765
|
+
*/
|
|
2766
|
+
declare function required(message: string): CellValidator;
|
|
2767
|
+
/**
|
|
2768
|
+
* Creates a validator that rejects non-numeric values.
|
|
2769
|
+
*
|
|
2770
|
+
* @param message - Error message shown when the value is not a valid number.
|
|
2771
|
+
*/
|
|
2772
|
+
declare function numeric(message: string): CellValidator;
|
|
2773
|
+
/**
|
|
2774
|
+
* Creates a validator that rejects invalid email addresses.
|
|
2775
|
+
*
|
|
2776
|
+
* @param message - Error message shown when the value is not a valid email.
|
|
2777
|
+
*/
|
|
2778
|
+
declare function email(message: string): CellValidator;
|
|
2779
|
+
/**
|
|
2780
|
+
* Creates a validator that rejects values not matching `YYYY-MM-DD` or `DD/MM/YYYY`.
|
|
2781
|
+
*
|
|
2782
|
+
* @param message - Error message shown when the value is not a valid date.
|
|
2783
|
+
*/
|
|
2784
|
+
declare function date(message: string): CellValidator;
|
|
2785
|
+
/**
|
|
2786
|
+
* Creates a validator that ensures this date is on or after the date in another column.
|
|
2787
|
+
*
|
|
2788
|
+
* @param startDateField - Column ID of the start-date field to compare against.
|
|
2789
|
+
* @param message - Error message shown when the end date is before the start date.
|
|
2790
|
+
*
|
|
2791
|
+
* @example
|
|
2792
|
+
* ```ts
|
|
2793
|
+
* const columns = [
|
|
2794
|
+
* { id: "startDate", title: "Start", dependentFields: ["endDate"] },
|
|
2795
|
+
* { id: "endDate", title: "End", validate: [endDateAfterStart("startDate", "End must be after start")] },
|
|
2796
|
+
* ];
|
|
2797
|
+
* ```
|
|
2798
|
+
*/
|
|
2799
|
+
/**
|
|
2800
|
+
* Creates a validator that rejects values not in the allowed set.
|
|
2801
|
+
*
|
|
2802
|
+
* @param values - Array of allowed values (matched by strict equality).
|
|
2803
|
+
* @param message - Error message shown when the value is not in the set.
|
|
2804
|
+
*
|
|
2805
|
+
* @example
|
|
2806
|
+
* ```ts
|
|
2807
|
+
* const columns = [
|
|
2808
|
+
* { id: "status", title: "Status", validate: [oneOf(["active", "inactive"], "Invalid status")] },
|
|
2809
|
+
* ];
|
|
2810
|
+
* ```
|
|
2811
|
+
*/
|
|
2812
|
+
declare function oneOf(values: string[], message: string): CellValidator;
|
|
2813
|
+
declare function endDateAfterStart(startDateField: string, message: string): CellValidator;
|
|
2814
|
+
|
|
2815
|
+
type ExportParams<TRow extends DataEditorRow> = {
|
|
2816
|
+
store: DataStore<TRow>;
|
|
2817
|
+
columns: DataEditorColumn[];
|
|
2818
|
+
format: DataEditorFormat;
|
|
2819
|
+
scope: "all" | "filtered";
|
|
2820
|
+
fileName?: string;
|
|
2821
|
+
rtl?: boolean;
|
|
2822
|
+
};
|
|
2823
|
+
/**
|
|
2824
|
+
* Download a template file with column headers and a sample row matching your editor config.
|
|
2825
|
+
* Useful for giving users a file to fill in and import back.
|
|
2826
|
+
*
|
|
2827
|
+
* @param columns - Your column definitions (headers are taken from `column.title`).
|
|
2828
|
+
* @param format - File format to generate (`"csv"`, `"xlsx"`, etc.).
|
|
2829
|
+
*/
|
|
2830
|
+
declare function downloadExampleFile(columns: DataEditorColumn[], format: DataEditorFormat, sampleData?: Record<string, unknown>[]): Promise<void>;
|
|
2831
|
+
/**
|
|
2832
|
+
* Export the editor's data as a downloadable file.
|
|
2833
|
+
*
|
|
2834
|
+
* @param params.store - The DataStore instance (available from the editor context).
|
|
2835
|
+
* @param params.columns - Column definitions (for headers and formatting).
|
|
2836
|
+
* @param params.format - File format (`"csv"`, `"xlsx"`, `"tsv"`, `"json"`, `"xml"`).
|
|
2837
|
+
* @param params.scope - `"all"` exports every row, `"filtered"` exports only currently visible rows.
|
|
2838
|
+
* @param params.fileName - Base filename without extension. Defaults to `"data-export-YYYY-MM-DD"`.
|
|
2839
|
+
*/
|
|
2840
|
+
declare function exportDataEditor<TRow extends DataEditorRow>(params: ExportParams<TRow>): Promise<void>;
|
|
2841
|
+
|
|
2842
|
+
/**
|
|
2843
|
+
* Full-screen modal spreadsheet editor for large datasets.
|
|
2844
|
+
*
|
|
2845
|
+
* Opens as a controlled modal (`open` / `onClose`). Supports CSV/XLSX import,
|
|
2846
|
+
* cell validation, undo/redo, find & replace, and exporting.
|
|
2847
|
+
*
|
|
2848
|
+
* @see {@link DataEditorProps} for all available props.
|
|
2849
|
+
*
|
|
2850
|
+
* @example
|
|
2851
|
+
* ```tsx
|
|
2852
|
+
* <DataEditor
|
|
2853
|
+
* apiKey="your-license-key"
|
|
2854
|
+
* open={isOpen}
|
|
2855
|
+
* onClose={() => setIsOpen(false)}
|
|
2856
|
+
* columns={columns}
|
|
2857
|
+
* primaryKey="id"
|
|
2858
|
+
* loadData={async (onChunk) => onChunk(await fetchRows())}
|
|
2859
|
+
* onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
|
|
2860
|
+
* />
|
|
2861
|
+
* ```
|
|
2862
|
+
*/
|
|
2863
|
+
declare function DataEditor<TRow extends DataEditorRow = DataEditorRow>(allProps: DataEditorProps<TRow>): react_jsx_runtime.JSX.Element;
|
|
2864
|
+
|
|
2865
|
+
export { DataEditor, date, downloadExampleFile, email, endDateAfterStart, exportDataEditor, numeric, oneOf, required };
|
|
2866
|
+
export type { CellValidator, ChatContext, ChatErrorSummary, ChatResponseChunk, ChatRow, ChatRowStatus, ChunkSourceOptions, ColumnDeleteParams, ColumnEditParams, DataEditorChat, DataEditorColumn, DataEditorFormat, DataEditorInlineProps, DataEditorLocalStorage, DataEditorModalProps, DataEditorMode, DataEditorProps, DataEditorResult, DataEditorRow, DataEditorServer, DataEditorSourceResult, DataEditorTranslations, DataEditorVariant, EditParams, EditResponse, ExportParams$1 as ExportParams, FileImportParams, FilterOptionsResponse, FindNavigateParams, FindParams, FormulaParams, ImportMappings, QueryFilters, QueryParams, QueryResponse, Region, RemoteSource, ReplaceParams, ResultRow, RowsImportParams, RowsImportResponse, ServerCallOptions, ServerCellChange, ServerCellError, ServerQueryCounts, ServerRow, ServerRowMeta, ServerRowStatus, SourceRemoveParams, TransformParams, UpdogError, UpdogErrorCode, ValidationError, ValueMatchInput, ValueMatchOutput };
|