@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
@@ -12268,7 +12268,7 @@ class AuroHelpText extends i$3 {
12268
12268
  }
12269
12269
  }
12270
12270
 
12271
- var formkitVersion = '202608041729';
12271
+ var formkitVersion = '202608041712';
12272
12272
 
12273
12273
  /**
12274
12274
  * @license
@@ -12268,7 +12268,7 @@ class AuroHelpText extends i$3 {
12268
12268
  }
12269
12269
  }
12270
12270
 
12271
- var formkitVersion = '202608041729';
12271
+ var formkitVersion = '202608041712';
12272
12272
 
12273
12273
  /**
12274
12274
  * @license
@@ -12268,7 +12268,7 @@ class AuroHelpText extends i$3 {
12268
12268
  }
12269
12269
  }
12270
12270
 
12271
- var formkitVersion = '202608041729';
12271
+ var formkitVersion = '202608041712';
12272
12272
 
12273
12273
  /**
12274
12274
  * @license
@@ -12210,7 +12210,7 @@ class AuroHelpText extends LitElement {
12210
12210
  }
12211
12211
  }
12212
12212
 
12213
- var formkitVersion = '202608041729';
12213
+ var formkitVersion = '202608041712';
12214
12214
 
12215
12215
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12216
12216
  // See LICENSE in the project root for license information.
@@ -12210,7 +12210,7 @@ class AuroHelpText extends LitElement {
12210
12210
  }
12211
12211
  }
12212
12212
 
12213
- var formkitVersion = '202608041729';
12213
+ var formkitVersion = '202608041712';
12214
12214
 
12215
12215
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12216
12216
  // See LICENSE in the project root for license information.
@@ -259,107 +259,6 @@ function isSelectableByValue(option) {
259
259
  !option.hasAttribute('static');
260
260
  }
261
261
 
262
- /* eslint-disable no-underscore-dangle */
263
- /**
264
- * Resolves the single selected option for a given `value`, preferring the
265
- * option tracked by `selectedKey` (a user-initiated selection) over a
266
- * first-by-value match. When multiple options share the same `value`, matching
267
- * by `value` alone cannot distinguish which one the user picked; the key
268
- * disambiguates it.
269
- *
270
- * The key is trusted only when it still resolves to an option whose `value`
271
- * matches the requested `value`. If the key is stale (option removed) or the
272
- * value was changed programmatically, resolution falls back to value matching —
273
- * preserving backward-compatible behavior for preselection and `selectByValue`.
274
- * @private
275
- * @param {Array<HTMLElement>} items - The menu's flat option list.
276
- * @param {string} value - The value to resolve.
277
- * @param {string|undefined} selectedKey - The `_optionKey` of the user-selected option, if any.
278
- * @returns {HTMLElement|undefined} The resolved option, or undefined when none match.
279
- */
280
- function resolveSelectedOption(items, value, selectedKey) {
281
- if (!items) {
282
- return undefined;
283
- }
284
-
285
- if (selectedKey !== undefined) {
286
- const keyed = items.find((item) => item._optionKey === selectedKey);
287
- if (keyed && isSelectableByValue(keyed) && keyed.value === value) {
288
- return keyed;
289
- }
290
- // Key exists but the option is gone or its value no longer matches — fall
291
- // through to value-based matching.
292
- }
293
-
294
- return items.find((item) => isSelectableByValue(item) && item.value === value);
295
- }
296
-
297
- /**
298
- * Resolves the selected options for a multi-select `value` array, preferring
299
- * options tracked by `selectedKeys` (user-initiated selections) and falling
300
- * back to value matching for any values not resolved by key. The result is
301
- * always sorted into DOM order regardless of selection sequence.
302
- * @private
303
- * @param {Array<HTMLElement>} items - The menu's flat option list.
304
- * @param {Array<string>} valueArray - The selected values.
305
- * @param {Array<string>|undefined} selectedKeys - The `_optionKey`s of the user-selected options, if any.
306
- * @returns {Array<HTMLElement>} The resolved options in DOM order.
307
- */
308
- function resolveSelectedOptions(items, valueArray, selectedKeys) {
309
- if (!items) {
310
- return [];
311
- }
312
-
313
- const resolved = [];
314
-
315
- // Track how many of each value are still available to resolve. A value that
316
- // appears N times in `valueArray` may be satisfied at most N times total across
317
- // the key pass and the value fallback below — matching by count, not presence,
318
- // on BOTH passes. This stops a duplicate value from being over-resolved: e.g.
319
- // two keyed options that both carry `SEA` cannot both match a single requested
320
- // `SEA` (which happens when `value` is set directly without clearing
321
- // `_selectedKey`, so more keys survive than the value set now asks for).
322
- const remaining = new Map();
323
- valueArray.forEach((val) => remaining.set(val, (remaining.get(val) || 0) + 1));
324
-
325
- // Resolve by key first: trust a key only when its option is still selectable
326
- // and there is still an unmatched occurrence of its value in the request set.
327
- if (Array.isArray(selectedKeys)) {
328
- selectedKeys.forEach((key) => {
329
- const keyed = items.find((item) => item._optionKey === key);
330
- if (keyed && isSelectableByValue(keyed) && (remaining.get(keyed.value) || 0) > 0 && !resolved.includes(keyed)) {
331
- resolved.push(keyed);
332
- remaining.set(keyed.value, remaining.get(keyed.value) - 1);
333
- }
334
- });
335
- }
336
-
337
- // Fall back to value matching for the occurrences not resolved by key. Iterate
338
- // the leftover per-value counts so a value that appears twice but was only
339
- // resolved once by key still matches its remaining occurrence(s).
340
- remaining.forEach((count, val) => {
341
- for (let occurrence = 0; occurrence < count; occurrence += 1) {
342
- const option = items.find((item) => isSelectableByValue(item) && item.value === val && !resolved.includes(item));
343
- if (option) {
344
- resolved.push(option);
345
- }
346
- }
347
- });
348
-
349
- // Always return in DOM order so display is consistent regardless of the order
350
- // keys/values were selected. Every resolved option came from `items`, so an
351
- // O(1) index lookup mirrors `_sortSelectedByDomOrder` and avoids the O(n)
352
- // `items.indexOf` per comparison for large combobox option sets.
353
- const indexMap = new Map(items.map((item, index) => [
354
- item,
355
- index
356
- ]));
357
- resolved.sort((optionA, optionB) => indexMap.get(optionA) - indexMap.get(optionB));
358
-
359
- return resolved;
360
- }
361
- /* eslint-enable no-underscore-dangle */
362
-
363
262
  /**
364
263
  * Helper method to dispatch custom events.
365
264
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -398,14 +297,6 @@ const t={ATTRIBUTE:1},e$1=t=>(...e)=>({_$litDirective$:t,values:e});let i$1 = cl
398
297
  // See LICENSE in the project root for license information.
399
298
 
400
299
 
401
- /**
402
- * Monotonically increasing counter used to give each menu instance a unique
403
- * `_menuInstanceId` prefix. Auto-generating the id (rather than using a random
404
- * string) keeps option keys deterministic and collision-free across menus.
405
- * @private
406
- */
407
- let menuInstanceIdCounter = 0;
408
-
409
300
 
