@aurodesignsystem-dev/auro-formkit 0.0.0-pr1576.7 → 0.0.0-pr1577.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 (51) hide show
  1. package/components/checkbox/demo/customize.min.js +1 -1
  2. package/components/checkbox/demo/getting-started.min.js +1 -1
  3. package/components/checkbox/demo/index.min.js +1 -1
  4. package/components/checkbox/dist/index.js +1 -1
  5. package/components/checkbox/dist/registered.js +1 -1
  6. package/components/combobox/demo/customize.md +0 -40
  7. package/components/combobox/demo/customize.min.js +65 -359
  8. package/components/combobox/demo/getting-started.min.js +65 -359
  9. package/components/combobox/demo/index.min.js +65 -359
  10. package/components/combobox/dist/index.js +38 -56
  11. package/components/combobox/dist/registered.js +38 -56
  12. package/components/counter/demo/customize.min.js +11 -2
  13. package/components/counter/demo/index.min.js +11 -2
  14. package/components/counter/dist/index.js +11 -2
  15. package/components/counter/dist/registered.js +11 -2
  16. package/components/datepicker/demo/customize.min.js +12 -3
  17. package/components/datepicker/demo/index.min.js +12 -3
  18. package/components/datepicker/dist/index.js +12 -3
  19. package/components/datepicker/dist/registered.js +12 -3
  20. package/components/dropdown/demo/customize.min.js +10 -1
  21. package/components/dropdown/demo/getting-started.min.js +10 -1
  22. package/components/dropdown/demo/index.min.js +10 -1
  23. package/components/dropdown/dist/index.js +10 -1
  24. package/components/dropdown/dist/registered.js +10 -1
  25. package/components/form/demo/customize.min.js +102 -369
  26. package/components/form/demo/getting-started.min.js +102 -369
  27. package/components/form/demo/index.min.js +102 -369
  28. package/components/form/demo/registerDemoDeps.min.js +102 -369
  29. package/components/input/demo/customize.min.js +1 -1
  30. package/components/input/demo/getting-started.min.js +1 -1
  31. package/components/input/demo/index.min.js +1 -1
  32. package/components/input/dist/index.js +1 -1
  33. package/components/input/dist/registered.js +1 -1
  34. package/components/menu/demo/index.min.js +27 -303
  35. package/components/menu/dist/auro-menu-utils.d.ts +0 -30
  36. package/components/menu/dist/auro-menu.d.ts +0 -23
  37. package/components/menu/dist/index.js +27 -303
  38. package/components/menu/dist/registered.js +27 -303
  39. package/components/radio/demo/customize.min.js +1 -1
  40. package/components/radio/demo/getting-started.min.js +1 -1
  41. package/components/radio/demo/index.min.js +1 -1
  42. package/components/radio/dist/index.js +1 -1
  43. package/components/radio/dist/registered.js +1 -1
  44. package/components/select/demo/customize.md +0 -72
  45. package/components/select/demo/customize.min.js +38 -305
  46. package/components/select/demo/getting-started.min.js +38 -305
  47. package/components/select/demo/index.min.js +38 -305
  48. package/components/select/dist/index.js +11 -2
  49. package/components/select/dist/registered.js +11 -2
  50. package/custom-elements.json +1511 -1614
  51. package/package.json +1 -1
@@ -279,107 +279,6 @@ function isSelectableByValue(option) {
279
279
  !option.hasAttribute('static');
280
280
  }
281
281
 
