@ni/nimble-components 24.1.6 → 24.1.7

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.
@@ -5398,7 +5398,7 @@
5398
5398
  * Ensures that a value is between a min and max value. If value is lower than min, min will be returned.
5399
5399
  * If value is greater than max, max will be returned.
5400
5400
  */
5401
- function limit(min, max, value) {
5401
+ function limit$1(min, max, value) {
5402
5402
  return Math.min(Math.max(value, min), max);
5403
5403
  }
5404
5404
  /**
@@ -8380,12 +8380,12 @@
8380
8380
  *
8381
8381
  * @internal
8382
8382
  */
8383
- class FormAssociatedCombobox extends FormAssociated(_Combobox) {
8383
+ let FormAssociatedCombobox$1 = class FormAssociatedCombobox extends FormAssociated(_Combobox) {
8384
8384
  constructor() {
8385
8385
  super(...arguments);
8386
8386
  this.proxy = document.createElement("input");
8387
8387
  }
8388
- }
8388
+ };
8389
8389
 
8390
8390
  /**
8391
8391
  * Autocomplete values for combobox.
@@ -8415,7 +8415,7 @@
8415
8415
  *
8416
8416
  * @public
8417
8417
  */
8418
- let Combobox$1 = class Combobox extends FormAssociatedCombobox {
8418
+ let Combobox$2 = class Combobox extends FormAssociatedCombobox$1 {
8419
8419
  constructor() {
8420
8420
  super(...arguments);
8421
8421
  /**
@@ -8796,7 +8796,7 @@
8796
8796
  */
8797
8797
  selectedIndexChanged(prev, next) {
8798
8798
  if (this.$fastController.isConnected) {
8799
- next = limit(-1, this.options.length - 1, next);
8799
+ next = limit$1(-1, this.options.length - 1, next);
8800
8800
  // we only want to call the super method when the selectedIndex is in range
8801
8801
  if (next !== this.selectedIndex) {
8802
8802
  this.selectedIndex = next;
@@ -8943,22 +8943,22 @@
8943
8943
  };
8944
8944
  __decorate([
8945
8945
  attr({ attribute: "autocomplete", mode: "fromView" })
8946
- ], Combobox$1.prototype, "autocomplete", void 0);
8946
+ ], Combobox$2.prototype, "autocomplete", void 0);
8947
8947
  __decorate([
8948
8948
  observable
8949
- ], Combobox$1.prototype, "maxHeight", void 0);
8949
+ ], Combobox$2.prototype, "maxHeight", void 0);
8950
8950
  __decorate([
8951
8951
  attr({ attribute: "open", mode: "boolean" })
8952
- ], Combobox$1.prototype, "open", void 0);
8952
+ ], Combobox$2.prototype, "open", void 0);
8953
8953
  __decorate([
8954
8954
  attr
8955
- ], Combobox$1.prototype, "placeholder", void 0);
8955
+ ], Combobox$2.prototype, "placeholder", void 0);
8956
8956
  __decorate([
8957
8957
  attr({ attribute: "position" })
8958
- ], Combobox$1.prototype, "positionAttribute", void 0);
8958
+ ], Combobox$2.prototype, "positionAttribute", void 0);
8959
8959
  __decorate([
8960
8960
  observable
8961
- ], Combobox$1.prototype, "position", void 0);
8961
+ ], Combobox$2.prototype, "position", void 0);
8962
8962
  /**
8963
8963
  * Includes ARIA states and properties relating to the ARIA combobox role.
8964
8964
  *
@@ -8973,7 +8973,7 @@
8973
8973
  observable
8974
8974
  ], DelegatesARIACombobox.prototype, "ariaControls", void 0);
8975
8975
  applyMixins(DelegatesARIACombobox, DelegatesARIAListbox);
8976
- applyMixins(Combobox$1, StartEnd, DelegatesARIACombobox);
8976
+ applyMixins(Combobox$2, StartEnd, DelegatesARIACombobox);
8977
8977
 
8978
8978
  /**
8979
8979
  * Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
@@ -13545,7 +13545,7 @@
13545
13545
  adjust(adjustment) {
13546
13546
  const focusableTabs = this.tabs.filter(t => this.isFocusableElement(t));
13547
13547
  const currentActiveTabIndex = focusableTabs.indexOf(this.activetab);
13548
- const nextTabIndex = limit(0, focusableTabs.length - 1, currentActiveTabIndex + adjustment);
13548
+ const nextTabIndex = limit$1(0, focusableTabs.length - 1, currentActiveTabIndex + adjustment);
13549
13549
  // the index of the next focusable tab within the context of all available tabs
13550
13550
  const nextIndex = this.tabs.indexOf(focusableTabs[nextTabIndex]);
13551
13551
  if (nextIndex > -1) {
@@ -13942,7 +13942,7 @@
13942
13942
  }
13943
13943
  set activeIndex(value) {
13944
13944
  if (this.$fastController.isConnected) {
13945
- this._activeIndex = limit(0, this.focusableElements.length - 1, value);
13945
+ this._activeIndex = limit$1(0, this.focusableElements.length - 1, value);
13946
13946
  Observable.notify(this, "activeIndex");
13947
13947
  }
13948
13948
  }
@@ -16301,7 +16301,7 @@
16301
16301
 
16302
16302
  /**
16303
16303
  * Do not edit directly
16304
- * Generated on Thu, 04 Apr 2024 14:40:58 GMT
16304
+ * Generated on Thu, 04 Apr 2024 17:54:36 GMT
16305
16305
  */
