@ni/nimble-components 28.0.1 → 28.0.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.
@@ -5208,7 +5208,7 @@
5208
5208
  * @param array - the array to test
5209
5209
  * @param predicate - find calls predicate once for each element of the array, in descending order, until it finds one where predicate returns true. If such an element is found, findLastIndex immediately returns that element index. Otherwise, findIndex returns -1.
5210
5210
  */
5211
- function findLastIndex(array, predicate) {
5211
+ function findLastIndex$1(array, predicate) {
5212
5212
  let k = array.length;
5213
5213
  while (k--) {
5214
5214
  if (predicate(array[k], k, array)) {
@@ -8234,7 +8234,7 @@
8234
8234
  */
8235
8235
  selectLastOption() {
8236
8236
  if (!this.disabled) {
8237
- this.selectedIndex = findLastIndex(this.options, o => !o.disabled);
8237
+ this.selectedIndex = findLastIndex$1(this.options, o => !o.disabled);
8238
8238
  }
8239
8239
  }
8240
8240
  /**
@@ -16333,7 +16333,7 @@
16333
16333
 
16334
16334
  /**
16335
16335
  * Do not edit directly
16336
- * Generated on Tue, 07 May 2024 13:04:38 GMT
16336
+ * Generated on Tue, 07 May 2024 20:03:30 GMT
16337
16337
  */
16338
16338
 
16339
16339
  const Information100DarkUi = "#a46eff";
@@ -16696,6 +16696,22 @@
16696
16696
  const prefix = 'ni-nimble';
16697
16697
  const styleNameFromTokenName = (tokenName) => `${prefix}-${tokenName}`;
16698
16698
 
16699
+ /**
16700
+ * Returns the index of the last element in the array where predicate is true, and -1 otherwise.
16701
+ *
16702
+ * @param array - the array to test
16703
+ * @param predicate - find calls predicate once for each element of the array, in descending order, until it finds one where predicate returns true. If such an element is found, findLastIndex immediately returns that element index. Otherwise, findIndex returns -1.
16704
+ */
16705
+ function findLastIndex(array, predicate) {
16706
+ let k = array.length;
16707
+ while (k--) {
16708
+ if (predicate(array[k], k, array)) {
16709
+ return k;
16710
+ }
16711
+ }
16712
+ return -1;
16713
+ }
16714
+
16699
16715
  /**
16700
16716
  * This set of exported strings reference https://developer.mozilla.org/en-US/docs/Web/Events
16701
16717
  * and should include all non-deprecated and non-experimental Standard events
@@ -24861,6 +24877,15 @@
24861
24877
  * by the filtering process.
24862
24878
  */
24863
24879
  this.visuallyHidden = false;
24880
+ /**
24881
+ * @internal
24882
+ * This attribute is used to control the visual selected state of an option. This
24883
+ * is handled independently of the public 'selected' attribute, as 'selected' is
24884
+ * representative of the current value of the container control. However, while
24885
+ * a dropdown is open users can navigate through the options (requiring visual
24886
+ * updates) without changing the value of the container control.
24887
+ */
24888
+ this.activeOption = false;
24864
24889
  /** @internal */
24865
24890
  this.hasOverflow = false;
24866
24891
  }
@@ -24890,6 +24915,9 @@
24890
24915
  __decorate$1([
24891
24916
  attr({ attribute: 'visually-hidden', mode: 'boolean' })
24892
24917
  ], ListOption.prototype, "visuallyHidden", void 0);
24918
+ __decorate$1([
24919
+ attr({ attribute: 'active-option', mode: 'boolean' })
24920
+ ], ListOption.prototype, "activeOption", void 0);
24893
24921
  __decorate$1([
24894
24922
  observable
24895
24923
  ], ListOption.prototype, "hasOverflow", void 0);
@@ -58677,6 +58705,22 @@ img.ProseMirror-separator {
58677
58705
  overflow: auto;
58678
58706
  }
58679
58707
 
58708
+ ::slotted([role='option']) {
58709
+ background-color: transparent;
58710
+ }
58711
+
58712
+ ::slotted([role='option']:hover) {
58713
+ background-color: ${fillHoverColor};
58714
+ }
58715
+
58716
+ ::slotted([role='option'][active-option]) {
58717
+ background-color: ${fillSelectedColor};
58718
+ }
58719
+
58720
+ ::slotted([role='option'][active-option]:hover) {
58721
+ background-color: ${fillHoverSelectedColor};
58722
+ }
58723
+
58680
58724
  .no-results-label {
58681
58725
  color: ${placeholderFontColor};
58682
58726
  height: ${controlHeight};
@@ -58885,7 +58929,9 @@ img.ProseMirror-separator {
58885
58929
  connectedCallback() {
58886
58930
  super.connectedCallback();
58887
58931
  this.forcedPosition = !!this.positionAttribute;
58888
- this.initializeOpenState();
58932
+ if (this.open) {
58933
+ this.initializeOpenState();
58934
+ }
58889
58935
  }
58890
58936
  get value() {
58891
58937
  Observable.track(this, 'value');
@@ -58984,7 +59030,7 @@ img.ProseMirror-separator {
58984
59030
  }
58985
59031
  super.clickHandler(e);
58986
59032
  this.open = this.collapsible && !this.open;
58987
- if (!this.open && this.indexWhenOpened !== this.selectedIndex) {
59033
+ if (!this.open && this.selectedIndex !== -1) {
58988
59034
  this.updateValue(true);
58989
59035
  }
58990
59036
  }
@@ -59094,22 +59140,19 @@ img.ProseMirror-separator {
59094
59140
  */
59095
59141
  inputHandler(e) {
59096
59142
  this.filter = this.filterInput?.value ?? '';
59097
- this.clearSelection();
59098
59143
  this.filterOptions();
59099
- if (this.filteredOptions.length > 0) {
59100
- const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
59101
- if (enabledOptions.length > 0) {
59102
- enabledOptions[0].selected = true;
59103
- }
59104
- else {
59105
- // only filtered option is disabled
59106
- this.selectedOptions = [];
59107
- this.selectedIndex = -1;
59108
- }
59144
+ const enabledOptions = this.filteredOptions.filter(o => !o.disabled);
59145
+ let activeOptionIndex = this.filter !== ''
59146
+ ? this.openActiveIndex ?? this.selectedIndex
59147
+ : this.selectedIndex;
59148
+ if (enabledOptions.length > 0
59149
+ && !enabledOptions.find(o => o === this.options[activeOptionIndex])) {
59150
+ activeOptionIndex = this.options.indexOf(enabledOptions[0]);
59109
59151
  }
59110
- else if (this.committedSelectedOption) {
59111
- this.committedSelectedOption.selected = true;
59152
+ else if (enabledOptions.length === 0) {
59153
+ activeOptionIndex = -1;
59112
59154
  }
59155
+ this.setActiveOption(activeOptionIndex);
59113
59156
  if (e.inputType.includes('deleteContent') || !this.filter.length) {
59114
59157
  return true;
59115
59158
  }
@@ -59130,11 +59173,13 @@ img.ProseMirror-separator {
59130
59173
  return true;
59131
59174
  }
59132
59175
  if (!this.options?.includes(focusTarget)) {
59176
+ let currentActiveIndex = this.openActiveIndex ?? this.selectedIndex;
59133
59177
  this.open = false;
59134
- if (this.selectedIndex === -1) {
59135
- this.selectedIndex = this.indexWhenOpened;
59178
+ if (currentActiveIndex === -1) {
59179
+ currentActiveIndex = this.selectedIndex;
59136
59180
  }
59137
- if (this.indexWhenOpened !== this.selectedIndex) {
59181
+ if (this.selectedIndex !== currentActiveIndex) {
59182
+ this.selectedIndex = currentActiveIndex;
59138
59183
  this.updateValue(true);
59139
59184
  }
59140
59185
  }
@@ -59144,11 +59189,13 @@ img.ProseMirror-separator {
59144
59189
  * @internal
59145
59190
  */
59146
59191
  keydownHandler(e) {
59192
+ const initialSelectedIndex = this.selectedIndex;
59147
59193
  super.keydownHandler(e);
59148
59194
  const key = e.key;
59149
59195
  if (e.ctrlKey || e.shiftKey) {
59150
59196
  return true;
59151
59197
  }
59198
+ let currentActiveIndex = this.openActiveIndex ?? this.selectedIndex;
59152
59199
  switch (key) {
59153
59200
  case keySpace: {
59154
59201
  // when dropdown is open allow user to enter a space for filter text
@@ -59189,24 +59236,16 @@ img.ProseMirror-separator {
59189
59236
  e.preventDefault();
59190
59237
  this.open = false;
59191
59238
  }
59192
- if (this.selectedIndex !== this.indexWhenOpened) {
59193
- this.options[this.selectedIndex].selected = false;
59194
- this.selectedIndex = this.indexWhenOpened;
59195
- }
59239
+ currentActiveIndex = this.selectedIndex;
59196
59240
  this.focus();
59197
59241
  break;
59198
59242
  }
59199
- case keyTab: {
59200
- if (this.collapsible && this.open) {
59201
- e.preventDefault();
59202
- this.open = false;
59203
- }
59204
- return true;
59205
- }
59206
59243
  }
59207
- if (!this.open && this.indexWhenOpened !== this.selectedIndex) {
59244
+ if (!this.open && this.selectedIndex !== currentActiveIndex) {
59245
+ this.selectedIndex = currentActiveIndex;
59246
+ }
59247
+ if (!this.open && initialSelectedIndex !== this.selectedIndex) {
59208
59248
  this.updateValue(true);
59209
- this.indexWhenOpened = this.selectedIndex;
59210
59249
  }
59211
59250
  return !(key === keyArrowDown || key === keyArrowUp);
59212
59251
  }
@@ -59224,8 +59263,28 @@ img.ProseMirror-separator {
59224
59263
  // implementation handles skipping non-selected disabled options for the initial
59225
59264
  // selected value.
59226
59265
  this.setSelectedOptions();
59266
+ if (this.open) {
59267
+ this.setActiveOption(this.selectedIndex);
59268
+ }
59227
59269
  this.updateValue();
59228
59270
  }
59271
+ /**
59272
+ * @internal
59273
+ * Fork of Listbox implementation, so that the selectedIndex is not changed while the dropdown
59274
+ * is open.
59275
+ */
59276
+ typeaheadBufferChanged(_, __) {
59277
+ if (this.$fastController.isConnected) {
59278
+ const typeaheadMatches = this.getTypeaheadMatches();
59279
+ if (typeaheadMatches.length) {
59280
+ const activeOptionIndex = this.options.indexOf(typeaheadMatches[0]);
59281
+ if (!(this.open && this.filterMode !== FilterMode.none)) {
59282
+ this.setActiveOption(activeOptionIndex);
59283
+ }
59284
+ }
59285
+ this.typeaheadExpired = false;
59286
+ }
59287
+ }
59229
59288
  /**
59230
59289
  * Synchronize the `aria-disabled` property when the `disabled` property changes.
59231
59290
  *
@@ -59254,30 +59313,52 @@ img.ProseMirror-separator {
59254
59313
  this.selectedIndex = 0;
59255
59314
  }
59256
59315
  }
59316
+ /**
59317
+ * @internal
59318
+ */
59257
59319
  selectNextOption() {
59258
59320
  // don't call super.selectNextOption as that relies on side-effecty
59259
59321
  // behavior to not select disabled option (which no longer works)
59260
- for (let i = this.selectedIndex + 1; i < this.options.length; i++) {
59322
+ const startIndex = this.openActiveIndex ?? this.selectedIndex;
59323
+ for (let i = startIndex + 1; i < this.options.length; i++) {
59261
59324
  const listOption = this.options[i];
59262
59325
  if (isNimbleListOption(listOption)
59263
59326
  && isOptionSelectable(listOption)) {
59264
- this.selectedIndex = i;
59327
+ this.setActiveOption(i);
59265
59328
  break;
59266
59329
  }
59267
59330
  }
59268
59331
  }
59332
+ /**
59333
+ * @internal
59334
+ */
59269
59335
  selectPreviousOption() {
59270
59336
  // don't call super.selectPreviousOption as that relies on side-effecty
59271
59337
  // behavior to not select disabled option (which no longer works)
59272
- for (let i = this.selectedIndex - 1; i >= 0; i--) {
59338
+ const startIndex = this.openActiveIndex ?? this.selectedIndex;
59339
+ for (let i = startIndex - 1; i >= 0; i--) {
59273
59340
  const listOption = this.options[i];
59274
59341
  if (isNimbleListOption(listOption)
59275
59342
  && isOptionSelectable(listOption)) {
59276
- this.selectedIndex = i;
59343
+ this.setActiveOption(i);
59277
59344
  break;
59278
59345
  }
59279
59346
  }
59280
59347
  }
59348
+ /**
59349
+ * @internal
59350
+ */
59351
+ selectFirstOption() {
59352
+ const newActiveOptionIndex = this.options.findIndex(o => isNimbleListOption(o) && isOptionSelectable(o));
59353
+ this.setActiveOption(newActiveOptionIndex);
59354
+ }
59355
+ /**
59356
+ * @internal
59357
+ */
59358
+ selectLastOption() {
59359
+ const newActiveOptionIndex = findLastIndex(this.options, o => isNimbleListOption(o) && isOptionSelectable(o));
59360
+ this.setActiveOption(newActiveOptionIndex);
59361
+ }
59281
59362
  /**
59282
59363
  * @internal
59283
59364
  */
@@ -59310,6 +59391,11 @@ img.ProseMirror-separator {
59310
59391
  });
59311
59392
  }
59312
59393
  }
59394
+ getTypeaheadMatches() {
59395
+ const matches = super.getTypeaheadMatches();
59396
+ // Don't allow placeholder to be matched
59397
+ return matches.filter(o => !o.hidden && !o.disabled);
59398
+ }
59313
59399
  positionChanged(_, next) {
59314
59400
  this.positionAttribute = next;
59315
59401
  this.setPositioning();
@@ -59335,9 +59421,13 @@ img.ProseMirror-separator {
59335
59421
  }
59336
59422
  if (this.open) {
59337
59423
  this.initializeOpenState();
59338
- this.indexWhenOpened = this.selectedIndex;
59339
59424
  return;
59340
59425
  }
59426
+ const activeOption = this.options[this.openActiveIndex ?? this.selectedIndex];
59427
+ if (isNimbleListOption(activeOption)) {
59428
+ activeOption.activeOption = false;
59429
+ }
59430
+ this.openActiveIndex = undefined;
59341
59431
  this.filter = '';
59342
59432
  if (this.filterInput) {
59343
59433
  this.filterInput.value = '';
@@ -59404,6 +59494,40 @@ img.ProseMirror-separator {
59404
59494
  }
59405
59495
  this.committedSelectedOption = options[this.selectedIndex];
59406
59496
  }
59497
+ setActiveOption(newActiveIndex) {
59498
+ const activeOption = this.options[newActiveIndex];
59499
+ if (this.open) {
59500
+ if (isNimbleListOption(activeOption)) {
59501
+ activeOption.activeOption = true;
59502
+ }
59503
+ const previousActiveIndex = this.openActiveIndex ?? this.selectedIndex;
59504
+ const previousActiveOption = this.options[previousActiveIndex];
59505
+ if (previousActiveIndex !== newActiveIndex
59506
+ && isNimbleListOption(previousActiveOption)) {
59507
+ previousActiveOption.activeOption = false;
59508
+ }
59509
+ this.openActiveIndex = newActiveIndex;
59510
+ this.focusAndScrollActiveOptionIntoView();
59511
+ }
59512
+ else {
59513
+ this.selectedIndex = newActiveIndex;
59514
+ }
59515
+ this.ariaActiveDescendant = activeOption?.id ?? '';
59516
+ }
59517
+ focusAndScrollActiveOptionIntoView() {
59518
+ const optionToFocus = this.options[this.openActiveIndex ?? this.selectedIndex];
59519
+ // Copied from FAST: To ensure that the browser handles both `focus()` and
59520
+ // `scrollIntoView()`, the timing here needs to guarantee that they happen on
59521
+ // different frames. Since this function is typically called from the `openChanged`
59522
+ // observer, `DOM.queueUpdate` causes the calls to be grouped into the same frame.
59523
+ // To prevent this, `requestAnimationFrame` is used instead of `DOM.queueUpdate`.
59524
+ if (optionToFocus !== undefined && this.contains(optionToFocus)) {
59525
+ optionToFocus.focus();
59526
+ requestAnimationFrame(() => {
59527
+ optionToFocus.scrollIntoView({ block: 'nearest' });
59528
+ });
59529
+ }
59530
+ }
59407
59531
  committedSelectedOptionChanged() {
59408
59532
  this.updateDisplayValue();
59409
59533
  }
@@ -59499,11 +59623,6 @@ img.ProseMirror-separator {
59499
59623
  });
59500
59624
  }
59501
59625
  }
59502
- clearSelection() {
59503
- this.options.forEach(option => {
59504
- option.selected = false;
59505
- });
59506
- }
59507
59626
  filterChanged() {
59508
59627
  this.filterOptions();
59509
59628
  }
@@ -59511,12 +59630,8 @@ img.ProseMirror-separator {
59511
59630
  this.updateListboxMaxHeightCssVariable();
59512
59631
  }
59513
59632
  initializeOpenState() {
59514
- if (!this.open) {
59515
- this.ariaExpanded = 'false';
59516
- this.ariaControls = '';
59517
- return;
59518
- }
59519
59633
  this.committedSelectedOption = this.options[this.selectedIndex];
59634
+ this.setActiveOption(this.selectedIndex);
59520
59635
  this.ariaControls = this.listboxId;
59521
59636
  this.ariaExpanded = 'true';
59522
59637
  this.setPositioning();