282
- /* eslint-disable no-underscore-dangle */
283
- /**
284
- * Resolves the single selected option for a given `value`, preferring the
285
- * option tracked by `selectedKey` (a user-initiated selection) over a
286
- * first-by-value match. When multiple options share the same `value`, matching
287
- * by `value` alone cannot distinguish which one the user picked; the key
288
- * disambiguates it.
289
- *
290
- * The key is trusted only when it still resolves to an option whose `value`
291
- * matches the requested `value`. If the key is stale (option removed) or the
292
- * value was changed programmatically, resolution falls back to value matching —
293
- * preserving backward-compatible behavior for preselection and `selectByValue`.
294
- * @private
295
- * @param {Array<HTMLElement>} items - The menu's flat option list.
296
- * @param {string} value - The value to resolve.
297
- * @param {string|undefined} selectedKey - The `_optionKey` of the user-selected option, if any.
298
- * @returns {HTMLElement|undefined} The resolved option, or undefined when none match.
299
- */
300
- function resolveSelectedOption(items, value, selectedKey) {
301
- if (!items) {
302
- return undefined;
303
- }
304
-
305
- if (selectedKey !== undefined) {
306
- const keyed = items.find((item) => item._optionKey === selectedKey);
307
- if (keyed && isSelectableByValue(keyed) && keyed.value === value) {
308
- return keyed;
309
- }
310
- // Key exists but the option is gone or its value no longer matches — fall
311
- // through to value-based matching.
312
- }
313
-
314
- return items.find((item) => isSelectableByValue(item) && item.value === value);
315
- }
316
-
317
- /**
318
- * Resolves the selected options for a multi-select `value` array, preferring
319
- * options tracked by `selectedKeys` (user-initiated selections) and falling
320
- * back to value matching for any values not resolved by key. The result is
321
- * always sorted into DOM order regardless of selection sequence.
322
- * @private
323
- * @param {Array<HTMLElement>} items - The menu's flat option list.
324
- * @param {Array<string>} valueArray - The selected values.
325
- * @param {Array<string>|undefined} selectedKeys - The `_optionKey`s of the user-selected options, if any.
326
- * @returns {Array<HTMLElement>} The resolved options in DOM order.
327
- */
328
- function resolveSelectedOptions(items, valueArray, selectedKeys) {
329
- if (!items) {
330
- return [];
331
- }
332
-
333
- const resolved = [];
334
-
335
- // Track how many of each value are still available to resolve. A value that
336
- // appears N times in `valueArray` may be satisfied at most N times total across
337
- // the key pass and the value fallback below — matching by count, not presence,
338
- // on BOTH passes. This stops a duplicate value from being over-resolved: e.g.
339
- // two keyed options that both carry `SEA` cannot both match a single requested
340
- // `SEA` (which happens when `value` is set directly without clearing
341
- // `_selectedKey`, so more keys survive than the value set now asks for).
342
- const remaining = new Map();
343
- valueArray.forEach((val) => remaining.set(val, (remaining.get(val) || 0) + 1));
344
-
345
- // Resolve by key first: trust a key only when its option is still selectable
346
- // and there is still an unmatched occurrence of its value in the request set.
347
- if (Array.isArray(selectedKeys)) {
348
- selectedKeys.forEach((key) => {
349
- const keyed = items.find((item) => item._optionKey === key);
350
- if (keyed && isSelectableByValue(keyed) && (remaining.get(keyed.value) || 0) > 0 && !resolved.includes(keyed)) {
351
- resolved.push(keyed);
352
- remaining.set(keyed.value, remaining.get(keyed.value) - 1);
353
- }
354
- });
355
- }
356
-
357
- // Fall back to value matching for the occurrences not resolved by key. Iterate
358
- // the leftover per-value counts so a value that appears twice but was only
359
- // resolved once by key still matches its remaining occurrence(s).
360
- remaining.forEach((count, val) => {
361
- for (let occurrence = 0; occurrence < count; occurrence += 1) {
362
- const option = items.find((item) => isSelectableByValue(item) && item.value === val && !resolved.includes(item));
363
- if (option) {
364
- resolved.push(option);
365
- }
366
- }
367
- });
368
-
369
- // Always return in DOM order so display is consistent regardless of the order
370
- // keys/values were selected. Every resolved option came from `items`, so an
371
- // O(1) index lookup mirrors `_sortSelectedByDomOrder` and avoids the O(n)
372
- // `items.indexOf` per comparison for large combobox option sets.
373
- const indexMap = new Map(items.map((item, index) => [
374
- item,
375
- index
376
- ]));
377
- resolved.sort((optionA, optionB) => indexMap.get(optionA) - indexMap.get(optionB));
378
-
379
- return resolved;
380
- }
381
- /* eslint-enable no-underscore-dangle */
382
-
383
282
  /**
384
283
  * Helper method to dispatch custom events.
385
284
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -405,14 +304,6 @@ function dispatchMenuEvent(element, eventName, detail = null) {
405
304
  // See LICENSE in the project root for license information.
406
305
 
407
306
 
408
- /**
409
- * Monotonically increasing counter used to give each menu instance a unique
410
- * `_menuInstanceId` prefix. Auto-generating the id (rather than using a random
411
- * string) keeps option keys deterministic and collision-free across menus.
412
- * @private
413
- */
414
- let menuInstanceIdCounter = 0;
415
-
416
307
 