410
301
  /**
411
302
  * The `auro-menu` element provides users a way to select from a list of options.
@@ -477,8 +368,9 @@ class AuroMenu extends AuroElement {
477
368
 
478
369
  // Instance properties (non-reactive)
479
370
 
480
- menuInstanceIdCounter += 1;
481
-
371
+ /**
372
+ * @private
373
+ */
482
374
  Object.assign(this, {
483
375
  // Root-level menu (true) or a nested submenu (false)
484
376
  rootMenu: true,
@@ -488,21 +380,6 @@ class AuroMenu extends AuroElement {
488
380
  nestingSpacer: '<span class="nestingSpacer"></span>',
489
381
  // Loading indicator for slot elements
490
382
  loadingSlots: null,
491
- // Unique id for this menu instance; prefixes every auto-generated option
492
- // key so keys never collide across menus in the same document.
493
- _menuInstanceId: `menu-${menuInstanceIdCounter}`,
494
- // Monotonically increasing counter for option key generation. Never
495
- // resets, so a key is never reused within this instance's lifetime.
496
- _optionKeyCounter: 0,
497
- // Key(s) of the option(s) the user has actively selected. A single string
498
- // in single-select, an array in multi-select, undefined when nothing is
499
- // user-selected. Used to disambiguate options that share a `value`.
500
- _selectedKey: undefined,
501
- // True only for the one updated() cycle following a user selection, so
502
- // reconciliation trusts `_selectedKey`. A `value` change from a consumer's
503
- // direct property assignment leaves this false, dropping the stale key so
504
- // reconciliation falls back to first-by-value (see updated()).
505
- _valueChangeFromSelection: false,
506
383
  });
507
384
  }
