@aurodesignsystem-dev/auro-formkit 0.0.0-pr1573.2 → 0.0.0-pr1576.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.
- package/components/checkbox/demo/customize.min.js +1 -1
- package/components/checkbox/demo/getting-started.min.js +1 -1
- package/components/checkbox/demo/index.min.js +1 -1
- package/components/checkbox/dist/index.js +1 -1
- package/components/checkbox/dist/registered.js +1 -1
- package/components/combobox/demo/customize.md +40 -0
- package/components/combobox/demo/customize.min.js +247 -17
- package/components/combobox/demo/getting-started.min.js +247 -17
- package/components/combobox/demo/index.min.js +247 -17
- package/components/combobox/dist/index.js +3 -3
- package/components/combobox/dist/registered.js +3 -3
- package/components/counter/demo/customize.min.js +2 -2
- package/components/counter/demo/index.min.js +2 -2
- package/components/counter/dist/index.js +2 -2
- package/components/counter/dist/registered.js +2 -2
- package/components/datepicker/demo/customize.min.js +3 -3
- package/components/datepicker/demo/index.min.js +3 -3
- package/components/datepicker/dist/index.js +3 -3
- package/components/datepicker/dist/registered.js +3 -3
- package/components/dropdown/demo/customize.min.js +1 -1
- package/components/dropdown/demo/getting-started.min.js +1 -1
- package/components/dropdown/demo/index.min.js +1 -1
- package/components/dropdown/dist/index.js +1 -1
- package/components/dropdown/dist/registered.js +1 -1
- package/components/form/demo/customize.min.js +257 -27
- package/components/form/demo/getting-started.min.js +257 -27
- package/components/form/demo/index.min.js +257 -27
- package/components/form/demo/registerDemoDeps.min.js +257 -27
- package/components/input/demo/customize.min.js +1 -1
- package/components/input/demo/getting-started.min.js +1 -1
- package/components/input/demo/index.min.js +1 -1
- package/components/input/dist/index.js +1 -1
- package/components/input/dist/registered.js +1 -1
- package/components/menu/demo/index.min.js +244 -14
- package/components/menu/dist/auro-menu-utils.d.ts +30 -0
- package/components/menu/dist/auro-menu.d.ts +23 -0
- package/components/menu/dist/index.js +244 -14
- package/components/menu/dist/registered.js +244 -14
- package/components/radio/demo/customize.min.js +1 -1
- package/components/radio/demo/getting-started.min.js +1 -1
- package/components/radio/demo/index.min.js +1 -1
- package/components/radio/dist/index.js +1 -1
- package/components/radio/dist/registered.js +1 -1
- package/components/select/demo/customize.md +72 -0
- package/components/select/demo/customize.min.js +246 -16
- package/components/select/demo/getting-started.min.js +246 -16
- package/components/select/demo/index.min.js +246 -16
- package/components/select/dist/index.js +2 -2
- package/components/select/dist/registered.js +2 -2
- package/custom-elements.json +1617 -1513
- package/package.json +1 -1
|
@@ -238,6 +238,107 @@ function isSelectableByValue(option) {
|
|
|
238
238
|
!option.hasAttribute('static');
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
/* eslint-disable no-underscore-dangle */
|
|
242
|
+
/**
|
|
243
|
+
* Resolves the single selected option for a given `value`, preferring the
|
|
244
|
+
* option tracked by `selectedKey` (a user-initiated selection) over a
|
|
245
|
+
* first-by-value match. When multiple options share the same `value`, matching
|
|
246
|
+
* by `value` alone cannot distinguish which one the user picked; the key
|
|
247
|
+
* disambiguates it.
|
|
248
|
+
*
|
|
249
|
+
* The key is trusted only when it still resolves to an option whose `value`
|
|
250
|
+
* matches the requested `value`. If the key is stale (option removed) or the
|
|
251
|
+
* value was changed programmatically, resolution falls back to value matching —
|
|
252
|
+
* preserving backward-compatible behavior for preselection and `selectByValue`.
|
|
253
|
+
* @private
|
|
254
|
+
* @param {Array<HTMLElement>} items - The menu's flat option list.
|
|
255
|
+
* @param {string} value - The value to resolve.
|
|
256
|
+
* @param {string|undefined} selectedKey - The `_optionKey` of the user-selected option, if any.
|
|
257
|
+
* @returns {HTMLElement|undefined} The resolved option, or undefined when none match.
|
|
258
|
+
*/
|
|
259
|
+
function resolveSelectedOption(items, value, selectedKey) {
|
|
260
|
+
if (!items) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (selectedKey !== undefined) {
|
|
265
|
+
const keyed = items.find((item) => item._optionKey === selectedKey);
|
|
266
|
+
if (keyed && isSelectableByValue(keyed) && keyed.value === value) {
|
|
267
|
+
return keyed;
|
|
268
|
+
}
|
|
269
|
+
// Key exists but the option is gone or its value no longer matches — fall
|
|
270
|
+
// through to value-based matching.
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return items.find((item) => isSelectableByValue(item) && item.value === value);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Resolves the selected options for a multi-select `value` array, preferring
|
|
278
|
+
* options tracked by `selectedKeys` (user-initiated selections) and falling
|
|
279
|
+
* back to value matching for any values not resolved by key. The result is
|
|
280
|
+
* always sorted into DOM order regardless of selection sequence.
|
|
281
|
+
* @private
|
|
282
|
+
* @param {Array<HTMLElement>} items - The menu's flat option list.
|
|
283
|
+
* @param {Array<string>} valueArray - The selected values.
|
|
284
|
+
* @param {Array<string>|undefined} selectedKeys - The `_optionKey`s of the user-selected options, if any.
|
|
285
|
+
* @returns {Array<HTMLElement>} The resolved options in DOM order.
|
|
286
|
+
*/
|
|
287
|
+
function resolveSelectedOptions(items, valueArray, selectedKeys) {
|
|
288
|
+
if (!items) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const resolved = [];
|
|
293
|
+
|
|
294
|
+
// Track how many of each value are still available to resolve. A value that
|
|
295
|
+
// appears N times in `valueArray` may be satisfied at most N times total across
|
|
296
|
+
// the key pass and the value fallback below — matching by count, not presence,
|
|
297
|
+
// on BOTH passes. This stops a duplicate value from being over-resolved: e.g.
|
|
298
|
+
// two keyed options that both carry `SEA` cannot both match a single requested
|
|
299
|
+
// `SEA` (which happens when `value` is set directly without clearing
|
|
300
|
+
// `_selectedKey`, so more keys survive than the value set now asks for).
|
|
301
|
+
const remaining = new Map();
|
|
302
|
+
valueArray.forEach((val) => remaining.set(val, (remaining.get(val) || 0) + 1));
|
|
303
|
+
|
|
304
|
+
// Resolve by key first: trust a key only when its option is still selectable
|
|
305
|
+
// and there is still an unmatched occurrence of its value in the request set.
|
|
306
|
+
if (Array.isArray(selectedKeys)) {
|
|
307
|
+
selectedKeys.forEach((key) => {
|
|
308
|
+
const keyed = items.find((item) => item._optionKey === key);
|
|
309
|
+
if (keyed && isSelectableByValue(keyed) && (remaining.get(keyed.value) || 0) > 0 && !resolved.includes(keyed)) {
|
|
310
|
+
resolved.push(keyed);
|
|
311
|
+
remaining.set(keyed.value, remaining.get(keyed.value) - 1);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Fall back to value matching for the occurrences not resolved by key. Iterate
|
|
317
|
+
// the leftover per-value counts so a value that appears twice but was only
|
|
318
|
+
// resolved once by key still matches its remaining occurrence(s).
|
|
319
|
+
remaining.forEach((count, val) => {
|
|
320
|
+
for (let occurrence = 0; occurrence < count; occurrence += 1) {
|
|
321
|
+
const option = items.find((item) => isSelectableByValue(item) && item.value === val && !resolved.includes(item));
|
|
322
|
+
if (option) {
|
|
323
|
+
resolved.push(option);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Always return in DOM order so display is consistent regardless of the order
|
|
329
|
+
// keys/values were selected. Every resolved option came from `items`, so an
|
|
330
|
+
// O(1) index lookup mirrors `_sortSelectedByDomOrder` and avoids the O(n)
|
|
331
|
+
// `items.indexOf` per comparison for large combobox option sets.
|
|
332
|
+
const indexMap = new Map(items.map((item, index) => [
|
|
333
|
+
item,
|
|
334
|
+
index
|
|
335
|
+
]));
|
|
336
|
+
resolved.sort((optionA, optionB) => indexMap.get(optionA) - indexMap.get(optionB));
|
|
337
|
+
|
|
338
|
+
return resolved;
|
|
339
|
+
}
|
|
340
|
+
/* eslint-enable no-underscore-dangle */
|
|
341
|
+
|
|
241
342
|
/**
|
|
242
343
|
* Helper method to dispatch custom events.
|
|
243
344
|
* @param {HTMLElement} element - Element to dispatch event from.
|
|
@@ -263,6 +364,14 @@ function dispatchMenuEvent(element, eventName, detail = null) {
|
|
|
263
364
|
// See LICENSE in the project root for license information.
|
|
264
365
|
|
|
265
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Monotonically increasing counter used to give each menu instance a unique
|
|
369
|
+
* `_menuInstanceId` prefix. Auto-generating the id (rather than using a random
|
|
370
|
+
* string) keeps option keys deterministic and collision-free across menus.
|
|
371
|
+
* @private
|
|
372
|
+
*/
|
|
373
|
+
let menuInstanceIdCounter = 0;
|
|
374
|
+
|
|
266
375
|
|
|
267
376
|
/**
|
|
268
377
|
* The `auro-menu` element provides users a way to select from a list of options.
|
|
@@ -337,6 +446,8 @@ class AuroMenu extends AuroElement {
|
|
|
337
446
|
/**
|
|
338
447
|
* @private
|
|
339
448
|
*/
|
|
449
|
+
menuInstanceIdCounter += 1;
|
|
450
|
+
|
|
340
451
|
Object.assign(this, {
|
|
341
452
|
// Root-level menu (true) or a nested submenu (false)
|
|
342
453
|
rootMenu: true,
|
|
@@ -346,6 +457,21 @@ class AuroMenu extends AuroElement {
|
|
|
346
457
|
nestingSpacer: '<span class="nestingSpacer"></span>',
|
|
347
458
|
// Loading indicator for slot elements
|
|
348
459
|
loadingSlots: null,
|
|
460
|
+
// Unique id for this menu instance; prefixes every auto-generated option
|
|
461
|
+
// key so keys never collide across menus in the same document.
|
|
462
|
+
_menuInstanceId: `menu-${menuInstanceIdCounter}`,
|
|
463
|
+
// Monotonically increasing counter for option key generation. Never
|
|
464
|
+
// resets, so a key is never reused within this instance's lifetime.
|
|
465
|
+
_optionKeyCounter: 0,
|
|
466
|
+
// Key(s) of the option(s) the user has actively selected. A single string
|
|
467
|
+
// in single-select, an array in multi-select, undefined when nothing is
|
|
468
|
+
// user-selected. Used to disambiguate options that share a `value`.
|
|
469
|
+
_selectedKey: undefined,
|
|
470
|
+
// True only for the one updated() cycle following a user selection, so
|
|
471
|
+
// reconciliation trusts `_selectedKey`. A `value` change from a consumer's
|
|
472
|
+
// direct property assignment leaves this false, dropping the stale key so
|
|
473
|
+
// reconciliation falls back to first-by-value (see updated()).
|
|
474
|
+
_valueChangeFromSelection: false,
|
|
349
475
|
});
|
|
350
476
|
}
|
|
351
477
|
|
|
@@ -565,6 +691,13 @@ class AuroMenu extends AuroElement {
|
|
|
565
691
|
return;
|
|
566
692
|
}
|
|
567
693
|
|
|
694
|
+
// A programmatic value set carries no positional intent, so drop any
|
|
695
|
+
// `_selectedKey` left over from a prior user click. This makes reconciliation
|
|
696
|
+
// in updated() fall back to first-by-value (single) / value-in-DOM-order
|
|
697
|
+
// (multi), matching the documented contract for programmatic selection even
|
|
698
|
+
// when a stale key would still resolve to a duplicate-value option.
|
|
699
|
+
this._selectedKey = undefined;
|
|
700
|
+
|
|
568
701
|
// `value` is a String property; stringify arrays so attribute reflection and `formattedValue` parsing stay correct.
|
|
569
702
|
this.value = Array.isArray(value) ? JSON.stringify(value) : value;
|
|
570
703
|
}
|
|
@@ -612,6 +745,7 @@ class AuroMenu extends AuroElement {
|
|
|
612
745
|
updated(changedProperties) {
|
|
613
746
|
super.updated(changedProperties);
|
|
614
747
|
|
|
748
|
+
|
|
615
749
|
// Single source of truth for 'auroMenu-selectedOption'. Selection handlers
|
|
616
750
|
// mutate optionSelected and let Lit's update cycle dispatch here; the prior
|
|
617
751
|
// .value comparison missed multi-select array changes and combined with the
|
|
@@ -635,6 +769,18 @@ class AuroMenu extends AuroElement {
|
|
|
635
769
|
this.initItems();
|
|
636
770
|
}
|
|
637
771
|
|
|
772
|
+
// Distinguish a selection-driven `value` change (a user click, which set
|
|
773
|
+
// the flag in handleSelectState / _sortSelectedByDomOrder) from a
|
|
774
|
+
// programmatic assignment by a consumer. A programmatic set carries no
|
|
775
|
+
// positional intent, so drop any leftover `_selectedKey` and let
|
|
776
|
+
// reconciliation fall back to first-by-value (single) / value-in-DOM-order
|
|
777
|
+
// (multi) — the same contract selectByValue() guarantees, even when a
|
|
778
|
+
// stale key would otherwise still resolve to a duplicate-value option.
|
|
779
|
+
if (!this._valueChangeFromSelection) {
|
|
780
|
+
this._selectedKey = undefined;
|
|
781
|
+
}
|
|
782
|
+
this._valueChangeFromSelection = false;
|
|
783
|
+
|
|
638
784
|
// Set when reconciliation reassigns `value` below. That reassignment schedules a
|
|
639
785
|
// second updated() cycle, so the `event`-attribute dispatch is deferred to that
|
|
640
786
|
// cycle to avoid firing option custom events twice on the same selection.
|
|
@@ -652,7 +798,10 @@ class AuroMenu extends AuroElement {
|
|
|
652
798
|
// Defensive default: `formattedValue` can be undefined for unexpected value types,
|
|
653
799
|
// and calling `.includes` on undefined would throw during reconciliation.
|
|
654
800
|
const valueArray = this.formattedValue || [];
|
|
655
|
-
|
|
801
|
+
// Resolve by key first (the user's exact picks), then fall back to
|
|
802
|
+
// value matching for any values not resolved by key — so pre-selection
|
|
803
|
+
// and programmatic value sets keep working. Result is DOM-ordered.
|
|
804
|
+
const matchingOptions = resolveSelectedOptions(this.items, valueArray, this._selectedKey);
|
|
656
805
|
newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
|
|
657
806
|
|
|
658
807
|
// Reconcile `value` with the selectable set. Drop only entries whose option is
|
|
@@ -665,6 +814,12 @@ class AuroMenu extends AuroElement {
|
|
|
665
814
|
: [];
|
|
666
815
|
if (rejectedValues.length > 0) {
|
|
667
816
|
const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
|
|
817
|
+
// This is an internal correction, not a consumer's programmatic set,
|
|
818
|
+
// so preserve the selection-driven flag through the re-entrant
|
|
819
|
+
// updated() cycle it schedules. Otherwise that cycle would treat the
|
|
820
|
+
// reassignment as programmatic and drop `_selectedKey` mid-cascade,
|
|
821
|
+
// flipping resolution and looping.
|
|
822
|
+
this._valueChangeFromSelection = true;
|
|
668
823
|
this.value = serializeMultiSelectValue(reconciled);
|
|
669
824
|
valueReconciled = true;
|
|
670
825
|
}
|
|
@@ -676,7 +831,11 @@ class AuroMenu extends AuroElement {
|
|
|
676
831
|
// `hidden` is intentionally NOT excluded: the combobox toggles
|
|
677
832
|
// `hidden` as its type-ahead filter, so a filtered-out option is
|
|
678
833
|
// still a valid programmatic selection.
|
|
679
|
-
|
|
834
|
+
// Prefer the option the user actually selected (tracked by
|
|
835
|
+
// `_selectedKey`) so a click on the second of two options sharing a
|
|
836
|
+
// `value` resolves back to that exact element instead of the first
|
|
837
|
+
// value match. Falls back to first-by-value for programmatic sets.
|
|
838
|
+
const matchingOption = resolveSelectedOption(this.items, this.value, this._selectedKey);
|
|
680
839
|
|
|
681
840
|
if (matchingOption) {
|
|
682
841
|
newSelected = matchingOption;
|
|
@@ -904,6 +1063,14 @@ class AuroMenu extends AuroElement {
|
|
|
904
1063
|
}
|
|
905
1064
|
});
|
|
906
1065
|
|
|
1066
|
+
// Assign private keys once items are populated. Only the root menu assigns
|
|
1067
|
+
// keys: its `items` is a deep query that already includes nested submenu
|
|
1068
|
+
// options, so a single pass keys the entire tree. Nested menus skip this
|
|
1069
|
+
// and inherit keys from the root.
|
|
1070
|
+
if (this.rootMenu) {
|
|
1071
|
+
this._assignOptionKeys();
|
|
1072
|
+
}
|
|
1073
|
+
|
|
907
1074
|
if (this.noCheckmark) {
|
|
908
1075
|
this.updateItemsState(new Map([
|
|
909
1076
|
[
|
|
@@ -920,6 +1087,31 @@ class AuroMenu extends AuroElement {
|
|
|
920
1087
|
}));
|
|
921
1088
|
}
|
|
922
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* Assigns a private, auto-generated unique key (`_optionKey`) to each menu
|
|
1092
|
+
* option that does not already have one. Keys are internal state on the
|
|
1093
|
+
* element instance — never reflected as an attribute or exposed publicly —
|
|
1094
|
+
* and let selection tracking distinguish options that share the same `value`.
|
|
1095
|
+
*
|
|
1096
|
+
* The `_optionKey === undefined` guard makes this idempotent: options keep the
|
|
1097
|
+
* key they were first assigned across re-renders and slot changes, and if a
|
|
1098
|
+
* nested menu's lifecycle runs a pass before the root, options simply wait for
|
|
1099
|
+
* the root to key them (or keep whatever key they already hold).
|
|
1100
|
+
* @private
|
|
1101
|
+
*/
|
|
1102
|
+
_assignOptionKeys() {
|
|
1103
|
+
if (!this.items) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
this.items.forEach((option) => {
|
|
1108
|
+
if (option._optionKey === undefined) {
|
|
1109
|
+
this._optionKeyCounter += 1;
|
|
1110
|
+
option._optionKey = `${this._menuInstanceId}-${this._optionKeyCounter}`;
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
|
|
923
1115
|
// Logic Methods
|
|
924
1116
|
|
|
925
1117
|
/**
|
|
@@ -929,24 +1121,28 @@ class AuroMenu extends AuroElement {
|
|
|
929
1121
|
*/
|
|
930
1122
|
handleSelectState(option) {
|
|
931
1123
|
if (this.multiSelect) {
|
|
932
|
-
const currentValue = this.formattedValue || [];
|
|
933
1124
|
const currentSelected = this.optionSelected || [];
|
|
934
1125
|
|
|
935
|
-
if (!currentValue.includes(option.value)) {
|
|
936
|
-
this.value = serializeMultiSelectValue([
|
|
937
|
-
...currentValue,
|
|
938
|
-
option.value
|
|
939
|
-
]);
|
|
940
|
-
}
|
|
941
1126
|
if (!currentSelected.includes(option)) {
|
|
942
1127
|
this.optionSelected = [
|
|
943
1128
|
...currentSelected,
|
|
944
1129
|
option
|
|
945
1130
|
];
|
|
946
1131
|
}
|
|
1132
|
+
|
|
1133
|
+
// Re-sort by DOM order and rebuild `_selectedKey`/`value` from the
|
|
1134
|
+
// selected set so display order stays consistent with the menu, not with
|
|
1135
|
+
// click order.
|
|
1136
|
+
this._sortSelectedByDomOrder();
|
|
947
1137
|
} else {
|
|
948
1138
|
this.value = option.value;
|
|
949
1139
|
this.optionSelected = option;
|
|
1140
|
+
// Track the specific option the user selected so the value→option
|
|
1141
|
+
// reconciliation in updated() resolves back to this exact element even
|
|
1142
|
+
// when another option shares the same `value`.
|
|
1143
|
+
this._selectedKey = option._optionKey;
|
|
1144
|
+
// Mark this `value` change as selection-driven so updated() trusts the key.
|
|
1145
|
+
this._valueChangeFromSelection = true;
|
|
950
1146
|
}
|
|
951
1147
|
|
|
952
1148
|
this._index = this.items.indexOf(option);
|
|
@@ -959,18 +1155,22 @@ class AuroMenu extends AuroElement {
|
|
|
959
1155
|
*/
|
|
960
1156
|
handleDeselectState(option) {
|
|
961
1157
|
if (this.multiSelect) {
|
|
962
|
-
// Remove this
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
this.optionSelected = this.optionSelected.filter((val) => val !== option);
|
|
1158
|
+
// Remove this exact element from the selection (identity, not value — two
|
|
1159
|
+
// options can share a `value`), then rebuild `value`/`_selectedKey` from
|
|
1160
|
+
// the remaining set in DOM order. An empty result collapses to undefined.
|
|
1161
|
+
this.optionSelected = this.optionSelected.filter((selected) => selected !== option);
|
|
967
1162
|
if (this.optionSelected.length === 0) {
|
|
968
1163
|
this.optionSelected = undefined;
|
|
1164
|
+
this._selectedKey = undefined;
|
|
1165
|
+
this.value = undefined;
|
|
1166
|
+
} else {
|
|
1167
|
+
this._sortSelectedByDomOrder();
|
|
969
1168
|
}
|
|
970
1169
|
} else {
|
|
971
1170
|
// For single-select: Back to undefined when deselected
|
|
972
1171
|
this.value = undefined;
|
|
973
1172
|
this.optionSelected = undefined;
|
|
1173
|
+
this._selectedKey = undefined;
|
|
974
1174
|
}
|
|
975
1175
|
|
|
976
1176
|
// Update the index tracking
|
|
@@ -994,9 +1194,38 @@ class AuroMenu extends AuroElement {
|
|
|
994
1194
|
clearSelection() {
|
|
995
1195
|
this.optionSelected = undefined;
|
|
996
1196
|
this.value = undefined;
|
|
1197
|
+
this._selectedKey = undefined;
|
|
997
1198
|
this._index = -1;
|
|
998
1199
|
}
|
|
999
1200
|
|
|
1201
|
+
/**
|
|
1202
|
+
* Re-sorts the multi-select selection into DOM order and rebuilds the derived
|
|
1203
|
+
* `_selectedKey` and `value` from `optionSelected`. Selection is always stored
|
|
1204
|
+
* and serialized in the order options appear in the menu, never in click
|
|
1205
|
+
* order — so selecting C then A yields `[A, C]`.
|
|
1206
|
+
* @private
|
|
1207
|
+
*/
|
|
1208
|
+
_sortSelectedByDomOrder() {
|
|
1209
|
+
if (!this.multiSelect || !Array.isArray(this.optionSelected) || !this.items) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const indexMap = new Map(this.items.map((item, index) => [
|
|
1214
|
+
item,
|
|
1215
|
+
index
|
|
1216
|
+
]));
|
|
1217
|
+
|
|
1218
|
+
// Sort any element no longer in `items` (a stale selection left over from a
|
|
1219
|
+
// dynamic rebuild that the consumer has not cleared) to the END rather than
|
|
1220
|
+
// the front, so it never displaces a live option to the head of the
|
|
1221
|
+
// serialized order. Value reconciliation drops it on the next updated() cycle.
|
|
1222
|
+
this.optionSelected.sort((optionA, optionB) => (indexMap.get(optionA) ?? this.items.length) - (indexMap.get(optionB) ?? this.items.length));
|
|
1223
|
+
this._selectedKey = this.optionSelected.map((option) => option._optionKey);
|
|
1224
|
+
this.value = serializeMultiSelectValue(this.optionSelected.map((option) => option.value));
|
|
1225
|
+
// Mark this `value` change as selection-driven so updated() trusts the keys.
|
|
1226
|
+
this._valueChangeFromSelection = true;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1000
1229
|
/**
|
|
1001
1230
|
* Resets the menu to its initial state.
|
|
1002
1231
|
* This is the only way to return value to undefined.
|
|
@@ -1006,6 +1235,7 @@ class AuroMenu extends AuroElement {
|
|
|
1006
1235
|
// Reset to undefined - initial state
|
|
1007
1236
|
this.value = undefined;
|
|
1008
1237
|
this.optionSelected = undefined;
|
|
1238
|
+
this._selectedKey = undefined;
|
|
1009
1239
|
this._index = -1;
|
|
1010
1240
|
|
|
1011
1241
|
// Clear active option state so a follow-up open/navigation starts fresh
|
|
@@ -1227,7 +1227,7 @@ class AuroHelpText extends i$2 {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
}
|
|
1229
1229
|
|
|
1230
|
-
var formkitVersion = '
|
|
1230
|
+
var formkitVersion = '202608032144';
|
|
1231
1231
|
|
|
1232
1232
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
1233
1233
|
// See LICENSE in the project root for license information.
|
|
@@ -1227,7 +1227,7 @@ class AuroHelpText extends i$2 {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
}
|
|
1229
1229
|
|
|
1230
|
-
var formkitVersion = '
|
|
1230
|
+
var formkitVersion = '202608032144';
|
|
1231
1231
|
|
|
1232
1232
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
1233
1233
|
// See LICENSE in the project root for license information.
|
|
@@ -1227,7 +1227,7 @@ class AuroHelpText extends i$2 {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
}
|
|
1229
1229
|
|
|
1230
|
-
var formkitVersion = '
|
|
1230
|
+
var formkitVersion = '202608032144';
|
|
1231
1231
|
|
|
1232
1232
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
1233
1233
|
// See LICENSE in the project root for license information.
|
|
@@ -1166,7 +1166,7 @@ class AuroHelpText extends LitElement {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
}
|
|
1168
1168
|
|
|
1169
|
-
var formkitVersion = '
|
|
1169
|
+
var formkitVersion = '202608032144';
|
|
1170
1170
|
|
|
1171
1171
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
1172
1172
|
// See LICENSE in the project root for license information.
|
|
@@ -1166,7 +1166,7 @@ class AuroHelpText extends LitElement {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
}
|
|
1168
1168
|
|
|
1169
|
-
var formkitVersion = '
|
|
1169
|
+
var formkitVersion = '202608032144';
|
|
1170
1170
|
|
|
1171
1171
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
1172
1172
|
// See LICENSE in the project root for license information.
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
<auro-anchorlink fluid href="#noValidate" class="level2 body-xs">No Validation</auro-anchorlink>
|
|
34
34
|
<auro-anchorlink fluid href="#placeholder" class="level2 body-xs">Placeholder</auro-anchorlink>
|
|
35
35
|
<auro-anchorlink fluid href="#loading" class="level2 body-xs">Loading</auro-anchorlink>
|
|
36
|
+
<auro-anchorlink fluid href="#nonUniqueValues" class="level2 body-xs">Non-Unique Option Values</auro-anchorlink>
|
|
36
37
|
</auro-nav>
|
|
37
38
|
</nav>
|
|
38
39
|
<div class="mainContent">
|
|
@@ -1297,6 +1298,77 @@
|
|
|
1297
1298
|
</auro-select></code></pre>
|
|
1298
1299
|
<!-- AURO-GENERATED-CONTENT:END -->
|
|
1299
1300
|
</auro-accordion>
|
|
1301
|
+
<auro-header level="3" id="nonUniqueValues">Non-Unique Option Values</auro-header>
|
|
1302
|
+
<p>Two or more <code>auro-menuoption</code> elements may share the same <code>value</code>. This is common when the <code>value</code> represents a coarser grouping than the option label — for example, several airports that all serve the same city. The component tracks the specific option a user selects, so the correct label is displayed even when the underlying <code>value</code> is duplicated.</p>
|
|
1303
|
+
<div class="exampleWrapper">
|
|
1304
|
+
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/duplicate-values.html) -->
|
|
1305
|
+
<!-- The below content is automatically added from ./../apiExamples/duplicate-values.html -->
|
|
1306
|
+
<auro-select>
|
|
1307
|
+
<span slot="ariaLabel.bib.close">Close Popup</span>
|
|
1308
|
+
<span slot="bib.fullscreen.headline">Choose an airport</span>
|
|
1309
|
+
<span slot="label">Departure airport</span>
|
|
1310
|
+
<auro-menu>
|
|
1311
|
+
<auro-menuoption value="seattle">Seattle–Tacoma International (SEA)</auro-menuoption>
|
|
1312
|
+
<auro-menuoption value="seattle">Seattle Paine Field (PAE)</auro-menuoption>
|
|
1313
|
+
<auro-menuoption value="portland">Portland International (PDX)</auro-menuoption>
|
|
1314
|
+
<auro-menuoption value="spokane">Spokane International (GEG)</auro-menuoption>
|
|
1315
|
+
</auro-menu>
|
|
1316
|
+
</auro-select>
|
|
1317
|
+
<!-- AURO-GENERATED-CONTENT:END -->
|
|
1318
|
+
</div>
|
|
1319
|
+
<auro-accordion alignRight>
|
|
1320
|
+
<span slot="trigger">See code</span>
|
|
1321
|
+
<!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/duplicate-values.html) -->
|
|
1322
|
+
<!-- The below code snippet is automatically added from ./../apiExamples/duplicate-values.html -->
|
|
1323
|
+
<pre class="language-html"><code class="language-html"><auro-select>
|
|
1324
|
+
<span slot="ariaLabel.bib.close">Close Popup</span>
|
|
1325
|
+
<span slot="bib.fullscreen.headline">Choose an airport</span>
|
|
1326
|
+
<span slot="label">Departure airport</span>
|
|
1327
|
+
<auro-menu>
|
|
1328
|
+
<auro-menuoption value="seattle">Seattle&ndash;Tacoma International (SEA)</auro-menuoption>
|
|
1329
|
+
<auro-menuoption value="seattle">Seattle Paine Field (PAE)</auro-menuoption>
|
|
1330
|
+
<auro-menuoption value="portland">Portland International (PDX)</auro-menuoption>
|
|
1331
|
+
<auro-menuoption value="spokane">Spokane International (GEG)</auro-menuoption>
|
|
1332
|
+
</auro-menu>
|
|
1333
|
+
</auro-select></code></pre>
|
|
1334
|
+
<!-- AURO-GENERATED-CONTENT:END -->
|
|
1335
|
+
</auro-accordion>
|
|
1336
|
+
<p>In <code>multiselect</code> mode, options that share a <code>value</code> are tracked independently, so each can be selected and removed on its own. Selections are stored in DOM order regardless of the order in which they were chosen.</p>
|
|
1337
|
+
<div class="exampleWrapper">
|
|
1338
|
+
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../apiExamples/duplicate-values-multiselect.html) -->
|
|
1339
|
+
<!-- The below content is automatically added from ./../apiExamples/duplicate-values-multiselect.html -->
|
|
1340
|
+
<auro-select multiselect>
|
|
1341
|
+
<span slot="ariaLabel.bib.close">Close Popup</span>
|
|
1342
|
+
<span slot="bib.fullscreen.headline">Choose airports</span>
|
|
1343
|
+
<label slot="placeholder">Select one or more airports</label>
|
|
1344
|
+
<span slot="label">Airports served</span>
|
|
1345
|
+
<auro-menu>
|
|
1346
|
+
<auro-menuoption value="seattle">Seattle–Tacoma International (SEA)</auro-menuoption>
|
|
1347
|
+
<auro-menuoption value="seattle">Seattle Paine Field (PAE)</auro-menuoption>
|
|
1348
|
+
<auro-menuoption value="portland">Portland International (PDX)</auro-menuoption>
|
|
1349
|
+
<auro-menuoption value="spokane">Spokane International (GEG)</auro-menuoption>
|
|
1350
|
+
</auro-menu>
|
|
1351
|
+
</auro-select>
|
|
1352
|
+
<!-- AURO-GENERATED-CONTENT:END -->
|
|
1353
|
+
</div>
|
|
1354
|
+
<auro-accordion alignRight>
|
|
1355
|
+
<span slot="trigger">See code</span>
|
|
1356
|
+
<!-- AURO-GENERATED-CONTENT:START (CODE:src=./../apiExamples/duplicate-values-multiselect.html) -->
|
|
1357
|
+
<!-- The below code snippet is automatically added from ./../apiExamples/duplicate-values-multiselect.html -->
|
|
1358
|
+
<pre class="language-html"><code class="language-html"><auro-select multiselect>
|
|
1359
|
+
<span slot="ariaLabel.bib.close">Close Popup</span>
|
|
1360
|
+
<span slot="bib.fullscreen.headline">Choose airports</span>
|
|
1361
|
+
<label slot="placeholder">Select one or more airports</label>
|
|
1362
|
+
<span slot="label">Airports served</span>
|
|
1363
|
+
<auro-menu>
|
|
1364
|
+
<auro-menuoption value="seattle">Seattle&ndash;Tacoma International (SEA)</auro-menuoption>
|
|
1365
|
+
<auro-menuoption value="seattle">Seattle Paine Field (PAE)</auro-menuoption>
|
|
1366
|
+
<auro-menuoption value="portland">Portland International (PDX)</auro-menuoption>
|
|
1367
|
+
<auro-menuoption value="spokane">Spokane International (GEG)</auro-menuoption>
|
|
1368
|
+
</auro-menu>
|
|
1369
|
+
</auro-select></code></pre>
|
|
1370
|
+
<!-- AURO-GENERATED-CONTENT:END -->
|
|
1371
|
+
</auro-accordion>
|
|
1300
1372
|
</section>
|
|
1301
1373
|
</div>
|
|
1302
1374
|
</div>
|