@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
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
DROPDOWN EDITORS IN @keenmate/web-grid
|
|
2
|
+
=======================================
|
|
3
|
+
|
|
4
|
+
Three editor types use dropdown menus: select, combobox, and autocomplete.
|
|
5
|
+
All three share a common set of options (EditorOptions) plus type-specific behavior.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
EDITOR TYPES
|
|
9
|
+
------------
|
|
10
|
+
|
|
11
|
+
select
|
|
12
|
+
Static dropdown list. User clicks to open, picks from list.
|
|
13
|
+
Cannot type to filter directly in the trigger, but typing while open
|
|
14
|
+
filters the list. Renders a styled div trigger (not native <select>).
|
|
15
|
+
CSS class: wg__editor--select, trigger: wg__select-trigger
|
|
16
|
+
|
|
17
|
+
combobox
|
|
18
|
+
Filterable dropdown. User types in a text input to narrow options.
|
|
19
|
+
Options are filtered client-side by matching against display text.
|
|
20
|
+
CSS class: wg__editor--combobox, input: wg__combobox-input
|
|
21
|
+
|
|
22
|
+
autocomplete
|
|
23
|
+
Async search dropdown. User types, then after debounce, searchCallback
|
|
24
|
+
is called to fetch results from a server. Supports AbortSignal for
|
|
25
|
+
cancellation, loading indicator, initialOptions, and multiple selection.
|
|
26
|
+
CSS class: wg__editor--autocomplete, input: wg__autocomplete-input
|
|
27
|
+
|
|
28
|
+
Column definition example:
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
field: 'status',
|
|
32
|
+
title: 'Status',
|
|
33
|
+
editor: 'select',
|
|
34
|
+
isEditable: true,
|
|
35
|
+
editorOptions: {
|
|
36
|
+
options: [
|
|
37
|
+
{ value: 'active', label: 'Active' },
|
|
38
|
+
{ value: 'inactive', label: 'Inactive' }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
PROVIDING OPTIONS
|
|
45
|
+
-----------------
|
|
46
|
+
|
|
47
|
+
There are two ways to supply options to any dropdown editor:
|
|
48
|
+
|
|
49
|
+
1. Static array (options)
|
|
50
|
+
|
|
51
|
+
editorOptions: {
|
|
52
|
+
options: [
|
|
53
|
+
{ value: 1, label: 'One' },
|
|
54
|
+
{ value: 2, label: 'Two' }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
The EditorOption type requires value (string | number | boolean) and
|
|
59
|
+
label (string), plus allows arbitrary extra properties via [key: string]: unknown.
|
|
60
|
+
|
|
61
|
+
2. Async loader (loadOptions)
|
|
62
|
+
|
|
63
|
+
editorOptions: {
|
|
64
|
+
loadOptions: async (row, field) => {
|
|
65
|
+
const response = await fetch('/api/options')
|
|
66
|
+
return response.json()
|
|
67
|
+
},
|
|
68
|
+
optionsLoadTrigger: 'oneditstart'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
loadOptions receives the current row and field name, returns Promise<EditorOption[]>.
|
|
72
|
+
|
|
73
|
+
optionsLoadTrigger controls when loadOptions is called:
|
|
74
|
+
|
|
75
|
+
'immediate' Load options as soon as the grid renders.
|
|
76
|
+
'oneditstart' Load options when the cell enters edit mode (default).
|
|
77
|
+
'ondropdownopen' Load options when the dropdown is actually opened.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
DISPLAY MAPPING
|
|
81
|
+
---------------
|
|
82
|
+
|
|
83
|
+
By default, options use { value, label } properties. When your data uses
|
|
84
|
+
different property names, use member strings or callback functions.
|
|
85
|
+
|
|
86
|
+
Each mapping has two alternatives: a member (string property name) and a
|
|
87
|
+
callback (function that receives the option object and returns the value).
|
|
88
|
+
Callbacks take priority over members when both are set.
|
|
89
|
+
|
|
90
|
+
valueMember / getValueCallback
|
|
91
|
+
Which property holds the option's committed value.
|
|
92
|
+
Default property: "value"
|
|
93
|
+
|
|
94
|
+
valueMember: 'id'
|
|
95
|
+
-- or --
|
|
96
|
+
getValueCallback: (opt) => opt.id
|
|
97
|
+
|
|
98
|
+
displayMember / getDisplayCallback
|
|
99
|
+
Which property holds the text shown in the dropdown and in the cell.
|
|
100
|
+
Default property: "label"
|
|
101
|
+
|
|
102
|
+
displayMember: 'name'
|
|
103
|
+
-- or --
|
|
104
|
+
getDisplayCallback: (opt) => opt.firstName + ' ' + opt.lastName
|
|
105
|
+
|
|
106
|
+
searchMember / getSearchCallback
|
|
107
|
+
Which property to search/filter against. Falls back to displayMember.
|
|
108
|
+
Useful when display text differs from searchable text.
|
|
109
|
+
|
|
110
|
+
searchMember: 'searchText'
|
|
111
|
+
-- or --
|
|
112
|
+
getSearchCallback: (opt) => opt.code + ' ' + opt.name
|
|
113
|
+
|
|
114
|
+
iconMember / getIconCallback
|
|
115
|
+
Property containing an icon string (emoji or text) shown before the label.
|
|
116
|
+
|
|
117
|
+
iconMember: 'emoji'
|
|
118
|
+
-- or --
|
|
119
|
+
getIconCallback: (opt) => opt.priority === 'high' ? '!' : ''
|
|
120
|
+
|
|
121
|
+
subtitleMember / getSubtitleCallback
|
|
122
|
+
Property containing a subtitle/description shown below the label.
|
|
123
|
+
|
|
124
|
+
subtitleMember: 'description'
|
|
125
|
+
-- or --
|
|
126
|
+
getSubtitleCallback: (opt) => opt.department + ' - ' + opt.role
|
|
127
|
+
|
|
128
|
+
disabledMember / getDisabledCallback
|
|
129
|
+
Property indicating the option cannot be selected.
|
|
130
|
+
|
|
131
|
+
disabledMember: 'isArchived'
|
|
132
|
+
-- or --
|
|
133
|
+
getDisabledCallback: (opt) => opt.stock === 0
|
|
134
|
+
|
|
135
|
+
groupMember / getGroupCallback
|
|
136
|
+
Property for grouping options under headers.
|
|
137
|
+
|
|
138
|
+
groupMember: 'category'
|
|
139
|
+
-- or --
|
|
140
|
+
getGroupCallback: (opt) => opt.type === 'fruit' ? 'Fruits' : 'Vegetables'
|
|
141
|
+
|
|
142
|
+
Fallback chain for resolving display value in non-editing cells:
|
|
143
|
+
1. Find option by valueMember (or getValueCallback)
|
|
144
|
+
2. Use getDisplayCallback if provided
|
|
145
|
+
3. Use displayMember property
|
|
146
|
+
4. Use 'label' property
|
|
147
|
+
5. Fall back to raw value as string
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
RENDERING AND BEHAVIOR
|
|
151
|
+
----------------------
|
|
152
|
+
|
|
153
|
+
renderOptionCallback(option, context)
|
|
154
|
+
Returns an HTML string for a single dropdown option. When provided,
|
|
155
|
+
completely replaces the default option rendering. The context object has:
|
|
156
|
+
|
|
157
|
+
index number Zero-based position in filtered list.
|
|
158
|
+
isHighlighted boolean True if this option has keyboard/mouse highlight.
|
|
159
|
+
isSelected boolean True if this option matches the cell's current value.
|
|
160
|
+
isDisabled boolean True if the option is disabled.
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
|
|
164
|
+
renderOptionCallback: (opt, ctx) => {
|
|
165
|
+
const classes = ['wg__dropdown-option']
|
|
166
|
+
if (ctx.isHighlighted) classes.push('wg__dropdown-option--highlighted')
|
|
167
|
+
if (ctx.isSelected) classes.push('wg__dropdown-option--selected')
|
|
168
|
+
if (ctx.isDisabled) classes.push('wg__dropdown-option--disabled')
|
|
169
|
+
return '<div class="' + classes.join(' ') + '" data-index="' + ctx.index + '">' +
|
|
170
|
+
'<strong>' + opt.label + '</strong>' +
|
|
171
|
+
'<small style="margin-left:8px;opacity:0.6">' + opt.code + '</small>' +
|
|
172
|
+
'</div>'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
IMPORTANT: The returned HTML must include data-index="N" on the clickable
|
|
176
|
+
element for mouse selection to work. Use data-disabled="true" on disabled
|
|
177
|
+
options to prevent selection.
|
|
178
|
+
|
|
179
|
+
onselect(option, row)
|
|
180
|
+
Event fired after an option is selected and committed. The row parameter
|
|
181
|
+
is the current row data. Return value is ignored (fire-and-forget event).
|
|
182
|
+
|
|
183
|
+
editorOptions: {
|
|
184
|
+
onselect: (option, row) => {
|
|
185
|
+
console.log('Selected', option.label, 'for row', row.id)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
allowEmpty (boolean)
|
|
190
|
+
When true, adds an empty/null option at the top of the dropdown.
|
|
191
|
+
Selecting it commits null/empty to the cell.
|
|
192
|
+
|
|
193
|
+
emptyLabel (string)
|
|
194
|
+
Label for the empty option. Default: "-- Select --"
|
|
195
|
+
|
|
196
|
+
noOptionsText (string)
|
|
197
|
+
Message shown when filter returns no matches.
|
|
198
|
+
Falls back to grid.labels.dropdownNoOptions (default: "No options").
|
|
199
|
+
|
|
200
|
+
searchingText (string)
|
|
201
|
+
Message shown during async search.
|
|
202
|
+
Falls back to grid.labels.dropdownSearching (default: "Searching...").
|
|
203
|
+
|
|
204
|
+
dropdownMinWidth (string)
|
|
205
|
+
CSS minimum width for the dropdown popup. Useful when the cell is narrow
|
|
206
|
+
but options need more space. Example: '300px'
|
|
207
|
+
By default, dropdown matches the cell width.
|
|
208
|
+
|
|
209
|
+
placeholder (string)
|
|
210
|
+
Placeholder text for combobox and autocomplete input fields.
|
|
211
|
+
|
|
212
|
+
Grid-level dropdown settings (set on the grid element, overridable per-column):
|
|
213
|
+
|
|
214
|
+
dropdownToggleVisibility 'always' or 'on-focus'
|
|
215
|
+
Controls when the dropdown arrow is visible in display mode.
|
|
216
|
+
'always' shows it permanently; 'on-focus' shows it on hover/focus.
|
|
217
|
+
Per-column override: column.dropdownToggleVisibility
|
|
218
|
+
|
|
219
|
+
shouldShowDropdownOnFocus boolean (default: false)
|
|
220
|
+
When true, the dropdown opens automatically when a cell is focused.
|
|
221
|
+
|
|
222
|
+
shouldOpenDropdownOnEnter boolean (default: false)
|
|
223
|
+
When true, pressing Enter opens the dropdown instead of moving down.
|
|
224
|
+
Per-column override: column.shouldOpenDropdownOnEnter
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
AUTOCOMPLETE-SPECIFIC OPTIONS
|
|
228
|
+
------------------------------
|
|
229
|
+
|
|
230
|
+
searchCallback(query, row, signal?)
|
|
231
|
+
Async function called after debounce when user types. Returns
|
|
232
|
+
Promise<EditorOption[]>. Receives the search query, current row data,
|
|
233
|
+
and an optional AbortSignal for cancellation of in-flight requests.
|
|
234
|
+
|
|
235
|
+
editorOptions: {
|
|
236
|
+
searchCallback: async (query, row, signal) => {
|
|
237
|
+
const res = await fetch('/api/search?q=' + query, { signal })
|
|
238
|
+
return res.json()
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
If searchCallback is not provided, autocomplete falls back to local
|
|
243
|
+
filtering of initialOptions (same behavior as combobox).
|
|
244
|
+
|
|
245
|
+
initialOptions (EditorOption[])
|
|
246
|
+
Options shown before the user starts typing. Also used as the base
|
|
247
|
+
list for fallback local filtering when no searchCallback is set.
|
|
248
|
+
|
|
249
|
+
minSearchLength (number, default: 1)
|
|
250
|
+
Minimum characters the user must type before searchCallback fires.
|
|
251
|
+
Below this threshold, initialOptions are shown.
|
|
252
|
+
|
|
253
|
+
debounceMs (number, default: 300)
|
|
254
|
+
Delay in milliseconds before searchCallback is invoked after
|
|
255
|
+
the user stops typing. Previous in-flight requests are aborted.
|
|
256
|
+
|
|
257
|
+
multiple (boolean, default: false)
|
|
258
|
+
Allow selecting more than one option.
|
|
259
|
+
|
|
260
|
+
maxSelections (number)
|
|
261
|
+
Maximum number of selected items when multiple is true.
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
CUSTOM OPTION RENDERING
|
|
265
|
+
------------------------
|
|
266
|
+
|
|
267
|
+
When using renderOptionCallback, you must include certain CSS classes and
|
|
268
|
+
data attributes for the dropdown interaction to work correctly.
|
|
269
|
+
|
|
270
|
+
Required CSS classes:
|
|
271
|
+
|
|
272
|
+
wg__dropdown-option Base class for every option div.
|
|
273
|
+
wg__dropdown-option--highlighted Applied to the keyboard/mouse-highlighted option.
|
|
274
|
+
wg__dropdown-option--selected Applied to the option matching the current value.
|
|
275
|
+
wg__dropdown-option--disabled Applied to disabled options. Also add data-disabled="true".
|
|
276
|
+
|
|
277
|
+
Available inner-element classes (used by default rendering):
|
|
278
|
+
|
|
279
|
+
wg__dropdown-option-icon Container for icon/emoji (flex-shrink: 0, 1.5em wide).
|
|
280
|
+
wg__dropdown-option-content Flex container for label + subtitle.
|
|
281
|
+
wg__dropdown-option-label Primary text (ellipsis overflow).
|
|
282
|
+
wg__dropdown-option-subtitle Secondary text (smaller, muted color, ellipsis overflow).
|
|
283
|
+
|
|
284
|
+
Alignment classes (inherited from column.horizontalAlign):
|
|
285
|
+
|
|
286
|
+
wg__dropdown-option--align-left
|
|
287
|
+
wg__dropdown-option--align-center
|
|
288
|
+
wg__dropdown-option--align-right
|
|
289
|
+
wg__dropdown-option--align-justify
|
|
290
|
+
|
|
291
|
+
Empty state class:
|
|
292
|
+
|
|
293
|
+
wg__dropdown-empty Shown when no options match (italic, centered, muted).
|
|
294
|
+
|
|
295
|
+
Full custom rendering example:
|
|
296
|
+
|
|
297
|
+
editorOptions: {
|
|
298
|
+
options: [
|
|
299
|
+
{ id: 'us', name: 'United States', flag: 'US', population: '331M' },
|
|
300
|
+
{ id: 'de', name: 'Germany', flag: 'DE', population: '83M' },
|
|
301
|
+
{ id: 'jp', name: 'Japan', flag: 'JP', population: '125M' }
|
|
302
|
+
],
|
|
303
|
+
valueMember: 'id',
|
|
304
|
+
displayMember: 'name',
|
|
305
|
+
renderOptionCallback: (opt, ctx) => {
|
|
306
|
+
const cls = ['wg__dropdown-option']
|
|
307
|
+
if (ctx.isHighlighted) cls.push('wg__dropdown-option--highlighted')
|
|
308
|
+
if (ctx.isSelected) cls.push('wg__dropdown-option--selected')
|
|
309
|
+
if (ctx.isDisabled) cls.push('wg__dropdown-option--disabled')
|
|
310
|
+
return '<div class="' + cls.join(' ') + '" data-index="' + ctx.index + '"' +
|
|
311
|
+
(ctx.isDisabled ? ' data-disabled="true"' : '') + '>' +
|
|
312
|
+
'<span class="wg__dropdown-option-icon">' + opt.flag + '</span>' +
|
|
313
|
+
'<div class="wg__dropdown-option-content">' +
|
|
314
|
+
'<span class="wg__dropdown-option-label">' + opt.name + '</span>' +
|
|
315
|
+
'<span class="wg__dropdown-option-subtitle">Pop: ' + opt.population + '</span>' +
|
|
316
|
+
'</div>' +
|
|
317
|
+
'</div>'
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
EXAMPLES
|
|
323
|
+
--------
|
|
324
|
+
|
|
325
|
+
Example 1: Select with custom value/display members
|
|
326
|
+
|
|
327
|
+
{
|
|
328
|
+
field: 'countryId',
|
|
329
|
+
title: 'Country',
|
|
330
|
+
editor: 'select',
|
|
331
|
+
isEditable: true,
|
|
332
|
+
editorOptions: {
|
|
333
|
+
options: [
|
|
334
|
+
{ code: 'US', name: 'United States', region: 'Americas' },
|
|
335
|
+
{ code: 'DE', name: 'Germany', region: 'Europe' },
|
|
336
|
+
{ code: 'JP', name: 'Japan', region: 'Asia' }
|
|
337
|
+
],
|
|
338
|
+
valueMember: 'code',
|
|
339
|
+
displayMember: 'name',
|
|
340
|
+
groupMember: 'region',
|
|
341
|
+
allowEmpty: true,
|
|
342
|
+
emptyLabel: '(none)'
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
Example 2: Combobox with icons and subtitles
|
|
347
|
+
|
|
348
|
+
{
|
|
349
|
+
field: 'priority',
|
|
350
|
+
title: 'Priority',
|
|
351
|
+
editor: 'combobox',
|
|
352
|
+
isEditable: true,
|
|
353
|
+
editorOptions: {
|
|
354
|
+
options: [
|
|
355
|
+
{ value: 'critical', label: 'Critical', icon: '!', desc: 'Immediate action' },
|
|
356
|
+
{ value: 'high', label: 'High', icon: 'H', desc: 'Resolve within 24h' },
|
|
357
|
+
{ value: 'medium', label: 'Medium', icon: 'M', desc: 'Resolve within 1 week' },
|
|
358
|
+
{ value: 'low', label: 'Low', icon: 'L', desc: 'When time permits' }
|
|
359
|
+
],
|
|
360
|
+
iconMember: 'icon',
|
|
361
|
+
subtitleMember: 'desc',
|
|
362
|
+
placeholder: 'Search priorities...',
|
|
363
|
+
dropdownMinWidth: '280px',
|
|
364
|
+
onselect: (option, row) => {
|
|
365
|
+
console.log('Priority changed to', option.label)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Example 3: Autocomplete with async search
|
|
371
|
+
|
|
372
|
+
{
|
|
373
|
+
field: 'assigneeId',
|
|
374
|
+
title: 'Assignee',
|
|
375
|
+
editor: 'autocomplete',
|
|
376
|
+
isEditable: true,
|
|
377
|
+
editorOptions: {
|
|
378
|
+
initialOptions: [
|
|
379
|
+
{ value: 1, label: 'Alice Smith' },
|
|
380
|
+
{ value: 2, label: 'Bob Johnson' }
|
|
381
|
+
],
|
|
382
|
+
searchCallback: async (query, row, signal) => {
|
|
383
|
+
const response = await fetch(
|
|
384
|
+
'/api/users?search=' + encodeURIComponent(query),
|
|
385
|
+
{ signal }
|
|
386
|
+
)
|
|
387
|
+
const users = await response.json()
|
|
388
|
+
return users.map(u => ({
|
|
389
|
+
value: u.id,
|
|
390
|
+
label: u.name,
|
|
391
|
+
subtitle: u.email
|
|
392
|
+
}))
|
|
393
|
+
},
|
|
394
|
+
subtitleMember: 'subtitle',
|
|
395
|
+
minSearchLength: 2,
|
|
396
|
+
debounceMs: 250,
|
|
397
|
+
placeholder: 'Search users...',
|
|
398
|
+
dropdownMinWidth: '350px',
|
|
399
|
+
allowEmpty: true
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
Example 4: Combobox with callback-based display mapping
|
|
404
|
+
|
|
405
|
+
{
|
|
406
|
+
field: 'productId',
|
|
407
|
+
title: 'Product',
|
|
408
|
+
editor: 'combobox',
|
|
409
|
+
isEditable: true,
|
|
410
|
+
editorOptions: {
|
|
411
|
+
options: products,
|
|
412
|
+
getValueCallback: (opt) => opt.sku,
|
|
413
|
+
getDisplayCallback: (opt) => opt.sku + ' - ' + opt.productName,
|
|
414
|
+
getSearchCallback: (opt) => opt.sku + ' ' + opt.productName + ' ' + opt.category,
|
|
415
|
+
getIconCallback: (opt) => opt.inStock ? '' : '!',
|
|
416
|
+
getSubtitleCallback: (opt) => '$' + opt.price.toFixed(2) + ' | ' + opt.category,
|
|
417
|
+
getDisabledCallback: (opt) => opt.discontinued,
|
|
418
|
+
dropdownMinWidth: '400px'
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
Example 5: Select with async options loaded on edit start
|
|
423
|
+
|
|
424
|
+
{
|
|
425
|
+
field: 'departmentId',
|
|
426
|
+
title: 'Department',
|
|
427
|
+
editor: 'select',
|
|
428
|
+
isEditable: true,
|
|
429
|
+
editorOptions: {
|
|
430
|
+
loadOptions: async (row, field) => {
|
|
431
|
+
const response = await fetch('/api/departments?company=' + row.companyId)
|
|
432
|
+
return response.json()
|
|
433
|
+
},
|
|
434
|
+
optionsLoadTrigger: 'oneditstart',
|
|
435
|
+
valueMember: 'id',
|
|
436
|
+
displayMember: 'name',
|
|
437
|
+
noOptionsText: 'No departments found'
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
Example 6: Autocomplete with multiple selection
|
|
442
|
+
|
|
443
|
+
{
|
|
444
|
+
field: 'tags',
|
|
445
|
+
title: 'Tags',
|
|
446
|
+
editor: 'autocomplete',
|
|
447
|
+
isEditable: true,
|
|
448
|
+
editorOptions: {
|
|
449
|
+
searchCallback: async (query, row, signal) => {
|
|
450
|
+
const res = await fetch('/api/tags?q=' + query, { signal })
|
|
451
|
+
return res.json()
|
|
452
|
+
},
|
|
453
|
+
multiple: true,
|
|
454
|
+
maxSelections: 5,
|
|
455
|
+
minSearchLength: 1,
|
|
456
|
+
debounceMs: 200,
|
|
457
|
+
placeholder: 'Add tags...'
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
DROPDOWN POSITIONING
|
|
463
|
+
--------------------
|
|
464
|
+
|
|
465
|
+
Dropdowns are positioned using Floating UI (@floating-ui/dom).
|
|
466
|
+
They appear below the cell by default, flipping above if not enough space.
|
|
467
|
+
The dropdown is appended to the shadow DOM root (not inside the cell)
|
|
468
|
+
and uses position: fixed with z-index: 1000.
|
|
469
|
+
|
|
470
|
+
The dropdown width defaults to the cell width (via minWidth), but can be
|
|
471
|
+
made wider with dropdownMinWidth in editorOptions.
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
GRID MODE DEFAULTS FOR DROPDOWNS
|
|
475
|
+
---------------------------------
|
|
476
|
+
|
|
477
|
+
Grid modes set different defaults for dropdown behavior:
|
|
478
|
+
|
|
479
|
+
read-only dropdownToggleVisibility: 'on-focus', shouldShowDropdownOnFocus: false
|
|
480
|
+
excel dropdownToggleVisibility: 'always', shouldShowDropdownOnFocus: false
|
|
481
|
+
input-matrix dropdownToggleVisibility: 'always', shouldShowDropdownOnFocus: true
|
|
482
|
+
|
|
483
|
+
In input-matrix mode, dropdown cells auto-open when focused, making it
|
|
484
|
+
behave like a form. In excel mode, toggles are always visible but require
|
|
485
|
+
explicit interaction to open.
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
TYPE DEFINITIONS
|
|
489
|
+
----------------
|
|
490
|
+
|
|
491
|
+
EditorOption = {
|
|
492
|
+
value: string | number | boolean
|
|
493
|
+
label: string
|
|
494
|
+
[key: string]: unknown
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
OptionRenderContext = {
|
|
498
|
+
index: number
|
|
499
|
+
isHighlighted: boolean
|
|
500
|
+
isSelected: boolean
|
|
501
|
+
isDisabled: boolean
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
OptionsLoadTrigger = 'immediate' | 'oneditstart' | 'ondropdownopen'
|