508
385
 
@@ -722,13 +599,6 @@ class AuroMenu extends AuroElement {
722
599
  return;
723
600
  }
724
601
 
725
- // A programmatic value set carries no positional intent, so drop any
726
- // `_selectedKey` left over from a prior user click. This makes reconciliation
727
- // in updated() fall back to first-by-value (single) / value-in-DOM-order
728
- // (multi), matching the documented contract for programmatic selection even
729
- // when a stale key would still resolve to a duplicate-value option.
730
- this._selectedKey = undefined;
731
-
732
602
  // `value` is a String property; stringify arrays so attribute reflection and `formattedValue` parsing stay correct.
733
603
  this.value = Array.isArray(value) ? JSON.stringify(value) : value;
734
604
  }
@@ -776,17 +646,6 @@ class AuroMenu extends AuroElement {
776
646
  updated(changedProperties) {
777
647
  super.updated(changedProperties);
778
648
 
779
- // Consume the selection-driven flag for THIS cycle up front. Clearing it
780
- // unconditionally — not only inside the `value` branch below — prevents it
781
- // from lingering `true` when a selection produces a serialized `value`
782
- // byte-identical to the current one, in which case Lit schedules no
783
- // `value`-change cycle to consume it. A lingering flag would misclassify a
784
- // later consumer's programmatic `value` set as selection-driven and keep a
785
- // stale `_selectedKey`. The reconcile path below re-sets the instance flag
786
- // after this point, so its intentional cross-cycle hand-off still works.
787
- const valueChangeFromSelection = this._valueChangeFromSelection;
788
- this._valueChangeFromSelection = false;
789
-
790
649
  // Single source of truth for 'auroMenu-selectedOption'. Selection handlers
791
650
  // mutate optionSelected and let Lit's update cycle dispatch here; the prior
792
651
  // .value comparison missed multi-select array changes and combined with the
@@ -810,17 +669,6 @@ class AuroMenu extends AuroElement {
810
669
  this.initItems();
811
670
  }
812
671
 
813
- // Distinguish a selection-driven `value` change (a user click, which set
814
- // the flag in handleSelectState / _sortSelectedByDomOrder) from a
815
- // programmatic assignment by a consumer. A programmatic set carries no
816
- // positional intent, so drop any leftover `_selectedKey` and let
817
- // reconciliation fall back to first-by-value (single) / value-in-DOM-order
818
- // (multi) — the same contract selectByValue() guarantees, even when a
819
- // stale key would otherwise still resolve to a duplicate-value option.
820
- if (!valueChangeFromSelection) {
821
- this._selectedKey = undefined;
822
- }
823
-
824
672
  // Set when reconciliation reassigns `value` below. That reassignment schedules a
825
673
  // second updated() cycle, so the `event`-attribute dispatch is deferred to that
826
674
  // cycle to avoid firing option custom events twice on the same selection.
@@ -838,55 +686,19 @@ class AuroMenu extends AuroElement {
838
686
  // Defensive default: `formattedValue` can be undefined for unexpected value types,
839
687
  // and calling `.includes` on undefined would throw during reconciliation.
840
688
  const valueArray = this.formattedValue || [];
841
- // Resolve by key first (the user's exact picks), then fall back to
842
- // value matching for any values not resolved by key — so pre-selection
843
- // and programmatic value sets keep working. Result is DOM-ordered.
844
- const matchingOptions = resolveSelectedOptions(this.items, valueArray, this._selectedKey);
689
+ const matchingOptions = this.items ? this.items.filter((item) => isSelectableByValue(item) && valueArray.includes(item.value)) : [];
845
690
  newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
846
691
 
847
- // Reconcile `value` with the selectable set. An occurrence is dropped
848
- // only when it is loaded but no selectable option can satisfy it —
849
- // every loaded item sharing that value is non-selectable, or the value
850
- // recurs more often than it has selectable options (a duplicate value
851
- // whose extra siblings are disabled/static). This is count-based, not
852
- // presence-based, so an enabled option is kept even when a disabled
853
- // sibling shares its value — mirroring how `resolveSelectedOptions`
854
- // resolves the same set. Entries with no matching item yet are
855
- // preserved so async preselection still works, and the toggle handlers
856
- // rebuild `value` from `formattedValue`, so a rejected entry cannot
857
- // resurface on the next select/deselect.
858
- const selectableByValue = new Map();
859
- const loadedValues = new Set();
860
- if (this.items) {
861
- this.items.forEach((item) => {
862
- loadedValues.add(item.value);
863
- if (isSelectableByValue(item)) {
864
- selectableByValue.set(item.value, (selectableByValue.get(item.value) || 0) + 1);
865
- }
866
- });
867
- }
868
-
869
- const reconciled = valueArray.filter((val) => {
870
- // Not loaded yet (async preselection) — keep for a later cycle.
871
- if (!loadedValues.has(val)) {
872
- return true;
873
- }
874
- // Consume one selectable option per occurrence; drop once exhausted.
875
- const remaining = selectableByValue.get(val) || 0;
876
- if (remaining > 0) {
877
- selectableByValue.set(val, remaining - 1);
878
- return true;
879
- }
880
- return false;
881
- });
882
-
883
- if (reconciled.length !== valueArray.length) {
884
- // This is an internal correction, not a consumer's programmatic set,
885
- // so preserve the selection-driven flag through the re-entrant
886
- // updated() cycle it schedules. Otherwise that cycle would treat the
887
- // reassignment as programmatic and drop `_selectedKey` mid-cascade,
888
- // flipping resolution and looping.
889
- this._valueChangeFromSelection = true;
692
+ // Reconcile `value` with the selectable set. Drop only entries whose option is
693
+ // loaded but non-selectable (disabled/static) — leaving them would desync `value`
694
+ // from `optionSelected`, and the toggle handlers rebuild `value` from `formattedValue`,
695
+ // so the rejected entry would resurface on the next select/deselect. Entries with no
696
+ // matching item yet are preserved so async preselection still works once options render.
697
+ const rejectedValues = this.items
698
+ ? this.items.filter((item) => !isSelectableByValue(item) && valueArray.includes(item.value)).map((item) => item.value)
699
+ : [];
700
+ if (rejectedValues.length > 0) {
701
+ const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
890
702
  this.value = serializeMultiSelectValue(reconciled);
891
703
  valueReconciled = true;
892
704
  }
@@ -898,11 +710,7 @@ class AuroMenu extends AuroElement {
898
710
  // `hidden` is intentionally NOT excluded: the combobox toggles
899
711
  // `hidden` as its type-ahead filter, so a filtered-out option is
900
712
  // still a valid programmatic selection.
901
- // Prefer the option the user actually selected (tracked by
902
- // `_selectedKey`) so a click on the second of two options sharing a
903
- // `value` resolves back to that exact element instead of the first
904
- // value match. Falls back to first-by-value for programmatic sets.
905
- const matchingOption = resolveSelectedOption(this.items, this.value, this._selectedKey);
713
+ const matchingOption = this.items ? this.items.find((item) => isSelectableByValue(item) && item.value === this.value) : undefined;
906
714
 
907
715
  if (matchingOption) {
908
716
  newSelected = matchingOption;
@@ -1130,14 +938,6 @@ class AuroMenu extends AuroElement {
1130
938
  }
1131
939
  });
1132
940
 
1133
- // Assign private keys once items are populated. Only the root menu assigns
1134
- // keys: its `items` is a deep query that already includes nested submenu
1135
- // options, so a single pass keys the entire tree. Nested menus skip this
1136
- // and inherit keys from the root.
1137
- if (this.rootMenu) {
1138
- this._assignOptionKeys();
1139
- }
1140
-
1141
941
  if (this.noCheckmark) {
1142
942
  this.updateItemsState(new Map([
1143
943
  [
@@ -1154,31 +954,6 @@ class AuroMenu extends AuroElement {
1154
954
  }));
1155
955
  }
1156
956
 
1157
- /**
1158
- * Assigns a private, auto-generated unique key (`_optionKey`) to each menu
1159
- * option that does not already have one. Keys are internal state on the
1160
- * element instance — never reflected as an attribute or exposed publicly —
1161
- * and let selection tracking distinguish options that share the same `value`.
1162
- *
1163
- * The `_optionKey === undefined` guard makes this idempotent: options keep the
1164
- * key they were first assigned across re-renders and slot changes, and if a
1165
- * nested menu's lifecycle runs a pass before the root, options simply wait for
1166
- * the root to key them (or keep whatever key they already hold).
1167
- * @private
1168
- */
1169
- _assignOptionKeys() {
1170
- if (!this.items) {
1171
- return;
1172
- }
1173
-
1174
- this.items.forEach((option) => {
1175
- if (option._optionKey === undefined) {
1176
- this._optionKeyCounter += 1;
1177
- option._optionKey = `${this._menuInstanceId}-${this._optionKeyCounter}`;
1178
- }
1179
- });
1180
- }
1181
-
1182
957
  // Logic Methods
1183
958
 
1184
959
  /**
@@ -1188,28 +963,24 @@ class AuroMenu extends AuroElement {
1188
963
  */
1189
964
  handleSelectState(option) {
1190
965
  if (this.multiSelect) {
966
+ const currentValue = this.formattedValue || [];
1191
967
  const currentSelected = this.optionSelected || [];
1192
968
 
969
+ if (!currentValue.includes(option.value)) {
970
+ this.value = serializeMultiSelectValue([
971
+ ...currentValue,
972
+ option.value
973
+ ]);
974
+ }
1193
975
  if (!currentSelected.includes(option)) {
1194
976
  this.optionSelected = [
1195
977
  ...currentSelected,
1196
978
  option
1197
979
  ];
1198
980
  }
1199
-
1200
- // Re-sort by DOM order and rebuild `_selectedKey`/`value` from the
1201
- // selected set so display order stays consistent with the menu, not with
1202
- // click order.
1203
- this._sortSelectedByDomOrder();
1204
981
  } else {
1205
982
  this.value = option.value;
1206
983
  this.optionSelected = option;
1207
- // Track the specific option the user selected so the value→option
1208
- // reconciliation in updated() resolves back to this exact element even
1209
- // when another option shares the same `value`.
1210
- this._selectedKey = option._optionKey;
1211
- // Mark this `value` change as selection-driven so updated() trusts the key.
1212
- this._valueChangeFromSelection = true;
1213
984
  }
1214
985
 
1215
986
  this._index = this.items.indexOf(option);
@@ -1222,22 +993,18 @@ class AuroMenu extends AuroElement {
1222
993
  */
1223
994
  handleDeselectState(option) {
1224
995
  if (this.multiSelect) {
1225
- // Remove this exact element from the selection (identity, not value — two
1226
- // options can share a `value`), then rebuild `value`/`_selectedKey` from
1227
- // the remaining set in DOM order. An empty result collapses to undefined.
1228
- this.optionSelected = this.optionSelected.filter((selected) => selected !== option);
996
+ // Remove this option from array; an empty result collapses `value` to undefined.
997
+ const newFormattedValue = (this.formattedValue || []).filter((val) => val !== option.value);
998
+ this.value = serializeMultiSelectValue(newFormattedValue);
999
+
1000
+ this.optionSelected = this.optionSelected.filter((val) => val !== option);
1229
1001
  if (this.optionSelected.length === 0) {
1230
1002
  this.optionSelected = undefined;
1231
- this._selectedKey = undefined;
1232
- this.value = undefined;
1233
- } else {
1234
- this._sortSelectedByDomOrder();
1235
1003
  }
1236
1004
  } else {
1237
1005
  // For single-select: Back to undefined when deselected
1238
1006
  this.value = undefined;
1239
1007
  this.optionSelected = undefined;
1240
- this._selectedKey = undefined;
1241
1008
  }
1242
1009
 
1243
1010
  // Update the index tracking
@@ -1261,38 +1028,9 @@ class AuroMenu extends AuroElement {
1261
1028
  clearSelection() {
1262
1029
  this.optionSelected = undefined;
1263
1030
  this.value = undefined;
1264
- this._selectedKey = undefined;
1265
1031
  this._index = -1;
1266
1032
  }
1267
1033
 
1268
- /**
1269
- * Re-sorts the multi-select selection into DOM order and rebuilds the derived
1270
- * `_selectedKey` and `value` from `optionSelected`. Selection is always stored
1271
- * and serialized in the order options appear in the menu, never in click
1272
- * order — so selecting C then A yields `[A, C]`.
1273
- * @private
1274
- */
1275
- _sortSelectedByDomOrder() {
1276
- if (!this.multiSelect || !Array.isArray(this.optionSelected) || !this.items) {
1277
- return;
1278
- }
1279
-
1280
- const indexMap = new Map(this.items.map((item, index) => [
1281
- item,
1282
- index
1283
- ]));
1284
-
1285
- // Sort any element no longer in `items` (a stale selection left over from a
1286
- // dynamic rebuild that the consumer has not cleared) to the END rather than
1287
- // the front, so it never displaces a live option to the head of the
1288
- // serialized order. Value reconciliation drops it on the next updated() cycle.
1289
- this.optionSelected.sort((optionA, optionB) => (indexMap.get(optionA) ?? this.items.length) - (indexMap.get(optionB) ?? this.items.length));
1290
- this._selectedKey = this.optionSelected.map((option) => option._optionKey);
1291
- this.value = serializeMultiSelectValue(this.optionSelected.map((option) => option.value));
1292
- // Mark this `value` change as selection-driven so updated() trusts the keys.
1293
- this._valueChangeFromSelection = true;
1294
- }
1295
-
1296
1034
  /**
1297
1035
  * Resets the menu to its initial state.
1298
1036
  * This is the only way to return value to undefined.
@@ -1302,7 +1040,6 @@ class AuroMenu extends AuroElement {
1302
1040
  // Reset to undefined - initial state
1303
1041
  this.value = undefined;
1304
1042
  this.optionSelected = undefined;
1305
- this._selectedKey = undefined;
1306
1043
  this._index = -1;
1307
1044
 
1308
1045
  // Clear active option state so a follow-up open/navigation starts fresh
@@ -1364,19 +1101,6 @@ class AuroMenu extends AuroElement {
1364
1101
  this.initItems();
1365
1102
  }
1366
1103
 
1367
- // Recover `_index` from the highlighted option when it has been reset to -1.
1368
- // In multi-select, deselecting the last remaining option collapses the value
1369
- // to undefined, and the updated() reconciliation resets `_index = -1` even
1370
- // though `optionActive` still points at the highlighted option. Without this,
1371
- // reading `items[-1]` returns undefined and the re-select no-ops until the
1372
- // highlight is moved away and back. Mirrors auro-combobox's reconcileMenuIndex.
1373
- if (this._index < 0 && this.optionActive && this.items) {
1374
- const activeIndex = this.items.indexOf(this.optionActive);
1375
- if (activeIndex >= 0) {
1376
- this._index = activeIndex;
1377
- }
1378
- }
1379
-
1380
1104
  // Get currently selected menu option based on index
1381
1105
  const option = this.items ? this.items[this._index] : undefined;
1382
1106
 
@@ -44,36 +44,6 @@ export function isOptionInteractive(option: HTMLElement): boolean;
44
44
  * @returns {boolean} True if option can be selected by value.
45
45
  */
46
46
  export function isSelectableByValue(option: HTMLElement): boolean;
47
- /**
48
- * Resolves the single selected option for a given `value`, preferring the
49
- * option tracked by `selectedKey` (a user-initiated selection) over a
50
- * first-by-value match. When multiple options share the same `value`, matching
51
- * by `value` alone cannot distinguish which one the user picked; the key
52
- * disambiguates it.
53
- *
54
- * The key is trusted only when it still resolves to an option whose `value`
55
- * matches the requested `value`. If the key is stale (option removed) or the
56
- * value was changed programmatically, resolution falls back to value matching —
57
- * preserving backward-compatible behavior for preselection and `selectByValue`.
58
- * @private
59
- * @param {Array<HTMLElement>} items - The menu's flat option list.
60
- * @param {string} value - The value to resolve.
61
- * @param {string|undefined} selectedKey - The `_optionKey` of the user-selected option, if any.
62
- * @returns {HTMLElement|undefined} The resolved option, or undefined when none match.
63
- */
64
- export function resolveSelectedOption(items: Array<HTMLElement>, value: string, selectedKey: string | undefined): HTMLElement | undefined;
65
- /**
66
- * Resolves the selected options for a multi-select `value` array, preferring
67
- * options tracked by `selectedKeys` (user-initiated selections) and falling
68
- * back to value matching for any values not resolved by key. The result is
69
- * always sorted into DOM order regardless of selection sequence.
70
- * @private
71
- * @param {Array<HTMLElement>} items - The menu's flat option list.
72
- * @param {Array<string>} valueArray - The selected values.
73
- * @param {Array<string>|undefined} selectedKeys - The `_optionKey`s of the user-selected options, if any.
74
- * @returns {Array<HTMLElement>} The resolved options in DOM order.
75
- */
76
- export function resolveSelectedOptions(items: Array<HTMLElement>, valueArray: Array<string>, selectedKeys: Array<string> | undefined): Array<HTMLElement>;
77
47
  /**
78
48
  * Helper method to dispatch custom events.
79
49
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -172,7 +172,6 @@ export class AuroMenu extends AuroElement {
172
172
  * @public
173
173
  */
174
174
  public selectByValue(value: string | string[] | undefined | null): void;
175
- _selectedKey: any;
176
175
  firstUpdated(): void;
177
176
  loadingSlots: NodeListOf<Element> | undefined;
178
177
  /**
@@ -182,7 +181,6 @@ export class AuroMenu extends AuroElement {
182
181
  */
183
182
  private setTagAttribute;
184
183
  updated(changedProperties: any): void;
185
- _valueChangeFromSelection: boolean | undefined;
186
184
  _index: number | undefined;
187
185
  /**
188
186
  * Updates the UI state and appearance of menu items based on changed properties.
@@ -201,19 +199,6 @@ export class AuroMenu extends AuroElement {
201
199
  */
202
200
  private initItems;
203
201
  items: Element[] | undefined;
204
- /**
205
- * Assigns a private, auto-generated unique key (`_optionKey`) to each menu
206
- * option that does not already have one. Keys are internal state on the
207
- * element instance — never reflected as an attribute or exposed publicly —
208
- * and let selection tracking distinguish options that share the same `value`.
209
- *
210
- * The `_optionKey === undefined` guard makes this idempotent: options keep the
211
- * key they were first assigned across re-renders and slot changes, and if a
212
- * nested menu's lifecycle runs a pass before the root, options simply wait for
213
- * the root to key them (or keep whatever key they already hold).
214
- * @private
215
- */
216
- private _assignOptionKeys;
217
202
  /**
218
203
  * Updates menu state when an option is selected.
219
204
  * @private
@@ -231,14 +216,6 @@ export class AuroMenu extends AuroElement {
231
216
  * @private
232
217
  */
233
218
  private clearSelection;
234
- /**
235
- * Re-sorts the multi-select selection into DOM order and rebuilds the derived
236
- * `_selectedKey` and `value` from `optionSelected`. Selection is always stored
237
- * and serialized in the order options appear in the menu, never in click
238
- * order — so selecting C then A yields `[A, C]`.
239
- * @private
240
- */
241
- private _sortSelectedByDomOrder;
242
219
  /**
243
220
  * Resets the menu to its initial state.
244
221
  * This is the only way to return value to undefined.