16306
16306
 
16307
16307
  const Information100DarkUi = "#a46eff";
@@ -16717,6 +16717,19 @@
16717
16717
  Direction["rtl"] = "rtl";
16718
16718
  })(Direction || (Direction = {}));
16719
16719
 
16720
+ /**
16721
+ * This method keeps a given value within the bounds of a min and max value. If the value
16722
+ * is larger than the max, the minimum value will be returned. If the value is smaller than the minimum,
16723
+ * the maximum will be returned. Otherwise, the value is returned un-changed.
16724
+ */
16725
+ /**
16726
+ * Ensures that a value is between a min and max value. If value is lower than min, min will be returned.
16727
+ * If value is greater than max, max will be returned.
16728
+ */
16729
+ function limit(min, max, value) {
16730
+ return Math.min(Math.max(value, min), max);
16731
+ }
16732
+
16720
16733
  let uniqueIdCounter = 0;
16721
16734
  /**
16722
16735
  * Generates a unique ID based on incrementing a counter.
@@ -21077,65 +21090,173 @@
21077
21090
  </template>
21078
21091
  `;
21079
21092
 
21093
+ // Based on: https://github.com/microsoft/fast/blob/%40microsoft/fast-foundation_v2.49.5/packages/web-components/fast-foundation/src/combobox/combobox.form-associated.ts
21094
+ /* eslint-disable max-classes-per-file */
21095
+ // eslint-disable-next-line jsdoc/require-jsdoc
21096
+ let Combobox$1 = class Combobox extends ListboxElement {
21097
+ };
21098
+ /**
21099
+ * A form-associated base class for the Combobox component. This was copied from the
21100
+ * FAST FormAssociatedCombobox (which is not exported by fast-foundation)
21101
+ *
21102
+ * @internal
21103
+ */
21104
+ class FormAssociatedCombobox extends FormAssociated(Combobox$1) {
21105
+ constructor() {
21106
+ super(...arguments);
21107
+ this.proxy = document.createElement('input');
21108
+ }
21109
+ }
21110
+
21080
21111
  /**
21081
21112
  * A nimble-styed HTML combobox
21082
21113
  */
