@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/README.md +13 -6
- package/ai/INDEX.txt +360 -0
- package/ai/basic-setup.txt +165 -0
- package/ai/callbacks-events.txt +297 -0
- package/ai/columns.txt +486 -0
- package/ai/dropdown-editors.txt +504 -0
- package/ai/editing.txt +545 -0
- package/ai/fill-handle.txt +128 -0
- package/ai/frozen-columns.txt +142 -0
- package/ai/grid-modes.txt +267 -0
- package/ai/keyboard-navigation.txt +429 -0
- package/ai/public-methods.txt +231 -0
- package/ai/row-locking.txt +214 -0
- package/ai/selection.txt +403 -0
- package/ai/sorting-filtering-pagination.txt +375 -0
- package/ai/styling-theming.txt +291 -0
- package/ai/toolbar-actions.txt +475 -0
- package/ai/typescript-types.txt +498 -0
- package/ai/virtual-scroll.txt +146 -0
- package/dist/grid.d.ts +4 -0
- package/dist/modules/navigation/focus.d.ts +4 -0
- package/dist/modules/navigation/index.d.ts +1 -1
- package/dist/modules/toolbar/index.d.ts +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/web-grid.js +2038 -1997
- package/dist/web-grid.umd.js +80 -79
- package/package.json +2 -1
- package/src/css/_cell-selection.css +5 -3
- package/src/css/_dark-mode.css +44 -7
- package/src/css/_dialogs.css +1 -1
- package/src/css/_freeze.css +10 -5
- package/src/css/_navigation.css +15 -8
- package/src/css/_selection.css +5 -3
- package/src/css/_variables.css +3 -0
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.
|