@aurodesignsystem-dev/auro-formkit 0.0.0-pr1544.0 → 0.0.0-pr1544.1

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 (69) 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/api.md +1 -1
  7. package/components/combobox/demo/customize.min.js +91 -22
  8. package/components/combobox/demo/getting-started.min.js +91 -22
  9. package/components/combobox/demo/index.md +1 -0
  10. package/components/combobox/demo/index.min.js +91 -22
  11. package/components/combobox/dist/auro-combobox.d.ts +1 -1
  12. package/components/combobox/dist/index.js +4 -4
  13. package/components/combobox/dist/registered.js +4 -4
  14. package/components/counter/demo/customize.min.js +2 -2
  15. package/components/counter/demo/index.min.js +2 -2
  16. package/components/counter/dist/index.js +2 -2
  17. package/components/counter/dist/registered.js +2 -2
  18. package/components/datepicker/demo/customize.min.js +3 -3
  19. package/components/datepicker/demo/index.min.js +3 -3
  20. package/components/datepicker/dist/index.js +3 -3
  21. package/components/datepicker/dist/registered.js +3 -3
  22. package/components/dropdown/demo/customize.min.js +1 -1
  23. package/components/dropdown/demo/getting-started.min.js +1 -1
  24. package/components/dropdown/demo/index.min.js +1 -1
  25. package/components/dropdown/dist/index.js +1 -1
  26. package/components/dropdown/dist/registered.js +1 -1
  27. package/components/form/demo/customize.min.js +106 -33
  28. package/components/form/demo/getting-started.min.js +106 -33
  29. package/components/form/demo/index.min.js +106 -33
  30. package/components/form/demo/registerDemoDeps.min.js +106 -33
  31. package/components/input/demo/customize.min.js +1 -1
  32. package/components/input/demo/getting-started.min.js +1 -1
  33. package/components/input/demo/index.min.js +1 -1
  34. package/components/input/dist/index.js +1 -1
  35. package/components/input/dist/registered.js +1 -1
  36. package/components/menu/demo/accessibility.md +4 -4
  37. package/components/menu/demo/api.md +26 -19
  38. package/components/menu/demo/css-only.md +26 -19
  39. package/components/menu/demo/customize.md +307 -47
  40. package/components/menu/demo/design.md +1 -1
  41. package/components/menu/demo/getting-started.md +144 -7
  42. package/components/menu/demo/index.min.js +214 -19
  43. package/components/menu/demo/keyboard-behavior.md +83 -4
  44. package/components/menu/demo/voiceover.md +12 -8
  45. package/components/menu/demo/why-menu.md +8 -9
  46. package/components/menu/dist/auro-menu-utils.d.ts +19 -0
  47. package/components/menu/dist/auro-menu.d.ts +4 -8
  48. package/components/menu/dist/auro-menuoption.d.ts +12 -0
  49. package/components/menu/dist/index.js +87 -18
  50. package/components/menu/dist/registered.js +87 -18
  51. package/components/radio/demo/customize.min.js +1 -1
  52. package/components/radio/demo/getting-started.min.js +1 -1
  53. package/components/radio/demo/index.min.js +1 -1
  54. package/components/radio/dist/index.js +1 -1
  55. package/components/radio/dist/registered.js +1 -1
  56. package/components/select/demo/api.md +2 -2
  57. package/components/select/demo/customize.md +5 -2
  58. package/components/select/demo/customize.min.js +94 -21
  59. package/components/select/demo/design.md +10 -10
  60. package/components/select/demo/getting-started.md +1 -1
  61. package/components/select/demo/getting-started.min.js +94 -21
  62. package/components/select/demo/index.md +2 -2
  63. package/components/select/demo/index.min.js +94 -21
  64. package/components/select/demo/keyboard-behavior.md +49 -18
  65. package/components/select/dist/auro-select.d.ts +6 -2
  66. package/components/select/dist/index.js +7 -3
  67. package/components/select/dist/registered.js +7 -3
  68. package/custom-elements.json +428 -354
  69. package/package.json +1 -1
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607082218';
1278
+ var formkitVersion = '202607082226';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607082218';
1278
+ var formkitVersion = '202607082226';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607082218';
1278
+ var formkitVersion = '202607082226';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1228,7 +1228,7 @@ class AuroHelpText extends LitElement {
1228
1228
  }