21083
- class Combobox extends Combobox$1 {
21114
+ class Combobox extends FormAssociatedCombobox {
21084
21115
  constructor() {
21085
21116
  super(...arguments);
21086
21117
  this.appearance = DropdownAppearance.underline;
21087
21118
  this.errorVisible = false;
21119
+ /**
21120
+ * The open attribute.
21121
+ */
21122
+ this.open = false;
21123
+ /**
21124
+ * The collection of currently filtered options.
21125
+ */
21126
+ this.filteredOptions = [];
21088
21127
  /** @internal */
21089
21128
  this.hasOverflow = false;
21129
+ /**
21130
+ * The unique id for the internal listbox element.
21131
+ *
21132
+ * @internal
21133
+ */
21134
+ this.listboxId = uniqueId('listbox-');
21135
+ /**
21136
+ * The max height for the listbox when opened.
21137
+ *
21138
+ * @internal
21139
+ */
21140
+ this.maxHeight = 0;
21090
21141
  this.valueUpdatedByInput = false;
21142
+ this._value = '';
21143
+ this.filter = '';
21144
+ /**
21145
+ * The initial state of the position attribute.
21146
+ */
21147
+ this.forcedPosition = false;
21091
21148
  }
21092
21149
  get value() {
21093
- return super.value;
21150
+ Observable.track(this, 'value');
21151
+ return this._value;
21094
21152
  }
21095
- // This override is to work around an issue in FAST where an old filter value
21096
- // is used after programmatically setting the value property.
21097
- // See: https://github.com/microsoft/fast/issues/6749
21098
21153
  set value(next) {
21099
- super.value = next;
21100
- // Workaround using index notation to manipulate private member
21154
+ const prev = `${this._value}`;
21155
+ let updatedValue = next;
21156
+ if (this.$fastController.isConnected && this.options) {
21157
+ const selectedIndex = this.options.findIndex(el => el.text.toLowerCase() === next.toLowerCase());
21158
+ const prevSelectedValue = this.options[this.selectedIndex]?.text;
21159
+ const nextSelectedValue = this.options[selectedIndex]?.text;
21160
+ this.selectedIndex = prevSelectedValue !== nextSelectedValue
21161
+ ? selectedIndex
21162
+ : this.selectedIndex;
21163
+ updatedValue = this.firstSelectedOption?.text || next;
21164
+ }
21165
+ if (prev !== updatedValue) {
21166
+ this._value = updatedValue;
21167
+ super.valueChanged(prev, updatedValue);
21168
+ Observable.notify(this, 'value');
21169
+ }
21101
21170
  // Can remove when following resolved: https://github.com/microsoft/fast/issues/6749
21102
- // eslint-disable-next-line @typescript-eslint/dot-notation
21103
- this['filter'] = next;
21171
+ this.filter = next;
21104
21172
  this.filterOptions();
21105
21173
  this.selectedIndex = this.options
21106
21174
  .map(option => option.text)
21107
21175
  .indexOf(this.value);
21108
21176
  }
21109
- // Workaround for https://github.com/microsoft/fast/issues/5123
21110
- setPositioning() {
21111
- if (!this.$fastController.isConnected) {
21112
- // Don't call setPositioning() until we're connected,
21113
- // since this.forcedPosition isn't initialized yet.
21114
- return;
21115
- }
21116
- super.setPositioning();
21177
+ /**
21178
+ * The list of options.
21179
+ *
21180
+ * Overrides `Listbox.options`.
21181
+ */
21182
+ get options() {
21183
+ Observable.track(this, 'options');
21184
+ return this.filteredOptions?.length
21185
+ ? this.filteredOptions
21186
+ : this._options;
21187
+ }
21188
+ set options(value) {
21189
+ this._options = value;
21190
+ Observable.notify(this, 'options');
21191
+ }
21192
+ get isAutocompleteInline() {
21193
+ return (this.autocomplete === ComboboxAutocomplete.inline
21194
+ || this.isAutocompleteBoth);
21195
+ }
21196
+ get isAutocompleteList() {
21197
+ return (this.autocomplete === ComboboxAutocomplete.list
21198
+ || this.isAutocompleteBoth);
21199
+ }
21200
+ get isAutocompleteBoth() {
21201
+ return this.autocomplete === ComboboxAutocomplete.both;
21117
21202
  }
21118
- // Workaround for https://github.com/microsoft/fast/issues/5773
21119
21203
  slottedOptionsChanged(prev, next) {
21204
+ // Workaround for https://github.com/microsoft/fast/issues/5773
21120
21205
  const value = this.value;
21121
21206
  super.slottedOptionsChanged(prev, next);
21207
+ this.updateValue();
21122
21208
  if (value) {
21123
21209
  this.value = value;
21124
21210
  }
21125
21211
  }
21126
21212
  connectedCallback() {
21127
21213
  super.connectedCallback();
21128
- // Call setPositioning() after this.forcedPosition is initialized.
21214
+ this.forcedPosition = !!this.positionAttribute;
21215
+ if (this.value) {
21216
+ this.initialValue = this.value;
21217
+ }
21129
21218
  this.setPositioning();
21130
21219
  this.updateInputAriaLabel();
21131
21220
  }
21221
+ /**
21222
+ * @internal
21223
+ */
21224
+ clickHandler(e) {
21225
+ if (this.disabled) {
21226
+ return false;
21227
+ }
21228
+ if (this.open) {
21229
+ const captured = e.target.closest('option,[role=option]');
21230
+ if (!captured || captured.disabled) {
21231
+ return false;
21232
+ }
21233
+ this.selectedOptions = [captured];
21234
+ this.control.value = captured.text;
21235
+ this.clearSelectionRange();
21236
+ this.updateValue(true);
21237
+ }
21238
+ this.open = !this.open;
21239
+ if (this.open) {
21240
+ this.control.focus();
21241
+ }
21242
+ return true;
21243
+ }
21244
+ /**
21245
+ * @internal
21246
+ */
21132
21247
  toggleButtonClickHandler(e) {
21133
21248
  e.stopImmediatePropagation();
21134
21249
  }
21250
+ /**
21251
+ * @internal
21252
+ */
21135
21253
  toggleButtonChangeHandler(e) {
21136
21254
  this.open = this.dropdownButton.checked;
21137
21255
  e.stopImmediatePropagation();
21138
21256
  }
21257
+ /**
21258
+ * @internal
21259
+ */
21139
21260
  toggleButtonKeyDownHandler(e) {
21140
21261
  switch (e.key) {
21141
21262
  case keyArrowUp:
@@ -21149,20 +21270,57 @@
21149
21270
  return true;
21150
21271
  }
21151
21272
  }
21273
+ /**
21274
+ * @internal
21275
+ */
21152
21276
  filterOptions() {
21153
- super.filterOptions();
21277
+ if (!this.autocomplete
21278
+ || this.autocomplete === ComboboxAutocomplete.none) {
21279
+ this.filter = '';
21280
+ }
21281
+ const filter = this.filter.toLowerCase();
21282
+ this.filteredOptions = this._options.filter(o => o.text.toLowerCase().startsWith(filter));
21283
+ if (this.isAutocompleteList) {
21284
+ if (!this.filteredOptions.length && !filter) {
21285
+ this.filteredOptions = this._options;
21286
+ }
21287
+ this._options.forEach(o => {
21288
+ o.visuallyHidden = !this.filteredOptions.includes(o);
21289
+ });
21290
+ }
21154
21291
  const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
21155
21292
  this.filteredOptions = enabledOptions;
21156
21293
  }
21157
21294
  /**
21158
- * This is a workaround for the issue described here: https://github.com/microsoft/fast/issues/6267
21159
- * For now, we will update the value ourselves while a user types in text. Note that there is other
21160
- * implementation related to this (like the 'keydownEventHandler') needed to create the complete set
21161
- * of desired behavior described in the issue noted above.
21295
+ * @internal
21162
21296
  */
21163
- // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
21164
21297
  inputHandler(e) {
21165
- const returnValue = super.inputHandler(e);
21298
+ this.filter = this.control.value;
21299
+ this.filterOptions();
21300
+ if (!this.isAutocompleteInline) {
21301
+ this.selectedIndex = this.options
21302
+ .map(option => option.text)
21303
+ .indexOf(this.control.value);
21304
+ }
21305
+ if (!(e.inputType.includes('deleteContent') || !this.filter.length)) {
21306
+ if (this.isAutocompleteList && !this.open) {
21307
+ this.open = true;
21308
+ }
21309
+ if (this.isAutocompleteInline) {
21310
+ if (this.filteredOptions.length) {
21311
+ this.selectedOptions = [this.filteredOptions[0]];
21312
+ this.selectedIndex = this.options.indexOf(this.firstSelectedOption);
21313
+ this.setInlineSelection();
21314
+ }
21315
+ else {
21316
+ this.selectedIndex = -1;
21317
+ }
21318
+ }
21319
+ }
21320
+ // This is a workaround for the issue described here: https://github.com/microsoft/fast/issues/6267
21321
+ // For now, we will update the value ourselves while a user types in text. Note that there is other
21322
+ // implementation related to this (like the 'keydownEventHandler') needed to create the complete set
21323
+ // of desired behavior described in the issue noted above.
21166
21324
  if (!this.valueUpdatedByInput) {
21167
21325
  this.valueBeforeTextUpdate = this.value;
21168
21326
  }
@@ -21172,47 +21330,257 @@
21172
21330
  this.focusAndScrollOptionIntoView();
21173
21331
  }
21174
21332
  this.value = this.control.value;
21175
- return returnValue;
21333
+ return true;
21176
21334
  }
21177
- // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
21178
21335
  keydownHandler(e) {
21179
- const returnValue = super.keydownHandler(e);
21180
21336
  if (e.ctrlKey || e.altKey) {
21181
- return returnValue;
21337
+ return true;
21182
21338
  }
21183
21339
  switch (e.key) {
21184
21340
  case keyEnter:
21341
+ this.syncValue();
21342
+ if (this.isAutocompleteInline) {
21343
+ this.filter = this.value;
21344
+ }
21345
+ this.open = false;
21346
+ this.clearSelectionRange();
21185
21347
  this.emitChangeIfValueUpdated();
21186
21348
  break;
21349
+ case keyEscape:
21350
+ if (!this.isAutocompleteInline) {
21351
+ this.selectedIndex = -1;
21352
+ }
21353
+ if (this.open) {
21354
+ this.open = false;
21355
+ break;
21356
+ }
21357
+ this.value = '';
21358
+ this.control.value = '';
21359
+ this.filter = '';
21360
+ this.filterOptions();
21361
+ break;
21362
+ case keyTab:
21363
+ this.setInputToSelection();
21364
+ if (!this.open) {
21365
+ return true;
21366
+ }
21367
+ e.preventDefault();
21368
+ this.open = false;
21369
+ break;
21187
21370
  case keyArrowDown:
21188
21371
  case keyArrowUp:
21372
+ this.filterOptions();
21373
+ if (!this.open) {
21374
+ this.open = true;
21375
+ break;
21376
+ }
21377
+ if (this.filteredOptions.length > 0) {
21378
+ super.keydownHandler(e);
21379
+ }
21380
+ if (this.isAutocompleteInline) {
21381
+ this.setInlineSelection();
21382
+ }
21189
21383
  if (this.open && this.valueUpdatedByInput) {
21190
21384
  this.valueUpdatedByInput = false;
21191
21385
  }
21192
21386
  break;
21193
21387
  default:
21194
- return returnValue;
21388
+ return true;
21389
+ }
21390
+ return true;
21391
+ }
21392
+ /**
21393
+ * @internal
21394
+ */
21395
+ keyupHandler(e) {
21396
+ const key = e.key;
21397
+ switch (key) {
21398
+ case 'ArrowLeft':
21399
+ case 'ArrowRight':
21400
+ case 'Backspace':
21401
+ case 'Delete':
21402
+ case 'Home':
21403
+ case 'End': {
21404
+ this.filter = this.control.value;
21405
+ this.selectedIndex = -1;
21406
+ this.filterOptions();
21407
+ break;
21408
+ }
21195
21409
  }
21196
- return returnValue;
21410
+ return true;
21197
21411
  }
21198
- // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
21412
+ /**
21413
+ * @internal
21414
+ */
21199
21415
  focusoutHandler(e) {
21200
- const returnValue = super.focusoutHandler(e);
21416
+ this.syncValue();
21417
+ if (this.open) {
21418
+ const focusTarget = e.relatedTarget;
21419
+ if (this.isSameNode(focusTarget)) {
21420
+ this.focus();
21421
+ }
21422
+ }
21201
21423
  this.open = false;
21202
21424
  this.emitChangeIfValueUpdated();
21203
- return returnValue;
21425
+ return true;
21426
+ }
21427
+ /**
21428
+ * Reset the element to its first selectable option when its parent form is reset.
21429
+ *
21430
+ * @internal
21431
+ */
21432
+ formResetCallback() {
21433
+ super.formResetCallback();
21434
+ this.setDefaultSelectedOption();
21435
+ this.updateValue();
21436
+ }
21437
+ /** {@inheritDoc (FormAssociated:interface).validate} */
21438
+ validate() {
21439
+ super.validate(this.control);
21440
+ }
21441
+ /**
21442
+ * Set the default selected options at initialization or reset.
21443
+ *
21444
+ * @internal
21445
+ * @remarks
21446
+ * Overrides `Listbox.setDefaultSelectedOption`
21447
+ */
21448
+ setDefaultSelectedOption() {
21449
+ if (this.$fastController.isConnected && this.options) {
21450
+ const selectedIndex = this.options.findIndex(el => el.getAttribute('selected') !== null || el.selected);
21451
+ this.selectedIndex = selectedIndex;
21452
+ if (!this.dirtyValue && this.firstSelectedOption) {
21453
+ this.value = this.firstSelectedOption.text;
21454
+ }
21455
+ this.setSelectedOptions();
21456
+ }
21457
+ }
21458
+ /**
21459
+ * @internal
21460
+ */
21461
+ selectedIndexChanged(prev, next) {
21462
+ if (this.$fastController.isConnected) {
21463
+ const pinnedSelectedIndex = limit(-1, this.options.length - 1, next);
21464
+ // we only want to call the super method when the selectedIndex is in range
21465
+ if (pinnedSelectedIndex !== this.selectedIndex) {
21466
+ this.selectedIndex = pinnedSelectedIndex;
21467
+ return;
21468
+ }
21469
+ super.selectedIndexChanged(prev, pinnedSelectedIndex);
21470
+ }
21471
+ }
21472
+ /**
21473
+ * Synchronize the `aria-disabled` property when the `disabled` property changes.
21474
+ *
21475
+ * @internal
21476
+ */
21477
+ disabledChanged(prev, next) {
21478
+ if (super.disabledChanged) {
21479
+ super.disabledChanged(prev, next);
21480
+ }
21481
+ this.ariaDisabled = this.disabled ? 'true' : 'false';
21482
+ }
21483
+ /**
21484
+ * Move focus to the previous selectable option.
21485
+ *
21486
+ * @internal
21487
+ * @remarks
21488
+ * Overrides `Listbox.selectPreviousOption`
21489
+ */
21490
+ selectPreviousOption() {
21491
+ if (!this.disabled && this.selectedIndex >= 0) {
21492
+ this.selectedIndex -= 1;
21493
+ }
21494
+ }
21495
+ /**
21496
+ * @internal
21497
+ */
21498
+ setPositioning() {
21499
+ // Workaround for https://github.com/microsoft/fast/issues/5123
21500
+ if (!this.$fastController.isConnected) {
21501
+ // Don't call setPositioning() until we're connected,
21502
+ // since this.forcedPosition isn't initialized yet.
21503
+ return;
21504
+ }
21505
+ const currentBox = this.getBoundingClientRect();
21506
+ const viewportHeight = window.innerHeight;
21507
+ const availableBottom = viewportHeight - currentBox.bottom;
21508
+ if (this.forcedPosition) {
21509
+ this.position = this.positionAttribute;
21510
+ }
21511
+ else if (currentBox.top > availableBottom) {
21512
+ this.position = SelectPosition.above;
21513
+ }
21514
+ else {
21515
+ this.position = SelectPosition.below;
21516
+ }
21517
+ this.positionAttribute = this.forcedPosition
21518
+ ? this.positionAttribute
21519
+ : this.position;
21520
+ this.maxHeight = this.position === SelectPosition.above
21521
+ ? Math.trunc(currentBox.top)
21522
+ : Math.trunc(availableBottom);
21204
21523
  }
21524
+ /**
21525
+ * Focus the control and scroll the first selected option into view.
21526
+ *
21527
+ * @internal
21528
+ * @remarks
21529
+ * Overrides: `Listbox.focusAndScrollOptionIntoView`
21530
+ */
21205
21531
  focusAndScrollOptionIntoView() {
21206
21532
  if (this.open) {
21207
- super.focusAndScrollOptionIntoView();
21533
+ if (this.contains(document.activeElement)) {
21534
+ this.control.focus();
21535
+ if (this.firstSelectedOption) {
21536
+ requestAnimationFrame(() => {
21537
+ this.firstSelectedOption?.scrollIntoView({
21538
+ block: 'nearest'
21539
+ });
21540
+ });
21541
+ }
21542
+ }
21208
21543
  }
21209
21544
  }
21210
21545
  openChanged() {
21211
- super.openChanged();
21546
+ if (this.open) {
21547
+ this.ariaControls = this.listboxId;
21548
+ this.ariaExpanded = 'true';
21549
+ this.setPositioning();
21550
+ this.focusAndScrollOptionIntoView();
21551
+ // focus is directed to the element when `open` is changed programmatically
21552
+ DOM.queueUpdate(() => this.focus());
21553
+ }
21554
+ else {
21555
+ this.ariaControls = '';
21556
+ this.ariaExpanded = 'false';
21557
+ }
21212
21558
  if (this.dropdownButton) {
21213
21559
  this.dropdownButton.checked = this.open;
21214
21560
  }
21215
21561
  }
21562
+ placeholderChanged() {
21563
+ if (this.proxy instanceof HTMLInputElement) {
21564
+ this.proxy.placeholder = this.placeholder ?? '';
21565
+ }
21566
+ }
21567
+ /**
21568
+ * Ensure that the entire list of options is used when setting the selected property.
21569
+ * @internal
21570
+ * @remarks
21571
+ * Overrides: `Listbox.selectedOptionsChanged`
21572
+ */
21573
+ selectedOptionsChanged(_, next) {
21574
+ if (this.$fastController.isConnected) {
21575
+ this._options.forEach(o => {
21576
+ o.selected = next.includes(o);
21577
+ });
21578
+ }
21579
+ }
21580
+ positionChanged(_, next) {
21581
+ this.positionAttribute = next;
21582
+ this.setPositioning();
21583
+ }
21216
21584
  regionChanged(_prev, _next) {
21217
21585
  if (this.region && this.controlWrapper) {
21218
21586
  this.region.anchorElement = this.controlWrapper;
@@ -21230,6 +21598,49 @@
21230
21598
  maxHeightChanged() {
21231
21599
  this.updateListboxMaxHeightCssVariable();
21232
21600
  }
21601
+ /**
21602
+ * Sets the value and to match the first selected option.
21603
+ */
21604
+ updateValue(shouldEmit) {
21605
+ if (this.$fastController.isConnected) {
21606
+ this.value = this.firstSelectedOption?.text || this.control.value;
21607
+ this.control.value = this.value;
21608
+ }
21609
+ if (shouldEmit) {
21610
+ this.$emit('change');
21611
+ }
21612
+ }
21613
+ /**
21614
+ * Focus and set the content of the control based on the first selected option.
21615
+ */
21616
+ setInputToSelection() {
21617
+ if (this.firstSelectedOption) {
21618
+ this.control.value = this.firstSelectedOption.text;
21619
+ this.control.focus();
21620
+ }
21621
+ }
21622
+ /**
21623
+ * Focus, set and select the content of the control based on the first selected option.
21624
+ */
21625
+ setInlineSelection() {
21626
+ if (this.firstSelectedOption) {
21627
+ this.setInputToSelection();
21628
+ this.control.setSelectionRange(this.filter.length, this.control.value.length, 'backward');
21629
+ }
21630
+ }
21631
+ clearSelectionRange() {
21632
+ const controlValueLength = this.control.value.length;
21633
+ this.control.setSelectionRange(controlValueLength, controlValueLength);
21634
+ }
21635
+ /**
21636
+ * Determines if a value update should involve emitting a change event, then updates the value.
21637
+ */
21638
+ syncValue() {
21639
+ const newValue = this.selectedIndex > -1
21640
+ ? this.firstSelectedOption?.text
21641
+ : this.control.value;
21642
+ this.updateValue(this.value !== newValue);
21643
+ }
21233
21644
  updateListboxMaxHeightCssVariable() {
21234
21645
  if (this.listbox) {
21235
21646
  this.listbox.style.setProperty('--ni-private-select-max-height', `${this.maxHeight}px`);
@@ -21266,27 +21677,51 @@
21266
21677
  __decorate$1([
21267
21678
  attr
21268
21679
  ], Combobox.prototype, "appearance", void 0);
21269
- __decorate$1([
21270
- observable
21271
- ], Combobox.prototype, "dropdownButton", void 0);
21272
21680
  __decorate$1([
21273
21681
  attr({ attribute: 'error-text' })
21274
21682
  ], Combobox.prototype, "errorText", void 0);
21275
21683
  __decorate$1([
21276
21684
  attr({ attribute: 'error-visible', mode: 'boolean' })
21277
21685
  ], Combobox.prototype, "errorVisible", void 0);
21686
+ __decorate$1([
21687
+ attr({ attribute: 'autocomplete', mode: 'fromView' })
21688
+ ], Combobox.prototype, "autocomplete", void 0);
21689
+ __decorate$1([
21690
+ attr({ attribute: 'position' })
21691
+ ], Combobox.prototype, "positionAttribute", void 0);
21692
+ __decorate$1([
21693
+ attr({ attribute: 'open', mode: 'boolean' })
21694
+ ], Combobox.prototype, "open", void 0);
21695
+ __decorate$1([
21696
+ attr
21697
+ ], Combobox.prototype, "placeholder", void 0);
21698
+ __decorate$1([
21699
+ observable
21700
+ ], Combobox.prototype, "position", void 0);
21278
21701
  __decorate$1([
21279
21702
  observable
21280
21703
  ], Combobox.prototype, "region", void 0);
21281
21704
  __decorate$1([
21282
21705
  observable
21283
21706
  ], Combobox.prototype, "controlWrapper", void 0);
21707
+ __decorate$1([
21708
+ observable
21709
+ ], Combobox.prototype, "control", void 0);
21710
+ __decorate$1([
21711
+ observable
21712
+ ], Combobox.prototype, "listbox", void 0);
21713
+ __decorate$1([
21714
+ observable
21715
+ ], Combobox.prototype, "dropdownButton", void 0);
21284
21716
  __decorate$1([
21285
21717
  observable
21286
21718
  ], Combobox.prototype, "hasOverflow", void 0);
21719
+ __decorate$1([
21720
+ observable
21721
+ ], Combobox.prototype, "maxHeight", void 0);
21287
21722
  const nimbleCombobox = Combobox.compose({
21288
21723
  baseName: 'combobox',
21289
- baseClass: Combobox$1,
21724
+ baseClass: FormAssociatedCombobox,
21290
21725
  template: template$u,
21291
21726
  styles: styles$D,
21292
21727
  shadowOptions: {
@@ -21324,6 +21759,7 @@
21324
21759
  ${errorTextTemplate}
21325
21760
  `
21326
21761
  });
21762
+ applyMixins(Combobox, StartEnd, DelegatesARIACombobox);
21327
21763
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleCombobox());
21328
21764
 
21329
21765
  /**