@keenmate/web-grid 1.0.0-rc15 → 1.0.1

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/ai/editing.txt ADDED
@@ -0,0 +1,545 @@
1
+ EDITING IN @keenmate/web-grid
2
+ =============================
3
+
4
+ Inline cell editing with 8 editor types, draft management, validation,
5
+ and per-column overrides. Editing operates on draft copies of rows --
6
+ original data is never mutated until the consumer explicitly applies
7
+ changes.
8
+
9
+
10
+ GRID-LEVEL EDITING PROPERTIES
11
+ ------------------------------
12
+
13
+ isEditable (boolean, default: false)
14
+ Master switch. Must be true for any editing to occur.
15
+
16
+ editTrigger (EditTrigger, default: 'dblclick')
17
+ How editing starts. Values:
18
+ 'click' -- Single click on a cell starts editing.
19
+ 'dblclick' -- Double-click starts editing (default).
20
+ 'button' -- Only the edit button in the cell starts editing.
21
+ 'always' -- Cells are always rendered as editors (input-matrix style).
22
+ 'navigate' -- Arrow-key navigation mode. Typing a character starts editing
23
+ the focused cell with that character as initial input. Similar
24
+ to Excel behavior.
25
+
26
+ editStartSelection (EditStartSelection, default: 'selectAll')
27
+ Where the cursor goes when entering edit mode via navigate trigger:
28
+ 'mousePosition' -- Cursor at click position.
29
+ 'selectAll' -- All text is selected (default).
30
+ 'cursorAtStart' -- Cursor at the beginning.
31
+ 'cursorAtEnd' -- Cursor at the end.
32
+
33
+ dropdownToggleVisibility (ToggleVisibility, default: 'on-focus')
34
+ When the dropdown arrow button is visible for select/combobox/autocomplete:
35
+ 'always' -- Always visible in every cell.
36
+ 'on-focus' -- Only visible when the cell is focused.
37
+
38
+ shouldShowDropdownOnFocus (boolean, default: false)
39
+ When true, the dropdown opens automatically as soon as the cell is focused.
40
+ Typically used with editTrigger='always' in input-matrix mode.
41
+
42
+ shouldOpenDropdownOnEnter (boolean, default: false)
43
+ Controls Enter key behavior for dropdown cells:
44
+ true -- Enter opens the dropdown.
45
+ false -- Enter commits and moves focus to the next row.
46
+
47
+ isCheckboxAlwaysEditable (boolean, default: false)
48
+ When true, checkboxes can be toggled even when the grid is in navigate mode
49
+ (i.e., the cell does not need to enter full edit mode first).
50
+
51
+
52
+ GRID MODES (PRESETS)
53
+ --------------------
54
+
55
+ The mode property sets sensible defaults for common use cases. Individual
56
+ properties can still be overridden after setting mode.
57
+
58
+ mode='read-only'
59
+ isEditable=false, cellSelectionMode='click',
60
+ dropdownToggleVisibility='on-focus', shouldShowDropdownOnFocus=false
61
+
62
+ mode='excel'
63
+ isEditable=true, editTrigger='navigate', cellSelectionMode='click',
64
+ dropdownToggleVisibility='always', shouldShowDropdownOnFocus=false
65
+
66
+ mode='input-matrix'
67
+ isEditable=true, editTrigger='always', cellSelectionMode='shift',
68
+ dropdownToggleVisibility='always', shouldShowDropdownOnFocus=true
69
+
70
+
71
+ EDITOR TYPES
72
+ ------------
73
+
74
+ Each column specifies its editor type via the editor property on the column
75
+ definition. The editorOptions property holds editor-specific configuration.
76
+
77
+ column = {
78
+ field: 'name',
79
+ editor: 'text',
80
+ editorOptions: { maxLength: 100, placeholder: 'Enter name' }
81
+ }
82
+
83
+
84
+ TEXT EDITOR (editor: 'text')
85
+ ----------------------------
86
+
87
+ Standard text input. Default editor when none is specified.
88
+
89
+ Options (editorOptions):
90
+ maxLength (number) -- Maximum character count.
91
+ placeholder (string) -- Placeholder text.
92
+ pattern (string) -- HTML input pattern attribute.
93
+ inputMode (string) -- Virtual keyboard hint.
94
+ Values: 'text', 'numeric', 'email', 'tel', 'url'.
95
+ editStartSelection (EditStartSelection) -- Per-column cursor position override.
96
+ Values: 'mousePosition', 'selectAll', 'cursorAtStart',
97
+ 'cursorAtEnd'. Default: 'selectAll'.
98
+
99
+ Example:
100
+ {
101
+ field: 'email',
102
+ editor: 'text',
103
+ editorOptions: {
104
+ placeholder: 'user@example.com',
105
+ inputMode: 'email',
106
+ maxLength: 255
107
+ }
108
+ }
109
+
110
+
111
+ NUMBER EDITOR (editor: 'number')
112
+ ---------------------------------
113
+
114
+ Numeric input rendered as type="text" with inputmode="numeric" for full cursor
115
+ control. Values are parsed to float on commit; empty string becomes null.
116
+
117
+ Options (editorOptions):
118
+ min (number) -- Minimum allowed value.
119
+ max (number) -- Maximum allowed value.
120
+ step (number) -- Step increment.
121
+ decimalPlaces (number) -- Fixed decimal places.
122
+ allowNegative (boolean) -- Allow negative values.
123
+
124
+ Example:
125
+ {
126
+ field: 'price',
127
+ editor: 'number',
128
+ editorOptions: { min: 0, max: 10000, step: 0.01, decimalPlaces: 2 }
129
+ }
130
+
131
+
132
+ CHECKBOX EDITOR (editor: 'checkbox')
133
+ -------------------------------------
134
+
135
+ Boolean toggle. Commits immediately on change (no explicit save step).
136
+ In navigate mode, Space toggles the value and moves focus to the next row.
137
+
138
+ Options (editorOptions):
139
+ trueValue (unknown, default: true) -- Value stored when checked.
140
+ falseValue (unknown, default: false) -- Value stored when unchecked.
141
+
142
+ Example with custom values:
143
+ {
144
+ field: 'active',
145
+ editor: 'checkbox',
146
+ editorOptions: { trueValue: 'Y', falseValue: 'N' }
147
+ }
148
+
149
+
150
+ DATE EDITOR (editor: 'date')
151
+ -----------------------------
152
+
153
+ Date picker with text input and calendar trigger button. Supports custom
154
+ display formats and multiple output formats.
155
+
156
+ Options (editorOptions):
157
+ minDate (Date | string) -- Minimum selectable date.
158
+ maxDate (Date | string) -- Maximum selectable date.
159
+ dateFormat (string) -- Display format. Examples: 'YYYY-MM-DD',
160
+ 'DD.MM.YYYY', 'MM/DD/YYYY'.
161
+ outputFormat (DateOutputFormat) -- What to store on commit:
162
+ 'date' -- JavaScript Date object.
163
+ 'iso' -- ISO date string (default). Actually stores
164
+ 'YYYY-MM-DD' for cleaner display.
165
+ 'timestamp' -- Unix timestamp (milliseconds).
166
+
167
+ Example:
168
+ {
169
+ field: 'birthDate',
170
+ editor: 'date',
171
+ editorOptions: {
172
+ dateFormat: 'DD.MM.YYYY',
173
+ outputFormat: 'iso',
174
+ minDate: '1900-01-01',
175
+ maxDate: new Date()
176
+ }
177
+ }
178
+
179
+
180
+ SELECT EDITOR (editor: 'select')
181
+ ---------------------------------
182
+
183
+ Dropdown list. User picks from a fixed set of options. Cannot type custom
184
+ values. For full dropdown documentation including option loading, member
185
+ properties, rendering callbacks, and grouping, see dropdown-editors.txt.
186
+
187
+ Key options (editorOptions):
188
+ options -- Static array of { value, label } objects.
189
+ loadOptions -- Async function returning options.
190
+ optionsLoadTrigger -- When to call loadOptions: 'immediate',
191
+ 'oneditstart' (default), 'ondropdownopen'.
192
+ valueMember -- Property name for value (default: 'value').
193
+ displayMember -- Property name for display text (default: 'label').
194
+ renderOptionCallback -- Custom HTML for each option.
195
+ allowEmpty -- Allow null/empty selection.
196
+ emptyLabel -- Label for empty option (default: '-- Select --').
197
+
198
+ Example:
199
+ {
200
+ field: 'status',
201
+ editor: 'select',
202
+ editorOptions: {
203
+ options: [
204
+ { value: 'active', label: 'Active' },
205
+ { value: 'inactive', label: 'Inactive' }
206
+ ]
207
+ }
208
+ }
209
+
210
+
211
+ COMBOBOX EDITOR (editor: 'combobox')
212
+ -------------------------------------
213
+
214
+ Filterable dropdown. Same as select, but the user can type to filter options
215
+ and can also enter custom values that are not in the list. For full dropdown
216
+ documentation, see dropdown-editors.txt.
217
+
218
+ Key options (editorOptions):
219
+ Same as select, plus:
220
+ placeholder -- Placeholder text for the input.
221
+
222
+ Example:
223
+ {
224
+ field: 'category',
225
+ editor: 'combobox',
226
+ editorOptions: {
227
+ options: [
228
+ { value: 'electronics', label: 'Electronics' },
229
+ { value: 'clothing', label: 'Clothing' }
230
+ ],
231
+ placeholder: 'Type or select...'
232
+ }
233
+ }
234
+
235
+
236
+ AUTOCOMPLETE EDITOR (editor: 'autocomplete')
237
+ ----------------------------------------------
238
+
239
+ Async search dropdown. Options are fetched from a server based on user input.
240
+ For full dropdown documentation, see dropdown-editors.txt.
241
+
242
+ Key options (editorOptions):
243
+ searchCallback -- (query, row, signal?) => Promise<EditorOption[]>.
244
+ Receives an AbortSignal for request cancellation.
245
+ initialOptions -- Options shown before user types anything.
246
+ minSearchLength -- Minimum characters before search fires (default: 1).
247
+ debounceMs -- Debounce delay in ms for search calls (default: 300).
248
+ multiple -- Allow multiple selections (boolean, default: false).
249
+ maxSelections -- Maximum items when multiple is true.
250
+ placeholder -- Placeholder text for the input.
251
+
252
+ Example:
253
+ {
254
+ field: 'assignee',
255
+ editor: 'autocomplete',
256
+ editorOptions: {
257
+ searchCallback: async (query, row, signal) => {
258
+ const res = await fetch(`/api/users?q=${query}`, { signal })
259
+ return res.json()
260
+ },
261
+ minSearchLength: 2,
262
+ debounceMs: 250,
263
+ placeholder: 'Search users...'
264
+ }
265
+ }
266
+
267
+
268
+ CUSTOM EDITOR (editor: 'custom')
269
+ ---------------------------------
270
+
271
+ The grid renders a placeholder cell. The consumer provides all editing UI
272
+ via the cellEditCallback on the column. Call commit(newValue) to save or
273
+ cancel() to discard.
274
+
275
+ Column property:
276
+ cellEditCallback -- (context: CustomEditorContext) => void
277
+
278
+ CustomEditorContext fields:
279
+ value -- Current cell value.
280
+ row -- The row data object.
281
+ rowIndex -- Index of the row.
282
+ field -- Field name.
283
+ commit -- Function: call commit(newValue) to save.
284
+ cancel -- Function: call cancel() to discard changes.
285
+
286
+ Example:
287
+ {
288
+ field: 'color',
289
+ editor: 'custom',
290
+ cellEditCallback: ({ value, row, rowIndex, field, commit, cancel }) => {
291
+ // Open a color picker modal, then:
292
+ // commit('#ff0000') -- save new value
293
+ // cancel() -- discard
294
+ }
295
+ }
296
+
297
+ Public method for programmatic use:
298
+ grid.openCustomEditor(rowIndex, colIndex)
299
+
300
+
301
+ EDITING LIFECYCLE
302
+ -----------------
303
+
304
+ The editing flow is: Start -> Edit -> Commit or Cancel.
305
+
306
+ 1. START
307
+ - Triggered by the configured editTrigger (click, dblclick, navigate, etc).
308
+ - grid.startEdit(rowIndex, field) is called internally.
309
+ - A draft copy of the row is created (if not already drafted).
310
+ - The cell DOM is replaced with the appropriate editor.
311
+ - The onroweditstart callback fires.
312
+ - For editTrigger='navigate', typing a printable character starts editing
313
+ with that character pre-filled.
314
+
315
+ 2. EDIT
316
+ - The user modifies the value in the editor.
317
+ - For checkbox, changes commit immediately.
318
+ - For text/number/date, the value is held in the editor until commit.
319
+ - For dropdown types, selecting an option commits immediately.
320
+
321
+ 3. COMMIT
322
+ - Triggered by: Enter key, Tab key, blur (clicking away), or selecting a
323
+ dropdown option.
324
+ - Validation runs (beforeCommitCallback or deprecated validateCallback).
325
+ - If valid: draft row is updated, cell returns to display mode, onrowchange
326
+ fires.
327
+ - If valid with transform: beforeCommitCallback can return
328
+ { valid: true, transformedValue: cleanedValue } to modify the value.
329
+ - If invalid: value is still stored in the draft (marked invalid), cell
330
+ shows validation error, onvalidationerror fires. The original data is
331
+ never modified.
332
+ - After commit, focus moves based on the trigger:
333
+ Enter -- moves focus down one row (to the tab-start column if Tab
334
+ was used before Enter).
335
+ Tab -- moves focus to the next editable cell.
336
+ Shift+Tab -- moves focus to the previous editable cell.
337
+
338
+ 4. CANCEL
339
+ - Triggered by: Escape key.
340
+ - grid.cancelEdit() is called.
341
+ - The editor is removed; cell returns to display mode showing the draft
342
+ value (or original if no prior edits).
343
+ - The onroweditcancel callback fires.
344
+ - Focus returns to the cell in navigate mode.
345
+
346
+ Note: The original items array is NEVER mutated. All changes live in draft
347
+ rows. The consumer decides when/how to persist changes (e.g., after
348
+ onrowchange fires).
349
+
350
+
351
+ VALIDATION
352
+ ----------
353
+
354
+ beforeCommitCallback (column-level, preferred)
355
+ Called before each cell value is committed. Receives a context object:
356
+ { value, oldValue, row, rowIndex, field }
357
+
358
+ Return values:
359
+ { valid: true } -- Accept value.
360
+ { valid: true, transformedValue: cleaned } -- Accept with transformation.
361
+ { valid: false, message: 'Error text' } -- Reject with message.
362
+ true / null / undefined -- Accept.
363
+ false -- Reject (no message).
364
+ 'Error text' (string) -- Reject with that message.
365
+
366
+ Supports async (return a Promise).
367
+
368
+ Example:
369
+ {
370
+ field: 'age',
371
+ editor: 'number',
372
+ beforeCommitCallback: ({ value, row }) => {
373
+ if (value < 0) return { valid: false, message: 'Age cannot be negative' }
374
+ if (value > 150) return 'Unrealistic age'
375
+ return { valid: true, transformedValue: Math.round(value) }
376
+ }
377
+ }
378
+
379
+ validateCallback (column-level, DEPRECATED -- use beforeCommitCallback)
380
+ (value, row) => string | null | Promise<string | null>
381
+ Return a string to reject with that error message, or null to accept.
382
+
383
+ validationTooltipCallback (grid-level and column-level)
384
+ Customizes the validation error tooltip. Receives:
385
+ { field, error, value, row, rowIndex }
386
+ Return an HTML string for rich error display, or null for default.
387
+ Column-level overrides grid-level.
388
+
389
+ invalidCells (grid-level property)
390
+ CellValidationState[] -- External validation state. Set this to mark cells
391
+ as invalid from outside the grid (e.g., server-side validation).
392
+ Each entry: { rowIndex, field, error }.
393
+
394
+ Validation helper methods:
395
+ grid.isCellInvalid(rowIndex, field) -- Returns boolean.
396
+ grid.getCellValidationError(rowIndex, field) -- Returns error string or null.
397
+ grid.canEditCell(rowIndex, field) -- Checks editability and locking.
398
+
399
+
400
+ DRAFT MANAGEMENT
401
+ ----------------
402
+
403
+ Edits are stored in draft rows, not in the original items. This allows the
404
+ consumer to review, batch-save, or discard changes.
405
+
406
+ Methods:
407
+ grid.getRowDraft(rowIndex) -- Returns the draft row object (T) or
408
+ undefined if no draft exists.
409
+ grid.hasRowDraft(rowIndex) -- Returns true if the row has been edited.
410
+ grid.discardRowDraft(rowIndex) -- Discards all draft changes for a row.
411
+ Also clears validation errors for that row.
412
+ grid.getDraftRowIndices() -- Returns number[] of all row indices that
413
+ have drafts.
414
+ grid.discardAllDrafts() -- Discards all drafts and clears all
415
+ validation errors.
416
+ grid.discardCellDraft(rowIndex, field)
417
+ -- Discards the draft for a single cell.
418
+ If the draft row then matches the original,
419
+ the entire draft row is removed.
420
+
421
+ Draft behavior:
422
+ - A draft is created the first time a cell in a row enters edit mode.
423
+ - The draft is a shallow clone of the original row ({ ...item }).
424
+ - Subsequent edits to other cells in the same row reuse the existing draft.
425
+ - Invalid values ARE stored in the draft (so the user's input is preserved).
426
+ - getCellRawValue returns the draft value when a draft exists.
427
+ - The onrowchange callback includes both row (original) and draftRow (with
428
+ changes) so the consumer can compare.
429
+
430
+
431
+ EVENTS (CALLBACKS)
432
+ ------------------
433
+
434
+ These are property-based callbacks set on the grid element, not DOM events.
435
+ Return values are ignored (fire-and-forget).
436
+
437
+ onroweditstart
438
+ Fires when editing starts on a cell.
439
+ Signature: (detail: { row, rowIndex, field }) => void
440
+
441
+ onroweditcancel
442
+ Fires when editing is cancelled (Escape key).
443
+ Signature: (detail: { row, rowIndex, field }) => void
444
+
445
+ onrowchange
446
+ Fires when a cell value is committed (whether valid or invalid).
447
+ Signature: (detail: RowChangeDetail) => void
448
+ RowChangeDetail fields:
449
+ row -- Original row (unchanged).
450
+ draftRow -- Draft row with the user's changes.
451
+ rowIndex -- Index of the row.
452
+ field -- Field that changed.
453
+ oldValue -- Previous value.
454
+ newValue -- New value (after transformation if any).
455
+ isValid -- Whether validation passed.
456
+ validationError -- Error message if validation failed, or null.
457
+
458
+ onvalidationerror
459
+ Fires when validation fails during commit.
460
+ Signature: (detail: { row, rowIndex, field, error }) => void
461
+
462
+
463
+ PER-COLUMN EDITING OVERRIDES
464
+ -----------------------------
465
+
466
+ Several grid-level editing properties can be overridden on individual columns.
467
+
468
+ Column-level overrides:
469
+ isEditable -- Enable/disable editing for this specific column.
470
+ editor -- Editor type for this column.
471
+ editTrigger -- Override the grid's editTrigger for this column.
472
+ Example: most columns use 'navigate' but one
473
+ column uses 'click'.
474
+ dropdownToggleVisibility -- Override when the dropdown arrow is visible
475
+ for this column: 'always' or 'on-focus'.
476
+ shouldOpenDropdownOnEnter -- Override Enter behavior for dropdown columns
477
+ on this column.
478
+ isEditButtonVisible -- Show an edit button (pencil icon) in the cell.
479
+ editorOptions -- Editor-specific options for this column.
480
+ cellEditCallback -- Handler for custom editor type.
481
+ beforeCommitCallback -- Validation/transform for this column.
482
+ validationTooltipCallback -- Custom tooltip for this column's errors.
483
+
484
+ Example with per-column overrides:
485
+ grid.editTrigger = 'navigate' // grid default
486
+ grid.columns = [
487
+ { field: 'id', isEditable: false },
488
+ { field: 'name', editor: 'text' },
489
+ { field: 'status', editor: 'select', editTrigger: 'click',
490
+ dropdownToggleVisibility: 'always',
491
+ shouldOpenDropdownOnEnter: true,
492
+ editorOptions: { options: [...] } },
493
+ { field: 'notes', editor: 'custom',
494
+ cellEditCallback: (ctx) => openModal(ctx) }
495
+ ]
496
+
497
+
498
+ PUBLIC METHODS FOR EDITING
499
+ ---------------------------
500
+
501
+ grid.focusCell(rowIndex, colIndex)
502
+ Programmatically focus a cell (navigate mode).
503
+
504
+ grid.startEditing(rowIndex, colIndex)
505
+ Programmatically start editing a cell.
506
+
507
+ grid.openCustomEditor(rowIndex, colIndex)
508
+ Open the custom editor for a cell with editor='custom'.
509
+
510
+
511
+ NEW ROW ENTRY (INLINE DATA ENTRY)
512
+ -----------------------------------
513
+ Adds an empty row at the top or bottom of the grid for entering new records.
514
+ The empty row is visually distinct and commits to the items array once a
515
+ non-empty value is entered and committed.
516
+
517
+ Properties:
518
+
519
+ isNewRowEnabled (boolean, default: false)
520
+ Enable the new row entry feature.
521
+
522
+ newRowPosition ('top' | 'bottom', default: 'bottom')
523
+ Where the empty row appears.
524
+
525
+ newRowIndicator (string, default: '+')
526
+ Text shown in the row number column for the empty row.
527
+
528
+ createEmptyRowCallback (() => T | Promise<T>)
529
+ Creates the blank row object. Called once when the empty row is first
530
+ accessed. Can be async.
531
+
532
+ Example:
533
+
534
+ grid.isNewRowEnabled = true
535
+ grid.newRowPosition = 'bottom'
536
+ grid.createEmptyRowCallback = () => ({
537
+ id: 0,
538
+ name: '',
539
+ email: '',
540
+ status: 'draft'
541
+ })
542
+
543
+ When the user edits the empty row and commits, the new row is inserted into
544
+ the items array at the appropriate position. The empty row then resets for
545
+ the next entry.
@@ -0,0 +1,128 @@
1
+ FILL HANDLE
2
+ ===========
3
+ @keenmate/web-grid - Excel-like drag-to-fill for copying cell values.
4
+
5
+
6
+ OVERVIEW
7
+ --------
8
+ The fill handle is a small square that appears at the corner of the focused
9
+ cell. Dragging it fills adjacent cells with the source cell's value, similar
10
+ to Excel's autofill. The user drags vertically (up/down) or horizontally
11
+ (left/right) to select a rectangular region of target cells.
12
+
13
+
14
+ REQUIREMENTS
15
+ ------------
16
+ The fill handle appears only when ALL of these conditions are met:
17
+ - editTrigger is 'navigate' (or mode is 'excel', which sets navigate)
18
+ - isEditable is true on the grid
19
+ - The focused cell's column has isEditable: true
20
+ - The cell is not currently in edit mode
21
+ - No dropdown is open
22
+
23
+ The fill handle appears at the bottom-right corner of the focused cell, or
24
+ at the top-right corner when the cell is on the last row (so the user can
25
+ drag upward).
26
+
27
+
28
+ FILL DRAG CALLBACK
29
+ ------------------
30
+ fillDragCallback (property on grid element)
31
+ Type: (detail: FillDragDetail) => boolean | void
32
+ Called when the user completes a fill drag. Return false to cancel the
33
+ entire fill operation. Return anything else (or nothing) to allow it.
34
+
35
+ FillDragDetail structure:
36
+ sourceCell: {
37
+ rowIndex: number Row index of the dragged-from cell
38
+ colIndex: number Column index of the dragged-from cell
39
+ field: string Field name of the source column
40
+ value: unknown Current value of the source cell (uses draft if exists)
41
+ }
42
+ targetCells: [ Array of cells that will be filled
43
+ {
44
+ rowIndex: number
45
+ colIndex: number
46
+ field: string
47
+ }
48
+ ...
49
+ ]
50
+ direction: 'up' | 'down' | 'left' | 'right'
51
+ Dominant direction of the drag. Determined by which axis has the
52
+ larger delta. Same-cell defaults to 'down'.
53
+
54
+ Example:
55
+ grid.fillDragCallback = (detail) => {
56
+ console.log('Filling', detail.sourceCell.value, 'into', detail.targetCells.length, 'cells')
57
+ if (detail.sourceCell.field === 'id') {
58
+ return false // Don't allow filling ID column
59
+ }
60
+ }
61
+
62
+
63
+ PER-COLUMN FILL DIRECTION
64
+ --------------------------
65
+ fillDirection on Column<T>
66
+ Type: 'vertical' | 'all'
67
+ Default: inherits from grid-level fillDirection
68
+
69
+ 'vertical' Only fills in the same column (constrains to vertical axis)
70
+ 'all' Allows filling across multiple columns in the drag region
71
+
72
+ grid.columns = [
73
+ { field: 'price', fillDirection: 'vertical' },
74
+ { field: 'notes', fillDirection: 'all' }
75
+ ]
76
+
77
+
78
+ NON-EDITABLE CELLS ARE SKIPPED
79
+ -------------------------------
80
+ During a fill operation, cells that cannot be edited are automatically
81
+ skipped. This includes:
82
+ - Cells in columns where isEditable is false or not set
83
+ - Cells in locked rows (when row locking is configured)
84
+ - Cells checked by canEditCell(rowIndex, field)
85
+
86
+ Additionally, value compatibility is checked:
87
+ - Number columns only accept numeric values
88
+ - Select/combobox columns only accept values that exist in their options
89
+ - Date columns only accept valid dates (Date objects, ISO strings, timestamps)
90
+ - Text, autocomplete, checkbox, and custom editors accept any value
91
+
92
+
93
+ ONROWCHANGE EVENT
94
+ -----------------
95
+ After a fill operation completes, onrowchange fires for each filled cell
96
+ individually. Each call receives the standard RowChangeDetail with the
97
+ field, oldValue, newValue, row, draftRow, rowIndex, isValid, and
98
+ validationError.
99
+
100
+ The fill is applied via commitEdit internally, which means
101
+ beforeCommitCallback validation runs for each target cell. Invalid values
102
+ still appear in the draft row but are flagged with validation errors.
103
+
104
+
105
+ DRAG BEHAVIOR
106
+ -------------
107
+ Mouse must move at least 5 pixels before the drag activates (threshold
108
+ prevents accidental fills from clicks). During the drag:
109
+ - A semi-transparent overlay shows the fill range
110
+ - The container gets a wg--filling CSS class
111
+ - Pressing Escape cancels the fill operation
112
+ - Releasing the mouse applies the fill
113
+
114
+ CSS variables for the fill handle visual:
115
+ --wg-fill-handle-size Default: 8px
116
+ --wg-fill-handle-bg Default: var(--wg-surface-1)
117
+ --wg-fill-handle-border-color Default: var(--wg-accent-color)
118
+ --wg-fill-handle-border-width Default: 2px
119
+ --wg-fill-range-bg Default: color-mix(accent 15%, transparent)
120
+ --wg-fill-range-border-color Default: var(--wg-accent-color)
121
+
122
+
123
+ SOURCE VALUE
124
+ ------------
125
+ The source cell value comes from the draft row if one exists (contains
126
+ edited but uncommitted values), otherwise from the display item. This
127
+ means if the user has edited a cell but not committed it, the fill will
128
+ use the edited value.