1229
1229
  }
1230
1230
 
1231
- var formkitVersion = '202607082218';
1231
+ var formkitVersion = '202607082226';
1232
1232
 
1233
1233
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1234
1234
  // See LICENSE in the project root for license information.
@@ -1228,7 +1228,7 @@ class AuroHelpText extends LitElement {
1228
1228
  }
1229
1229
  }
1230
1230
 
1231
- var formkitVersion = '202607082218';
1231
+ var formkitVersion = '202607082226';
1232
1232
 
1233
1233
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1234
1234
  // See LICENSE in the project root for license information.
@@ -42,7 +42,7 @@ The `auro-combobox` element provides users with a way to select an option from a
42
42
  | `type` | `type` | | `string` | | Applies the defined value as the type attribute on `auro-input`. |
43
43
  | `typedValue` | `typedValue` | | `string` | | Specifies the value of the input element within the combobox. |
44
44
  | `validity` | `validity` | | `string` | | Specifies the `validityState` this element is in. |
45
- | `value` | `value` | | `string` | | Value selected for the dropdown menu. |
45
+ | `value` | `value` | | `string` | | Value selected for the dropdown menu. When set programmatically or as a preset attribute, the value must match a selectable option. If it matches an option marked `disabled` or `static`, the selection is rejected: `value` and `optionSelected` are cleared to `undefined`. `hidden` options (including those filtered out by type-ahead) remain selectable by value. |
46
46
 
47
47
  ## Methods
48
48
 
@@ -4896,7 +4896,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4896
4896
  }
4897
4897
  };
4898
4898
 
4899
- var formkitVersion$2 = '202607082218';
4899
+ var formkitVersion$2 = '202607082226';
4900
4900
 
4901
4901
  let AuroElement$2 = class AuroElement extends i$3 {
4902
4902
  static get properties() {
@@ -18521,7 +18521,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18521
18521
  }
18522
18522
  };
18523
18523
 
18524
- var formkitVersion$1 = '202607082218';
18524
+ var formkitVersion$1 = '202607082226';
18525
18525
 
18526
18526
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18527
18527
  // See LICENSE in the project root for license information.
@@ -19642,7 +19642,7 @@ class AuroBibtemplate extends i$3 {
19642
19642
  }
19643
19643
  }
19644
19644
 
19645
- var formkitVersion = '202607082218';
19645
+ var formkitVersion = '202607082226';
19646
19646
 
