@aquera/nile-elements 1.7.1 → 1.7.3

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.
Files changed (88) hide show
  1. package/README.md +7 -0
  2. package/dist/index.cjs.js +1 -1
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.js +1524 -419
  5. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js +1 -1
  6. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js.map +1 -1
  7. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.esm.js +8 -6
  8. package/dist/nile-combobox/group-utils.cjs.js +2 -0
  9. package/dist/nile-combobox/group-utils.cjs.js.map +1 -0
  10. package/dist/nile-combobox/group-utils.esm.js +1 -0
  11. package/dist/nile-combobox/index.cjs.js +2 -0
  12. package/dist/nile-combobox/index.cjs.js.map +1 -0
  13. package/dist/nile-combobox/index.esm.js +1 -0
  14. package/dist/nile-combobox/nile-combobox.cjs.js +2 -0
  15. package/dist/nile-combobox/nile-combobox.cjs.js.map +1 -0
  16. package/dist/nile-combobox/nile-combobox.css.cjs.js +2 -0
  17. package/dist/nile-combobox/nile-combobox.css.cjs.js.map +1 -0
  18. package/dist/nile-combobox/nile-combobox.css.esm.js +715 -0
  19. package/dist/nile-combobox/nile-combobox.esm.js +238 -0
  20. package/dist/nile-combobox/portal-manager.cjs.js +2 -0
  21. package/dist/nile-combobox/portal-manager.cjs.js.map +1 -0
  22. package/dist/nile-combobox/portal-manager.esm.js +1 -0
  23. package/dist/nile-combobox/renderer.cjs.js +2 -0
  24. package/dist/nile-combobox/renderer.cjs.js.map +1 -0
  25. package/dist/nile-combobox/renderer.esm.js +147 -0
  26. package/dist/nile-combobox/search-manager.cjs.js +2 -0
  27. package/dist/nile-combobox/search-manager.cjs.js.map +1 -0
  28. package/dist/nile-combobox/search-manager.esm.js +1 -0
  29. package/dist/nile-combobox/selection-manager.cjs.js +2 -0
  30. package/dist/nile-combobox/selection-manager.cjs.js.map +1 -0
  31. package/dist/nile-combobox/selection-manager.esm.js +1 -0
  32. package/dist/nile-combobox/types.cjs.js +2 -0
  33. package/dist/nile-combobox/types.cjs.js.map +1 -0
  34. package/dist/nile-combobox/types.esm.js +1 -0
  35. package/dist/src/index.d.ts +1 -0
  36. package/dist/src/index.js +1 -0
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js +4 -2
  39. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js.map +1 -1
  40. package/dist/src/nile-combobox/group-utils.d.ts +26 -0
  41. package/dist/src/nile-combobox/group-utils.js +140 -0
  42. package/dist/src/nile-combobox/group-utils.js.map +1 -0
  43. package/dist/src/nile-combobox/index.d.ts +1 -0
  44. package/dist/src/nile-combobox/index.js +2 -0
  45. package/dist/src/nile-combobox/index.js.map +1 -0
  46. package/dist/src/nile-combobox/nile-combobox.css.d.ts +9 -0
  47. package/dist/src/nile-combobox/nile-combobox.css.js +724 -0
  48. package/dist/src/nile-combobox/nile-combobox.css.js.map +1 -0
  49. package/dist/src/nile-combobox/nile-combobox.d.ts +320 -0
  50. package/dist/src/nile-combobox/nile-combobox.js +1739 -0
  51. package/dist/src/nile-combobox/nile-combobox.js.map +1 -0
  52. package/dist/src/nile-combobox/nile-combobox.test.d.ts +1 -0
  53. package/dist/src/nile-combobox/nile-combobox.test.js +551 -0
  54. package/dist/src/nile-combobox/nile-combobox.test.js.map +1 -0
  55. package/dist/src/nile-combobox/portal-manager.d.ts +26 -0
  56. package/dist/src/nile-combobox/portal-manager.js +218 -0
  57. package/dist/src/nile-combobox/portal-manager.js.map +1 -0
  58. package/dist/src/nile-combobox/renderer.d.ts +24 -0
  59. package/dist/src/nile-combobox/renderer.js +279 -0
  60. package/dist/src/nile-combobox/renderer.js.map +1 -0
  61. package/dist/src/nile-combobox/search-manager.d.ts +15 -0
  62. package/dist/src/nile-combobox/search-manager.js +41 -0
  63. package/dist/src/nile-combobox/search-manager.js.map +1 -0
  64. package/dist/src/nile-combobox/selection-manager.d.ts +12 -0
  65. package/dist/src/nile-combobox/selection-manager.js +44 -0
  66. package/dist/src/nile-combobox/selection-manager.js.map +1 -0
  67. package/dist/src/nile-combobox/types.d.ts +53 -0
  68. package/dist/src/nile-combobox/types.js +8 -0
  69. package/dist/src/nile-combobox/types.js.map +1 -0
  70. package/dist/src/version.js +1 -1
  71. package/dist/src/version.js.map +1 -1
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +3 -1
  74. package/src/index.ts +1 -0
  75. package/src/nile-breadcrumb-item/nile-breadcrumb-item.ts +4 -2
  76. package/src/nile-combobox/group-utils.ts +157 -0
  77. package/src/nile-combobox/index.ts +1 -0
  78. package/src/nile-combobox/nile-combobox.css.ts +726 -0
  79. package/src/nile-combobox/nile-combobox.test.ts +704 -0
  80. package/src/nile-combobox/nile-combobox.ts +1816 -0
  81. package/src/nile-combobox/portal-manager.ts +263 -0
  82. package/src/nile-combobox/renderer.ts +466 -0
  83. package/src/nile-combobox/search-manager.ts +53 -0
  84. package/src/nile-combobox/selection-manager.ts +57 -0
  85. package/src/nile-combobox/types.ts +63 -0
  86. package/vscode-html-custom-data.json +311 -4
  87. package/web-dev-server.config.mjs +9 -0
  88. package/web-test-runner.config.mjs +11 -0