417
308
  /**
418
309
  * The `auro-menu` element provides users a way to select from a list of options.
@@ -484,8 +375,9 @@ class AuroMenu extends AuroElement {
484
375
 
485
376
  // Instance properties (non-reactive)
486
377
 
487
- menuInstanceIdCounter += 1;
488
-
378
+ /**
379
+ * @private
380
+ */
489
381
  Object.assign(this, {
490
382
  // Root-level menu (true) or a nested submenu (false)
491
383
  rootMenu: true,
@@ -495,21 +387,6 @@ class AuroMenu extends AuroElement {
495
387
  nestingSpacer: '<span class="nestingSpacer"></span>',
496
388
  // Loading indicator for slot elements
497
389
  loadingSlots: null,
498
- // Unique id for this menu instance; prefixes every auto-generated option
499
- // key so keys never collide across menus in the same document.
500
- _menuInstanceId: `menu-${menuInstanceIdCounter}`,
501
- // Monotonically increasing counter for option key generation. Never
502
- // resets, so a key is never reused within this instance's lifetime.
503
- _optionKeyCounter: 0,
504
- // Key(s) of the option(s) the user has actively selected. A single string
505
- // in single-select, an array in multi-select, undefined when nothing is
506
- // user-selected. Used to disambiguate options that share a `value`.
507
- _selectedKey: undefined,
508
- // True only for the one updated() cycle following a user selection, so
509
- // reconciliation trusts `_selectedKey`. A `value` change from a consumer's
510
- // direct property assignment leaves this false, dropping the stale key so
511
- // reconciliation falls back to first-by-value (see updated()).
512
- _valueChangeFromSelection: false,
513
390
  });
514
391
  }
515
392
 
@@ -729,13 +606,6 @@ class AuroMenu extends AuroElement {
729
606
  return;
730
607
  }
731
608
 
732
- // A programmatic value set carries no positional intent, so drop any
733
- // `_selectedKey` left over from a prior user click. This makes reconciliation
734
- // in updated() fall back to first-by-value (single) / value-in-DOM-order
735
- // (multi), matching the documented contract for programmatic selection even
736
- // when a stale key would still resolve to a duplicate-value option.
737
- this._selectedKey = undefined;
738
-
739
609
  // `value` is a String property; stringify arrays so attribute reflection and `formattedValue` parsing stay correct.
740
610
  this.value = Array.isArray(value) ? JSON.stringify(value) : value;
741
611
  }