19647
19647
  var styleCss$3 = i$6`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
19648
19648
 
@@ -20458,7 +20458,7 @@ class AuroCombobox extends AuroElement {
20458
20458
  },
20459
20459
 
20460
20460
  /**
20461
- * Value selected for the dropdown menu.
20461
+ * Value selected for the dropdown menu. When set programmatically or as a preset attribute, the value must match a selectable option. If it matches an option marked `disabled` or `static`, the selection is rejected: `value` and `optionSelected` are cleared to `undefined`. `hidden` options (including those filtered out by type-ahead) remain selectable by value.
20462
20462
  */
20463
20463
  value: {
20464
20464
  type: String
@@ -21976,6 +21976,18 @@ var tokensCss = i$6`:host{--ds-auro-menu-divider-color: var(--ds-basic-color-bor
21976
21976
  // See LICENSE in the project root for license information.
21977
21977
 
21978
21978
 
21979
+ /**
21980
+ * Serializes a multi-select value array back into the String `value` property.
21981
+ * An empty (or missing) array collapses to `undefined` so an emptied selection
21982
+ * clears `value` rather than reflecting a `"[]"` attribute.
21983
+ * @private
21984
+ * @param {Array<string>|undefined} values - The selected values.
21985
+ * @returns {string|undefined} JSON string of the values, or undefined when empty.
21986
+ */
21987
+ function serializeMultiSelectValue(values) {
21988
+ return values && values.length > 0 ? JSON.stringify(values) : undefined;
21989
+ }
21990
+
21979
21991
  /**
21980
21992
  * Validates if an option can be interacted with.
21981
21993
  * @private
@@ -21988,6 +22000,20 @@ function isOptionInteractive(option) {
21988
22000
  !option.hasAttribute('static');
21989
22001
  }
21990
22002
 
22003
+ /**
22004
+ * Validates if an option may be selected by matching a programmatic value.
22005
+ * Unlike `isOptionInteractive`, `hidden` is allowed: the combobox toggles
22006
+ * `hidden` as its type-ahead filter, so a filtered-out option is still a
22007
+ * valid programmatic selection. Only disabled and static options — which are
22008
+ * never selectable — are rejected.
22009
+ * @param {HTMLElement} option - The option to check.
22010
+ * @returns {boolean} True if option can be selected by value.
22011
+ */
22012
+ function isSelectableByValue(option) {
22013
+ return !option.hasAttribute('disabled') &&
22014
+ !option.hasAttribute('static');
22015
+ }
22016
+
21991
22017
  /**
21992
22018
  * Helper method to dispatch custom events.
21993
22019
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -22157,22 +22183,26 @@ class AuroMenu extends AuroElement {
22157
22183
 
22158
22184
  /**
22159
22185
  * Specifies the current active menuOption.
22186
+ * @readonly
22160
22187
  */
22161
22188
  optionActive: {
22162
22189
  type: Object,
22163
- attribute: 'optionactive'
22190
+ attribute: false
22164
22191
  },
22165
22192
 
22166
22193
  /**
22167
- * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
22194
+ * The currently selected menu option(s). In single-select mode this is a single `HTMLElement` (or `undefined` when nothing is selected). In multi-select mode this is an array of `HTMLElement`s.
22195
+ * @readonly
22168
22196
  */
22169
22197
  optionSelected: {
22170
22198
  // Allow HTMLElement, HTMLElement[] arrays and undefined
22171
- type: Object
22199
+ type: Object,
22200
+ attribute: false
22172
22201
  },
22173
22202
 
22174
22203
  /**
22175
22204
  * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
22205
+ * Options marked `disabled` or `static` are not selectable by value; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared (`optionSelected` becomes `undefined`) and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped from the value and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option.
22176
22206
  */
22177
22207
  value: {
22178
22208
  type: String,
@@ -22296,7 +22326,7 @@ class AuroMenu extends AuroElement {
22296
22326
  }
22297
22327
 
22298
22328
  /**
22299
- * Selects options by value.
22329
+ * Selects options by value. Options marked `disabled` or `static` are not selectable; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option. Passing `undefined`, `null`, an empty string, or an empty array clears the selection without dispatching a failure.
22300
22330
  * @param {string|string[]|undefined|null} value - The value(s) to select.
22301
22331
  * @public
22302
22332
  */
@@ -22381,6 +22411,11 @@ class AuroMenu extends AuroElement {
22381
22411
  this.initItems();
22382
22412
  }
22383
22413
 
22414
+ // Set when reconciliation reassigns `value` below. That reassignment schedules a
22415
+ // second updated() cycle, so the `event`-attribute dispatch is deferred to that
22416
+ // cycle to avoid firing option custom events twice on the same selection.
22417
+ let valueReconciled = false;
22418
+
22384
22419
  // Handle null/undefined/empty case — empty/whitespace strings clear selection
22385
22420
  // consistently with selectByValue(''), and avoid downstream `.includes('')` matches.
22386
22421
  if (this.value === undefined || this.value === null || (typeof this.value === 'string' && this.value.trim() === '')) {
@@ -22393,11 +22428,31 @@ class AuroMenu extends AuroElement {
22393
22428
  // Defensive default: `formattedValue` can be undefined for unexpected value types,
22394
22429
  // and calling `.includes` on undefined would throw during reconciliation.
22395
22430
  const valueArray = this.formattedValue || [];
22396
- const matchingOptions = this.items ? this.items.filter((item) => valueArray.includes(item.value)) : [];
22431
+ const matchingOptions = this.items ? this.items.filter((item) => isSelectableByValue(item) && valueArray.includes(item.value)) : [];
22397
22432
  newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
22433
+
22434
+ // Reconcile `value` with the selectable set. Drop only entries whose option is
22435
+ // loaded but non-selectable (disabled/static) — leaving them would desync `value`
22436
+ // from `optionSelected`, and the toggle handlers rebuild `value` from `formattedValue`,
22437
+ // so the rejected entry would resurface on the next select/deselect. Entries with no
22438
+ // matching item yet are preserved so async preselection still works once options render.
22439
+ const rejectedValues = this.items
22440
+ ? this.items.filter((item) => !isSelectableByValue(item) && valueArray.includes(item.value)).map((item) => item.value)
22441
+ : [];
22442
+ if (rejectedValues.length > 0) {
22443
+ const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
22444
+ this.value = serializeMultiSelectValue(reconciled);
22445
+ valueReconciled = true;
22446
+ }
22398
22447
  } else {
22399
- // In single-select mode, this.value should be a string
22400
- const matchingOption = this.items ? this.items.find((item) => item.value === this.value) : undefined;
22448
+ // In single-select mode, this.value should be a string. Reject
22449
+ // disabled/static options so a programmatic value pointing at a
22450
+ // non-selectable option falls through to the no-match path below
22451
+ // (dispatching auroMenu-selectValueFailure) instead of pinning it.
22452
+ // `hidden` is intentionally NOT excluded: the combobox toggles
22453
+ // `hidden` as its type-ahead filter, so a filtered-out option is
22454
+ // still a valid programmatic selection.
22455
+ const matchingOption = this.items ? this.items.find((item) => isSelectableByValue(item) && item.value === this.value) : undefined;
22401
22456
 
22402
22457
  if (matchingOption) {
22403
22458
  newSelected = matchingOption;
@@ -22439,8 +22494,9 @@ class AuroMenu extends AuroElement {
22439
22494
  ]
22440
22495
  ]));
22441
22496
 
22442
- // Notify of changes
22443
- if (this.optionSelected !== undefined) {
22497
+ // Notify of changes. Skip when reconciliation just reassigned `value`: the
22498
+ // follow-on update cycle re-runs this branch and fires the events exactly once.
22499
+ if (this.optionSelected !== undefined && !valueReconciled) {
22444
22500
  const selected = Array.isArray(this.optionSelected) ? this.optionSelected : [this.optionSelected];
22445
22501
  selected.forEach((opt) => {
22446
22502
  if (opt.hasAttribute('event')) {
@@ -22653,7 +22709,7 @@ class AuroMenu extends AuroElement {
22653
22709
  const currentSelected = this.optionSelected || [];
22654
22710
 
22655
22711
  if (!currentValue.includes(option.value)) {
22656
- this.value = JSON.stringify([
22712
+ this.value = serializeMultiSelectValue([
22657
22713
  ...currentValue,
22658
22714
  option.value
22659
22715
  ]);
@@ -22679,15 +22735,9 @@ class AuroMenu extends AuroElement {
22679
22735
  */
22680
22736
  handleDeselectState(option) {
22681
22737
  if (this.multiSelect) {
22682
- // Remove this option from array
22738
+ // Remove this option from array; an empty result collapses `value` to undefined.
22683
22739
  const newFormattedValue = (this.formattedValue || []).filter((val) => val !== option.value);
22684
-
22685
- // If array is empty after removal, set back to undefined
22686
- if (newFormattedValue && newFormattedValue.length === 0) {
22687
- this.value = undefined;
22688
- } else {
22689
- this.value = JSON.stringify(newFormattedValue);
22690
- }
22740
+ this.value = serializeMultiSelectValue(newFormattedValue);
22691
22741
 
22692
22742
  this.optionSelected = this.optionSelected.filter((val) => val !== option);
22693
22743
  if (this.optionSelected.length === 0) {
@@ -22734,6 +22784,11 @@ class AuroMenu extends AuroElement {
22734
22784
  this.optionSelected = undefined;
22735
22785
  this._index = -1;
22736
22786
 
22787
+ // Clear active option state so a follow-up open/navigation starts fresh
22788
+ // rather than reusing a stale reference from before the reset.
22789
+ this.items?.forEach((item) => item.classList.remove('active'));
22790
+ this.optionActive = undefined;
22791
+
22737
22792
  // Reset UI state
22738
22793
  this.updateItemsState(new Map([
22739
22794
  [
@@ -23143,6 +23198,7 @@ let menuOptionIdCounter = 0;
23143
23198
  * The `auro-menuoption` element provides users a way to define a menu option.
23144
23199
  * @customElement auro-menuoption
23145
23200
  *
23201
+ * @attr {Boolean} static - When present, marks the option as non-interactive — it renders but is skipped during keyboard navigation and cannot be selected. Useful for section headers, informational rows inside a menu, or attaching event listeners.
23146
23202
  * @slot default - The default slot for the menu option text.
23147
23203
  *
23148
23204
  * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
@@ -23172,6 +23228,7 @@ class AuroMenuOption extends AuroElement {
23172
23228
  this.noCheckmark = false;
23173
23229
  this.disabled = false;
23174
23230
  this.noMatch = false;
23231
+ this.persistent = false;
23175
23232
 
23176
23233
  /**
23177
23234
  * @private
@@ -23193,6 +23250,12 @@ class AuroMenuOption extends AuroElement {
23193
23250
  type: Boolean,
23194
23251
  reflect: true
23195
23252
  },
23253
+
23254
+ /**
23255
+ * **Deprecated.** Use the `value` attribute on `auro-menu` to set the selected option when the menu renders (or call `menu.selectByValue(value)` programmatically). Support for the child-level `selected` attribute will be removed in a future major release.
23256
+ *
23257
+ * @deprecated Use the `value` attribute on `auro-menu` instead.
23258
+ */
23196
23259
  selected: {
23197
23260
  type: Boolean,
23198
23261
  reflect: true
@@ -23227,6 +23290,12 @@ class AuroMenuOption extends AuroElement {
23227
23290
  reflect: true,
23228
23291
  attribute: 'nomatch'
23229
23292
  },
23293
+
23294
+ /** When true, this option is excluded from `matchWord` DOM rewriting — useful for utility rows (e.g., "Add new…") that must render identically regardless of the current filter. */
23295
+ persistent: {
23296
+ type: Boolean,
23297
+ reflect: true
23298
+ },
23230
23299
  };
23231
23300
  }
23232
23301
 
@@ -4896,7 +4896,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4896
4896
  }
4897
4897
  };
4898
4898
 
4899
- var formkitVersion$2 = '202607082218';
4899
+ var formkitVersion$2 = '202607082226';
4900
4900
 
4901
4901
  let AuroElement$2 = class AuroElement extends i$3 {
4902
4902
  static get properties() {
@@ -18521,7 +18521,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18521
18521
  }
18522
18522
  };
18523
18523
 
18524
- var formkitVersion$1 = '202607082218';
18524
+ var formkitVersion$1 = '202607082226';
18525
18525
 
18526
18526
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18527
18527
  // See LICENSE in the project root for license information.
@@ -19642,7 +19642,7 @@ class AuroBibtemplate extends i$3 {
19642
19642
  }
19643
19643
  }
19644
19644
 
19645
- var formkitVersion = '202607082218';
19645
+ var formkitVersion = '202607082226';
19646
19646
 
19647
19647
  var styleCss$3 = i$6`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
19648
19648
 
@@ -20458,7 +20458,7 @@ class AuroCombobox extends AuroElement {
20458
20458
  },
20459
20459
 
20460
20460
  /**
20461
- * Value selected for the dropdown menu.
20461
+ * Value selected for the dropdown menu. When set programmatically or as a preset attribute, the value must match a selectable option. If it matches an option marked `disabled` or `static`, the selection is rejected: `value` and `optionSelected` are cleared to `undefined`. `hidden` options (including those filtered out by type-ahead) remain selectable by value.
20462
20462
  */
20463
20463
  value: {
20464
20464
  type: String
@@ -21976,6 +21976,18 @@ var tokensCss = i$6`:host{--ds-auro-menu-divider-color: var(--ds-basic-color-bor
21976
21976
  // See LICENSE in the project root for license information.
21977
21977
 
21978
21978
 
21979
+ /**
21980
+ * Serializes a multi-select value array back into the String `value` property.
21981
+ * An empty (or missing) array collapses to `undefined` so an emptied selection
21982
+ * clears `value` rather than reflecting a `"[]"` attribute.
21983
+ * @private
21984
+ * @param {Array<string>|undefined} values - The selected values.
21985
+ * @returns {string|undefined} JSON string of the values, or undefined when empty.
21986
+ */
21987
+ function serializeMultiSelectValue(values) {
21988
+ return values && values.length > 0 ? JSON.stringify(values) : undefined;
21989
+ }
21990
+
21979
21991
  /**
21980
21992
  * Validates if an option can be interacted with.
21981
21993
  * @private
@@ -21988,6 +22000,20 @@ function isOptionInteractive(option) {
21988
22000
  !option.hasAttribute('static');
21989
22001
  }
21990
22002
 
22003
+ /**
22004
+ * Validates if an option may be selected by matching a programmatic value.
22005
+ * Unlike `isOptionInteractive`, `hidden` is allowed: the combobox toggles
22006
+ * `hidden` as its type-ahead filter, so a filtered-out option is still a
22007
+ * valid programmatic selection. Only disabled and static options — which are
22008
+ * never selectable — are rejected.
22009
+ * @param {HTMLElement} option - The option to check.
22010
+ * @returns {boolean} True if option can be selected by value.
22011
+ */
22012
+ function isSelectableByValue(option) {
22013
+ return !option.hasAttribute('disabled') &&
22014
+ !option.hasAttribute('static');
22015
+ }
22016
+
21991
22017
  /**
21992
22018
  * Helper method to dispatch custom events.
21993
22019
  * @param {HTMLElement} element - Element to dispatch event from.
@@ -22157,22 +22183,26 @@ class AuroMenu extends AuroElement {
22157
22183
 
22158
22184
  /**
22159
22185
  * Specifies the current active menuOption.
22186
+ * @readonly
22160
22187
  */
22161
22188
  optionActive: {
22162
22189
  type: Object,
22163
- attribute: 'optionactive'
22190
+ attribute: false
22164
22191
  },
22165
22192
 
22166
22193
  /**
22167
- * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
22194
+ * The currently selected menu option(s). In single-select mode this is a single `HTMLElement` (or `undefined` when nothing is selected). In multi-select mode this is an array of `HTMLElement`s.
22195
+ * @readonly
22168
22196
  */
22169
22197
  optionSelected: {
22170
22198
  // Allow HTMLElement, HTMLElement[] arrays and undefined
22171
- type: Object
22199
+ type: Object,
22200
+ attribute: false
22172
22201
  },
22173
22202
 
22174
22203
  /**
22175
22204
  * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
22205
+ * Options marked `disabled` or `static` are not selectable by value; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared (`optionSelected` becomes `undefined`) and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped from the value and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option.
22176
22206
  */
22177
22207
  value: {
22178
22208
  type: String,
@@ -22296,7 +22326,7 @@ class AuroMenu extends AuroElement {
22296
22326
  }
22297
22327
 
22298
22328
  /**
22299
- * Selects options by value.
22329
+ * Selects options by value. Options marked `disabled` or `static` are not selectable; `hidden` options remain selectable. In single-select mode, if the value matches a non-selectable option the selection is cleared and `auroMenu-selectValueFailure` is dispatched. In multi-select mode, non-selectable entries are dropped and the remaining selectable entries are selected; `auroMenu-selectValueFailure` is dispatched only when none of the entries match a selectable option. Passing `undefined`, `null`, an empty string, or an empty array clears the selection without dispatching a failure.
22300
22330
  * @param {string|string[]|undefined|null} value - The value(s) to select.
22301
22331
  * @public
22302
22332
  */
@@ -22381,6 +22411,11 @@ class AuroMenu extends AuroElement {
22381
22411
  this.initItems();
22382
22412
  }
22383
22413
 
22414
+ // Set when reconciliation reassigns `value` below. That reassignment schedules a
22415
+ // second updated() cycle, so the `event`-attribute dispatch is deferred to that
22416
+ // cycle to avoid firing option custom events twice on the same selection.
22417
+ let valueReconciled = false;
22418
+
22384
22419
  // Handle null/undefined/empty case — empty/whitespace strings clear selection
22385
22420
  // consistently with selectByValue(''), and avoid downstream `.includes('')` matches.
22386
22421
  if (this.value === undefined || this.value === null || (typeof this.value === 'string' && this.value.trim() === '')) {
@@ -22393,11 +22428,31 @@ class AuroMenu extends AuroElement {
22393
22428
  // Defensive default: `formattedValue` can be undefined for unexpected value types,
22394
22429
  // and calling `.includes` on undefined would throw during reconciliation.
22395
22430
  const valueArray = this.formattedValue || [];
22396
- const matchingOptions = this.items ? this.items.filter((item) => valueArray.includes(item.value)) : [];
22431
+ const matchingOptions = this.items ? this.items.filter((item) => isSelectableByValue(item) && valueArray.includes(item.value)) : [];
22397
22432
  newSelected = matchingOptions.length > 0 ? matchingOptions : undefined;
22433
+
22434
+ // Reconcile `value` with the selectable set. Drop only entries whose option is
22435
+ // loaded but non-selectable (disabled/static) — leaving them would desync `value`
22436
+ // from `optionSelected`, and the toggle handlers rebuild `value` from `formattedValue`,
22437
+ // so the rejected entry would resurface on the next select/deselect. Entries with no
22438
+ // matching item yet are preserved so async preselection still works once options render.
22439
+ const rejectedValues = this.items
22440
+ ? this.items.filter((item) => !isSelectableByValue(item) && valueArray.includes(item.value)).map((item) => item.value)
22441
+ : [];
22442
+ if (rejectedValues.length > 0) {
22443
+ const reconciled = valueArray.filter((val) => !rejectedValues.includes(val));
22444
+ this.value = serializeMultiSelectValue(reconciled);
22445
+ valueReconciled = true;
22446
+ }
22398
22447
  } else {
22399
- // In single-select mode, this.value should be a string
22400
- const matchingOption = this.items ? this.items.find((item) => item.value === this.value) : undefined;
22448
+ // In single-select mode, this.value should be a string. Reject
22449
+ // disabled/static options so a programmatic value pointing at a
22450
+ // non-selectable option falls through to the no-match path below
22451
+ // (dispatching auroMenu-selectValueFailure) instead of pinning it.
22452
+ // `hidden` is intentionally NOT excluded: the combobox toggles
22453
+ // `hidden` as its type-ahead filter, so a filtered-out option is
22454
+ // still a valid programmatic selection.
22455
+ const matchingOption = this.items ? this.items.find((item) => isSelectableByValue(item) && item.value === this.value) : undefined;
22401
22456
 
22402
22457
  if (matchingOption) {
22403
22458
  newSelected = matchingOption;
@@ -22439,8 +22494,9 @@ class AuroMenu extends AuroElement {
22439
22494
  ]
22440
22495
  ]));
22441
22496
 
22442
- // Notify of changes
22443
- if (this.optionSelected !== undefined) {
22497
+ // Notify of changes. Skip when reconciliation just reassigned `value`: the
22498
+ // follow-on update cycle re-runs this branch and fires the events exactly once.
22499
+ if (this.optionSelected !== undefined && !valueReconciled) {
22444
22500
  const selected = Array.isArray(this.optionSelected) ? this.optionSelected : [this.optionSelected];
22445
22501
  selected.forEach((opt) => {
22446
22502
  if (opt.hasAttribute('event')) {
@@ -22653,7 +22709,7 @@ class AuroMenu extends AuroElement {
22653
22709
  const currentSelected = this.optionSelected || [];
22654
22710
 
22655
22711
  if (!currentValue.includes(option.value)) {
22656
- this.value = JSON.stringify([
22712
+ this.value = serializeMultiSelectValue([
22657
22713
  ...currentValue,
22658
22714
  option.value
22659
22715
  ]);
@@ -22679,15 +22735,9 @@ class AuroMenu extends AuroElement {
22679
22735
  */
22680
22736
  handleDeselectState(option) {
22681
22737
  if (this.multiSelect) {
22682
- // Remove this option from array
22738
+ // Remove this option from array; an empty result collapses `value` to undefined.
22683
22739
  const newFormattedValue = (this.formattedValue || []).filter((val) => val !== option.value);
22684
-
22685
- // If array is empty after removal, set back to undefined
22686
- if (newFormattedValue && newFormattedValue.length === 0) {
22687
- this.value = undefined;
22688
- } else {
22689
- this.value = JSON.stringify(newFormattedValue);
22690
- }
22740
+ this.value = serializeMultiSelectValue(newFormattedValue);
22691
22741
 
22692
22742
  this.optionSelected = this.optionSelected.filter((val) => val !== option);
22693
22743
  if (this.optionSelected.length === 0) {
@@ -22734,6 +22784,11 @@ class AuroMenu extends AuroElement {
22734
22784
  this.optionSelected = undefined;
22735
22785
  this._index = -1;
22736
22786
 
22787
+ // Clear active option state so a follow-up open/navigation starts fresh
22788
+ // rather than reusing a stale reference from before the reset.
22789
+ this.items?.forEach((item) => item.classList.remove('active'));
22790
+ this.optionActive = undefined;
22791
+
22737
22792
  // Reset UI state
22738
22793
  this.updateItemsState(new Map([
22739
22794
  [
@@ -23143,6 +23198,7 @@ let menuOptionIdCounter = 0;
23143
23198
  * The `auro-menuoption` element provides users a way to define a menu option.
23144
23199
  * @customElement auro-menuoption
23145
23200
  *
23201
+ * @attr {Boolean} static - When present, marks the option as non-interactive — it renders but is skipped during keyboard navigation and cannot be selected. Useful for section headers, informational rows inside a menu, or attaching event listeners.
23146
23202
  * @slot default - The default slot for the menu option text.
23147
23203
  *
23148
23204
  * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
@@ -23172,6 +23228,7 @@ class AuroMenuOption extends AuroElement {
23172
23228
  this.noCheckmark = false;
23173
23229
  this.disabled = false;
23174
23230
  this.noMatch = false;
23231
+ this.persistent = false;
23175
23232
 
23176
23233
  /**
23177
23234
  * @private
@@ -23193,6 +23250,12 @@ class AuroMenuOption extends AuroElement {
23193
23250
  type: Boolean,
23194
23251
  reflect: true
23195
23252
  },
23253
+
23254
+ /**
23255
+ * **Deprecated.** Use the `value` attribute on `auro-menu` to set the selected option when the menu renders (or call `menu.selectByValue(value)` programmatically). Support for the child-level `selected` attribute will be removed in a future major release.
23256
+ *
23257
+ * @deprecated Use the `value` attribute on `auro-menu` instead.
23258
+ */
23196
23259
  selected: {
23197
23260
  type: Boolean,
23198
23261
  reflect: true
@@ -23227,6 +23290,12 @@ class AuroMenuOption extends AuroElement {
23227
23290
  reflect: true,
23228
23291
  attribute: 'nomatch'
23229
23292
  },
23293
+
23294
+ /** When true, this option is excluded from `matchWord` DOM rewriting — useful for utility rows (e.g., "Add new…") that must render identically regardless of the current filter. */
23295
+ persistent: {
23296
+ type: Boolean,
23297
+ reflect: true
23298
+ },
23230
23299
  };
23231
23300
  }
23232
23301
 
@@ -136,6 +136,7 @@
136
136
  <auro-header level="3" id="presetValue">Preset the value</auro-header>
137
137
  <p>In some cases it is necessary to preset the value of the component as part of the initial render.</p>
138
138
  <p>When a value is preset, the matching option in the menu will be marked as both <code>active</code> and <code>selected</code>, and the input will display the corresponding text.</p>
139
+ <p>If a preset value matches an option marked <code>disabled</code> or <code>static</code>, the selection is rejected: <code>value</code> and <code>optionSelected</code> are cleared to <code>undefined</code>. A value that matches a <code>hidden</code> option is still applied.</p>
139
140
  <p>If a preset value does not match any option, the component value is preserved and displayed in the input, but no menu option will be marked as selected. In suggestion mode, this value is treated as valid freeform input. In filter mode, the value will fail validation if the component is <code>required</code>.</p>
140
141
  <auro-header level="3" id="autocomplete">Autocomplete</auro-header>
141
142
  <p>The component supports the use of <auro-hyperlink href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete">autocomplete</auro-hyperlink> configuration through HTML attributes.</p>