@@ -0,0 +1,466 @@
1
+ /**
2
+ * Copyright Aquera Inc 2025
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import { html, type TemplateResult } from 'lit';
9
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
10
+ import { classMap } from 'lit/directives/class-map.js';
11
+ import { repeat } from 'lit/directives/repeat.js';
12
+ import type { VirtualItem } from '@tanstack/virtual-core';
13
+ import type { ComboboxRow, ComboboxHeaderRow } from './types';
14
+
15
+ export class ComboboxRenderer {
16
+
17
+ static renderGroupHeader(row: ComboboxHeaderRow): TemplateResult {
18
+ return html`
19
+ <div
20
+ part="group-header"
21
+ class="combobox__group-header"
22
+ role="presentation"
23
+ data-group-id=${row.id}
24
+ style=${`--group-depth:${row.depth}`}
25
+ >
26
+ ${row.prefix
27
+ ? html`<nile-icon
28
+ class="combobox__group-prefix"
29
+ name=${row.prefix}
30
+ size="14"
31
+ method="fill"
32
+ ></nile-icon>`
33
+ : ''}
34
+ <span class="combobox__group-label">${row.label}</span>
35
+ </div>
36
+ `;
37
+ }
38
+
39
+ static renderRowsPlain(
40
+ rows: ComboboxRow[],
41
+ value: string | string[],
42
+ multiple: boolean,
43
+ getDisplayText: (item: any) => string,
44
+ getItemValue: (item: any) => string,
45
+ showNoResults: boolean,
46
+ noResultsMessage: string,
47
+ isLoading: boolean,
48
+ onScroll: (e: Event) => void,
49
+ allowHtmlLabel: boolean,
50
+ getItemDescription?: (item: any) => string,
51
+ getItemPrefix?: (item: any) => string,
52
+ getItemSuffix?: (item: any) => string,
53
+ enableDescription?: boolean,
54
+ noResultsSubtitle?: string,
55
+ ): TemplateResult {
56
+ if (showNoResults && !isLoading && rows.length === 0) {
57
+ return ComboboxRenderer.renderNoResults(noResultsMessage, noResultsSubtitle);
58
+ }
59
+
60
+ return html`
61
+ <div
62
+ part="select-options"
63
+ class="combobox__options ${isLoading ? 'loading' : ''}"
64
+ >
65
+ <div class="combobox__options-plain" @scroll=${onScroll}>
66
+ ${rows.map((row) =>
67
+ row.kind === 'header'
68
+ ? ComboboxRenderer.renderGroupHeader(row)
69
+ : ComboboxRenderer.renderItem(
70
+ row.item, value, multiple, getDisplayText, getItemValue,
71
+ allowHtmlLabel, getItemDescription, getItemPrefix,
72
+ getItemSuffix, enableDescription,
73
+ ),
74
+ )}
75
+ </div>
76
+ </div>
77
+ `;
78
+ }
79
+
80
+ static renderRowsVirtualized(
81
+ virtualItems: VirtualItem[],
82
+ totalSize: number,
83
+ rows: ComboboxRow[],
84
+ value: string | string[],
85
+ multiple: boolean,
86
+ getDisplayText: (item: any) => string,
87
+ getItemValue: (item: any) => string,
88
+ isLoading: boolean,
89
+ allowHtmlLabel: boolean,
90
+ getItemDescription?: (item: any) => string,
91
+ getItemPrefix?: (item: any) => string,
92
+ getItemSuffix?: (item: any) => string,
93
+ enableDescription?: boolean,
94
+ ): TemplateResult {
95
+ return html`
96
+ <div
97
+ part="select-options"
98
+ class="combobox__options ${isLoading ? 'loading' : ''}"
99
+ >
100
+ <div style="position:relative;height:${totalSize}px;width:100%;">
101
+ ${repeat(
102
+ virtualItems,
103
+ (vItem) => vItem.key,
104
+ (vItem) => {
105
+ const row = rows[vItem.index];
106
+ if (!row) return html``;
107
+ const posStyle =
108
+ `position:absolute;top:0;left:0;right:0;` +
109
+ `transform:translateY(${vItem.start}px);` +
110
+ `height:${vItem.size}px;`;
111
+ if (row.kind === 'header') {
112
+ return html`
113
+ <div style=${posStyle} class="combobox__group-header-slot">
114
+ ${ComboboxRenderer.renderGroupHeader(row)}
115
+ </div>
116
+ `;
117
+ }
118
+ return html`
119
+ <div style=${posStyle}>
120
+ ${ComboboxRenderer.renderItem(
121
+ row.item, value, multiple, getDisplayText, getItemValue,
122
+ allowHtmlLabel, getItemDescription, getItemPrefix,
123
+ getItemSuffix, enableDescription,
124
+ )}
125
+ </div>
126
+ `;
127
+ },
128
+ )}
129
+ </div>
130
+ </div>
131
+ `;
132
+ }
133
+
134
+ static renderVirtualizedOptions(
135
+ virtualItems: VirtualItem[],
136
+ totalSize: number,
137
+ data: any[],
138
+ value: string | string[],
139
+ multiple: boolean,
140
+ getDisplayText: (item: any) => string,
141
+ getItemValue: (item: any) => string,
142
+ isLoading: boolean,
143
+ allowHtmlLabel: boolean,
144
+ measureElement: (el: Element | null) => void,
145
+ getItemDescription?: (item: any) => string,
146
+ getItemPrefix?: (item: any) => string,
147
+ getItemSuffix?: (item: any) => string,
148
+ enableDescription?: boolean,
149
+ ): TemplateResult {
150
+ const offsetTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
151
+
152
+ return html`
153
+ <div
154
+ part="select-options"
155
+ class="combobox__options ${isLoading ? 'loading' : ''}"
156
+ >
157
+ <div style="position:relative;height:${totalSize}px;width:100%;">
158
+ <div style="position:absolute;top:0;left:0;width:100%;transform:translateY(${offsetTop}px);">
159
+ ${repeat(
160
+ virtualItems,
161
+ (vItem) => vItem.key,
162
+ (vItem) => {
163
+ const item = data[vItem.index];
164
+ return ComboboxRenderer.renderMeasuredItem(
165
+ item, vItem.index, value, multiple, getDisplayText, getItemValue,
166
+ allowHtmlLabel, getItemDescription, getItemPrefix,
167
+ getItemSuffix, enableDescription,
168
+ );
169
+ },
170
+ )}
171
+ </div>
172
+ </div>
173
+ </div>
174
+ `;
175
+ }
176
+
177
+ static renderPlainOptions(
178
+ data: any[],
179
+ value: string | string[],
180
+ multiple: boolean,
181
+ getDisplayText: (item: any) => string,
182
+ getItemValue: (item: any) => string,
183
+ showNoResults: boolean,
184
+ noResultsMessage: string,
185
+ isLoading: boolean,
186
+ onScroll: (e: Event) => void,
187
+ allowHtmlLabel: boolean,
188
+ getItemDescription?: (item: any) => string,
189
+ getItemPrefix?: (item: any) => string,
190
+ getItemSuffix?: (item: any) => string,
191
+ enableDescription?: boolean,
192
+ noResultsSubtitle?: string,
193
+ ): TemplateResult {
194
+ if (showNoResults && !isLoading && data.length === 0) {
195
+ return ComboboxRenderer.renderNoResults(noResultsMessage, noResultsSubtitle);
196
+ }
197
+
198
+ return html`
199
+ <div
200
+ part="select-options"
201
+ class="combobox__options ${isLoading ? 'loading' : ''}"
202
+ >
203
+ <div class="combobox__options-plain" @scroll=${onScroll}>
204
+ ${data.map((item: any) =>
205
+ ComboboxRenderer.renderItem(
206
+ item, value, multiple, getDisplayText, getItemValue,
207
+ allowHtmlLabel, getItemDescription, getItemPrefix,
208
+ getItemSuffix, enableDescription,
209
+ ),
210
+ )}
211
+ </div>
212
+ </div>
213
+ `;
214
+ }
215
+
216
+ static renderNoResults(noResultsMessage: string, noResultsSubtitle?: string): TemplateResult {
217
+ return html`
218
+ <div part="select-options" class="combobox__options">
219
+ <div part="no-results" class="combobox__no-results">
220
+ <div part="no-results-title" class="combobox__no-results-title">
221
+ ${noResultsMessage || 'No result found'}
222
+ </div>
223
+ ${noResultsSubtitle
224
+ ? html`<div part="no-results-subtitle" class="combobox__no-results-subtitle">${noResultsSubtitle}</div>`
225
+ : ''}
226
+ </div>
227
+ </div>
228
+ `;
229
+ }
230
+
231
+ static renderNoData(noDataMessage: string): TemplateResult {
232
+ return html`
233
+ <div part="select-options" class="combobox__options">
234
+ <div part="no-data" class="combobox__no-results">
235
+ <div part="no-data-title" class="combobox__no-results-title">
236
+ ${noDataMessage || 'No data available'}
237
+ </div>
238
+ </div>
239
+ </div>
240
+ `;
241
+ }
242
+
243
+ private static renderMeasuredItem(
244
+ item: any,
245
+ index: number,
246
+ value: string | string[],
247
+ multiple: boolean,
248
+ getDisplayText: (item: any) => string,
249
+ getItemValue: (item: any) => string,
250
+ allowHtmlLabel: boolean,
251
+ getItemDescription?: (item: any) => string,
252
+ getItemPrefix?: (item: any) => string,
253
+ getItemSuffix?: (item: any) => string,
254
+ enableDescription?: boolean,
255
+ ): TemplateResult {
256
+ if (!item) return html``;
257
+
258
+ const optionValue = getItemValue(item);
259
+ const displayText = getDisplayText(item);
260
+ const isDisabled = item?.disabled || false;
261
+ const className = item?.className;
262
+ const description = getItemDescription ? getItemDescription(item) : (item?.description ?? '');
263
+ const prefix = getItemPrefix ? getItemPrefix(item) : (item?.prefix ?? '');
264
+ const suffix = getItemSuffix ? getItemSuffix(item) : (item?.suffix ?? '');
265
+
266
+ let isSelected = false;
267
+ if (multiple) {
268
+ isSelected = Array.isArray(value) && value.some(v => String(v) === String(optionValue));
269
+ } else {
270
+ isSelected = String(Array.isArray(value) ? value[0] : value) === String(optionValue);
271
+ }
272
+
273
+ return html`
274
+ <nile-option
275
+ data-index=${index}
276
+ value=${optionValue}
277
+ .selected=${isSelected}
278
+ .disabled=${isDisabled}
279
+ .showCheckbox=${multiple}
280
+ class=${classMap({
281
+ option: enableDescription ?? false,
282
+ [className ?? '']: !!className,
283
+ })}
284
+ .description=${description}
285
+ .isDescriptionEnabled=${enableDescription}
286
+ >
287
+ ${unsafeHTML(prefix)}
288
+ ${allowHtmlLabel ? unsafeHTML(displayText) : displayText}
289
+ ${unsafeHTML(suffix)}
290
+ </nile-option>
291
+ `;
292
+ }
293
+
294
+ static renderItem(
295
+ item: any,
296
+ value: string | string[],
297
+ multiple: boolean,
298
+ getDisplayText: (item: any) => string,
299
+ getItemValue: (item: any) => string,
300
+ allowHtmlLabel: boolean,
301
+ getItemDescription?: (item: any) => string,
302
+ getItemPrefix?: (item: any) => string,
303
+ getItemSuffix?: (item: any) => string,
304
+ enableDescription?: boolean,
305
+ ): TemplateResult {
306
+ if (!item) return html``;
307
+
308
+ const optionValue = getItemValue(item);
309
+ const displayText = getDisplayText(item);
310
+ const isDisabled = item?.disabled || false;
311
+ const className = item?.className;
312
+ const description = getItemDescription ? getItemDescription(item) : (item?.description ?? '');
313
+ const prefix = getItemPrefix ? getItemPrefix(item) : (item?.prefix ?? '');
314
+ const suffix = getItemSuffix ? getItemSuffix(item) : (item?.suffix ?? '');
315
+
316
+ let isSelected = false;
317
+ if (multiple) {
318
+ isSelected = Array.isArray(value) && value.some(v => String(v) === String(optionValue));
319
+ } else {
320
+ isSelected = String(Array.isArray(value) ? value[0] : value) === String(optionValue);
321
+ }
322
+
323
+ return html`
324
+ <nile-option
325
+ value=${optionValue}
326
+ .selected=${isSelected}
327
+ .disabled=${isDisabled}
328
+ .showCheckbox=${multiple}
329
+ class=${classMap({
330
+ option: enableDescription ?? false,
331
+ [className ?? '']: !!className,
332
+ })}
333
+ .description=${description}
334
+ .isDescriptionEnabled=${enableDescription}
335
+ >
336
+ ${unsafeHTML(prefix)}
337
+ ${allowHtmlLabel ? unsafeHTML(displayText) : displayText}
338
+ ${unsafeHTML(suffix)}
339
+ </nile-option>
340
+ `;
341
+ }
342
+
343
+ static renderVirtualizedGrid(
344
+ virtualItems: VirtualItem[],
345
+ totalSize: number,
346
+ data: any[],
347
+ value: string | string[],
348
+ multiple: boolean,
349
+ gridColumns: number,
350
+ getDisplayText: (item: any) => string,
351
+ getItemValue: (item: any) => string,
352
+ isLoading: boolean,
353
+ allowHtmlLabel: boolean,
354
+ getItemDescription?: (item: any) => string,
355
+ getItemPrefix?: (item: any) => string,
356
+ getItemSuffix?: (item: any) => string,
357
+ gridColumnWidth?: number,
358
+ ): TemplateResult {
359
+ const offsetTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
360
+ const colTemplate = gridColumnWidth
361
+ ? `repeat(${gridColumns}, ${gridColumnWidth}px)`
362
+ : `repeat(${gridColumns}, 1fr)`;
363
+
364
+ return html`
365
+ <div
366
+ part="select-options"
367
+ class="combobox__options ${isLoading ? 'loading' : ''}"
368
+ >
369
+ <div style="position:relative;height:${totalSize}px;width:${gridColumnWidth ? 'max-content' : '100%'};min-width:100%;">
370
+ <div style="position:absolute;top:0;left:0;width:${gridColumnWidth ? 'max-content' : '100%'};min-width:100%;transform:translateY(${offsetTop}px);">
371
+ ${repeat(
372
+ virtualItems,
373
+ (vItem) => vItem.key,
374
+ (vItem) => {
375
+ const rowStart = vItem.index * gridColumns;
376
+ const rowItems = data.slice(rowStart, rowStart + gridColumns);
377
+ return html`
378
+ <div class="combobox__grid-row" style="display:grid;grid-template-columns:${colTemplate};gap:4px;">
379
+ ${rowItems.map((item: any) =>
380
+ ComboboxRenderer.renderItem(
381
+ item, value, multiple, getDisplayText, getItemValue,
382
+ allowHtmlLabel, getItemDescription, getItemPrefix,
383
+ getItemSuffix,
384
+ ),
385
+ )}
386
+ </div>
387
+ `;
388
+ },
389
+ )}
390
+ </div>
391
+ </div>
392
+ </div>
393
+ `;
394
+ }
395
+
396
+ static renderHorizontalGrid(
397
+ virtualItems: VirtualItem[],
398
+ totalSize: number,
399
+ data: any[],
400
+ value: string | string[],
401
+ multiple: boolean,
402
+ gridRows: number,
403
+ gridColumnWidth: number,
404
+ getDisplayText: (item: any) => string,
405
+ getItemValue: (item: any) => string,
406
+ isLoading: boolean,
407
+ allowHtmlLabel: boolean,
408
+ getItemDescription?: (item: any) => string,
409
+ getItemPrefix?: (item: any) => string,
410
+ getItemSuffix?: (item: any) => string,
411
+ ): TemplateResult {
412
+ const offsetLeft = virtualItems.length > 0 ? virtualItems[0].start : 0;
413
+ const rowHeight = 38;
414
+
415
+ return html`
416
+ <div
417
+ part="select-options"
418
+ class="combobox__options combobox__options--horizontal ${isLoading ? 'loading' : ''}"
419
+ >
420
+ <div style="position:relative;width:${totalSize}px;height:${rowHeight * gridRows}px;">
421
+ <div style="position:absolute;top:0;left:0;height:100%;display:flex;transform:translateX(${offsetLeft}px);">
422
+ ${repeat(
423
+ virtualItems,
424
+ (vCol) => vCol.key,
425
+ (vCol) => {
426
+ const colStart = vCol.index * gridRows;
427
+ const colItems = data.slice(colStart, colStart + gridRows);
428
+ return html`
429
+ <div class="combobox__grid-col" style="width:${gridColumnWidth}px;flex-shrink:0;display:flex;flex-direction:column;">
430
+ ${colItems.map((item: any) =>
431
+ ComboboxRenderer.renderItem(
432
+ item, value, multiple, getDisplayText, getItemValue,
433
+ allowHtmlLabel, getItemDescription, getItemPrefix,
434
+ getItemSuffix,
435
+ ),
436
+ )}
437
+ </div>
438
+ `;
439
+ },
440
+ )}
441
+ </div>
442
+ </div>
443
+ </div>
444
+ `;
445
+ }
446
+
447
+ static renderAddCustomOption(
448
+ searchValue: string,
449
+ multiple: boolean,
450
+ ): TemplateResult {
451
+ return html`
452
+ <nile-option
453
+ value=${searchValue}
454
+ class="combobox__add-option"
455
+ .showCheckbox=${multiple}
456
+ >
457
+ + Add "${searchValue}"
458
+ </nile-option>
459
+ `;
460
+ }
461
+
462
+ static shouldUseVirtualizer(data: any[], gridColumns = 1): boolean {
463
+ if (gridColumns > 1) return true;
464
+ return data.length >= 5;
465
+ }
466
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright Aquera Inc 2025
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ export class ComboboxSearchManager {
9
+ private debounceTimer: ReturnType<typeof setTimeout> | null = null;
10
+
11
+ filter(
12
+ searchValue: string,
13
+ originalItems: any[],
14
+ getSearchText: (item: any) => string,
15
+ ): { filteredItems: any[]; showNoResults: boolean } {
16
+ if (!originalItems || originalItems.length === 0) {
17
+ return { filteredItems: [], showNoResults: true };
18
+ }
19
+
20
+ if (!searchValue || searchValue.trim() === '') {
21
+ return { filteredItems: [...originalItems], showNoResults: false };
22
+ }
23
+
24
+ const needle = searchValue.toLowerCase();
25
+ const filteredItems = originalItems.filter((item: any) => {
26
+ const text = getSearchText(item);
27
+ return text.toLowerCase().includes(needle);
28
+ });
29
+
30
+ return { filteredItems, showNoResults: filteredItems.length === 0 };
31
+ }
32
+
33
+ debounceSearch(
34
+ callback: (query: string) => void,
35
+ query: string,
36
+ debounceMs: number,
37
+ ): void {
38
+ if (this.debounceTimer) {
39
+ clearTimeout(this.debounceTimer);
40
+ }
41
+ this.debounceTimer = setTimeout(() => {
42
+ callback(query);
43
+ this.debounceTimer = null;
44
+ }, debounceMs);
45
+ }
46
+
47
+ cancelDebounce(): void {
48
+ if (this.debounceTimer) {
49
+ clearTimeout(this.debounceTimer);
50
+ this.debounceTimer = null;
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Copyright Aquera Inc 2025
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import type { ComboboxOption } from './types.js';
9
+
10
+ export class ComboboxSelectionManager {
11
+ static createOptionsFromValues(
12
+ value: string | string[],
13
+ data: any[],
14
+ getDisplayText: (item: any) => string,
15
+ getItemValue?: (item: any) => string
16
+ ): ComboboxOption[] {
17
+ if (!value || (Array.isArray(value) && value.length === 0)) {
18
+ return [];
19
+ }
20
+
21
+ const values = Array.isArray(value) ? value : [value];
22
+ const valueFn = getItemValue || ((item: any) => item?.value ?? item);
23
+
24
+ return values.map(valueItem => {
25
+ const item = data.find((d: any) => {
26
+ const iv = valueFn(d);
27
+ return String(iv) === String(valueItem);
28
+ });
29
+
30
+ const displayText = item ? getDisplayText(item) : valueItem;
31
+
32
+ return {
33
+ value: valueItem,
34
+ selected: true,
35
+ getTextLabel: () => {
36
+ if (typeof displayText === 'string' && /<[^>]+>/.test(displayText)) {
37
+ const div = document.createElement('div');
38
+ div.innerHTML = displayText;
39
+ return div.textContent || div.innerText || '';
40
+ }
41
+ return displayText;
42
+ },
43
+ };
44
+ });
45
+ }
46
+
47
+ static toggleMultiValue(currentValues: string[], optionValue: string): string[] {
48
+ if (currentValues.includes(optionValue)) {
49
+ return currentValues.filter(v => v !== optionValue);
50
+ }
51
+ return [...currentValues, optionValue];
52
+ }
53
+
54
+ static removeValue(currentValues: string[], removeValue: string): string[] {
55
+ return currentValues.filter(v => v !== removeValue);
56
+ }
57
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Copyright Aquera Inc 2025
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ export type NileRemoveEvent = CustomEvent<Record<PropertyKey, never>>;
9
+
10
+ export interface ComboboxOption {
11
+ value: string;
12
+ selected: boolean;
13
+ getTextLabel: () => string;
14
+ }
15
+
16
+ export interface ComboboxRenderItemConfig {
17
+ getDisplayText: (item: any) => string;
18
+ getValue?: (item: any) => string;
19
+ getSearchText?: (item: any) => string;
20
+ getDescription?: (item: any) => string;
21
+ getPrefix?: (item: any) => string;
22
+ getSuffix?: (item: any) => string;
23
+ }
24
+
25
+ export type ComboboxTagLayout = 'single-line' | 'wrap' | 'fixed-height';
26
+ export type ComboboxSize = 'small' | 'medium' | 'large';
27
+ export type ComboboxPlacement = 'top' | 'bottom';
28
+
29
+ export interface ComboboxGroupItem {
30
+ type: 'group';
31
+ id: string;
32
+ label: string;
33
+ prefix?: string;
34
+ options: ComboboxDataItem[];
35
+ collapsible?: boolean;
36
+ }
37
+
38
+ export interface ComboboxOptionItem {
39
+ type?: 'option';
40
+ value: string;
41
+ label?: string;
42
+ [key: string]: any;
43
+ }
44
+
45
+ export type ComboboxDataItem = ComboboxGroupItem | ComboboxOptionItem;
46
+
47
+ export interface ComboboxHeaderRow {
48
+ kind: 'header';
49
+ id: string;
50
+ label: string;
51
+ prefix?: string;
52
+ depth: number;
53
+ optionCount: number;
54
+ }
55
+
56
+ export interface ComboboxOptionRow {
57
+ kind: 'option';
58
+ item: ComboboxOptionItem;
59
+ depth: number;
60
+ parentIds: string[];
61
+ }
62
+
63
+ export type ComboboxRow = ComboboxHeaderRow | ComboboxOptionRow;