@@ -783,17 +653,6 @@ class AuroMenu extends AuroElement {
783
653
  updated(changedProperties) {
784
654
  super.updated(changedProperties);
785
655
 
786
- // Consume the selection-driven flag for THIS cycle up front. Clearing it
787
- // unconditionally — not only inside the `value` branch below — prevents it
788
- // from lingering `true` when a selection produces a serialized `value`
789
- // byte-identical to the current one, in which case Lit schedules no
790
- // `value`-change cycle to consume it. A lingering flag would misclassify a
791
- // later consumer's programmatic `value` set as selection-driven and keep a
792
- // stale `_selectedKey`. The reconcile path below re-sets the instance flag
793
- // after this point, so its intentional cross-cycle hand-off still works.
794
- const valueChangeFromSelection = this._valueChangeFromSelection;
795
- this._valueChangeFromSelection = false;
796
-
797
656
  // Single source of truth for 'auroMenu-selectedOption'. Selection handlers
798
657
  // mutate optionSelected and let Lit's update cycle dispatch here; the prior
799
658
  // .value comparison missed multi-select array changes and combined with the
@@ -817,17 +676,6 @@ class AuroMenu extends AuroElement {
817
676
  this.initItems();
818
677
  }
819
678
 
820
- // Distinguish a selection-driven `value` change (a user click, which set
821
- // the flag in handleSelectState / _sortSelectedByDomOrder) from a
822
- // programmatic assignment by a consumer. A programmatic set carries no
823
- // positional intent, so drop any leftover `_selectedKey` and let
824
- // reconciliation fall back to first-by-value (single) / value-in-DOM-order
825
- // (multi) — the same contract selectByValue() guarantees, even when a
826
- // stale key would otherwise still resolve to a duplicate-value option.
827
- if (!valueChangeFromSelection) {
828
- this._selectedKey = undefined;
829
- }
830
-
831
679
  // Set when reconciliation reassigns `value` below. That reassignment schedules a
832
680
  // second updated() cycle, so the `event`-attribute dispatch is deferred to that
833
681
  // cycle to avoid firing option custom events twice on the same selection.
@@ -845,55 +693,19 @@ class AuroMenu extends AuroElement {
845
693
  // Defensive default: `formattedValue` can be undefined for unexpected value types,
846
694
  // and calling `.includes` on undefined would throw during reconciliation.
847
695
  const valueArray = this.formattedValue || [];
848
- // Resolve by key first (the user's exact picks), then fall back to
849
- // value matching for any values not resolved by key — so pre-selection
850
- // and programmatic value sets keep working. Result is DOM-ordered.
851
- const matchingOptions = resolveSelectedOptions(this.items, valueArray, this._selectedKey);
696
+ const matchingOptions = this.items ? this.items.filter((item) => isSelectableByValue(item) && valueArray.includes(item.value)) : [];
852
697
  newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
853
698
 
854
- // Reconcile `value` with the selectable set. An occurrence is dropped
855
- // only when it is loaded but no selectable option can satisfy it —
856
- // every loaded item sharing that value is non-selectable, or the value
857
- // recurs more often than it has selectable options (a duplicate value
858
- // whose extra siblings are disabled/static). This is count-based, not
859
- // presence-based, so an enabled option is kept even when a disabled
860
- // sibling shares its value — mirroring how `resolveSelectedOptions`
861
- // resolves the same set. Entries with no matching item yet are
862
- // preserved so async preselection still works, and the toggle handlers
863
- // rebuild `value` from `formattedValue`, so a rejected entry cannot
864
- // resurface on the next select/deselect.
865
- const selectableByValue = new Map();
866
- const loadedValues = new Set();
867
- if (this.items) {
868
- this.items.forEach((item) => {
869
- loadedValues.add(item.value);
870
- if (isSelectableByValue(item)) {
871
- selectableByValue.set(item.value, (selectableByValue.get(item.value) || 0) + 1);
872
- }
873
- });
874
- }
875
-
876
- const reconciled = valueArray.filter((val) => {
877
- // Not loaded yet (async preselection) — keep for a later cycle.
878
- if (!loadedValues.has(val)) {
879
- return true;
880
- }
881
- // Consume one selectable option per occurrence; drop once exhausted.
882
- const remaining = selectableByValue.get(val) || 0;
883
- if (remaining > 0) {
884
- selectableByValue.set(val, remaining - 1);
885
- return true;
886
- }
887
- return false;
888
- });
889
-
890
- if (reconciled.length !== valueArray.length) {
891
- // This is an internal correction, not a consumer's programmatic set,
892
- // so preserve the selection-driven flag through the re-entrant
893
- // updated() cycle it schedules. Otherwise that cycle would treat the
894
- // reassignment as programmatic and drop `_selectedKey` mid-cascade,
895
- // flipping resolution and looping.
896
- this._valueChangeFromSelection = true;
699
+ // Reconcile `value` with the selectable set. Drop only entries whose option is
700
+ // loaded but non-selectable (disabled/static) — leaving them would desync `value`
701
+ // from `optionSelected`, and the toggle handlers rebuild `value` from `formattedValue`,
702
+ // so the rejected entry would resurface on the next select/deselect. Entries with no
703
+ // matching item yet are preserved so async preselection still works once options render.
704
+ const rejectedValues = this.items
705
+ ? this.items.filter((item) => !isSelectableByValue(item) && valueArray.includes(item.value)).map((item) => item.value)
706
+ : [];
707
+ if (rejectedValues.length > 0) {
708
+ const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
897
709
  this.value = serializeMultiSelectValue(reconciled);
898
710
  valueReconciled = true;
899
711
  }
@@ -905,11 +717,7 @@ class AuroMenu extends AuroElement {
905
717
  // `hidden` is intentionally NOT excluded: the combobox toggles
906
718
  // `hidden` as its type-ahead filter, so a filtered-out option is
907
719
  // still a valid programmatic selection.
908
- // Prefer the option the user actually selected (tracked by
909
- // `_selectedKey`) so a click on the second of two options sharing a
910
- // `value` resolves back to that exact element instead of the first
911
- // value match. Falls back to first-by-value for programmatic sets.
912
- const matchingOption = resolveSelectedOption(this.items, this.value, this._selectedKey);
720
+ const matchingOption = this.items ? this.items.find((item) => isSelectableByValue(item) && item.value === this.value) : undefined;
913
721
 
914
722
  if (matchingOption) {
915
723
  newSelected = matchingOption;
@@ -1137,14 +945,6 @@ class AuroMenu extends AuroElement {
1137
945
  }
1138
946
  });
1139
947
 
1140
- // Assign private keys once items are populated. Only the root menu assigns
1141
- // keys: its `items` is a deep query that already includes nested submenu
1142
- // options, so a single pass keys the entire tree. Nested menus skip this
1143
- // and inherit keys from the root.
1144
- if (this.rootMenu) {
1145
- this._assignOptionKeys();
1146
- }
1147
-
1148
948
  if (this.noCheckmark) {
1149
949
  this.updateItemsState(new Map([
1150
950
  [
@@ -1161,31 +961,6 @@ class AuroMenu extends AuroElement {
1161
961
  }));
1162
962
  }
1163
963
 
1164
- /**
1165
- * Assigns a private, auto-generated unique key (`_optionKey`) to each menu
1166
- * option that does not already have one. Keys are internal state on the
1167
- * element instance — never reflected as an attribute or exposed publicly —
1168
- * and let selection tracking distinguish options that share the same `value`.
1169
- *
1170
- * The `_optionKey === undefined` guard makes this idempotent: options keep the
1171
- * key they were first assigned across re-renders and slot changes, and if a
1172
- * nested menu's lifecycle runs a pass before the root, options simply wait for
1173
- * the root to key them (or keep whatever key they already hold).
1174
- * @private
1175
- */
1176
- _assignOptionKeys() {
1177
- if (!this.items) {
1178
- return;
1179
- }
1180
-
1181
- this.items.forEach((option) => {
1182
- if (option._optionKey === undefined) {
1183
- this._optionKeyCounter += 1;
1184
- option._optionKey = `${this._menuInstanceId}-${this._optionKeyCounter}`;
1185
- }
1186
- });
1187
- }
1188
-
1189
964
  // Logic Methods
1190
965
 
1191
966
  /**
@@ -1195,28 +970,24 @@ class AuroMenu extends AuroElement {
1195
970
  */
1196
971
  handleSelectState(option) {
1197
972
  if (this.multiSelect) {
973
+ const currentValue = this.formattedValue || [];
1198
974
  const currentSelected = this.optionSelected || [];
1199
975
 
976
+ if (!currentValue.includes(option.value)) {
977
+ this.value = serializeMultiSelectValue([
978
+ ...currentValue,
979
+ option.value
980
+ ]);
981
+ }
1200
982
  if (!currentSelected.includes(option)) {
1201
983
  this.optionSelected = [
1202
984
  ...currentSelected,
1203
985
  option
1204
986
  ];
1205
987
  }
1206
-
1207
- // Re-sort by DOM order and rebuild `_selectedKey`/`value` from the
1208
- // selected set so display order stays consistent with the menu, not with
1209
- // click order.
1210
- this._sortSelectedByDomOrder();
1211
988
  } else {
1212
989
  this.value = option.value;
1213
990
  this.optionSelected = option;
1214
- // Track the specific option the user selected so the value→option
1215
- // reconciliation in updated() resolves back to this exact element even
1216
- // when another option shares the same `value`.
1217
- this._selectedKey = option._optionKey;
1218
- // Mark this `value` change as selection-driven so updated() trusts the key.
1219
- this._valueChangeFromSelection = true;
1220
991
  }
1221
992
 
1222
993
  this._index = this.items.indexOf(option);
@@ -1229,22 +1000,18 @@ class AuroMenu extends AuroElement {
1229
1000
  */
1230
1001
  handleDeselectState(option) {
1231
1002
  if (this.multiSelect) {
1232
- // Remove this exact element from the selection (identity, not value — two
1233
- // options can share a `value`), then rebuild `value`/`_selectedKey` from
1234
- // the remaining set in DOM order. An empty result collapses to undefined.
1235
- this.optionSelected = this.optionSelected.filter((selected) => selected !== option);
1003
+ // Remove this option from array; an empty result collapses `value` to undefined.
1004
+ const newFormattedValue = (this.formattedValue || []).filter((val) => val !== option.value);
1005
+ this.value = serializeMultiSelectValue(newFormattedValue);
1006
+
1007
+ this.optionSelected = this.optionSelected.filter((val) => val !== option);
1236
1008
  if (this.optionSelected.length === 0) {
1237
1009
  this.optionSelected = undefined;
1238
- this._selectedKey = undefined;
1239
- this.value = undefined;
1240
- } else {
1241
- this._sortSelectedByDomOrder();
1242
1010
  }
1243
1011
  } else {
1244
1012
  // For single-select: Back to undefined when deselected
1245
1013
  this.value = undefined;
1246
1014
  this.optionSelected = undefined;
1247
- this._selectedKey = undefined;
1248
1015
  }
1249
1016
 
1250
1017
  // Update the index tracking
@@ -1268,38 +1035,9 @@ class AuroMenu extends AuroElement {
1268
1035
  clearSelection() {
1269
1036
  this.optionSelected = undefined;
1270
1037
  this.value = undefined;
1271
- this._selectedKey = undefined;
1272
1038
  this._index = -1;
1273
1039
  }
1274
1040
 
1275
- /**
1276
- * Re-sorts the multi-select selection into DOM order and rebuilds the derived
1277
- * `_selectedKey` and `value` from `optionSelected`. Selection is always stored
1278
- * and serialized in the order options appear in the menu, never in click
1279
- * order — so selecting C then A yields `[A, C]`.
1280
- * @private
1281
- */
1282
- _sortSelectedByDomOrder() {
1283
- if (!this.multiSelect || !Array.isArray(this.optionSelected) || !this.items) {
1284
- return;
1285
- }
1286
-
1287
- const indexMap = new Map(this.items.map((item, index) => [
1288
- item,
1289
- index
1290
- ]));
1291
-
1292
- // Sort any element no longer in `items` (a stale selection left over from a
1293
- // dynamic rebuild that the consumer has not cleared) to the END rather than
1294
- // the front, so it never displaces a live option to the head of the
1295
- // serialized order. Value reconciliation drops it on the next updated() cycle.
1296
- this.optionSelected.sort((optionA, optionB) => (indexMap.get(optionA) ?? this.items.length) - (indexMap.get(optionB) ?? this.items.length));
1297
- this._selectedKey = this.optionSelected.map((option) => option._optionKey);
1298
- this.value = serializeMultiSelectValue(this.optionSelected.map((option) => option.value));
1299
- // Mark this `value` change as selection-driven so updated() trusts the keys.
1300
- this._valueChangeFromSelection = true;
1301
- }
1302
-
1303
1041
  /**
1304
1042
  * Resets the menu to its initial state.
1305
1043
  * This is the only way to return value to undefined.
@@ -1309,7 +1047,6 @@ class AuroMenu extends AuroElement {
1309
1047
  // Reset to undefined - initial state
1310
1048
  this.value = undefined;
1311
1049
  this.optionSelected = undefined;
1312
- this._selectedKey = undefined;
1313
1050
  this._index = -1;
1314
1051
 
1315
1052
  // Clear active option state so a follow-up open/navigation starts fresh
@@ -1371,19 +1108,6 @@ class AuroMenu extends AuroElement {
1371
1108
  this.initItems();
1372
1109
  }
1373
1110
 
1374
- // Recover `_index` from the highlighted option when it has been reset to -1.
1375
- // In multi-select, deselecting the last remaining option collapses the value
1376
- // to undefined, and the updated() reconciliation resets `_index = -1` even
1377
- // though `optionActive` still points at the highlighted option. Without this,
1378
- // reading `items[-1]` returns undefined and the re-select no-ops until the
1379
- // highlight is moved away and back. Mirrors auro-combobox's reconcileMenuIndex.
1380
- if (this._index < 0 && this.optionActive && this.items) {
1381
- const activeIndex = this.items.indexOf(this.optionActive);
1382
- if (activeIndex >= 0) {
1383
- this._index = activeIndex;
1384
- }
1385
- }
1386
-
1387
1111
  // Get currently selected menu option based on index
1388
1112
  const option = this.items ? this.items[this._index] : undefined;
1389
1113