@ni/nimble-components 21.4.0 → 21.5.0

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 (33) hide show
  1. package/dist/all-components-bundle.js +864 -73
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +3598 -3450
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/anchored-region/styles.js +6 -1
  6. package/dist/esm/anchored-region/styles.js.map +1 -1
  7. package/dist/esm/label-provider/core/index.d.ts +6 -0
  8. package/dist/esm/label-provider/core/index.js +10 -2
  9. package/dist/esm/label-provider/core/index.js.map +1 -1
  10. package/dist/esm/label-provider/core/label-token-defaults.js +3 -1
  11. package/dist/esm/label-provider/core/label-token-defaults.js.map +1 -1
  12. package/dist/esm/label-provider/core/label-tokens.d.ts +2 -0
  13. package/dist/esm/label-provider/core/label-tokens.js +8 -0
  14. package/dist/esm/label-provider/core/label-tokens.js.map +1 -1
  15. package/dist/esm/patterns/dropdown/styles.js +0 -4
  16. package/dist/esm/patterns/dropdown/styles.js.map +1 -1
  17. package/dist/esm/select/index.d.ts +251 -7
  18. package/dist/esm/select/index.js +653 -19
  19. package/dist/esm/select/index.js.map +1 -1
  20. package/dist/esm/select/models/select-form-associated.d.ts +16 -0
  21. package/dist/esm/select/models/select-form-associated.js +19 -0
  22. package/dist/esm/select/models/select-form-associated.js.map +1 -0
  23. package/dist/esm/select/styles.js +89 -1
  24. package/dist/esm/select/styles.js.map +1 -1
  25. package/dist/esm/select/template.js +71 -37
  26. package/dist/esm/select/template.js.map +1 -1
  27. package/dist/esm/select/testing/select.pageobject.d.ts +32 -0
  28. package/dist/esm/select/testing/select.pageobject.js +128 -0
  29. package/dist/esm/select/testing/select.pageobject.js.map +1 -0
  30. package/dist/esm/select/types.d.ts +9 -0
  31. package/dist/esm/select/types.js +8 -0
  32. package/dist/esm/select/types.js.map +1 -1
  33. package/package.json +4 -1
@@ -1,75 +1,708 @@
1
1
  import { __decorate } from "tslib";
2
- import { attr, html, observable } from '@microsoft/fast-element';
3
- import { DesignSystem, Select as FoundationSelect } from '@microsoft/fast-foundation';
2
+ // Based on: https://github.com/microsoft/fast/blob/%40microsoft/fast-foundation_v2.49.5/packages/web-components/fast-foundation/src/select/select.ts
3
+ import { attr, html, observable, Observable, volatile } from '@microsoft/fast-element';
4
+ import { DesignSystem, Select as FoundationSelect, SelectPosition, applyMixins, StartEnd, DelegatesARIASelect, Listbox } from '@microsoft/fast-foundation';
5
+ import { keyArrowDown, keyArrowUp, keyEnd, keyEnter, keyEscape, keyHome, keySpace, keyTab, uniqueId } from '@microsoft/fast-web-utilities';
4
6
  import { arrowExpanderDown16X16 } from '@ni/nimble-tokens/dist/icons/js';
5
7
  import { styles } from './styles';
6
8
  import { DropdownAppearance } from '../patterns/dropdown/types';
7
9
  import { errorTextTemplate } from '../patterns/error/template';
8
10
  import { iconExclamationMarkTag } from '../icons/exclamation-mark';
9
11
  import { template } from './template';
12
+ import { FilterMode } from './types';
13
+ import { diacriticInsensitiveStringNormalizer } from '../utilities/models/string-normalizers';
14
+ import { FormAssociatedSelect } from './models/select-form-associated';
10
15
  /**
11
- * A nimble-styled HTML select
16
+ * A nimble-styled HTML select.
12
17
  */
