@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/columns.txt ADDED
@@ -0,0 +1,486 @@
1
+ COLUMN DEFINITIONS IN @keenmate/web-grid
2
+ =========================================
3
+
4
+ A column is defined by the Column<T> interface. Each object in the columns
5
+ array controls how one column renders, edits, validates, and behaves.
6
+
7
+ grid.columns = [
8
+ { field: 'id', title: 'ID', width: '60px' },
9
+ { field: 'name', title: 'Name', isEditable: true, editor: 'text' },
10
+ { field: 'age', title: 'Age', horizontalAlign: 'right' }
11
+ ]
12
+
13
+ The field property is the only required field. It maps to a property on
14
+ each row object in grid.items.
15
+
16
+
17
+ 1. BASIC FIELDS
18
+ ----------------
19
+
20
+ field (keyof T | string) -- REQUIRED
21
+ Data property name on row objects. Used to read/write cell values.
22
+
23
+ title (string)
24
+ Text displayed in the column header. If omitted, the column has no
25
+ header text.
26
+
27
+ headerInfo (string)
28
+ Info tooltip shown next to the header title. Displays an (i) icon
29
+ that shows this text on hover.
30
+
31
+ width (string)
32
+ Column width as CSS value, e.g. '100px', '20%', '10rem'.
33
+
34
+ minWidth (string)
35
+ Minimum width during column resize, e.g. '50px'.
36
+
37
+ maxWidth (string)
38
+ Maximum width during column resize, e.g. '400px'.
39
+
40
+
41
+ 2. DISPLAY
42
+ ----------
43
+
44
+ horizontalAlign ('left' | 'center' | 'right' | 'justify')
45
+ Horizontal alignment for cell content. Default: 'left'.
46
+ Also affects dropdown option alignment.
47
+
48
+ verticalAlign ('top' | 'middle' | 'bottom')
49
+ Vertical alignment for cell content. Default: 'middle'.
50
+
51
+ headerHorizontalAlign ('left' | 'center' | 'right' | 'justify')
52
+ Horizontal alignment for the header cell. Defaults to the value of
53
+ horizontalAlign if not set.
54
+
55
+ headerVerticalAlign ('top' | 'middle' | 'bottom')
56
+ Vertical alignment for the header cell. Defaults to the value of
57
+ verticalAlign if not set.
58
+
59
+ textOverflow ('wrap' | 'ellipsis')
60
+ How text overflow is handled. 'ellipsis' truncates with ... and
61
+ 'wrap' allows multiple lines.
62
+
63
+ maxLines (number)
64
+ Maximum number of visible lines when textOverflow is 'wrap'. Uses
65
+ CSS -webkit-line-clamp. Content beyond maxLines is clipped.
66
+
67
+ cellClass (string)
68
+ Static CSS class applied to every cell in the column. Requires
69
+ customStylesCallback on the grid to inject styles into Shadow DOM.
70
+
71
+ cellClassCallback ((value: unknown, row: T) => string | null)
72
+ Dynamic CSS class computed per cell. Receives the cell value and the
73
+ full row object. Return null to apply no class.
74
+
75
+ Example:
76
+ cellClassCallback: (value, row) =>
77
+ value > 90000 ? 'high-value' : null
78
+
79
+
80
+ 3. CONTENT RENDERING
81
+ ---------------------
82
+
83
+ There are three ways to customize cell display content. They are checked
84
+ in this priority order: templateCallback > formatCallback > default.
85
+
86
+ formatCallback ((value: unknown, row: T) => string)
87
+ Formats the raw cell value into a display string. The result is
88
+ HTML-escaped before rendering. Use for text-only formatting like
89
+ currency, dates, percentages.
90
+
91
+ Example:
92
+ formatCallback: (value) => '$' + Number(value).toFixed(2)
93
+
94
+ Special behavior with checkbox editor: if formatCallback is set on a
95
+ checkbox column, the formatted text is shown instead of the default
96
+ checkbox element.
97
+
98
+ templateCallback ((row: T) => string)
99
+ Returns raw HTML to render in the cell. NOT HTML-escaped -- you can
100
+ include tags, images, links, etc. Receives the full row, not just
101
+ the field value. Takes priority over formatCallback.
102
+
103
+ Example:
104
+ templateCallback: (row) =>
105
+ '<span style="color:red">' + row.name + '</span>'
106
+
107
+ WARNING: You are responsible for escaping user input to prevent XSS.
108
+
109
+ renderCallback ((row: T, element: HTMLElement) => void)
110
+ Imperative rendering. Receives the <td> DOM element directly. Use
111
+ for complex content that needs direct DOM manipulation (e.g.,
112
+ mounting framework components into the cell).
113
+
114
+ Note: This is defined in the type system but is not yet wired into
115
+ the rendering pipeline as of the current version. Use
116
+ templateCallback for HTML content instead.
117
+
118
+ Summary of differences:
119
+ formatCallback -- text only, auto-escaped, receives (value, row)
120
+ templateCallback -- raw HTML, NOT escaped, receives (row)
121
+ renderCallback -- imperative DOM access, receives (row, td element)
122
+
123
+
124
+ 4. EDITING
125
+ ----------
126
+
127
+ isEditable (boolean)
128
+ Enable editing for this column. The grid-level isEditable must also
129
+ be true (or a grid mode like 'excel'/'input-matrix' must be set).
130
+
131
+ editor (EditorType)
132
+ Editor type to use. One of:
133
+ 'text' -- Text input
134
+ 'number' -- Numeric input with step/min/max
135
+ 'checkbox' -- Boolean toggle
136
+ 'select' -- Dropdown list
137
+ 'combobox' -- Filterable dropdown
138
+ 'autocomplete' -- Async search dropdown
139
+ 'date' -- Date picker
140
+ 'custom' -- Consumer-controlled via cellEditCallback
141
+
142
+ editTrigger (EditTrigger)
143
+ Per-column override for how editing starts. One of:
144
+ 'click' -- Single click
145
+ 'dblclick' -- Double click (grid default)
146
+ 'button' -- Edit button in cell
147
+ 'always' -- Cell is always in edit mode
148
+ 'navigate' -- Type to start editing (Excel-like)
149
+
150
+ If not set, uses the grid-level editTrigger.
151
+
152
+ editorOptions (EditorOptions<T>)
153
+ Configuration specific to the editor type. See the web-grid README
154
+ for full options per editor type. Common options:
155
+
156
+ Text: maxLength, placeholder, pattern, inputMode, editStartSelection
157
+ Number: min, max, step, decimalPlaces, allowNegative
158
+ Checkbox: trueValue, falseValue
159
+ Date: minDate, maxDate, dateFormat, outputFormat
160
+ Dropdown (select/combobox/autocomplete):
161
+ options, loadOptions, optionsLoadTrigger,
162
+ valueMember, displayMember, searchMember,
163
+ iconMember, subtitleMember, disabledMember, groupMember,
164
+ getValueCallback, getDisplayCallback, getSearchCallback,
165
+ getIconCallback, getSubtitleCallback, getDisabledCallback,
166
+ getGroupCallback, renderOptionCallback, onselect,
167
+ allowEmpty, emptyLabel, noOptionsText, searchingText,
168
+ dropdownMinWidth, placeholder
169
+ Autocomplete-specific:
170
+ searchCallback, initialOptions, minSearchLength, debounceMs,
171
+ multiple, maxSelections
172
+
173
+ dropdownToggleVisibility (ToggleVisibility)
174
+ Per-column override for when the dropdown toggle button is visible.
175
+ 'always' -- Always show the dropdown arrow
176
+ 'on-focus' -- Show only when cell is focused/hovered
177
+
178
+ shouldOpenDropdownOnEnter (boolean)
179
+ Per-column override. When true, pressing Enter opens the dropdown
180
+ instead of moving to the next row.
181
+
182
+ cellEditCallback ((context: CustomEditorContext<T>) => void)
183
+ Handler for 'custom' editor type. The context provides:
184
+ value -- Current cell value
185
+ row -- Row data object
186
+ rowIndex -- Row index
187
+ field -- Field name
188
+ commit -- Function: call commit(newValue) to save
189
+ cancel -- Function: call cancel() to discard
190
+
191
+ Example:
192
+ editor: 'custom',
193
+ cellEditCallback: ({ value, row, rowIndex, field, commit, cancel }) => {
194
+ const result = prompt('Enter value:', value)
195
+ if (result !== null) commit(result)
196
+ else cancel()
197
+ }
198
+
199
+ isEditButtonVisible (boolean)
200
+ Show an edit button inside the cell. Clicking it triggers editing.
201
+ Useful with editTrigger: 'button'.
202
+
203
+
204
+ 5. VALIDATION
205
+ -------------
206
+
207
+ beforeCommitCallback
208
+ Signature:
209
+ (context: BeforeCommitContext<T>) => BeforeCommitResult | Promise<BeforeCommitResult>
210
+
211
+ Called before a cell edit is committed to the data. Can validate and
212
+ optionally transform the value. Supports async (return a Promise).
213
+
214
+ The context object contains:
215
+ value -- The new value being committed
216
+ oldValue -- The previous value
217
+ row -- The row data object
218
+ rowIndex -- The row index
219
+ field -- The field name
220
+
221
+ Return values and their meaning:
222
+
223
+ { valid: true }
224
+ Accept the value as-is.
225
+
226
+ { valid: true, transformedValue: newVal }
227
+ Accept but replace with transformedValue. Use for
228
+ normalization (trim whitespace, clamp ranges, etc.).
229
+
230
+ { valid: false, message: 'Error text' }
231
+ Reject. Show 'Error text' in a validation tooltip on the
232
+ cell. The cell keeps the invalid value as a draft.
233
+
234
+ true
235
+ Accept (shorthand for { valid: true }).
236
+
237
+ null or undefined
238
+ Accept (shorthand for { valid: true }).
239
+
240
+ false
241
+ Reject with no error message displayed.
242
+
243
+ 'Error text' (string)
244
+ Reject. The string is used as the error message.
245
+
246
+ Example:
247
+ beforeCommitCallback: ({ value, oldValue, row }) => {
248
+ if (value === '') return { valid: false, message: 'Required' }
249
+ if (value < 0) return { valid: true, transformedValue: 0 }
250
+ return { valid: true }
251
+ }
252
+
253
+ Async example:
254
+ beforeCommitCallback: async ({ value, field }) => {
255
+ const exists = await checkDuplicate(field, value)
256
+ if (exists) return 'Value already exists'
257
+ return true
258
+ }
259
+
260
+ When validation fails:
261
+ - The cell shows a red border (invalid state)
262
+ - A tooltip with the error message appears on hover
263
+ - The value is stored as a draft (uncommitted change)
264
+ - The original row data is NOT modified
265
+ - The onrowchange callback fires with isValid: false
266
+
267
+ validationTooltipCallback
268
+ Signature:
269
+ (context: ValidationTooltipContext<T>) => string | null
270
+
271
+ Customize the HTML content of the validation error tooltip for this
272
+ column. Context contains: field, error, value, row, rowIndex.
273
+ Return null to use the default error display. Can return HTML.
274
+
275
+ validateCallback (DEPRECATED)
276
+ Signature:
277
+ (value: unknown, row: T) => string | null | Promise<string | null>
278
+
279
+ Legacy validation. Return null for valid, or an error string.
280
+ Use beforeCommitCallback instead -- it supports value transformation
281
+ and richer return types.
282
+
283
+
284
+ 6. TOOLTIP
285
+ ----------
286
+
287
+ tooltipMember (string)
288
+ Property name on the row object that contains tooltip text for
289
+ this column's cells.
290
+
291
+ Example:
292
+ // Row: { name: 'Alice', nameTooltip: 'Full name: Alice Smith' }
293
+ { field: 'name', tooltipMember: 'nameTooltip' }
294
+
295
+ tooltipCallback ((value: unknown, row: T) => string | null)
296
+ Dynamic tooltip computed per cell. Takes priority over tooltipMember.
297
+ Return null for no tooltip.
298
+
299
+ Example:
300
+ tooltipCallback: (value, row) =>
301
+ row.description ? row.description : null
302
+
303
+ Tooltip show/hide delays are controlled by grid-level properties:
304
+ tooltipShowDelay (default: 400ms)
305
+ tooltipHideDelay (default: 100ms)
306
+
307
+
308
+ 7. CLIPBOARD
309
+ -------------
310
+
311
+ beforeCopyCallback ((value: unknown, row: T) => string)
312
+ Transform the cell value before it is copied to the clipboard.
313
+ Useful for formatting values differently for clipboard than for
314
+ display (e.g., removing currency symbols, using ISO dates).
315
+
316
+ Example:
317
+ beforeCopyCallback: (value) => new Date(value).toISOString()
318
+
319
+ beforePasteCallback ((value: string, row: T) => unknown)
320
+ Process a pasted value before applying it to the cell. The input
321
+ is always a string (from clipboard TSV). Return the value in the
322
+ type the data model expects.
323
+
324
+ Example:
325
+ beforePasteCallback: (value) => parseFloat(value) || 0
326
+
327
+
328
+ 8. LAYOUT
329
+ ---------
330
+
331
+ isFrozen (boolean)
332
+ Column sticks to the left side during horizontal scrolling. Frozen
333
+ columns are visually reordered to appear first (before non-frozen
334
+ columns), regardless of their position in the columns array.
335
+
336
+ isResizable (boolean, default: true)
337
+ Allow the column width to be changed by dragging the column border.
338
+ Drag uses minWidth/maxWidth constraints if set.
339
+
340
+ isMovable (boolean, default: true)
341
+ Allow the column to be reordered by dragging. Requires
342
+ isColumnReorderAllowed on the grid to be true.
343
+
344
+ isHidden (boolean)
345
+ Column is not rendered in the table but remains in the columns
346
+ array. Useful for toggling visibility at runtime (e.g., via header
347
+ context menu 'columnVisibility' action).
348
+
349
+ isSortable (boolean)
350
+ Enable sorting for this column. The grid must also have sortMode
351
+ set to 'single' or 'multi'. Clicking the header toggles sort.
352
+
353
+ isFilterable (boolean)
354
+ Enable the per-column filter input for this column. The grid must
355
+ also have isFilterable set to true.
356
+
357
+
358
+ 9. FILL
359
+ -------
360
+
361
+ fillDirection (FillDirection)
362
+ Override the grid-level fill direction for this column.
363
+ 'vertical' -- Fill handle drags only up/down
364
+ 'all' -- Fill handle drags in any direction
365
+
366
+ This only applies when the grid has a fillDragCallback set.
367
+
368
+
369
+ 10. FULL COLUMN DEFINITION EXAMPLE
370
+ ------------------------------------
371
+
372
+ {
373
+ // Basic
374
+ field: 'salary',
375
+ title: 'Annual Salary',
376
+ headerInfo: 'Base salary before bonuses',
377
+ width: '140px',
378
+ minWidth: '80px',
379
+ maxWidth: '300px',
380
+
381
+ // Display
382
+ horizontalAlign: 'right',
383
+ verticalAlign: 'middle',
384
+ headerHorizontalAlign: 'center',
385
+ textOverflow: 'ellipsis',
386
+ cellClass: 'salary-cell',
387
+ cellClassCallback: (value) => value > 100000 ? 'high-salary' : null,
388
+
389
+ // Content
390
+ formatCallback: (value) => '$' + Number(value).toLocaleString(),
391
+
392
+ // Editing
393
+ isEditable: true,
394
+ editor: 'number',
395
+ editTrigger: 'dblclick',
396
+ editorOptions: {
397
+ min: 0,
398
+ max: 1000000,
399
+ step: 1000,
400
+ decimalPlaces: 2
401
+ },
402
+
403
+ // Validation
404
+ beforeCommitCallback: ({ value, oldValue }) => {
405
+ if (value === null || value === '') {
406
+ return { valid: false, message: 'Salary is required' }
407
+ }
408
+ if (value < 0) {
409
+ return { valid: true, transformedValue: 0 }
410
+ }
411
+ if (value > 1000000) {
412
+ return { valid: false, message: 'Max salary is $1,000,000' }
413
+ }
414
+ return { valid: true }
415
+ },
416
+
417
+ // Tooltip
418
+ tooltipCallback: (value, row) =>
419
+ 'Last updated: ' + row.salaryUpdatedAt,
420
+
421
+ // Clipboard
422
+ beforeCopyCallback: (value) => String(value),
423
+ beforePasteCallback: (value) => parseFloat(value.replace(/[$,]/g, '')) || 0,
424
+
425
+ // Layout
426
+ isFrozen: false,
427
+ isResizable: true,
428
+ isMovable: true,
429
+ isSortable: true,
430
+ isFilterable: true,
431
+ isHidden: false,
432
+
433
+ // Fill
434
+ fillDirection: 'vertical'
435
+ }
436
+
437
+ Here is a dropdown column example with editorOptions:
438
+
439
+ {
440
+ field: 'department',
441
+ title: 'Department',
442
+ width: '180px',
443
+ isEditable: true,
444
+ editor: 'combobox',
445
+ dropdownToggleVisibility: 'always',
446
+ shouldOpenDropdownOnEnter: true,
447
+ editorOptions: {
448
+ options: [
449
+ { value: 'eng', label: 'Engineering' },
450
+ { value: 'sales', label: 'Sales' },
451
+ { value: 'hr', label: 'Human Resources' }
452
+ ],
453
+ allowEmpty: true,
454
+ emptyLabel: '-- None --',
455
+ placeholder: 'Search departments...',
456
+ dropdownMinWidth: '250px',
457
+ onselect: (option, row) => {
458
+ console.log('Selected:', option.label, 'for row', row.id)
459
+ }
460
+ },
461
+ beforeCommitCallback: ({ value }) => {
462
+ if (!value) return { valid: false, message: 'Department required' }
463
+ return true
464
+ }
465
+ }
466
+
467
+ Here is a custom editor column example:
468
+
469
+ {
470
+ field: 'color',
471
+ title: 'Color',
472
+ isEditable: true,
473
+ editor: 'custom',
474
+ cellEditCallback: ({ value, row, commit, cancel }) => {
475
+ const picker = document.createElement('input')
476
+ picker.type = 'color'
477
+ picker.value = value || '#000000'
478
+ picker.onchange = () => commit(picker.value)
479
+ document.body.appendChild(picker)
480
+ picker.click()
481
+ },
482
+ templateCallback: (row) =>
483
+ '<span style="display:inline-block;width:20px;height:20px;'
484
+ + 'background:' + (row.color || '#ccc') + '"></span> '
485
+ + (row.color || 'None')
486
+ }