13
- export class Select extends FoundationSelect {
18
+ export class Select extends FormAssociatedSelect {
14
19
  constructor() {
15
20
  super(...arguments);
16
21
  this.appearance = DropdownAppearance.underline;
17
22
  this.errorVisible = false;
23
+ this.filterMode = FilterMode.none;
24
+ /**
25
+ * @internal
26
+ */
27
+ this.open = false;
28
+ /**
29
+ * The unique id for the internal listbox element.
30
+ *
31
+ * @internal
32
+ */
33
+ this.listboxId = uniqueId('listbox-');
18
34
  /** @internal */
19
35
  this.hasOverflow = false;
36
+ /**
37
+ * @internal
38
+ */
39
+ this.filteredOptions = [];
40
+ /**
41
+ * @internal
42
+ */
43
+ this.filter = '';
44
+ /**
45
+ * @internal
46
+ */
47
+ this.committedSelectedOption = undefined;
48
+ /**
49
+ * The max height for the listbox when opened.
50
+ *
51
+ * @internal
52
+ */
53
+ this.maxHeight = 0;
54
+ this._value = '';
55
+ this.forcedPosition = false;
56
+ }
57
+ /**
58
+ * The component is collapsible when in single-selection mode with no size attribute.
59
+ *
60
+ * @internal
61
+ */
62
+ get collapsible() {
63
+ return !(this.multiple || typeof this.size === 'number');
64
+ }
65
+ /**
66
+ * @internal
67
+ */
68
+ connectedCallback() {
69
+ super.connectedCallback();
70
+ this.forcedPosition = !!this.positionAttribute;
71
+ this.initializeOpenState();
72
+ }
73
+ /**
74
+ * The list of options. This mirrors FAST's override implementation for this
75
+ * member for the Combobox to support a filtered list in the dropdown.
76
+ *
77
+ * @public
78
+ * @remarks
79
+ * Overrides `Listbox.options`.
80
+ */
81
+ get options() {
82
+ Observable.track(this, 'options');
83
+ return this.filteredOptions?.length
84
+ ? this.filteredOptions
85
+ : this._options;
86
+ }
87
+ set options(value) {
88
+ this._options = value;
89
+ Observable.notify(this, 'options');
90
+ }
91
+ get value() {
92
+ Observable.track(this, 'value');
93
+ return this._value;
94
+ }
95
+ set value(next) {
96
+ const prev = this._value;
97
+ let newValue = next;
98
+ // use 'options' here instead of '_options' as 'selectedIndex' may be relative
99
+ // to filtered set
100
+ if (this.options?.length) {
101
+ const newValueIndex = this.options.findIndex(el => el.value === newValue);
102
+ const prevSelectedValue = this.options[this.selectedIndex]?.value ?? null;
103
+ const nextSelectedValue = this.options[newValueIndex]?.value ?? null;
104
+ if (newValueIndex === -1
105
+ || prevSelectedValue !== nextSelectedValue) {
106
+ newValue = '';
107
+ this.selectedIndex = newValueIndex;
108
+ }
109
+ newValue = this.firstSelectedOption?.value ?? newValue;
110
+ }
111
+ if (prev !== newValue && !(this.open && this.selectedIndex < 0)) {
112
+ this._value = newValue;
113
+ super.valueChanged(prev, newValue);
114
+ if (!this.open) {
115
+ this.committedSelectedOption = this._options.find(o => o.value === newValue);
116
+ }
117
+ Observable.notify(this, 'value');
118
+ if (this.collapsible) {
119
+ Observable.notify(this, 'displayValue');
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * @internal
125
+ */
126
+ get displayValue() {
127
+ Observable.track(this, 'displayValue');
128
+ return this.committedSelectedOption?.text ?? '';
129
+ }
130
+ /**
131
+ * @internal
132
+ */
133
+ anchoredRegionChanged(_prev, _next) {
134
+ if (this.anchoredRegion && this.control) {
135
+ this.anchoredRegion.anchorElement = this.control;
136
+ }
137
+ }
138
+ /**
139
+ * @internal
140
+ */
141
+ controlChanged(_prev, _next) {
142
+ if (this.anchoredRegion && this.control) {
143
+ this.anchoredRegion.anchorElement = this.control;
144
+ }
145
+ }
146
+ /**
147
+ * @internal
148
+ */
149
+ slottedOptionsChanged(prev, next) {
150
+ const value = this.value;
151
+ this._options.forEach(o => {
152
+ const notifier = Observable.getNotifier(o);
153
+ notifier.unsubscribe(this, 'value');
154
+ });
155
+ super.slottedOptionsChanged(prev, next);
156
+ this._options.forEach(o => {
157
+ const notifier = Observable.getNotifier(o);
158
+ notifier.subscribe(this, 'value');
159
+ });
160
+ this.setProxyOptions();
161
+ this.updateValue();
162
+ // We need to force an update to the filteredOptions observable
163
+ // (by calling 'filterOptions()) so that the template correctly updates.
164
+ this.filterOptions();
165
+ if (value) {
166
+ this.value = value;
167
+ }
168
+ this.committedSelectedOption = this.options[this.selectedIndex];
169
+ }
170
+ /**
171
+ * @internal
172
+ */
173
+ clickHandler(e) {
174
+ // do nothing if the select is disabled
175
+ if (this.disabled) {
176
+ return;
177
+ }
178
+ if (this.open) {
179
+ const captured = e.target.closest('option,[role=option]');
180
+ if (!captured?.disabled) {
181
+ this.updateSelectedIndexFromFilteredSet();
182
+ }
183
+ if (captured?.disabled) {
184
+ return;
185
+ }
186
+ }
187
+ super.clickHandler(e);
188
+ this.open = this.collapsible && !this.open;
189
+ if (!this.open && this.indexWhenOpened !== this.selectedIndex) {
190
+ this.updateValue(true);
191
+ }
192
+ }
193
+ /**
194
+ * Updates the value when an option's value changes.
195
+ *
196
+ * @param source - the source object
197
+ * @param propertyName - the property to evaluate
198
+ *
199
+ * @internal
200
+ * @override
201
+ */
202
+ handleChange(source, propertyName) {
203
+ super.handleChange(source, propertyName);
204
+ if (propertyName === 'value') {
205
+ this.updateValue();
206
+ }
207
+ }
208
+ /**
209
+ * Prevents focus when size is set and a scrollbar is clicked.
210
+ *
211
+ * @param e - the mouse event object
212
+ *
213
+ * @override
214
+ * @internal
215
+ */
216
+ mousedownHandler(e) {
217
+ if (e.offsetX >= 0 && e.offsetX <= this.listbox?.scrollWidth) {
218
+ return super.mousedownHandler(e);
219
+ }
220
+ return this.collapsible;
221
+ }
222
+ /**
223
+ * @internal
224
+ */
225
+ regionLoadedHandler() {
226
+ this.focusAndScrollOptionIntoView();
227
+ }
228
+ /**
229
+ * Sets the multiple property on the proxy element.
230
+ *
231
+ * @param prev - the previous multiple value
232
+ * @param next - the current multiple value
233
+ */
234
+ multipleChanged(prev, next) {
235
+ super.multipleChanged(prev, next);
236
+ if (this.proxy) {
237
+ this.proxy.multiple = next;
238
+ }
239
+ }
240
+ /**
241
+ * @internal
242
+ */
243
+ inputClickHandler(e) {
244
+ e.stopPropagation(); // clicking in filter input shouldn't close dropdown
245
+ }
246
+ /**
247
+ * @internal
248
+ */
249
+ changeValueHandler() {
250
+ this.committedSelectedOption = this.options.find(option => option.selected);
251
+ }
252
+ /**
253
+ * @internal
254
+ */
255
+ updateDisplayValue() {
256
+ if (this.collapsible) {
257
+ Observable.notify(this, 'displayValue');
258
+ }
259
+ }
260
+ /**
261
+ * Handle content changes on the control input.
262
+ *
263
+ * @param e - the input event
264
+ * @internal
265
+ */
266
+ inputHandler(e) {
267
+ this.filter = this.filterInput?.value ?? '';
268
+ if (!this.committedSelectedOption) {
269
+ this.committedSelectedOption = this._options.find(option => option.selected);
270
+ }
271
+ this.clearSelection();
272
+ this.filterOptions();
273
+ if (this.filteredOptions.length > 0
274
+ && this.committedSelectedOption
275
+ && !this.filteredOptions.includes(this.committedSelectedOption)) {
276
+ const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
277
+ if (enabledOptions.length > 0) {
278
+ enabledOptions[0].selected = true;
279
+ }
280
+ else {
281
+ // only filtered option is disabled
282
+ this.selectedOptions = [];
283
+ this.selectedIndex = -1;
284
+ }
285
+ }
286
+ else if (this.committedSelectedOption) {
287
+ this.committedSelectedOption.selected = true;
288
+ }
289
+ if (e.inputType.includes('deleteContent') || !this.filter.length) {
290
+ return true;
291
+ }
292
+ e.stopPropagation();
293
+ return true;
294
+ }
295
+ /**
296
+ * @internal
297
+ */
298
+ focusoutHandler(e) {
299
+ this.updateSelectedIndexFromFilteredSet();
300
+ super.focusoutHandler(e);
301
+ if (!this.open) {
302
+ return true;
303
+ }
304
+ const focusTarget = e.relatedTarget;
305
+ if (this.isSameNode(focusTarget)) {
306
+ this.focus();
307
+ return true;
308
+ }
309
+ if (!this.options?.includes(focusTarget)) {
310
+ this.open = false;
311
+ if (this.indexWhenOpened !== this.selectedIndex) {
312
+ this.updateValue(true);
313
+ }
314
+ }
315
+ return true;
316
+ }
317
+ /**
318
+ * @internal
319
+ */
320
+ keydownHandler(e) {
321
+ super.keydownHandler(e);
322
+ const key = e.key;
323
+ if (e.ctrlKey || e.shiftKey) {
324
+ return true;
325
+ }
326
+ switch (key) {
327
+ case keySpace: {
328
+ // when dropdown is open allow user to enter a space for filter text
329
+ if (this.open && this.filterMode !== FilterMode.none) {
330
+ break;
331
+ }
332
+ e.preventDefault();
333
+ if (this.collapsible && this.typeAheadExpired) {
334
+ this.open = !this.open;
335
+ }
336
+ if (!this.open) {
337
+ this.focus();
338
+ }
339
+ break;
340
+ }
341
+ case keyHome:
342
+ case keyEnd: {
343
+ e.preventDefault();
344
+ break;
345
+ }
346
+ case keyEnter: {
347
+ e.preventDefault();
348
+ if (this.filteredOptions.length === 0
349
+ || this.filteredOptions.every(o => o.disabled)) {
350
+ return false;
351
+ }
352
+ this.updateSelectedIndexFromFilteredSet();
353
+ this.open = !this.open;
354
+ if (!this.open) {
355
+ this.focus();
356
+ }
357
+ break;
358
+ }
359
+ case keyEscape: {
360
+ // clear filter as update to "selectedIndex" will result in processing
361
+ // "options" and not "_options"
362
+ this.filter = '';
363
+ if (this.committedSelectedOption) {
364
+ this.clearSelection();
365
+ this.selectedIndex = this._options.indexOf(this.committedSelectedOption);
366
+ }
367
+ if (this.collapsible && this.open) {
368
+ e.preventDefault();
369
+ this.open = false;
370
+ }
371
+ // reset 'selected' state otherwise the selected state doesn't stick.
372
+ const selectedOption = this._options[this.selectedIndex];
373
+ if (selectedOption) {
374
+ selectedOption.selected = true;
375
+ }
376
+ this.focus();
377
+ break;
378
+ }
379
+ case keyTab: {
380
+ if (this.collapsible && this.open) {
381
+ e.preventDefault();
382
+ this.open = false;
383
+ }
384
+ return true;
385
+ }
386
+ default: {
387
+ break;
388
+ }
389
+ }
390
+ if (!this.open && this.indexWhenOpened !== this.selectedIndex) {
391
+ this.updateValue(true);
392
+ this.indexWhenOpened = this.selectedIndex;
393
+ }
394
+ return !(key === keyArrowDown || key === keyArrowUp);
395
+ }
396
+ /**
397
+ * Updates the proxy value when the selected index changes.
398
+ *
399
+ * @param prev - the previous selected index
400
+ * @param next - the next selected index
401
+ *
402
+ * @internal
403
+ */
404
+ selectedIndexChanged(prev, next) {
405
+ super.selectedIndexChanged(prev, next);
406
+ this.updateValue();
407
+ }
408
+ /**
409
+ * Synchronize the `aria-disabled` property when the `disabled` property changes.
410
+ *
411
+ * @param prev - The previous disabled value
412
+ * @param next - The next disabled value
413
+ *
414
+ * @internal
415
+ */
416
+ disabledChanged(prev, next) {
417
+ if (super.disabledChanged) {
418
+ super.disabledChanged(prev, next);
419
+ }
420
+ this.ariaDisabled = this.disabled ? 'true' : 'false';
421
+ }
422
+ /**
423
+ * Reset the element to its first selectable option when its parent form is reset.
424
+ *
425
+ * @internal
426
+ */
427
+ formResetCallback() {
428
+ this.setProxyOptions();
429
+ // Call the base class's implementation setDefaultSelectedOption instead of the select's
430
+ // override, in order to reset the selectedIndex without using the value property.
431
+ super.setDefaultSelectedOption();
432
+ if (this.selectedIndex === -1) {
433
+ this.selectedIndex = 0;
434
+ }
435
+ }
436
+ // Prevents parent classes from resetting selectedIndex to a positive
437
+ // value while filtering, which can result in a disabled option being
438
+ // selected.
439
+ setSelectedOptions() {
440
+ if (this.open && this.selectedIndex === -1) {
441
+ return;
442
+ }
443
+ super.setSelectedOptions();
444
+ }
445
+ focusAndScrollOptionIntoView() {
446
+ super.focusAndScrollOptionIntoView();
447
+ if (this.open) {
448
+ window.requestAnimationFrame(() => {
449
+ this.filterInput?.focus();
450
+ });
451
+ }
452
+ }
453
+ positionChanged(_, next) {
454
+ this.positionAttribute = next;
455
+ this.setPositioning();
456
+ }
457
+ /**
458
+ * Updates the proxy's size property when the size attribute changes.
459
+ *
460
+ * @param prev - the previous size
461
+ * @param next - the current size
462
+ *
463
+ * @override
464
+ * @internal
465
+ */
466
+ sizeChanged(prev, next) {
467
+ super.sizeChanged(prev, next);
468
+ if (this.proxy) {
469
+ this.proxy.size = next;
470
+ }
471
+ }
472
+ openChanged() {
473
+ if (!this.collapsible) {
474
+ return;
475
+ }
476
+ if (this.open) {
477
+ this.initializeOpenState();
478
+ this.indexWhenOpened = this.selectedIndex;
479
+ return;
480
+ }
481
+ this.filter = '';
482
+ if (this.filterInput) {
483
+ this.filterInput.value = '';
484
+ }
485
+ this.ariaControls = '';
486
+ this.ariaExpanded = 'false';
487
+ }
488
+ /**
489
+ * Updates the selectedness of each option when the list of selected options changes.
490
+ *
491
+ * @param prev - the previous list of selected options
492
+ * @param next - the current list of selected options
493
+ *
494
+ * @override
495
+ * @internal
496
+ */
497
+ selectedOptionsChanged(prev, next) {
498
+ super.selectedOptionsChanged(prev, next);
499
+ this.options?.forEach((o, i) => {
500
+ const proxyOption = this.proxy?.options.item(i);
501
+ if (proxyOption) {
502
+ proxyOption.selected = o.selected;
503
+ }
504
+ });
505
+ }
506
+ /**
507
+ * Sets the selected index to match the first option with the selected attribute, or
508
+ * the first selectable option.
509
+ *
510
+ * @override
511
+ * @internal
512
+ */
513
+ setDefaultSelectedOption() {
514
+ const options = this.options
515
+ ?? Array.from(this.children).filter(o => Listbox.slottedOptionFilter(o));
516
+ const selectedIndex = options?.findIndex(el => el.hasAttribute('selected')
517
+ || el.selected
518
+ || el.value === this.value);
519
+ if (selectedIndex !== -1) {
520
+ this.selectedIndex = selectedIndex;
521
+ return;
522
+ }
523
+ this.selectedIndex = 0;
20
524
  }
21
- // Workaround for https://github.com/microsoft/fast/issues/5123
22
525
  setPositioning() {
23
526
  if (!this.$fastController.isConnected) {
24
527
  // Don't call setPositioning() until we're connected,
25
528
  // since this.forcedPosition isn't initialized yet.
26
529
  return;
27
530
  }
28
- super.setPositioning();
531
+ const currentBox = this.getBoundingClientRect();
532
+ const viewportHeight = window.innerHeight;
533
+ const availableBottom = viewportHeight - currentBox.bottom;
534
+ if (this.forcedPosition) {
535
+ this.position = this.positionAttribute;
536
+ }
537
+ else if (currentBox.top > availableBottom) {
538
+ this.position = SelectPosition.above;
539
+ }
540
+ else {
541
+ this.position = SelectPosition.below;
542
+ }
543
+ this.positionAttribute = this.forcedPosition
544
+ ? this.positionAttribute
545
+ : this.position;
546
+ this.maxHeight = this.position === SelectPosition.above
547
+ ? Math.trunc(currentBox.top)
548
+ : Math.trunc(availableBottom);
29
549
  this.updateListboxMaxHeightCssVariable();
30
550
  }
31
- // Workaround for https://github.com/microsoft/fast/issues/5773
32
- slottedOptionsChanged(prev, next) {
33
- const value = this.value;
34
- super.slottedOptionsChanged(prev, next);
35
- if (value) {
36
- this.value = value;
551
+ /**
552
+ * Filter available options by text value.
553
+ *
554
+ * @public
555
+ */
556
+ filterOptions() {
557
+ const filter = this.filter.toLowerCase();
558
+ if (filter) {
559
+ this.filteredOptions = this._options.filter(option => {
560
+ return diacriticInsensitiveStringNormalizer(option.text).includes(diacriticInsensitiveStringNormalizer(filter));
561
+ });
562
+ }
563
+ else {
564
+ this.filteredOptions = this._options;
37
565
  }
566
+ this._options.forEach(o => {
567
+ o.hidden = !this.filteredOptions.includes(o);
568
+ });
38
569
  }
39
- regionChanged(_prev, _next) {
40
- if (this.region && this.control) {
41
- this.region.anchorElement = this.control;
570
+ /**
571
+ * Sets the value and display value to match the first selected option.
572
+ *
573
+ * @param shouldEmit - if true, the input and change events will be emitted
574
+ *
575
+ * @internal
576
+ */
577
+ updateValue(shouldEmit) {
578
+ if (this.$fastController.isConnected) {
579
+ this.value = this.firstSelectedOption?.value ?? '';
580
+ }
581
+ if (shouldEmit) {
582
+ this.$emit('input');
583
+ this.$emit('change', this, {
584
+ bubbles: true,
585
+ composed: undefined
586
+ });
42
587
  }
43
588
  }
44
- controlChanged(_prev, _next) {
45
- if (this.region && this.control) {
46
- this.region.anchorElement = this.control;
589
+ /**
590
+ * Resets and fills the proxy to match the component's options.
591
+ *
592
+ * @internal
593
+ */
594
+ setProxyOptions() {
595
+ if (this.proxy instanceof HTMLSelectElement && this.options) {
596
+ this.proxy.options.length = 0;
597
+ this.options.forEach(option => {
598
+ const proxyOption = option.proxy
599
+ || (option instanceof HTMLOptionElement
600
+ ? option.cloneNode()
601
+ : null);
602
+ if (proxyOption) {
603
+ this.proxy.options.add(proxyOption);
604
+ }
605
+ });
47
606
  }
48
607
  }
608
+ clearSelection() {
609
+ this.options.forEach(option => {
610
+ option.selected = false;
611
+ });
612
+ }
613
+ filterChanged() {
614
+ this.filterOptions();
615
+ }
49
616
  maxHeightChanged() {
50
617
  this.updateListboxMaxHeightCssVariable();
51
618
  }
619
+ initializeOpenState() {
620
+ if (!this.open) {
621
+ this.ariaExpanded = 'false';
622
+ this.ariaControls = '';
623
+ return;
624
+ }
625
+ this.committedSelectedOption = this._options[this.selectedIndex];
626
+ this.ariaControls = this.listboxId;
627
+ this.ariaExpanded = 'true';
628
+ this.setPositioning();
629
+ this.focusAndScrollOptionIntoView();
630
+ }
52
631
  updateListboxMaxHeightCssVariable() {
53
632
  if (this.listbox) {
54
633
  this.listbox.style.setProperty('--ni-private-select-max-height', `${this.maxHeight}px`);
55
634
  }
56
635
  }
636
+ updateSelectedIndexFromFilteredSet() {
637
+ const selectedItem = this.filteredOptions.length > 0
638
+ ? this.options[this.selectedIndex]
639
+ ?? this.committedSelectedOption
640
+ : this.committedSelectedOption;
641
+ if (!selectedItem) {
642
+ return;
643
+ }
644
+ // Clear filter so any logic resolving against 'this.options' resolves against all options,
645
+ // since selectedIndex should be relative to entire set.
646
+ this.filter = '';
647
+ // translate selectedIndex for filtered list to selectedIndex for all items
648
+ this.selectedIndex = this._options.indexOf(selectedItem);
649
+ // force selected to true again if the selection hasn't actually changed
650
+ if (selectedItem === this.committedSelectedOption) {
651
+ selectedItem.selected = true;
652
+ }
653
+ }
57
654
  }
58
655
  __decorate([
59
656
  attr
60
657
  ], Select.prototype, "appearance", void 0);
658
+ __decorate([
659
+ attr({ attribute: 'position' })
660
+ ], Select.prototype, "positionAttribute", void 0);
61
661
  __decorate([
62
662
  attr({ attribute: 'error-text' })
63
663
  ], Select.prototype, "errorText", void 0);
64
664
  __decorate([
65
665
  attr({ attribute: 'error-visible', mode: 'boolean' })
66
666
  ], Select.prototype, "errorVisible", void 0);
667
+ __decorate([
668
+ attr({ attribute: 'filter-mode' })
669
+ ], Select.prototype, "filterMode", void 0);
670
+ __decorate([
671
+ attr({ attribute: 'open', mode: 'boolean' })
672
+ ], Select.prototype, "open", void 0);
673
+ __decorate([
674
+ observable
675
+ ], Select.prototype, "position", void 0);
676
+ __decorate([
677
+ observable
678
+ ], Select.prototype, "control", void 0);
679
+ __decorate([
680
+ observable
681
+ ], Select.prototype, "scrollableRegion", void 0);
67
682
  __decorate([
68
683
  observable
69
- ], Select.prototype, "region", void 0);
684
+ ], Select.prototype, "filterInput", void 0);
685
+ __decorate([
686
+ observable
687
+ ], Select.prototype, "anchoredRegion", void 0);
70
688
  __decorate([
71
689
  observable
72
690
  ], Select.prototype, "hasOverflow", void 0);
691
+ __decorate([
692
+ observable
693
+ ], Select.prototype, "filteredOptions", void 0);
694
+ __decorate([
695
+ observable
696
+ ], Select.prototype, "filter", void 0);
697
+ __decorate([
698
+ observable
699
+ ], Select.prototype, "committedSelectedOption", void 0);
700
+ __decorate([
701
+ observable
702
+ ], Select.prototype, "maxHeight", void 0);
703
+ __decorate([
704
+ volatile
705
+ ], Select.prototype, "collapsible", null);
73
706
  const nimbleSelect = Select.compose({
74
707
  baseName: 'select',
75
708
  baseClass: FoundationSelect,
@@ -84,6 +717,7 @@ const nimbleSelect = Select.compose({
84
717
  ${errorTextTemplate}
85
718
  `
86
719
  });
720
+ applyMixins(Select, StartEnd, DelegatesARIASelect);
87
721
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleSelect());
88
722
  export const selectTag = 'nimble-select';
89
723
  //# sourceMappingURL=index.js.map