@aurodesignsystem-dev/auro-formkit 0.0.0-pr1460.2 → 0.0.0-pr1464.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 (85) hide show
  1. package/components/checkbox/demo/api.html +4 -26
  2. package/components/checkbox/demo/getting-started.md +5 -6
  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.html +4 -25
  7. package/components/combobox/demo/getting-started.md +76 -27
  8. package/components/combobox/demo/index.min.js +15 -1
  9. package/components/combobox/demo/registered.min.js +1517 -1509
  10. package/components/combobox/dist/auro-combobox.d.ts +1 -1
  11. package/components/combobox/dist/index.js +18 -11
  12. package/components/combobox/dist/registered.js +18 -11
  13. package/components/counter/demo/api.html +5 -29
  14. package/components/counter/demo/getting-started.md +4 -3
  15. package/components/counter/demo/index.min.js +8392 -1
  16. package/components/counter/dist/index.js +53 -5284
  17. package/components/counter/dist/registered.js +106 -5110
  18. package/components/datepicker/demo/accessibility.html +0 -1
  19. package/components/datepicker/demo/api.html +4 -29
  20. package/components/datepicker/demo/getting-started.md +25 -2
  21. package/components/datepicker/demo/index.min.js +24612 -1
  22. package/components/datepicker/demo/readme.html +2 -10
  23. package/components/datepicker/dist/index.js +10 -10
  24. package/components/datepicker/dist/registered.js +10 -10
  25. package/components/dropdown/demo/api.html +5 -31
  26. package/components/dropdown/demo/getting-started.md +34 -2
  27. package/components/dropdown/demo/index.min.js +5097 -1
  28. package/components/dropdown/dist/index.js +1 -1
  29. package/components/dropdown/dist/registered.js +1 -1
  30. package/components/form/demo/api.html +5 -27
  31. package/components/form/demo/getting-started.md +5 -6
  32. package/components/form/demo/index.min.js +719 -2
  33. package/components/form/demo/keyboard-behavior.md +38 -0
  34. package/components/form/demo/pages.json +1 -1
  35. package/components/form/demo/registerDemoDeps.min.js +11842 -60908
  36. package/components/input/demo/accessibility.md +1 -1
  37. package/components/input/demo/api.html +15 -26
  38. package/components/input/demo/auro-input.min.js +1 -1
  39. package/components/input/demo/getting-started.md +1 -1
  40. package/components/input/demo/readme.html +2 -10
  41. package/components/input/dist/index.js +1 -1
  42. package/components/input/dist/registered.js +1 -1
  43. package/components/menu/demo/api.html +5 -30
  44. package/components/menu/demo/api.md +1 -1
  45. package/components/menu/demo/getting-started.md +1 -1
  46. package/components/menu/demo/index.min.js +1574 -1573
  47. package/components/menu/dist/auro-menu.context.d.ts +0 -1
  48. package/components/menu/dist/auro-menu.d.ts +1 -1
  49. package/components/menu/dist/index.js +1609 -1608
  50. package/components/menu/dist/registered.js +1553 -1552
  51. package/components/radio/demo/api.html +7 -28
  52. package/components/radio/demo/getting-started.md +27 -2
  53. package/components/radio/demo/index.md +1 -3
  54. package/components/radio/demo/index.min.js +1 -1
  55. package/components/radio/demo/readme.md +0 -2
  56. package/components/radio/dist/index.js +1 -1
  57. package/components/radio/dist/registered.js +1 -1
  58. package/components/select/demo/api.html +5 -42
  59. package/components/select/demo/getting-started.md +39 -5
  60. package/components/select/demo/registered.min.js +1503 -1502
  61. package/components/select/dist/index.js +2 -2
  62. package/components/select/dist/registered.js +2 -2
  63. package/custom-elements.json +1487 -1489
  64. package/package.json +37 -4
  65. package/components/combobox/demo/api.js +0 -39
  66. package/components/combobox/demo/api.min.js +0 -106
  67. package/components/combobox/demo/install.html +0 -50
  68. package/components/combobox/demo/styles.css +0 -974
  69. package/components/combobox/demo/swap-value.min.js +0 -16
  70. package/components/counter/demo/api.js +0 -24
  71. package/components/counter/demo/api.min.js +0 -52
  72. package/components/counter/demo/auro-counter-group.min.js +0 -8394
  73. package/components/datepicker/demo/api.js +0 -37
  74. package/components/datepicker/demo/api.min.js +0 -300
  75. package/components/datepicker/demo/auro-datepicker.min.js +0 -24614
  76. package/components/dropdown/demo/api.js +0 -26
  77. package/components/dropdown/demo/api.min.js +0 -109
  78. package/components/dropdown/demo/auro-dropdown.min.js +0 -5099
  79. package/components/form/demo/api.js +0 -5
  80. package/components/form/demo/api.min.js +0 -5
  81. package/components/form/demo/auro-form.min.js +0 -718
  82. package/components/form/demo/autocomplete.html +0 -31
  83. package/components/radio/demo/api.js +0 -19
  84. package/components/radio/demo/api.min.js +0 -44
  85. package/components/select/demo/keyboardBehavior.html +0 -48
@@ -5278,7 +5278,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$4 {
5278
5278
  }
5279
5279
  };
5280
5280
 
5281
- var formkitVersion$2 = '202605070044';
5281
+ var formkitVersion$2 = '202605081752';
5282
5282
 
5283
5283
  let AuroElement$2 = class AuroElement extends i$4 {
5284
5284
  static get properties() {
@@ -13030,7 +13030,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$4 {
13030
13030
  }
13031
13031
  };
13032
13032
 
13033
- var formkitVersion$1 = '202605070044';
13033
+ var formkitVersion$1 = '202605081752';
13034
13034
 
13035
13035
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
13036
13036
  // See LICENSE in the project root for license information.
@@ -14095,7 +14095,7 @@ class AuroBibtemplate extends i$4 {
14095
14095
  }
14096
14096
  }
14097
14097
 
14098
- var formkitVersion = '202605070044';
14098
+ var formkitVersion = '202605081752';
14099
14099
 
14100
14100
  var styleCss$3 = i$7`.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}`;
14101
14101
 
@@ -14492,7 +14492,6 @@ class AuroCombobox extends AuroElement {
14492
14492
  this.optionActive = null;
14493
14493
  this.persistInput = false;
14494
14494
  this.required = false;
14495
- this.value = undefined;
14496
14495
  this.typedValue = undefined;
14497
14496
  this.behavior = "suggestion";
14498
14497
  this.clearBtnFocused = false;
@@ -15105,6 +15104,10 @@ class AuroCombobox extends AuroElement {
15105
15104
  });
15106
15105
 
15107
15106
  if (this.value && this.input.value && !this.menu.value) {
15107
+ if (this.behavior === 'suggestion' && this.menu.options && this.menu.options.some((opt) => opt.value === this.value)) {
15108
+ this.setMenuValue(this.value);
15109
+ }
15110
+
15108
15111
  this.syncValuesAndStates();
15109
15112
  }
15110
15113
 
@@ -15718,10 +15721,6 @@ class AuroCombobox extends AuroElement {
15718
15721
  this.configureCombobox();
15719
15722
  this.configureMenu();
15720
15723
 
15721
- // Set the initial value in auro-menu if defined
15722
- if (this.hasAttribute('value') && this.getAttribute('value').length > 0) {
15723
- this.menu.value = this.value;
15724
- }
15725
15724
  }
15726
15725
 
15727
15726
  /**
@@ -15802,13 +15801,21 @@ class AuroCombobox extends AuroElement {
15802
15801
  this.input.value = this.value;
15803
15802
  }
15804
15803
 
15804
+ if (this.menu && this.hasValue && this.menu.options) {
15805
+ this.menu.options.forEach((opt) => {
15806
+ if (!opt.hasAttribute('static')) {
15807
+ opt.removeAttribute('hidden');
15808
+ }
15809
+ });
15810
+ }
15811
+
15805
15812
  if (this.behavior === 'suggestion') {
15806
- // if menu has an option that has matched value, then select it,
15807
- // otherwise clear the menu value since the input value doesn't match any option
15808
- if (this.menu.options && this.menu.options.filter((opt) => opt.value === this.value).length > 0) {
15813
+ if (!this.menu.options || this.menu.options.length === 0) ; else if (this.menu.options.filter((opt) => opt.value === this.value).length > 0) {
15809
15814
  this.setMenuValue(this.value);
15810
15815
  } else {
15811
15816
  this.menu.value = undefined;
15817
+ // Sync the input display for freeform values that don't match any option
15818
+ this.input.value = this.value;
15812
15819
  }
15813
15820
  } else {
15814
15821
  // Use setMenuValue like select does instead of direct assignment
@@ -16125,1943 +16132,1944 @@ class s{get value(){return this.o}set value(s){this.setValue(s);}setValue(s,t=fa
16125
16132
  * SPDX-License-Identifier: BSD-3-Clause
16126
16133
  */class e extends Event{constructor(t,s){super("context-provider",{bubbles:true,composed:true}),this.context=t,this.contextTarget=s;}}class i extends s{constructor(s,e,i){super(void 0!==e.context?e.initialValue:i),this.onContextRequest=t=>{if(t.context!==this.context)return;const s=t.contextTarget??t.composedPath()[0];s!==this.host&&(t.stopPropagation(),this.addCallback(t.callback,s,t.subscribe));},this.onProviderRequest=s=>{if(s.context!==this.context)return;if((s.contextTarget??s.composedPath()[0])===this.host)return;const e=new Set;for(const[s,{consumerHost:i}]of this.subscriptions)e.has(s)||(e.add(s),i.dispatchEvent(new s$2(this.context,i,s,true)));s.stopPropagation();},this.host=s,void 0!==e.context?this.context=e.context:this.context=e,this.attachListeners(),this.host.addController?.(this);}attachListeners(){this.host.addEventListener("context-request",this.onContextRequest),this.host.addEventListener("context-provider",this.onProviderRequest);}hostConnected(){this.host.dispatchEvent(new e(this.context,this.host));}}
16127
16134
 
16128
- var styleCss = i$7`.body-default{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-default-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-default-font-size, 1rem);line-height:var(--wcss-body-default-line-height, 1.5rem)}.body-default-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-default-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-default-emphasized-font-size, 1rem);line-height:var(--wcss-body-default-emphasized-line-height, 1.5rem)}.body-lg{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-lg-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-lg-font-size, 1.125rem);line-height:var(--wcss-body-lg-line-height, 1.625rem)}.body-lg-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-lg-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-lg-emphasized-font-size, 1.125rem);line-height:var(--wcss-body-lg-emphasized-line-height, 1.625rem)}.body-sm{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-sm-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-sm-font-size, 0.875rem);line-height:var(--wcss-body-sm-line-height, 1.25rem)}.body-sm-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-sm-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-sm-emphasized-font-size, 0.875rem);line-height:var(--wcss-body-sm-emphasized-line-height, 1.25rem)}.body-xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-xs-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-xs-font-size, 0.75rem);line-height:var(--wcss-body-xs-line-height, 1rem)}.body-xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-xs-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-xs-emphasized-font-size, 0.75rem);line-height:var(--wcss-body-xs-emphasized-line-height, 1rem)}.body-2xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-2xs-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-2xs-font-size, 0.625rem);line-height:var(--wcss-body-2xs-line-height, 0.875rem)}.body-2xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-2xs-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-2xs-emphasized-font-size, 0.625rem);line-height:var(--wcss-body-2xs-emphasized-line-height, 0.875rem)}.display-2xl{font-family:var(--wcss-display-2xl-family, "AS Circular"),var(--wcss-display-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-2xl-weight, 300);line-height:var(--wcss-display-2xl-line-height, 1.3);font-size:var(--wcss-display-2xl-font-size, clamp(3.5rem, 6vw, 5.375rem));letter-spacing:var(--wcss-display-2xl-letter-spacing, 0)}.display-xl{font-family:var(--wcss-display-xl-family, "AS Circular"),var(--wcss-display-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-xl-weight, 300);line-height:var(--wcss-display-xl-line-height, 1.3);font-size:var(--wcss-display-xl-font-size, clamp(3rem, 5.3333333333vw, 4.5rem));letter-spacing:var(--wcss-display-xl-letter-spacing, 0)}.display-lg{font-family:var(--wcss-display-lg-family, "AS Circular"),var(--wcss-display-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-lg-weight, 300);line-height:var(--wcss-display-lg-line-height, 1.3);font-size:var(--wcss-display-lg-font-size, clamp(2.75rem, 4.6666666667vw, 4rem));letter-spacing:var(--wcss-display-lg-letter-spacing, 0)}.display-md{font-family:var(--wcss-display-md-family, "AS Circular"),var(--wcss-display-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-md-weight, 300);line-height:var(--wcss-display-md-line-height, 1.3);font-size:var(--wcss-display-md-font-size, clamp(2.5rem, 4vw, 3.5rem));letter-spacing:var(--wcss-display-md-letter-spacing, 0)}.display-sm{font-family:var(--wcss-display-sm-family, "AS Circular"),var(--wcss-display-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-sm-weight, 300);line-height:var(--wcss-display-sm-line-height, 1.3);font-size:var(--wcss-display-sm-font-size, clamp(2rem, 3.6666666667vw, 3rem));letter-spacing:var(--wcss-display-sm-letter-spacing, 0)}.display-xs{font-family:var(--wcss-display-xs-family, "AS Circular"),var(--wcss-display-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-xs-weight, 300);line-height:var(--wcss-display-xs-line-height, 1.3);font-size:var(--wcss-display-xs-font-size, clamp(1.75rem, 3vw, 2.375rem));letter-spacing:var(--wcss-display-xs-letter-spacing, 0)}.heading-xl{font-family:var(--wcss-heading-xl-family, "AS Circular"),var(--wcss-heading-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-xl-weight, 300);line-height:var(--wcss-heading-xl-line-height, 1.3);font-size:var(--wcss-heading-xl-font-size, clamp(2rem, 3vw, 2.5rem));letter-spacing:var(--wcss-heading-xl-letter-spacing, 0)}.heading-lg{font-family:var(--wcss-heading-lg-family, "AS Circular"),var(--wcss-heading-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-lg-weight, 300);line-height:var(--wcss-heading-lg-line-height, 1.3);font-size:var(--wcss-heading-lg-font-size, clamp(1.75rem, 2.6666666667vw, 2.25rem));letter-spacing:var(--wcss-heading-lg-letter-spacing, 0)}.heading-md{font-family:var(--wcss-heading-md-family, "AS Circular"),var(--wcss-heading-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-md-weight, 300);line-height:var(--wcss-heading-md-line-height, 1.3);font-size:var(--wcss-heading-md-font-size, clamp(1.625rem, 2.3333333333vw, 1.75rem));letter-spacing:var(--wcss-heading-md-letter-spacing, 0)}.heading-sm{font-family:var(--wcss-heading-sm-family, "AS Circular"),var(--wcss-heading-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-sm-weight, 300);line-height:var(--wcss-heading-sm-line-height, 1.3);font-size:var(--wcss-heading-sm-font-size, clamp(1.375rem, 2vw, 1.5rem));letter-spacing:var(--wcss-heading-sm-letter-spacing, 0)}.heading-xs{font-family:var(--wcss-heading-xs-family, "AS Circular"),var(--wcss-heading-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-xs-weight, 300);line-height:var(--wcss-heading-xs-line-height, 1.3);font-size:var(--wcss-heading-xs-font-size, clamp(1.25rem, 1.6666666667vw, 1.25rem));letter-spacing:var(--wcss-heading-xs-letter-spacing, 0)}.heading-2xs{font-family:var(--wcss-heading-2xs-family, "AS Circular"),var(--wcss-heading-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-2xs-weight, 300);line-height:var(--wcss-heading-2xs-line-height, 1.3);font-size:var(--wcss-heading-2xs-font-size, clamp(1.125rem, 1.5vw, 1.125rem));letter-spacing:var(--wcss-heading-2xs-letter-spacing, 0)}.accent-2xl{font-family:var(--wcss-accent-2xl-family, "Good OT"),var(--wcss-accent-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-2xl-weight, 450);line-height:var(--wcss-accent-2xl-line-height, 1);font-size:var(--wcss-accent-2xl-font-size, clamp(2rem, 3.1666666667vw, 2.375rem));letter-spacing:var(--wcss-accent-2xl-letter-spacing, 0.05em);text-transform:uppercase}.accent-xl{font-family:var(--wcss-accent-xl-family, "Good OT"),var(--wcss-accent-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-xl-weight, 450);line-height:var(--wcss-accent-xl-line-height, 1.3);font-size:var(--wcss-accent-xl-font-size, clamp(1.625rem, 2.3333333333vw, 2rem));letter-spacing:var(--wcss-accent-xl-letter-spacing, 0.05em);text-transform:uppercase}.accent-lg{font-family:var(--wcss-accent-lg-family, "Good OT"),var(--wcss-accent-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-lg-weight, 450);line-height:var(--wcss-accent-lg-line-height, 1.3);font-size:var(--wcss-accent-lg-font-size, clamp(1.5rem, 2.1666666667vw, 1.75rem));letter-spacing:var(--wcss-accent-lg-letter-spacing, 0.05em);text-transform:uppercase}.accent-md{font-family:var(--wcss-accent-md-family, "Good OT"),var(--wcss-accent-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-md-weight, 500);line-height:var(--wcss-accent-md-line-height, 1.3);font-size:var(--wcss-accent-md-font-size, clamp(1.375rem, 1.8333333333vw, 1.5rem));letter-spacing:var(--wcss-accent-md-letter-spacing, 0.05em);text-transform:uppercase}.accent-sm{font-family:var(--wcss-accent-sm-family, "Good OT"),var(--wcss-accent-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-sm-weight, 500);line-height:var(--wcss-accent-sm-line-height, 1.3);font-size:var(--wcss-accent-sm-font-size, clamp(1.125rem, 1.5vw, 1.25rem));letter-spacing:var(--wcss-accent-sm-letter-spacing, 0.05em);text-transform:uppercase}.accent-xs{font-family:var(--wcss-accent-xs-family, "Good OT"),var(--wcss-accent-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-xs-weight, 500);line-height:var(--wcss-accent-xs-line-height, 1.3);font-size:var(--wcss-accent-xs-font-size, clamp(1rem, 1.3333333333vw, 1rem));letter-spacing:var(--wcss-accent-xs-letter-spacing, 0.1em);text-transform:uppercase}.accent-2xs{font-family:var(--wcss-accent-2xs-family, "Good OT"),var(--wcss-accent-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-2xs-weight, 450);line-height:var(--wcss-accent-2xs-line-height, 1.3);font-size:var(--wcss-accent-2xs-font-size, clamp(0.875rem, 1.1666666667vw, 0.875rem));letter-spacing:var(--wcss-accent-2xs-letter-spacing, 0.1em);text-transform:uppercase}:host{cursor:pointer;user-select:none;text-overflow:ellipsis;max-width:100dvw}:host .wrapper{display:flex;align-items:center;height:var(--ds-size-400, 2rem);padding-right:var(--ds-size-200, 1rem);padding-left:calc(var(--ds-size-150, 0.75rem) + var(--ds-size-300, 1.5rem) + var(--ds-size-100, 0.5rem));border-radius:var(--ds-size-100, 0.5rem);-webkit-tap-highlight-color:transparent}:host .wrapper[class*=shape-box]{border-radius:unset}:host .wrapper[class*=shape-snowflake]{border-radius:unset;line-height:24px}:host .wrapper[class*=shape-pill]{border-radius:30px}:host .wrapper[class*=-lg]{padding-top:var(--ds-size-75, 0.375rem);padding-bottom:var(--ds-size-75, 0.375rem);padding-right:var(--ds-size-150, 0.75rem);line-height:26px}:host .wrapper[class*=-xl]{padding-top:var(--ds-size-100, 0.5rem);padding-bottom:var(--ds-size-100, 0.5rem);padding-right:var(--ds-size-200, 1rem);line-height:26px}:host slot{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}:host [auro-icon]{--ds-auro-icon-size: var(--ds-size-300, 1.5rem);margin-right:var(--ds-size-150, 0.75rem);margin-left:var(--ds-size-100, 0.5rem)}:host ::slotted(.nestingSpacer){display:inline-block;width:var(--ds-size-300, 1.5rem)}[slot=displayValue]{display:none}:host([loadingplaceholder]) .wrapper{padding-left:calc(var(--ds-size-150, 0.75rem) + var(--ds-size-300, 1.5rem) + var(--ds-size-100, 0.5rem))}:host([selected]) .wrapper{padding-left:0}:host([nocheckmark]) .wrapper{padding-left:var(--ds-size-150, 0.75rem)}:host([nocheckmark]) .wrapper[class*=-lg]{padding-left:var(--ds-size-150, 0.75rem)}:host([nocheckmark]) .wrapper[class*=-xl]{padding-left:var(--ds-size-200, 1rem)}:host([hidden]){display:none}:host([static]){pointer-events:none}:host([disabled]:hover){cursor:auto}:host([disabled]){user-select:none;pointer-events:none}`;
16129
-
16130
- var colorCss = i$7`:host .wrapper{background-color:var(--ds-auro-menuoption-container-color, transparent);box-shadow:inset 0 0 0 1px var(--ds-auro-menuoption-container-border-color, transparent);color:var(--ds-auro-menuoption-text-color)}:host svg{fill:var(--ds-auro-menuoption-icon-color)}:host([disabled]){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-menuoption-text-color: var(--ds-basic-color-texticon-disabled, #d0d0d0);--ds-auro-menuoption-icon-color: var(--ds-basic-color-texticon-disabled, #d0d0d0)}:host(.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}@media(hover: hover){:host(:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}}:host(:focus){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}:host([selected]){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-subtle, #b4eff9);--ds-auro-menuoption-text-color: var(--ds-basic-color-texticon-default, #2a2a2a);--ds-auro-menuoption-icon-color: var(--ds-basic-color-texticon-default, #2a2a2a)}:host([selected].active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}@media(hover: hover){:host([selected]:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}}:host([selected]:focus){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-subtle, #b4eff9);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}:host(:focus.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}@media(hover: hover){:host(:focus:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}}:host([selected]:focus.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}@media(hover: hover){:host([selected]:focus:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}}`;
16131
-
16132
- class p{registerComponent(t,a){customElements.get(t)||customElements.define(t,class extends a{});}closestElement(t,a=this,e=(a,s=a&&a.closest(t))=>a&&a!==document&&a!==window?s||e(a.getRootNode().host):null){return e(a)}handleComponentTagRename(t,a){const e=a.toLowerCase();t.tagName.toLowerCase()!==e&&t.setAttribute(e,true);}elementMatch(t,a){const e=a.toLowerCase();return t.tagName.toLowerCase()===e||t.hasAttribute(e)}getSlotText(t,a){const e=t.shadowRoot?.querySelector(`slot[name="${a}"]`);return (e?.assignedNodes({flatten:true})||[]).map(t=>t.textContent?.trim()).join(" ").trim()||null}}var u='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-labelledby="error__desc" class="ico_squareLarge" data-deprecated="true" role="img" style="min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor" viewBox="0 0 24 24" part="svg"><title/><desc id="error__desc">Error alert indicator.</desc><path d="m13.047 5.599 6.786 11.586A1.207 1.207 0 0 1 18.786 19H5.214a1.207 1.207 0 0 1-1.047-1.815l6.786-11.586a1.214 1.214 0 0 1 2.094 0m-1.165.87a.23.23 0 0 0-.085.085L5.419 17.442a.232.232 0 0 0 .203.35h12.756a.234.234 0 0 0 .203-.35L12.203 6.554a.236.236 0 0 0-.321-.084M12 15.5a.75.75 0 1 1 0 1.5.75.75 0 0 1 0-1.5m-.024-6.22c.325 0 .589.261.589.583v4.434a.586.586 0 0 1-.589.583.586.586 0 0 1-.588-.583V9.863c0-.322.264-.583.588-.583"/></svg>',g='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" style="min-width:100%;height:auto;fill:currentColor" viewBox="0 0 142 138" part="svg"><title id="tail-DEFAULT__title">Airplane tail default image with light gray color</title><path fill="url(#paint0_linear_9225_2069)" fill-rule="evenodd" d="M124.093 14.665a3.26 3.26 0 0 1 3.208 3.829L101.097 119.85c-.47 1.087-2.211 3.398-5.692 3.471l-.106.001-80.65.013A659230 659230 0 0 1 99.08 17.412c1.214-1.497 3.275-2.722 5.372-2.747z" clip-rule="evenodd"/><defs><linearGradient id="paint0_linear_9225_2069" x1="114.823" x2="60.061" y1="14.665" y2="123.335" gradientUnits="userSpaceOnUse"><stop offset=".5" stop-color="#707984"/><stop offset=".5" stop-color="#646E7B"/></linearGradient></defs></svg>';class m extends i$4{static get properties(){return {hidden:{type:Boolean,reflect:true},hiddenVisually:{type:Boolean,reflect:true},hiddenAudible:{type:Boolean,reflect:true}}}hideAudible(t){return t?"true":"false"}}const f=new Map,w=(t,a={})=>{const e=a.responseParser||(t=>t.text());return f.has(t)||f.set(t,fetch(t).then(e)),f.get(t)};var z=i$7`:focus:not(:focus-visible){outline:3px solid transparent}.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock,:host{display:block}.util_displayFlex{display:flex}.util_displayHidden,:host([hidden]:not(:focus):not(:active)){display:none}.util_displayHiddenVisually,:host([hiddenVisually]:not(:focus):not(:active)){position:absolute;overflow:hidden;clip:rect(1px,1px,1px,1px);width:1px;height:1px;padding:0;border:0}.ico_squareLarge{fill:currentColor;height:var(--auro-size-lg, var(--ds-size-300, 1.5rem))}.ico_squareSmall{fill:currentColor;height:.6rem}.ico_squareMed{fill:currentColor;height:var(--auro-size-md, var(--ds-size-200, 1rem))}.ico_squareSml{fill:currentColor;height:var(--auro-size-sm, var(--ds-size-150, .75rem))}:host{color:currentColor;vertical-align:middle;display:inline-block}svg{min-width:var(--ds-auro-icon-size, 1.5rem)!important;width:var(--ds-auro-icon-size, 1.5rem)!important;height:var(--ds-auro-icon-size, 1.5rem)!important}.componentWrapper{display:flex;line-height:var(--ds-auro-icon-size)}.svgWrapper{height:var(--ds-auro-icon-size);width:var(--ds-auro-icon-size)}.svgWrapper [part=svg]{display:flex}.labelWrapper{margin-left:var(--ds-size-50, .25rem)}.labelWrapper ::slotted(*){line-height:inherit!important}
16133
- `;class y extends m{constructor(){super(),this._initializeDefaults();}_initializeDefaults(){this.onDark=false,this.appearance="default";}static get properties(){return {...m.properties,onDark:{type:Boolean,reflect:true},appearance:{type:String,reflect:true},svg:{attribute:false,reflect:true}}}static get styles(){return z}async fetchIcon(t,a){let e="";e="logos"===t?await w(`${this.uri}/${t}/${a}.svg`):await w(`${this.uri}/icons/${t}/${a}.svg`);return (new DOMParser).parseFromString(e,"text/html").body.querySelector("svg")}async firstUpdated(){try{if(!this.customSvg){const t=await this.fetchIcon(this.category,this.name);if(t)this.svg=t;else if(!t){const t=this.name?.startsWith("tail-")?g:u;this.svg=(new DOMParser).parseFromString(t,"text/html").body.querySelector("svg");}}}catch(t){this.svg=void 0;}}}i$7`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock,:host{display:block}.util_displayFlex{display:flex}.util_displayHidden,:host([hidden]:not(:focus):not(:active)){display:none}.util_displayHiddenVisually,:host([hiddenVisually]:not(:focus):not(:active)){position:absolute;overflow:hidden;clip:rect(1px,1px,1px,1px);width:1px;height:1px;padding:0;border:0}:host{display:inline-block;--ds-auro-icon-size: 100%;width:100%;height:100%}:host .logo{color:var(--ds-auro-alaska-color)}:host([onDark]),:host([appearance=inverse]){--ds-auro-alaska-color: #FFF}
16134
- `;var M=i$7`:host{--ds-auro-icon-color: var(--ds-basic-color-texticon-default, #2a2a2a);--ds-auro-alaska-color: #02426D;--ds-auro-icon-size: var(--ds-size-300, 1.5rem)}
16135
- `;var _=i$7`:host{color:var(--ds-auro-icon-color)}:host([customColor]){color:inherit}:host(:not([onDark])[variant=accent1]),:host(:not([appearance=inverse])[variant=accent1]){--ds-auro-icon-color: var(--ds-basic-color-texticon-accent1, #265688)}:host(:not([onDark])[variant=disabled]),:host(:not([appearance=inverse])[variant=disabled]){--ds-auro-icon-color: var(--ds-basic-color-texticon-disabled, #d0d0d0)}:host(:not([onDark])[variant=muted]),:host(:not([appearance=inverse])[variant=muted]){--ds-auro-icon-color: var(--ds-basic-color-texticon-muted, #676767)}:host(:not([onDark])[variant=statusDefault]),:host(:not([appearance=inverse])[variant=statusDefault]){--ds-auro-icon-color: var(--ds-basic-color-status-default, #afb9c6)}:host(:not([onDark])[variant=statusInfo]),:host(:not([appearance=inverse])[variant=statusInfo]){--ds-auro-icon-color: var(--ds-basic-color-status-info, #01426a)}:host(:not([onDark])[variant=statusSuccess]),:host(:not([appearance=inverse])[variant=statusSuccess]){--ds-auro-icon-color: var(--ds-basic-color-status-success, #447a1f)}:host(:not([onDark])[variant=statusWarning]),:host(:not([appearance=inverse])[variant=statusWarning]){--ds-auro-icon-color: var(--ds-basic-color-status-warning, #fac200)}:host(:not([onDark])[variant=statusError]),:host(:not([appearance=inverse])[variant=statusError]){--ds-auro-icon-color: var(--ds-basic-color-status-error, #e31f26)}:host(:not([onDark])[variant=statusInfoSubtle]),:host(:not([appearance=inverse])[variant=statusInfoSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-info-subtle, #ebf3f9)}:host(:not([onDark])[variant=statusSuccessSubtle]),:host(:not([appearance=inverse])[variant=statusSuccessSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-success-subtle, #d6eac7)}:host(:not([onDark])[variant=statusWarningSubtle]),:host(:not([appearance=inverse])[variant=statusWarningSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-warning-subtle, #fff0b2)}:host(:not([onDark])[variant=statusErrorSubtle]),:host(:not([appearance=inverse])[variant=statusErrorSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-error-subtle, #fbc6c6)}:host(:not([onDark])[variant=fareBasicEconomy]),:host(:not([appearance=inverse])[variant=fareBasicEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-basiceconomy, #97eaf8)}:host(:not([onDark])[variant=fareBusiness]),:host(:not([appearance=inverse])[variant=fareBusiness]){--ds-auro-icon-color: var(--ds-basic-color-fare-business, #01426a)}:host(:not([onDark])[variant=fareEconomy]),:host(:not([appearance=inverse])[variant=fareEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-economy, #0074ca)}:host(:not([onDark])[variant=fareFirst]),:host(:not([appearance=inverse])[variant=fareFirst]){--ds-auro-icon-color: var(--ds-basic-color-fare-first, #00274a)}:host(:not([onDark])[variant=farePremiumEconomy]),:host(:not([appearance=inverse])[variant=farePremiumEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-premiumeconomy, #005154)}:host(:not([onDark])[variant=tierOneWorldEmerald]),:host(:not([appearance=inverse])[variant=tierOneWorldEmerald]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-emerald, #139142)}:host(:not([onDark])[variant=tierOneWorldSapphire]),:host(:not([appearance=inverse])[variant=tierOneWorldSapphire]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-sapphire, #015daa)}:host(:not([onDark])[variant=tierOneWorldRuby]),:host(:not([appearance=inverse])[variant=tierOneWorldRuby]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-ruby, #a41d4a)}:host([onDark]),:host([appearance=inverse]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse, #ffffff)}:host([onDark][variant=disabled]),:host([appearance=inverse][variant=disabled]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse-disabled, #7e8894)}:host([onDark][variant=muted]),:host([appearance=inverse][variant=muted]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse-muted, #ccd2db)}:host([onDark][variant=statusError]),:host([appearance=inverse][variant=statusError]){--ds-auro-icon-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8)}
16136
- `;class k extends y{constructor(){super(),this._initializeDefaults();}_initializeDefaults(){this.variant=void 0,this.uri="https://cdn.jsdelivr.net/npm/@alaskaairux/icons@latest/dist",this.runtimeUtils=new p;}static get properties(){return {...y.properties,ariaHidden:{type:String,reflect:true},category:{type:String,reflect:true},customColor:{type:Boolean,reflect:true},customSvg:{type:Boolean},label:{type:Boolean,reflect:true},name:{type:String,reflect:true},variant:{type:String,reflect:true}}}static get styles(){return [y.styles,M,z,_]}static register(t="auro-icon"){p.prototype.registerComponent(t,k);}connectedCallback(){super.connectedCallback(),this.runtimeUtils.handleComponentTagRename(this,"auro-icon");}exposeCssParts(){this.setAttribute("exportparts","svg:iconSvg");}async firstUpdated(){if(await super.firstUpdated(),this.hasAttribute("ariaHidden")&&this.svg){const t=this.svg.querySelector("desc");t&&(t.remove(),this.svg.removeAttribute("aria-labelledby"));}}render(){const t={labelWrapper:true,util_displayHiddenVisually:!this.label};return b$2`
16137
- <div class="componentWrapper">
16138
- <div
16139
- class="${e$3({svgWrapper:true})}"
16140
- title="${o(this.title||void 0)}">
16141
- <span aria-hidden="${o(this.ariaHidden||true)}" part="svg">
16142
- ${this.customSvg?b$2`
16143
- <slot name="svg"></slot>
16144
- `:b$2`
16145
- ${this.svg}
16146
- `}
16147
- </span>
16148
- </div>
16149
-
16150
- <div class="${e$3(t)}" part="label">
16151
- <slot></slot>
16152
- </div>
16153
- </div>
16154
- `}}
16155
-
16156
- var iconVersion = '9.1.2';
16135
+ /* eslint-disable */
16157
16136
 
16158
- var checkmarkIcon = {"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-labelledby=\"checkmark-sm__desc\" class=\"ico_squareLarge\" role=\"img\" style=\"min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor\" viewBox=\"0 0 24 24\" part=\"svg\"><title/><desc id=\"checkmark-sm__desc\">a small check mark.</desc><path d=\"M8.461 11.84a.625.625 0 1 0-.922.844l2.504 2.738c.247.27.674.27.922 0l5.496-6a.625.625 0 1 0-.922-.844l-5.035 5.496z\"/></svg>"};
16137
+ class MenuService {
16159
16138
 
16160
- // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
16161
- // See LICENSE in the project root for license information.
16139
+ /**
16140
+ * PROPERTIES AND GETTERS
16141
+ */
16162
16142
 
16143
+ /**
16144
+ * Gets the list of registered menu options.
16145
+ * @returns {AuroMenuOption[]}
16146
+ */
16147
+ get menuOptions() {
16148
+ return this._menuOptions;
16149
+ }
16163
16150
 
16164
- /**
16165
- * Helper method to dispatch custom events.
16166
- * @param {HTMLElement} element - Element to dispatch event from.
16167
- * @param {string} eventName - Name of the event to dispatch.
16168
- * @param {Object} [detail] - Optional detail object to include with the event.
16169
- */
16170
- function dispatchMenuEvent(element, eventName, detail = null) {
16171
- const eventConfig = {
16172
- bubbles: true,
16173
- cancelable: false,
16174
- composed: true
16175
- };
16151
+ /**
16152
+ * Gets the currently highlighted option.
16153
+ * @returns {AuroMenuOption|null}
16154
+ */
16155
+ get highlightedOption() {
16156
+ return this._menuOptions[this.highlightedIndex] || null;
16157
+ }
16176
16158
 
16177
- if (detail !== null) {
16178
- eventConfig.detail = detail;
16159
+ /**
16160
+ * Gets the current value(s) of the selected option(s).
16161
+ * @returns {string|string[]|undefined}
16162
+ */
16163
+ get currentValue() {
16164
+ const values = (this.selectedOptions || []).map(option => option.value);
16165
+ return this.multiSelect ? values : values[0];
16179
16166
  }
16180
16167
 
16181
- element.dispatchEvent(new CustomEvent(eventName, eventConfig));
16182
- }
16168
+ /**
16169
+ * Gets the label(s) of the currently selected option(s).
16170
+ * @returns {string}
16171
+ */
16172
+ get currentLabel() {
16173
+ const labels = (this.selectedOptions || []).map(option => option.textContent);
16174
+ return this.multiSelect ? labels.join(", ") : labels[0] || '';
16175
+ }
16183
16176
 
16184
- // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
16185
- // See LICENSE in the project root for license information.
16177
+ /**
16178
+ * Gets the string representation of the current value(s).
16179
+ * For multi-select, this is a JSON stringified array.
16180
+ * @returns {string|undefined}
16181
+ */
16182
+ get stringValue() {
16183
+ const { currentValue } = this;
16186
16184
 
16185
+ if (Array.isArray(currentValue)) {
16186
+ if (currentValue.length > 0) {
16187
+ return JSON.stringify(currentValue);
16188
+ }
16189
+ return undefined;
16190
+ }
16187
16191
 
16188
- let menuOptionIdCounter = 0;
16192
+ if (typeof currentValue === 'string') {
16193
+ if (currentValue.length > 0) {
16194
+ return currentValue;
16195
+ }
16196
+ return undefined;
16197
+ }
16189
16198
 
16190
- /**
16191
- * The `auro-menuoption` element provides users a way to define a menu option.
16192
- * @customElement auro-menuoption
16193
- *
16194
- * @slot default - The default slot for the menu option text.
16195
- *
16196
- * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
16197
- */
16198
- class AuroMenuOption extends AuroElement {
16199
+ // Future: handle other types here (e.g., number, object, etc.)
16200
+ return undefined;
16201
+ }
16199
16202
 
16200
16203
  /**
16201
- * This will register this element with the browser.
16202
- * @param {string} [name="auro-menuoption"] - The name of the element that you want to register.
16203
- *
16204
- * @example
16205
- * AuroMenuOption.register("custom-menuoption") // this will register this element to <custom-menuoption/>
16206
- *
16204
+ * Gets the key(s) of the currently selected option(s).
16205
+ * @returns {string|string[]|undefined}
16207
16206
  */
16208
- static register(name = "auro-menuoption") {
16209
- AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroMenuOption);
16207
+ get currentKeys() {
16208
+ const keys = (this.selectedOptions || []).map(option => option.key);
16209
+ return this.multiSelect ? keys : keys[0];
16210
16210
  }
16211
16211
 
16212
16212
  /**
16213
- * Returns whether the menu option is currently active and selectable.
16214
- * An option is considered active if it is not hidden, not disabled, and not static.
16215
- * @returns {boolean} True if the option is active, false otherwise.
16213
+ * CONSTRUCTOR
16216
16214
  */
16217
- get isActive() {
16218
- return !this.hasAttribute('hidden') &&
16219
- !this.disabled &&
16220
- !this.hasAttribute('static');
16221
- }
16222
16215
 
16223
- constructor() {
16224
- super();
16216
+ /**
16217
+ * Creates a new MenuService instance.
16218
+ * @param {Object} options - The options object.
16219
+ * @param {AuroMenu} options.host - The host element that this service will control. Required.
16220
+ * @throws {Error} If the host is not provided.
16221
+ */
16222
+ constructor({ host } = {}) {
16225
16223
 
16226
- this.bindEvents();
16224
+ // Ensure a host was passed
16225
+ if (!host) {
16226
+ throw new Error("MenuService requires a host element.");
16227
+ }
16227
16228
 
16228
- /**
16229
- * @private
16230
- */
16231
- this.shape = undefined;
16229
+ // Attach the service to the host
16230
+ this.host = host;
16231
+ this.host.addController(this);
16232
16232
 
16233
- /**
16234
- * @private
16235
- */
16233
+ // Set default properties
16236
16234
  this.size = undefined;
16235
+ this.shape = undefined;
16236
+ this.noCheckmark = undefined;
16237
+ this.disabled = undefined;
16238
+ this.matchWord = undefined;
16239
+ this.multiSelect = undefined;
16240
+ this.allowDeselect = undefined;
16241
+ this.selectAllMatchingOptions = undefined;
16237
16242
 
16238
- /**
16239
- * Generate unique names for dependency components.
16240
- */
16241
- const versioning = new AuroDependencyVersioning$3();
16242
- this.iconTag = versioning.generateTag('auro-formkit-menuoption-icon', iconVersion, k);
16243
+ this.highlightedIndex = -1;
16243
16244
 
16244
- this.selected = false;
16245
- this.noCheckmark = false;
16246
- this.disabled = false;
16247
- this.noMatch = false;
16245
+ this._menuOptions = [];
16246
+ this._subscribers = [];
16247
+ this.internalUpdateInProgress = false;
16248
+ this.selectedOptions = [];
16249
+ this._pendingValue = null;
16250
+ this._pendingRetryScheduled = false;
16251
+ this._pendingRetryCount = 0;
16252
+ }
16248
16253
 
16249
- /**
16250
- * @private
16251
- */
16252
- this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
16254
+ /**
16255
+ * PROPERTY SYNCING
16256
+ */
16253
16257
 
16254
- // Initialize context-related properties
16255
- this.menuService = null;
16256
- this.unsubscribe = null;
16258
+ /**
16259
+ * Handles host updates.
16260
+ * This is a lit reactive lifecycle method.
16261
+ * This comes from the Lit controller interface provided by adding this service as a controller to the host.
16262
+ * See constructor for `this.host.addController(this)`
16263
+ * You can read more about Lit reactive controllers here: https://lit.dev/docs/composition/controllers/
16264
+ */
16265
+ hostUpdated() {
16257
16266
 
16258
- /**
16259
- * @private
16260
- */
16261
- this.handleMenuChange = this.handleMenuChange.bind(this);
16262
- }
16267
+ // Reset selection if multiSelect mode changes
16268
+ if (this.host.multiSelect !== this.multiSelect) {
16269
+ this.selectedOptions = [];
16270
+ }
16263
16271
 
16264
- static get properties() {
16265
- return {
16266
- ...super.properties,
16272
+ // Update properties on host update
16273
+ this.setProperties({
16274
+ size: this.host.size,
16275
+ shape: this.host.shape,
16276
+ noCheckmark: this.host.noCheckmark,
16277
+ disabled: this.host.disabled,
16278
+ matchWord: this.host.matchWord,
16279
+ multiSelect: this.host.multiSelect,
16280
+ allowDeselect: this.host.allowDeselect,
16281
+ selectAllMatchingOptions: this.host.selectAllMatchingOptions
16282
+ });
16283
+ }
16267
16284
 
16268
- /**
16269
- * When true, disables the menu option.
16270
- */
16271
- disabled: {
16272
- type: Boolean,
16273
- reflect: true
16274
- },
16285
+ /**
16286
+ * Handles host disconnection and memory cleanup.
16287
+ */
16288
+ hostDisconnected() {
16289
+ this._subscribers = [];
16290
+ this._menuOptions = [];
16291
+ this._pendingValue = null;
16292
+ this._pendingRetryScheduled = false;
16293
+ this._pendingRetryCount = 0;
16294
+ }
16275
16295
 
16276
- /**
16277
- * @private
16278
- */
16279
- event: {
16280
- type: String,
16281
- reflect: true
16282
- },
16283
-
16284
- /**
16285
- * @private
16286
- */
16287
- layout: {
16288
- type: String
16289
- },
16290
-
16291
- /**
16292
- * Allows users to set a unique key for the menu option for specified option selection. If no key is provided, the value property will be used.
16293
- */
16294
- key: {
16295
- type: String,
16296
- reflect: true
16297
- },
16298
-
16299
- /**
16300
- * @private
16301
- */
16302
- menuService: {
16303
- type: Object,
16304
- state: true
16305
- },
16306
-
16307
- /**
16308
- * @private
16309
- */
16310
- matchWord: {
16311
- type: String,
16312
- state: true
16313
- },
16314
-
16315
- /**
16316
- * @private
16317
- */
16318
- noCheckmark: {
16319
- type: Boolean,
16320
- reflect: true
16321
- },
16322
-
16323
- /**
16324
- * When true, marks this option as the "no matching results" placeholder shown by combobox when the user's input does not match any available options. Enables distinct styling and prevents the option from being treated as a selectable match.
16325
- */
16326
- noMatch: {
16327
- type: Boolean,
16328
- reflect: true,
16329
- attribute: 'nomatch'
16330
- },
16296
+ /**
16297
+ * Sets a property value if it exists on the instance and the value has changed.
16298
+ * @param {string} property
16299
+ * @param {any} value
16300
+ */
16301
+ setProperty(property, value) {
16331
16302
 
16332
- /**
16333
- * Specifies that an option is selected.
16334
- */
16335
- selected: {
16336
- type: Boolean,
16337
- reflect: true
16338
- },
16303
+ // Only update if we are tracking the property in this service
16304
+ if (this.hasOwnProperty(property)) {
16339
16305
 
16340
- /**
16341
- * Specifies the tab index of the menu option.
16342
- */
16343
- tabIndex: {
16344
- type: Number,
16345
- reflect: true
16346
- },
16306
+ // Check if the value has changed
16307
+ const valueChanged = this[property] !== value;
16347
16308
 
16348
- /**
16349
- * Specifies the value to be sent to a server.
16350
- */
16351
- value: {
16352
- type: String,
16353
- reflect: true
16354
- },
16355
- };
16309
+ // Update and notify if changed
16310
+ if (valueChanged) {
16311
+ this[property] = value;
16312
+ this.notify({ property, value });
16313
+ }
16314
+ }
16356
16315
  }
16357
16316
 
16358
- static get styles() {
16359
- return [
16360
- styleCss,
16361
- colorCss,
16362
- tokensCss
16363
- ];
16317
+ /**
16318
+ * Sets multiple properties on the instance.
16319
+ * @param {Object} properties - Key-value pairs of properties to set.
16320
+ */
16321
+ setProperties(properties) {
16322
+ for (const [key, value] of Object.entries(properties)) {
16323
+ this.setProperty(key, value);
16324
+ }
16364
16325
  }
16365
16326
 
16366
- connectedCallback() {
16367
- super.connectedCallback();
16368
-
16369
- // Add the tag name as an attribute if it is different than the component name
16370
- // Add this step soon as this node gets attached to the DOM to avoid racing condition with menu's value setting logic.
16371
- this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
16372
-
16373
- // Set up context consumption in connectedCallback
16374
- this._contextConsumer = new s$1(this, {
16375
- context: MenuContext,
16376
- callback: this.attachTo.bind(this),
16377
- subscribe: true
16378
- });
16327
+ /**
16328
+ * MENU OPTION HIGHLIGHTING
16329
+ */
16379
16330
 
16380
- // Establish the key property as early as possible.
16381
- // When a framework (e.g. Svelte) inserts the element into the DOM before
16382
- // setting its `value` property, both `getAttribute('value')` and
16383
- // `getAttribute('key')` return null here. Setting `this.key = null`
16384
- // would block the fallback in `updated()` that assigns key from the
16385
- // value property (the guard checked `=== undefined`). Only assign key
16386
- // if at least one source attribute is actually present so that the
16387
- // `updated()` fallback can run when the value property arrives later.
16388
- const valueAttr = this.getAttribute('value');
16389
- const keyAttr = this.getAttribute('key');
16390
- const resolvedKey = keyAttr !== null ? keyAttr : valueAttr;
16391
- if (resolvedKey !== null) {
16392
- this.key = resolvedKey;
16393
- }
16331
+ /**
16332
+ * Highlights the next active option in the menu.
16333
+ */
16334
+ highlightNext() {
16335
+ this.moveHighlightedOption("next");
16394
16336
  }
16395
16337
 
16396
- firstUpdated() {
16397
- // Add the tag name as an attribute if it is different than the component name
16398
- this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
16338
+ /**
16339
+ * Highlights the previous active option in the menu.
16340
+ */
16341
+ highlightPrevious() {
16342
+ this.moveHighlightedOption("previous");
16343
+ }
16399
16344
 
16400
- // Generate unique ID if not already set (required for aria-activedescendant)
16401
- if (!this.id) {
16402
- menuOptionIdCounter += 1;
16403
- this.id = `menuoption-${menuOptionIdCounter}`;
16404
- }
16345
+ /**
16346
+ * Moves the highlighted option in the specified direction.
16347
+ * @param {string} direction - The direction to move the highlight ("next" or "previous").
16348
+ */
16349
+ moveHighlightedOption(direction) {
16405
16350
 
16406
- this.setAttribute('role', 'option');
16407
- this.setAttribute('aria-selected', 'false');
16351
+ // Get the active options
16352
+ const activeOptions = this._menuOptions.filter(option => option.isActive);
16408
16353
 
16409
- this.addEventListener('mouseover', () => {
16410
- this.dispatchEvent(new CustomEvent('auroMenuOption-mouseover', {
16411
- bubbles: true,
16412
- cancelable: false,
16413
- composed: true,
16414
- detail: this
16415
- }));
16416
- });
16417
- }
16354
+ // Get the currently active option
16355
+ const currentActiveOption = activeOptions[activeOptions.indexOf(this.highlightedOption)];
16418
16356
 
16419
- updated(changedProperties) {
16420
- super.updated(changedProperties);
16357
+ // Determine the new index based on the currently active option and direction
16358
+ let newIndex = currentActiveOption
16359
+ ? direction === "previous"
16360
+ ? activeOptions.indexOf(currentActiveOption) - 1
16361
+ : activeOptions.indexOf(currentActiveOption) + 1
16362
+ : direction === "previous"
16363
+ ? activeOptions.length - 1
16364
+ : 0;
16421
16365
 
16422
- // Update aria-selected attribute if selected changed
16423
- if (changedProperties.has('selected')) {
16366
+ // Wrap around the index if needed
16367
+ newIndex = newIndex < 0 ? activeOptions.length - 1 : newIndex >= activeOptions.length ? 0 : newIndex;
16424
16368
 
16425
- // Update aria-selected attribute
16426
- this.setAttribute('aria-selected', this.selected.toString());
16369
+ // Get the new active option and set it as highlighted
16370
+ const newActiveOption = activeOptions[newIndex];
16371
+ this.setHighlightedOption(newActiveOption);
16372
+ }
16427
16373
 
16428
- // Update menu service selection state if this isn't an internal update
16429
- if (this.internalUpdateInProgress !== true && this.menuService) {
16430
- this.menuService[this.selected ? 'selectOption' : 'deselectOption'](this);
16431
- }
16432
- }
16374
+ /**
16375
+ * Sets the highlighted index to the specified option.
16376
+ * @param {AuroMenuOption} option - The option to highlight.
16377
+ */
16378
+ setHighlightedOption(option) {
16433
16379
 
16434
- if (changedProperties.has('disabled')) {
16435
- if (this.disabled) {
16436
- this.setAttribute('aria-disabled', 'true');
16437
- } else {
16438
- this.removeAttribute('aria-disabled');
16439
- }
16440
- }
16380
+ if (!option) return;
16441
16381
 
16442
- if (changedProperties.has('active')) {
16443
- this.updateActiveClasses();
16444
- }
16382
+ // Get the index of the option to highlight
16383
+ const index = this._menuOptions.indexOf(option);
16445
16384
 
16446
- // Update text highlight if matchWord changed
16447
- if (changedProperties.has('matchWord')) {
16448
- this.updateTextHighlight();
16449
- }
16385
+ // Update highlighted index
16386
+ this.highlightedIndex = index;
16450
16387
 
16451
- // Set the key to be the passed value if no key is provided.
16452
- // Loose equality (== null) is intentional: it catches both null AND
16453
- // undefined. When a framework (e.g. Svelte, React) inserts the element
16454
- // before setting its value property, connectedCallback skips key
16455
- // assignment because both attributes are null at that point. The Lit
16456
- // property default for `key` is undefined (not null), so strict
16457
- // === null would miss the case and the fallback would never run.
16458
- if (changedProperties.has('value') && this.key == null) { // eslint-disable-line eqeqeq, no-eq-null
16459
- this.key = this.value;
16460
- }
16461
- }
16388
+ // Notify subscribers of highlight change
16389
+ this.notify({ type: 'highlightChange', option, index: this.highlightedIndex });
16462
16390
 
16463
- disconnectedCallback() {
16464
- if (this.menuService) {
16465
- this.menuService.unsubscribe(this.handleMenuChange);
16466
- this.menuService.removeMenuOption(this);
16467
- }
16391
+ // Dispatch the change event
16392
+ this.dispatchChangeEvent('auroMenu-activatedOption', option);
16468
16393
  }
16469
16394
 
16470
16395
  /**
16471
- * Sets up event listeners for user interaction with the menu option.
16472
- * This function enables click and mouse enter events to trigger selection and highlighting logic.
16396
+ * Sets the highlighted option to the option at the specified index if it exists.
16397
+ * @param {number} index
16473
16398
  */
16474
- bindEvents() {
16475
- this.addEventListener('click', this.handleClick.bind(this));
16476
- this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
16399
+ setHighlightedIndex(index) {
16400
+ const option = this._menuOptions[index] || null;
16401
+ this.setHighlightedOption(option);
16477
16402
  }
16478
16403
 
16479
16404
  /**
16480
- * Attaches this menu option to a menu service and subscribes to its events.
16481
- * This method enables the option to participate in menu selection and highlighting logic.
16482
- * @param {Object} service - The menu service instance to attach to.
16405
+ * Selects the currently highlighted option.
16483
16406
  */
16484
- attachTo(service) {
16485
- if (!service) {
16486
- return;
16407
+ selectHighlightedOption() {
16408
+ if (this.highlightedOption) {
16409
+ this.toggleOption(this.highlightedOption);
16487
16410
  }
16488
- this.menuService = service;
16489
- this.menuService.addMenuOption(this);
16490
- this.menuService.subscribe(this.handleMenuChange);
16491
16411
  }
16492
16412
 
16493
16413
  /**
16494
- * Handles changes from the menu service and updates the option's state.
16495
- * This function synchronizes the option's properties and selection/highlight state with menu events.
16496
- * @param {Object} event - The event object from the menu service.
16414
+ * SELECTION AND DESELECTION METHODS
16497
16415
  */
16498
- handleMenuChange(event) {
16499
16416
 
16500
- // Ignore events without a type or property
16501
- if (!event || (!event.type && !event.property)) {
16502
- return;
16503
- }
16417
+ /**
16418
+ * Selects one or more options in a batch operation
16419
+ * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to select
16420
+ */
16421
+ selectOptions(options) {
16422
+ let optionsToSelect = Array.isArray(options) ? options : [options];
16504
16423
 
16505
- // Update reactive properties based on event type
16506
- if (event.property && Object.keys(AuroMenuOption.properties).includes(event.property)) {
16507
- this[event.property] = event.value;
16508
- }
16424
+ // Filter out options that are inactive
16425
+ optionsToSelect = optionsToSelect.filter(option => option.isActive);
16509
16426
 
16510
- // Handle highlight changes
16511
- if (event.type === 'highlightChange') {
16512
- const isActive = event.option === this;
16513
- this.active = isActive;
16514
- this.updateActiveClasses();
16515
- }
16427
+ if (!optionsToSelect.length) return;
16516
16428
 
16517
- if (event.type === 'stateChange') {
16518
- const isSelected = event.selectedOptions.includes(this);
16519
- this.setInternalSelected(isSelected);
16429
+ if (this.multiSelect) {
16430
+ this.selectedOptions = [...(this.selectedOptions || []), ...optionsToSelect];
16431
+ } else {
16432
+ // In single select mode, only take the last option
16433
+ this.selectedOptions = [optionsToSelect[optionsToSelect.length - 1]];
16520
16434
  }
16435
+
16436
+ this.stageUpdate();
16521
16437
  }
16522
16438
 
16523
16439
  /**
16524
- * Updates the internal selected state of the menu option bypassing 'updated' and triggers custom events if selected.
16525
- * This function ensures the option's selection state is synchronized with menu logic and notifies listeners.
16526
- * @param {boolean} isSelected - Whether the option should be marked as selected.
16440
+ * Deselects one or more options in a batch operation
16441
+ * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to deselect
16527
16442
  */
16528
- setInternalSelected(isSelected) {
16529
- this.internalUpdateInProgress = true;
16530
- this.selected = isSelected;
16443
+ deselectOptions(options) {
16444
+ const optionsToDeselect = Array.isArray(options) ? options : [options];
16531
16445
 
16532
- // Fire custom event if selected
16533
- if (isSelected) {
16534
- this.handleCustomEvent();
16446
+ if (!optionsToDeselect.length) return;
16447
+
16448
+ // Check if deselection should be prevented
16449
+ const shouldPreventDeselect = !this.allowDeselect && !this.multiSelect;
16450
+ const isOnlySelectedOption = this.selectedOptions.length === 1 && optionsToDeselect.includes(this.selectedOptions[0]);
16451
+
16452
+ // Prevent deselecting the only selected option if not allowed
16453
+ if (shouldPreventDeselect && isOnlySelectedOption) {
16454
+ optionsToDeselect.forEach(option => {
16455
+ option.selected = true;
16456
+ });
16457
+ this.dispatchChangeEvent('auroMenu-deselectPrevented', {
16458
+ values: optionsToDeselect
16459
+ });
16460
+ return;
16535
16461
  }
16536
16462
 
16537
- setTimeout(() => {
16538
- this.internalUpdateInProgress = false;
16539
- }, 0);
16463
+ const optionsSet = new Set(optionsToDeselect);
16464
+ const previousCount = this.selectedOptions.length;
16465
+ this.selectedOptions = (this.selectedOptions || [])
16466
+ .filter(opt => !optionsSet.has(opt));
16467
+
16468
+ if (this.selectedOptions.length < previousCount) {
16469
+ this.stageUpdate();
16470
+ }
16540
16471
  }
16541
16472
 
16542
16473
  /**
16543
- * Sets the selected state of the menu option.
16544
- * This function updates whether the option is currently selected.
16545
- * @param {boolean} isSelected - Whether the option should be marked as selected.
16546
- * @deprecated Simply modify the `selected` property directly instead.
16474
+ * Selects a single option.
16475
+ * @param {AuroMenuOption} option
16547
16476
  */
16548
- setSelected(isSelected) {
16549
- this.selected = isSelected;
16477
+ selectOption(option) {
16478
+ this.selectOptions(option);
16550
16479
  }
16551
16480
 
16552
16481
  /**
16553
- * Updates the active state and visual highlighting of the menu option.
16554
- * This function toggles the option's active status and applies or removes the active CSS class.
16555
- * @param {boolean} isActive - Whether the option should be marked as active.
16556
- * @deprecated Simply modify the `active` property directly instead.
16482
+ * Deselects a single option.
16483
+ * @param {AuroMenuOption} option
16557
16484
  */
16558
- updateActive(isActive) {
16559
-
16560
- // Set active state
16561
- this.active = isActive;
16562
- this.updateActiveClasses();
16485
+ deselectOption(option) {
16486
+ this.deselectOptions(option);
16563
16487
  }
16564
16488
 
16565
16489
  /**
16566
- * Updates the CSS class for the menu option based on its active state.
16567
- * This function adds or removes the 'active' class to visually indicate the option's active status.
16568
- * @private
16490
+ * Toggles the selection state of a single option.
16491
+ * @param {AuroMenuOption} option
16569
16492
  */
16570
- updateActiveClasses() {
16571
- // Update class based on active state
16572
- if (this.active) this.classList.add('active');
16573
- else this.classList.remove('active');
16493
+ toggleOption(option) {
16494
+ if (option.selected) {
16495
+ this.deselectOption(option);
16496
+ } else {
16497
+ this.selectOption(option);
16498
+ }
16574
16499
  }
16575
16500
 
16576
-
16577
16501
  /**
16578
- * Updates the visual highlighting of text within the menu option based on the current match word.
16579
- * This function highlights matching text segments and manages nested spacers for display formatting.
16580
- * @private
16502
+ * Selects options based on their value(s) when compared to a passed value or values.
16503
+ * Value or values are normalized to an array of strings that can be matched to option keys.
16504
+ * @param {string|number|Array<string|number>} value - The value(s) to select.
16581
16505
  */
16582
- updateTextHighlight() {
16506
+ selectByValue(value) {
16507
+ const isEmptyValue = value === undefined ||
16508
+ value === null ||
16509
+ (Array.isArray(value) && value.length === 0) ||
16510
+ (typeof value === 'string' && value.trim() === '');
16583
16511
 
16584
- // Regex for matchWord if needed
16585
- let regexWord = null;
16512
+ // Early exit for invalid/empty values
16513
+ if (isEmptyValue) {
16514
+ this.selectedOptions.forEach(opt => opt.selected = false);
16515
+ this.selectedOptions = [];
16516
+ return;
16517
+ }
16586
16518
 
16587
- if (this.matchWord && this.matchWord.length) {
16588
- const escapedWord = this.matchWord.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
16589
- regexWord = new RegExp(escapedWord, 'giu');
16519
+ // If an internal update cycle is still in progress, defer value application
16520
+ // rather than dropping it.
16521
+ if (this.internalUpdateInProgress || this.host.internalUpdateInProgress) {
16522
+ this.queuePendingValue(value);
16523
+ return;
16590
16524
  }
16591
16525
 
16592
- // Update text highlighting if matchWord changed
16593
- if (regexWord &&
16594
- this.isActive && !this.hasAttribute('persistent')) {
16595
- const nested = this.querySelectorAll('.nestingSpacer');
16526
+ // Normalize values to array of strings
16527
+ const normalizedValues = this._getNormalizedValues(value);
16596
16528
 
16597
- const displayValueEl = this.querySelector('[slot="displayValue"]');
16598
- if (displayValueEl) {
16599
- this.removeChild(displayValueEl);
16529
+ // Validate for single-select mode
16530
+ let validatedValues = normalizedValues;
16531
+ if (normalizedValues.length > 1 && !this.multiSelect) {
16532
+ console.warn("MenuService - Multiple values provided for single-select menu. Only the first value will be selected.");
16533
+ validatedValues = [normalizedValues[0]];
16534
+ }
16535
+
16536
+ if (this._menuOptions.length === 0) {
16537
+ this.queuePendingValue(value);
16538
+ return;
16539
+ }
16540
+
16541
+ // Find matching options by comparing available options to validated values
16542
+ const trackedKeys = new Set();
16543
+ const optionsToSelect = this._menuOptions.filter(option => {
16544
+ const passesFilter = validatedValues.includes(option.key);
16545
+ const alreadyTracked = trackedKeys.has(option.key);
16546
+ const isActive = option.isActive;
16547
+
16548
+ trackedKeys.add(option.key);
16549
+
16550
+ // Include the option in the options to be selected if it passes the filter check and
16551
+ // either hasn't been tracked yet or selectAllMatchingOptions is true
16552
+ return isActive && passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
16553
+ });
16554
+
16555
+ // Handle no matches: clear existing selection, but do not dispatch an intermediate
16556
+ // undefined value that can overwrite the host value in parent components.
16557
+ if (!optionsToSelect.length) {
16558
+ const hasUnresolvedKeys = this._menuOptions.some((option) => option.isActive && option.key == null);
16559
+
16560
+ if (hasUnresolvedKeys) {
16561
+ this.queuePendingValue(value);
16562
+ return;
16600
16563
  }
16601
16564
 
16602
- // Create nested spacers
16603
- const nestingSpacerBundle = [...nested].map(() => this.nestingSpacer).join('');
16565
+ this.clearPendingValue();
16604
16566
 
16605
- // Update with spacers and matchWord
16606
- this.innerHTML = nestingSpacerBundle +
16607
- this.textContent.replace(
16608
- regexWord,
16609
- (match) => `<strong>${match}</strong>`
16610
- );
16611
- if (displayValueEl) {
16612
- this.append(displayValueEl);
16567
+ if (this.selectedOptions.length > 0) {
16568
+ this.selectedOptions = [];
16569
+ }
16570
+
16571
+ // Always notify so the host resets any stale invalid value, even when
16572
+ // selectedOptions was already empty (e.g. double-clicking set-invalid).
16573
+ this.stageUpdate({ reason: 'no-match' });
16574
+
16575
+ // Dispatch failure event if no matches found
16576
+ if (validatedValues.length) {
16577
+ this.dispatchChangeEvent('auroMenu-selectValueFailure', {
16578
+ message: 'No matching options found for the provided value(s).',
16579
+ values: validatedValues
16580
+ });
16613
16581
  }
16582
+
16583
+ return;
16614
16584
  }
16615
- }
16616
16585
 
16617
- /**
16618
- * Handles click events on the menu option, toggling its selected state.
16619
- * This function dispatches a click event and updates selection if the option is not disabled.
16620
- * @private
16621
- */
16622
- handleClick() {
16623
- if (!this.disabled) {
16624
- this.dispatchClickEvent();
16625
- this.selected = !this.selected;
16586
+ this.clearPendingValue();
16587
+
16588
+ if (this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
16589
+ return;
16626
16590
  }
16591
+
16592
+ // Apply programmatic selection as a single transaction and emit one final state.
16593
+ this.selectedOptions = optionsToSelect;
16594
+ this.stageUpdate();
16627
16595
  }
16628
16596
 
16629
16597
  /**
16630
- * Handles mouse enter events to highlight the menu option.
16631
- * This function updates the menu service to set this option as the currently highlighted item if not disabled.
16632
- * @private
16598
+ * Queues a pending value and schedules a bounded retry.
16599
+ * @param {string|number|Array<string|number>} value - The value to retry.
16633
16600
  */
16634
- handleMouseEnter() {
16635
- if (!this.disabled) {
16636
- this.menuService.setHighlightedOption(this);
16601
+ queuePendingValue(value) {
16602
+ this._pendingValue = value;
16603
+
16604
+ if (this._pendingRetryScheduled || this._pendingRetryCount >= 5) {
16605
+ return;
16637
16606
  }
16638
- }
16639
16607
 
16640
- /**
16641
- * Dispatches custom events defined for this menu option.
16642
- * This function notifies listeners when a custom event is triggered by the option.
16643
- * @private
16644
- */
16645
- handleCustomEvent() {
16646
- if (this.event) {
16647
- dispatchMenuEvent(this, this.event, { option: this });
16648
- dispatchMenuEvent(this, 'auroMenu-customEventFired', { option: this });
16649
- }
16608
+ this._pendingRetryScheduled = true;
16609
+ this._pendingRetryCount += 1;
16610
+
16611
+ setTimeout(() => {
16612
+ this._pendingRetryScheduled = false;
16613
+
16614
+ if (this._pendingValue == null) {
16615
+ return;
16616
+ }
16617
+
16618
+ const pendingValue = this._pendingValue;
16619
+ this.selectByValue(pendingValue);
16620
+ }, 0);
16650
16621
  }
16651
16622
 
16652
16623
  /**
16653
- * Dispatches a click event for this menu option.
16654
- * This function notifies listeners that the option has been clicked.
16655
- * @private
16624
+ * Clears pending retry state.
16656
16625
  */
16657
- dispatchClickEvent() {
16658
- this.dispatchEvent(new CustomEvent('auroMenuOption-click', {
16659
- bubbles: true,
16660
- cancelable: false,
16661
- composed: true,
16662
- detail: this
16663
- }));
16626
+ clearPendingValue() {
16627
+ this._pendingValue = null;
16628
+ this._pendingRetryScheduled = false;
16629
+ this._pendingRetryCount = 0;
16664
16630
  }
16665
16631
 
16666
16632
  /**
16667
- * Generates an HTML element containing an SVG icon based on the provided `svgContent`.
16668
- *
16669
- * @private
16670
- * @param {string} svgContent - The SVG content to be embedded.
16671
- * @returns {Element} The HTML element containing the SVG icon.
16633
+ * Resets the selected options to an empty array.
16672
16634
  */
16673
- generateIconHtml(svgContent) {
16674
- const dom = new DOMParser().parseFromString(svgContent, 'text/html');
16675
- const svg = dom.body.firstChild;
16676
-
16677
- svg.setAttribute('slot', 'svg');
16635
+ reset() {
16636
+ const previousOptions = [...this.selectedOptions];
16637
+ previousOptions.forEach(opt => opt.selected = false);
16638
+ this.selectedOptions = [];
16678
16639
 
16679
- return u$7`<${this.iconTag} customColor customSvg>${svg}</${this.iconTag}>`;
16640
+ // Single update after clearing all
16641
+ if (previousOptions.length) {
16642
+ this.stageUpdate();
16643
+ }
16680
16644
  }
16681
16645
 
16682
16646
  /**
16683
- * Logic to determine the layout of the component.
16684
- * @protected
16685
- * @returns {void}
16647
+ * SUBSCRIPTION, NOTIFICATION AND EVENT DISPATCH METHODS
16686
16648
  */
16687
- renderLayout() {
16688
-
16689
- const fontClassMap = {
16690
- xs: 'body-sm',
16691
- sm: 'body-default',
16692
- md: 'body-default',
16693
- lg: 'body-lg',
16694
- xl: 'body-lg'
16695
- };
16696
-
16697
- const classes = e$3({
16698
- 'wrapper': true,
16699
- [this.size ? fontClassMap[this.size] : 'body-sm']: true,
16700
- });
16701
-
16702
- return u$7`
16703
- <div class="${classes}">
16704
- ${this.selected && !this.noCheckmark
16705
- ? this.generateIconHtml(checkmarkIcon.svg)
16706
- : undefined}
16707
- <slot></slot>
16708
- </div>
16709
- `;
16710
- }
16711
- }
16712
-
16713
- /* eslint-disable */
16714
-
16715
- class MenuService {
16716
16649
 
16717
16650
  /**
16718
- * PROPERTIES AND GETTERS
16651
+ * Subscribes a callback to menu service events.
16652
+ * @param {Function} callback - The callback to invoke on events.
16719
16653
  */
16654
+ subscribe(callback) {
16655
+ this._subscribers.push(callback);
16656
+ }
16720
16657
 
16721
16658
  /**
16722
- * Gets the list of registered menu options.
16723
- * @returns {AuroMenuOption[]}
16659
+ * Remove a previously subscribed callback from menu service events.
16660
+ * @param {Function} callback
16724
16661
  */
16725
- get menuOptions() {
16726
- return this._menuOptions;
16662
+ unsubscribe(callback) {
16663
+ this._subscribers = this._subscribers.filter(cb => cb !== callback);
16727
16664
  }
16728
16665
 
16729
16666
  /**
16730
- * Gets the currently highlighted option.
16731
- * @returns {AuroMenuOption|null}
16667
+ * Stages an update to notify subscribers of state and value changes.
16732
16668
  */
16733
- get highlightedOption() {
16734
- return this._menuOptions[this.highlightedIndex] || null;
16669
+ stageUpdate(meta = {}) {
16670
+ this.notifyStateChange(meta);
16671
+ this.notifyValueChange(meta);
16735
16672
  }
16736
16673
 
16737
16674
  /**
16738
- * Gets the current value(s) of the selected option(s).
16739
- * @returns {string|string[]|undefined}
16675
+ * Notifies subscribers of a menu service event.
16676
+ * All notifications are sent to all subscribers.
16677
+ * @param {string} event - The event to send to subscribers.
16740
16678
  */
16741
- get currentValue() {
16742
- const values = (this.selectedOptions || []).map(option => option.value);
16743
- return this.multiSelect ? values : values[0];
16679
+ notify(event) {
16680
+ this._subscribers.forEach(callback => callback(event));
16744
16681
  }
16745
16682
 
16746
16683
  /**
16747
- * Gets the label(s) of the currently selected option(s).
16748
- * @returns {string}
16684
+ * Notifies subscribers of a state change (selected options has changed).
16749
16685
  */
16750
- get currentLabel() {
16751
- const labels = (this.selectedOptions || []).map(option => option.textContent);
16752
- return this.multiSelect ? labels.join(", ") : labels[0] || '';
16686
+ notifyStateChange(meta = {}) {
16687
+ this.notify({
16688
+ type: 'stateChange',
16689
+ selectedOptions: this.selectedOptions,
16690
+ ...meta
16691
+ });
16753
16692
  }
16754
16693
 
16755
16694
  /**
16756
- * Gets the string representation of the current value(s).
16757
- * For multi-select, this is a JSON stringified array.
16758
- * @returns {string|undefined}
16695
+ * Notifies subscribers of a value change (current value has changed).
16759
16696
  */
16760
- get stringValue() {
16761
- const { currentValue } = this;
16697
+ notifyValueChange(meta = {}) {
16762
16698
 
16763
- if (Array.isArray(currentValue)) {
16764
- if (currentValue.length > 0) {
16765
- return JSON.stringify(currentValue);
16766
- }
16767
- return undefined;
16768
- }
16699
+ // Prepare details for the event
16700
+ const details = {
16701
+ value: this.currentValue,
16702
+ stringValue: this.stringValue,
16703
+ keys: this.currentKeys,
16704
+ options: this.selectedOptions,
16705
+ label: this.currentLabel
16706
+ };
16769
16707
 
16770
- if (typeof currentValue === 'string') {
16771
- if (currentValue.length > 0) {
16772
- return currentValue;
16773
- }
16774
- return undefined;
16775
- }
16708
+ // If only one option is selected, include its index
16709
+ if (this.selectedOptions.length === 1) details.index = this._menuOptions.indexOf(this.selectedOptions[0]);
16776
16710
 
16777
- // Future: handle other types here (e.g., number, object, etc.)
16778
- return undefined;
16711
+ this.notify({
16712
+ type: 'valueChange',
16713
+ ...meta,
16714
+ ...details
16715
+ });
16779
16716
  }
16780
16717
 
16781
16718
  /**
16782
- * Gets the key(s) of the currently selected option(s).
16783
- * @returns {string|string[]|undefined}
16719
+ * Dispatches a custom event from the host element.
16720
+ * @param {string} eventName
16721
+ * @param {any} detail
16784
16722
  */
16785
- get currentKeys() {
16786
- const keys = (this.selectedOptions || []).map(option => option.key);
16787
- return this.multiSelect ? keys : keys[0];
16723
+ dispatchChangeEvent(eventName, detail) {
16724
+ this.host.dispatchEvent(new CustomEvent(eventName, {
16725
+ bubbles: true,
16726
+ cancelable: false,
16727
+ composed: true,
16728
+ detail
16729
+ }));
16788
16730
  }
16789
16731
 
16790
16732
  /**
16791
- * CONSTRUCTOR
16733
+ * MENU OPTION MANAGEMENT METHODS
16792
16734
  */
16793
16735
 
16794
16736
  /**
16795
- * Creates a new MenuService instance.
16796
- * @param {Object} options - The options object.
16797
- * @param {AuroMenu} options.host - The host element that this service will control. Required.
16798
- * @throws {Error} If the host is not provided.
16737
+ * Adds a menu option to the service's list.
16738
+ * @param {AuroMenuOption} option - the option to track
16799
16739
  */
16800
- constructor({ host } = {}) {
16740
+ addMenuOption(option) {
16741
+ this._menuOptions.push(option);
16742
+ this.notify({ type: 'optionsChange', options: this._menuOptions });
16801
16743
 
16802
- // Ensure a host was passed
16803
- if (!host) {
16804
- throw new Error("MenuService requires a host element.");
16744
+ if (this._pendingValue != null) {
16745
+ this.queuePendingValue(this._pendingValue);
16805
16746
  }
16747
+ }
16806
16748
 
16807
- // Attach the service to the host
16808
- this.host = host;
16809
- this.host.addController(this);
16810
-
16811
- // Set default properties
16812
- this.size = undefined;
16813
- this.shape = undefined;
16814
- this.noCheckmark = undefined;
16815
- this.disabled = undefined;
16816
- this.matchWord = undefined;
16817
- this.multiSelect = undefined;
16818
- this.allowDeselect = undefined;
16819
- this.selectAllMatchingOptions = undefined;
16820
-
16821
- this.highlightedIndex = -1;
16749
+ /**
16750
+ * Removes a menu option from the service's list.
16751
+ * @param {AuroMenuOption} option - the option to remove
16752
+ */
16753
+ removeMenuOption(option) {
16754
+ this._menuOptions = this._menuOptions.filter(opt => opt !== option);
16755
+ this.notify({ type: 'optionsChange', options: this._menuOptions });
16822
16756
 
16823
- this._menuOptions = [];
16824
- this._subscribers = [];
16825
- this.internalUpdateInProgress = false;
16826
- this.selectedOptions = [];
16827
- this._pendingValue = null;
16828
- this._pendingRetryScheduled = false;
16829
- this._pendingRetryCount = 0;
16757
+ if (this._menuOptions.length === 0) {
16758
+ this.clearPendingValue();
16759
+ }
16830
16760
  }
16831
16761
 
16832
16762
  /**
16833
- * PROPERTY SYNCING
16763
+ * UTILITIES
16834
16764
  */
16835
16765
 
16836
16766
  /**
16837
- * Handles host updates.
16838
- * This is a lit reactive lifecycle method.
16839
- * This comes from the Lit controller interface provided by adding this service as a controller to the host.
16840
- * See constructor for `this.host.addController(this)`
16841
- * You can read more about Lit reactive controllers here: https://lit.dev/docs/composition/controllers/
16767
+ * Normalizes a value or array of values into an array of strings for option selection.
16768
+ * This function ensures that input values are consistently formatted for matching menu options.
16769
+ *
16770
+ * @param {string|number|Array<string|number>} value - The value(s) to normalize.
16771
+ * @returns {Array<string>} An array of string values suitable for option matching.
16772
+ * @throws {Error} If any value is not a string or number.
16842
16773
  */
16843
- hostUpdated() {
16774
+ _getNormalizedValues(value) {
16775
+ let values = value;
16844
16776
 
16845
- // Reset selection if multiSelect mode changes
16846
- if (this.host.multiSelect !== this.multiSelect) {
16847
- this.selectedOptions = [];
16848
- }
16777
+ // Handle JSON string and single value string input
16778
+ if (!Array.isArray(values) && typeof values === 'string') {
16849
16779
 
16850
- // Update properties on host update
16851
- this.setProperties({
16852
- size: this.host.size,
16853
- shape: this.host.shape,
16854
- noCheckmark: this.host.noCheckmark,
16855
- disabled: this.host.disabled,
16856
- matchWord: this.host.matchWord,
16857
- multiSelect: this.host.multiSelect,
16858
- allowDeselect: this.host.allowDeselect,
16859
- selectAllMatchingOptions: this.host.selectAllMatchingOptions
16860
- });
16861
- }
16780
+ // Attempt to parse as JSON array
16781
+ try {
16862
16782
 
16863
- /**
16864
- * Handles host disconnection and memory cleanup.
16865
- */
16866
- hostDisconnected() {
16867
- this._subscribers = [];
16868
- this._menuOptions = [];
16869
- this._pendingValue = null;
16870
- this._pendingRetryScheduled = false;
16871
- this._pendingRetryCount = 0;
16872
- }
16783
+ // Normalize single quotes to double quotes for JSON parsing
16784
+ // This will not handle complex cases but will cover basic usage
16785
+ const parseValue = values.replace(/'([^']*?)'/g, '"$1"');
16873
16786
 
16874
- /**
16875
- * Sets a property value if it exists on the instance and the value has changed.
16876
- * @param {string} property
16877
- * @param {any} value
16878
- */
16879
- setProperty(property, value) {
16787
+ // Attempt parse
16788
+ const parsed = JSON.parse(parseValue);
16880
16789
 
16881
- // Only update if we are tracking the property in this service
16882
- if (this.hasOwnProperty(property)) {
16790
+ // Ensure parsed value is an array
16791
+ if (!Array.isArray(parsed)) throw new Error('Not an array');
16883
16792
 
16884
- // Check if the value has changed
16885
- const valueChanged = this[property] !== value;
16793
+ // Set values to parsed array
16794
+ values = parsed;
16795
+ } catch (err) {
16886
16796
 
16887
- // Update and notify if changed
16888
- if (valueChanged) {
16889
- this[property] = value;
16890
- this.notify({ property, value });
16797
+ // If parsing fails, treat as single value
16798
+ values = [value];
16891
16799
  }
16892
16800
  }
16893
- }
16894
16801
 
16895
- /**
16896
- * Sets multiple properties on the instance.
16897
- * @param {Object} properties - Key-value pairs of properties to set.
16898
- */
16899
- setProperties(properties) {
16900
- for (const [key, value] of Object.entries(properties)) {
16901
- this.setProperty(key, value);
16802
+ // Handle a single number being passed
16803
+ if (typeof values === 'number') {
16804
+ values = [String(values)];
16902
16805
  }
16903
- }
16904
16806
 
16905
- /**
16906
- * MENU OPTION HIGHLIGHTING
16907
- */
16807
+ // Coerce each value to string and validate types
16808
+ values.forEach((val, index) => {
16908
16809
 
16909
- /**
16910
- * Highlights the next active option in the menu.
16911
- */
16912
- highlightNext() {
16913
- this.moveHighlightedOption("next");
16914
- }
16810
+ // Throw an error for invalid value types
16811
+ if (typeof val !== 'string' && typeof val !== 'number') {
16812
+ throw new Error('Value contains invalid value type. Supported types are string and number.');
16813
+ }
16915
16814
 
16916
- /**
16917
- * Highlights the previous active option in the menu.
16918
- */
16919
- highlightPrevious() {
16920
- this.moveHighlightedOption("previous");
16815
+ // Convert numbers to strings for consistency
16816
+ if (typeof val === 'number') {
16817
+ values[index] = String(val);
16818
+ }
16819
+ });
16820
+
16821
+ // Return the resulting array of string values
16822
+ return values;
16921
16823
  }
16922
16824
 
16923
16825
  /**
16924
- * Moves the highlighted option in the specified direction.
16925
- * @param {string} direction - The direction to move the highlight ("next" or "previous").
16826
+ * Returns whether two arrays of options contain the same elements.
16827
+ * @param {AuroMenuOption[]} arr1 - First array of options.
16828
+ * @param {AuroMenuOption[]} arr2 - Second array of options.
16829
+ * @returns {boolean} True if arrays match, false otherwise.
16926
16830
  */
16927
- moveHighlightedOption(direction) {
16928
-
16929
- // Get the active options
16930
- const activeOptions = this._menuOptions.filter(option => option.isActive);
16931
-
16932
- // Get the currently active option
16933
- const currentActiveOption = activeOptions[activeOptions.indexOf(this.highlightedOption)];
16831
+ optionsArraysMatch(arr1, arr2) {
16832
+ if (arr1.length !== arr2.length) return false;
16934
16833
 
16935
- // Determine the new index based on the currently active option and direction
16936
- let newIndex = currentActiveOption
16937
- ? direction === "previous"
16938
- ? activeOptions.indexOf(currentActiveOption) - 1
16939
- : activeOptions.indexOf(currentActiveOption) + 1
16940
- : direction === "previous"
16941
- ? activeOptions.length - 1
16942
- : 0;
16834
+ const set1 = new Set(arr1);
16835
+ const set2 = new Set(arr2);
16943
16836
 
16944
- // Wrap around the index if needed
16945
- newIndex = newIndex < 0 ? activeOptions.length - 1 : newIndex >= activeOptions.length ? 0 : newIndex;
16837
+ for (let item of set1) {
16838
+ if (!set2.has(item)) {
16839
+ return false;
16840
+ }
16841
+ }
16946
16842
 
16947
- // Get the new active option and set it as highlighted
16948
- const newActiveOption = activeOptions[newIndex];
16949
- this.setHighlightedOption(newActiveOption);
16843
+ return true;
16950
16844
  }
16845
+ }
16951
16846
 
16952
- /**
16953
- * Sets the highlighted index to the specified option.
16954
- * @param {AuroMenuOption} option - The option to highlight.
16955
- */
16956
- setHighlightedOption(option) {
16957
-
16958
- if (!option) return;
16959
-
16960
- // Get the index of the option to highlight
16961
- const index = this._menuOptions.indexOf(option);
16962
-
16963
- // Update highlighted index
16964
- this.highlightedIndex = index;
16847
+ const MenuContext = n('menu-context');
16965
16848
 
16966
- // Notify subscribers of highlight change
16967
- this.notify({ type: 'highlightChange', option, index: this.highlightedIndex });
16849
+ // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
16850
+ // See LICENSE in the project root for license information.
16968
16851
 
16969
- // Dispatch the change event
16970
- this.dispatchChangeEvent('auroMenu-activatedOption', option);
16971
- }
16972
16852
 
16973
- /**
16974
- * Sets the highlighted option to the option at the specified index if it exists.
16975
- * @param {number} index
16976
- */
16977
- setHighlightedIndex(index) {
16978
- const option = this._menuOptions[index] || null;
16979
- this.setHighlightedOption(option);
16980
- }
16853
+ /**
16854
+ * Helper method to dispatch custom events.
16855
+ * @param {HTMLElement} element - Element to dispatch event from.
16856
+ * @param {string} eventName - Name of the event to dispatch.
16857
+ * @param {Object} [detail] - Optional detail object to include with the event.
16858
+ */
16859
+ function dispatchMenuEvent(element, eventName, detail = null) {
16860
+ const eventConfig = {
16861
+ bubbles: true,
16862
+ cancelable: false,
16863
+ composed: true
16864
+ };
16981
16865
 
16982
- /**
16983
- * Selects the currently highlighted option.
16984
- */
16985
- selectHighlightedOption() {
16986
- if (this.highlightedOption) {
16987
- this.toggleOption(this.highlightedOption);
16988
- }
16866
+ if (detail !== null) {
16867
+ eventConfig.detail = detail;
16989
16868
  }
16990
16869
 
16991
- /**
16992
- * SELECTION AND DESELECTION METHODS
16993
- */
16870
+ element.dispatchEvent(new CustomEvent(eventName, eventConfig));
16871
+ }
16994
16872
 
16995
- /**
16996
- * Selects one or more options in a batch operation
16997
- * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to select
16998
- */
16999
- selectOptions(options) {
17000
- let optionsToSelect = Array.isArray(options) ? options : [options];
16873
+ /* eslint-disable no-underscore-dangle */
16874
+ // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
16875
+ // See LICENSE in the project root for license information.
17001
16876
 
17002
- // Filter out options that are inactive
17003
- optionsToSelect = optionsToSelect.filter(option => option.isActive);
17004
16877
 
17005
- if (!optionsToSelect.length) return;
17006
16878
 
17007
- if (this.multiSelect) {
17008
- this.selectedOptions = [...(this.selectedOptions || []), ...optionsToSelect];
17009
- } else {
17010
- // In single select mode, only take the last option
17011
- this.selectedOptions = [optionsToSelect[optionsToSelect.length - 1]];
17012
- }
16879
+ /**
16880
+ * The `auro-menu` element provides users a way to select from a list of options.
16881
+ * @customElement auro-menu
16882
+ *
16883
+ * @event {CustomEvent<Element>} auroMenu-activatedOption - Notifies that a menuoption has been made `active`.
16884
+ * @event {CustomEvent<any>} auroMenu-customEventFired - Notifies that a custom event has been fired.
16885
+ * @event {CustomEvent<{ loading: boolean; hasLoadingPlaceholder: boolean; }>} auroMenu-loadingChange - Notifies when the loading attribute is changed.
16886
+ * @event {CustomEvent<any>} auroMenu-selectValueFailure - Notifies that an attempt to select a menuoption by matching a value has failed.
16887
+ * @event {CustomEvent<{ values: HTMLElement[] }>} auroMenu-deselectPrevented - Notifies that deselection was prevented and includes the affected options in `detail.values`.
16888
+ * @event {CustomEvent<any>} auroMenu-selectValueReset - Notifies that the component value has been reset.
16889
+ * @event {CustomEvent<any>} auroMenu-selectedOption - Notifies that a new menuoption selection has been made.
16890
+ * @slot loadingText - Text to show while loading attribute is set
16891
+ * @slot loadingIcon - Icon to show while loading attribute is set
16892
+ * @slot - Slot for insertion of menu options.
16893
+ */
17013
16894
 
17014
- this.stageUpdate();
17015
- }
16895
+ /* eslint-disable max-lines */
17016
16896
 
17017
- /**
17018
- * Deselects one or more options in a batch operation
17019
- * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to deselect
17020
- */
17021
- deselectOptions(options) {
17022
- const optionsToDeselect = Array.isArray(options) ? options : [options];
16897
+ class AuroMenu extends AuroElement {
17023
16898
 
17024
- if (!optionsToDeselect.length) return;
16899
+ constructor() {
16900
+ super();
17025
16901
 
17026
- // Check if deselection should be prevented
17027
- const shouldPreventDeselect = !this.allowDeselect && !this.multiSelect;
17028
- const isOnlySelectedOption = this.selectedOptions.length === 1 && optionsToDeselect.includes(this.selectedOptions[0]);
16902
+ // State properties (reactive)
17029
16903
 
17030
- // Prevent deselecting the only selected option if not allowed
17031
- if (shouldPreventDeselect && isOnlySelectedOption) {
17032
- optionsToDeselect.forEach(option => {
17033
- option.selected = true;
17034
- });
17035
- this.dispatchChangeEvent('auroMenu-deselectPrevented', {
17036
- values: optionsToDeselect
17037
- });
17038
- return;
17039
- }
16904
+ /**
16905
+ * @private
16906
+ */
16907
+ this.shape = "box";
17040
16908
 
17041
- const optionsSet = new Set(optionsToDeselect);
17042
- this.selectedOptions = (this.selectedOptions || [])
17043
- .filter(opt => !optionsSet.has(opt));
16909
+ /**
16910
+ * @private
16911
+ */
16912
+ this.size = "sm";
17044
16913
 
17045
- this.stageUpdate();
17046
- }
17047
-
17048
- /**
17049
- * Selects a single option.
17050
- * @param {AuroMenuOption} option
17051
- */
17052
- selectOption(option) {
17053
- this.selectOptions(option);
17054
- }
17055
-
17056
- /**
17057
- * Deselects a single option.
17058
- * @param {AuroMenuOption} option
17059
- */
17060
- deselectOption(option) {
17061
- this.deselectOptions(option);
17062
- }
17063
-
17064
- /**
17065
- * Toggles the selection state of a single option.
17066
- * @param {AuroMenuOption} option
17067
- */
17068
- toggleOption(option) {
17069
- if (option.selected) {
17070
- this.deselectOption(option);
17071
- } else {
17072
- this.selectOption(option);
17073
- }
17074
- }
17075
-
17076
- /**
17077
- * Selects options based on their value(s) when compared to a passed value or values.
17078
- * Value or values are normalized to an array of strings that can be matched to option keys.
17079
- * @param {string|number|Array<string|number>} value - The value(s) to select.
17080
- */
17081
- selectByValue(value) {
17082
- const isEmptyValue = value === undefined ||
17083
- value === null ||
17084
- (Array.isArray(value) && value.length === 0) ||
17085
- (typeof value === 'string' && value.trim() === '');
17086
-
17087
- // Early exit for invalid/empty values
17088
- if (isEmptyValue) {
17089
- this.selectedOptions.forEach(opt => opt.selected = false);
17090
- this.selectedOptions = [];
17091
- return;
17092
- }
17093
-
17094
- // If an internal update cycle is still in progress, defer value application
17095
- // rather than dropping it.
17096
- if (this.internalUpdateInProgress || this.host.internalUpdateInProgress) {
17097
- this.queuePendingValue(value);
17098
- return;
17099
- }
17100
-
17101
- // Normalize values to array of strings
17102
- const normalizedValues = this._getNormalizedValues(value);
17103
-
17104
- // Validate for single-select mode
17105
- let validatedValues = normalizedValues;
17106
- if (normalizedValues.length > 1 && !this.multiSelect) {
17107
- console.warn("MenuService - Multiple values provided for single-select menu. Only the first value will be selected.");
17108
- validatedValues = [normalizedValues[0]];
17109
- }
16914
+ // Currently selected option
16915
+ this.optionSelected = undefined;
16916
+ // String used for highlighting/filtering
16917
+ this.matchWord = undefined;
16918
+ // Hide the checkmark icon on selected options
16919
+ this.noCheckmark = false;
16920
+ // Currently active option
16921
+ this.optionActive = undefined;
16922
+ // Loading state
16923
+ this.loading = false;
16924
+ // Multi-select mode
16925
+ this.multiSelect = false;
16926
+ // Allow deselecting of menu options
16927
+ this.allowDeselect = false;
16928
+ // Select all matching options when setting value in multi-select mode
16929
+ this.selectAllMatchingOptions = false;
17110
16930
 
17111
- if (this._menuOptions.length === 0) {
17112
- this.queuePendingValue(value);
17113
- return;
17114
- }
16931
+ // Event Bindings
17115
16932
 
17116
- // Find matching options by comparing available options to validated values
17117
- const trackedKeys = new Set();
17118
- const optionsToSelect = this._menuOptions.filter(option => {
17119
- const passesFilter = validatedValues.includes(option.key);
17120
- const alreadyTracked = trackedKeys.has(option.key);
17121
- const isActive = option.isActive;
16933
+ /**
16934
+ * @private
16935
+ */
16936
+ this.handleSlotChange = this.handleSlotChange.bind(this);
17122
16937
 
17123
- trackedKeys.add(option.key);
16938
+ // Instance properties (non-reactive)
17124
16939
 
17125
- // Include the option in the options to be selected if it passes the filter check and
17126
- // either hasn't been tracked yet or selectAllMatchingOptions is true
17127
- return isActive && passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
16940
+ /**
16941
+ * @private
16942
+ */
16943
+ Object.assign(this, {
16944
+ // Root-level menu (true) or a nested submenu (false)
16945
+ rootMenu: true,
16946
+ // Currently focused/active menu item index
16947
+ _index: -1,
16948
+ // Nested menu spacer
16949
+ nestingSpacer: '<span class="nestingSpacer"></span>',
16950
+ // Loading indicator for slot elements
16951
+ loadingSlots: null,
17128
16952
  });
16953
+ }
17129
16954
 
17130
- // Handle no matches: clear existing selection, but do not dispatch an intermediate
17131
- // undefined value that can overwrite the host value in parent components.
17132
- if (!optionsToSelect.length) {
17133
- const hasUnresolvedKeys = this._menuOptions.some((option) => option.isActive && option.key == null);
16955
+ static get properties() {
16956
+ return {
16957
+ ...super.properties,
17134
16958
 
17135
- if (hasUnresolvedKeys) {
17136
- this.queuePendingValue(value);
17137
- return;
17138
- }
16959
+ /**
16960
+ * Allows deselecting an already selected option when clicked again in single-select mode.
16961
+ */
16962
+ allowDeselect: {
16963
+ type: Boolean,
16964
+ reflect: true,
16965
+ },
17139
16966
 
17140
- this.clearPendingValue();
16967
+ /**
16968
+ * When true, the entire menu and all options are disabled.
16969
+ */
16970
+ disabled: {
16971
+ type: Boolean,
16972
+ reflect: true
16973
+ },
17141
16974
 
17142
- if (this.selectedOptions.length > 0) {
17143
- this.selectedOptions = [];
17144
- }
16975
+ /**
16976
+ * Indicates whether the menu has a loadingIcon or loadingText to render when in a loading state.
16977
+ */
16978
+ hasLoadingPlaceholder: {
16979
+ type: Boolean
16980
+ },
17145
16981
 
17146
- // Always notify so the host resets any stale invalid value, even when
17147
- // selectedOptions was already empty (e.g. double-clicking set-invalid).
17148
- this.stageUpdate({ reason: 'no-match' });
16982
+ /**
16983
+ * @private
16984
+ */
16985
+ layout: {
16986
+ type: String
16987
+ },
17149
16988
 
17150
- // Dispatch failure event if no matches found
17151
- if (validatedValues.length) {
17152
- this.dispatchChangeEvent('auroMenu-selectValueFailure', {
17153
- message: 'No matching options found for the provided value(s).',
17154
- values: validatedValues
17155
- });
17156
- }
16989
+ /**
16990
+ * Indent level for submenus.
16991
+ * @private
16992
+ */
16993
+ level: {
16994
+ type: Number,
16995
+ reflect: false,
16996
+ attribute: false
16997
+ },
17157
16998
 
17158
- return;
17159
- }
16999
+ /**
17000
+ * When true, displays a loading state using the loadingIcon and loadingText slots if provided.
17001
+ */
17002
+ loading: {
17003
+ type: Boolean,
17004
+ reflect: true
17005
+ },
17160
17006
 
17161
- this.clearPendingValue();
17007
+ /**
17008
+ * Specifies a string used to highlight matched string parts in options.
17009
+ */
17010
+ matchWord: {
17011
+ type: String,
17012
+ attribute: 'matchword'
17013
+ },
17162
17014
 
17163
- if (this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
17164
- return;
17165
- }
17015
+ /**
17016
+ * When true, the selected option can be multiple options.
17017
+ */
17018
+ multiSelect: {
17019
+ type: Boolean,
17020
+ reflect: true,
17021
+ attribute: 'multiselect'
17022
+ },
17166
17023
 
17167
- // Apply programmatic selection as a single transaction and emit one final state.
17168
- this.selectedOptions = optionsToSelect;
17169
- this.stageUpdate();
17170
- }
17024
+ /**
17025
+ * When true, selected option will not show the checkmark.
17026
+ */
17027
+ noCheckmark: {
17028
+ type: Boolean,
17029
+ reflect: true,
17030
+ attribute: 'nocheckmark'
17031
+ },
17171
17032
 
17172
- /**
17173
- * Queues a pending value and schedules a bounded retry.
17174
- * @param {string|number|Array<string|number>} value - The value to retry.
17175
- */
17176
- queuePendingValue(value) {
17177
- this._pendingValue = value;
17033
+ /**
17034
+ * Specifies the current active menuOption.
17035
+ */
17036
+ optionActive: {
17037
+ type: Object,
17038
+ attribute: 'optionactive'
17039
+ },
17178
17040
 
17179
- if (this._pendingRetryScheduled || this._pendingRetryCount >= 5) {
17180
- return;
17181
- }
17041
+ /**
17042
+ * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
17043
+ */
17044
+ optionSelected: {
17045
+ // Allow HTMLElement, HTMLElement[] arrays and undefined
17046
+ type: Object
17047
+ },
17182
17048
 
17183
- this._pendingRetryScheduled = true;
17184
- this._pendingRetryCount += 1;
17049
+ /**
17050
+ * Available menu options.
17051
+ * @readonly
17052
+ */
17053
+ options: {
17054
+ type: Array,
17055
+ reflect: false,
17056
+ attribute: false
17057
+ },
17185
17058
 
17186
- setTimeout(() => {
17187
- this._pendingRetryScheduled = false;
17059
+ /**
17060
+ * Sets the size of the menu.
17061
+ * @type {'sm' | 'md'}
17062
+ * @default 'sm'
17063
+ */
17064
+ size: {
17065
+ type: String,
17066
+ reflect: true
17067
+ },
17188
17068
 
17189
- if (this._pendingValue == null) {
17190
- return;
17191
- }
17069
+ /**
17070
+ * When true, selects all options that match the provided value/key when setting value and multiselect is enabled.
17071
+ */
17072
+ selectAllMatchingOptions: {
17073
+ type: Boolean,
17074
+ reflect: true,
17075
+ },
17192
17076
 
17193
- const pendingValue = this._pendingValue;
17194
- this.selectByValue(pendingValue);
17195
- }, 0);
17196
- }
17077
+ /**
17078
+ * Sets the shape of the menu.
17079
+ * @type {'box' | 'round'}
17080
+ * @default 'box'
17081
+ */
17082
+ shape: {
17083
+ type: String,
17084
+ reflect: true
17085
+ },
17197
17086
 
17198
- /**
17199
- * Clears pending retry state.
17200
- */
17201
- clearPendingValue() {
17202
- this._pendingValue = null;
17203
- this._pendingRetryScheduled = false;
17204
- this._pendingRetryCount = 0;
17087
+ /**
17088
+ * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
17089
+ */
17090
+ value: {
17091
+ type: String,
17092
+ reflect: true,
17093
+ attribute: 'value'
17094
+ }
17095
+ };
17205
17096
  }
17206
17097
 
17207
- /**
17208
- * Resets the selected options to an empty array.
17209
- */
17210
- reset() {
17211
- const previousOptions = [...this.selectedOptions];
17212
- previousOptions.forEach(opt => opt.selected = false);
17213
- this.selectedOptions = [];
17214
-
17215
- // Single update after clearing all
17216
- if (previousOptions.length) {
17217
- this.stageUpdate();
17218
- }
17098
+ static get styles() {
17099
+ return [
17100
+ styleCss$1,
17101
+ colorCss$1,
17102
+ tokensCss
17103
+ ];
17219
17104
  }
17220
17105
 
17221
17106
  /**
17222
- * SUBSCRIPTION, NOTIFICATION AND EVENT DISPATCH METHODS
17107
+ * @readonly
17108
+ * @returns {string} - Returns the label of the currently selected option(s).
17223
17109
  */
17110
+ get currentLabel() {
17111
+ return this.menuService.currentLabel;
17112
+ };
17224
17113
 
17225
17114
  /**
17226
- * Subscribes a callback to menu service events.
17227
- * @param {Function} callback - The callback to invoke on events.
17115
+ * @readonly
17116
+ * @returns {Array<HTMLElement>} - Returns the array of available menu options.
17117
+ * @deprecated Use `options` property instead.
17228
17118
  */
17229
- subscribe(callback) {
17230
- this._subscribers.push(callback);
17119
+ get items() {
17120
+ return this.options;
17231
17121
  }
17232
17122
 
17233
17123
  /**
17234
- * Remove a previously subscribed callback from menu service events.
17235
- * @param {Function} callback
17124
+ * @returns {number} - Returns the index of the currently active option.
17236
17125
  */
17237
- unsubscribe(callback) {
17238
- this._subscribers = this._subscribers.filter(cb => cb !== callback);
17126
+ get index() {
17127
+ return this._index;
17239
17128
  }
17240
17129
 
17241
17130
  /**
17242
- * Stages an update to notify subscribers of state and value changes.
17131
+ * @param {number} value - Sets the index of the currently active option.
17243
17132
  */
17244
- stageUpdate(meta = {}) {
17245
- this.notifyStateChange(meta);
17246
- this.notifyValueChange(meta);
17133
+ set index(value) {
17134
+ this.menuService.setHighlightedIndex(value);
17247
17135
  }
17248
17136
 
17249
17137
  /**
17250
- * Notifies subscribers of a menu service event.
17251
- * All notifications are sent to all subscribers.
17252
- * @param {string} event - The event to send to subscribers.
17138
+ * This will register this element with the browser.
17139
+ * @param {string} [name="auro-menu"] - The name of the element that you want to register.
17140
+ *
17141
+ * @example
17142
+ * AuroMenu.register("custom-menu") // this will register this element to <custom-menu/>
17143
+ *
17253
17144
  */
17254
- notify(event) {
17255
- this._subscribers.forEach(callback => callback(event));
17145
+ static register(name = "auro-menu") {
17146
+ AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroMenu);
17256
17147
  }
17257
17148
 
17258
17149
  /**
17259
- * Notifies subscribers of a state change (selected options has changed).
17150
+ * Formatted value based on `multiSelect` state.
17151
+ * Default type is `String`, changing to `Array<String>` when `multiSelect` is true.
17152
+ * @private
17153
+ * @returns {String|Array<String>}
17260
17154
  */
17261
- notifyStateChange(meta = {}) {
17262
- this.notify({
17263
- type: 'stateChange',
17264
- selectedOptions: this.selectedOptions,
17265
- ...meta
17266
- });
17155
+ get formattedValue() {
17156
+ return this.menuService.currentValue;
17267
17157
  }
17268
17158
 
17269
17159
  /**
17270
- * Notifies subscribers of a value change (current value has changed).
17160
+ * Gets the current property values for the menu service.
17161
+ * @private
17162
+ * @returns {Object}
17271
17163
  */
17272
- notifyValueChange(meta = {}) {
17273
-
17274
- // Prepare details for the event
17275
- const details = {
17276
- value: this.currentValue,
17277
- stringValue: this.stringValue,
17278
- keys: this.currentKeys,
17279
- options: this.selectedOptions,
17280
- label: this.currentLabel
17164
+ get propertyValues() {
17165
+ return {
17166
+ size: this.size,
17167
+ shape: this.shape,
17168
+ noCheckmark: this.nocheckmark,
17169
+ disabled: this.disabled
17281
17170
  };
17282
-
17283
- // If only one option is selected, include its index
17284
- if (this.selectedOptions.length === 1) details.index = this._menuOptions.indexOf(this.selectedOptions[0]);
17285
-
17286
- this.notify({
17287
- type: 'valueChange',
17288
- ...meta,
17289
- ...details
17290
- });
17291
17171
  }
17292
17172
 
17293
17173
  /**
17294
- * Dispatches a custom event from the host element.
17295
- * @param {string} eventName
17296
- * @param {any} detail
17174
+ * Provides the menu context to child components.
17175
+ * Initializes the MenuService and subscribes to menu changes.
17176
+ * @protected
17297
17177
  */
17298
- dispatchChangeEvent(eventName, detail) {
17299
- this.host.dispatchEvent(new CustomEvent(eventName, {
17300
- bubbles: true,
17301
- cancelable: false,
17302
- composed: true,
17303
- detail
17304
- }));
17305
- }
17178
+ provideContext() {
17179
+ if (this.parentElement && this.parentElement.closest('auro-menu, [auro-menu]')) {
17180
+ this.rootMenu = false;
17181
+ this.menuService = this.parentElement.menuService;
17182
+ this._contextProvider = this.parentElement._contextProvider;
17183
+ return;
17184
+ }
17306
17185
 
17307
- /**
17308
- * MENU OPTION MANAGEMENT METHODS
17309
- */
17186
+ this.menuService = new MenuService({host: this});
17187
+ this.menuService.setProperties(this.propertyValues);
17188
+ this.menuService.subscribe(this.handleMenuChange.bind(this));
17189
+ this._contextProvider = new i(this, {
17190
+ context: MenuContext,
17191
+ initialValue: this.menuService
17192
+ });
17193
+ }
17310
17194
 
17311
17195
  /**
17312
- * Adds a menu option to the service's list.
17313
- * @param {AuroMenuOption} option - the option to track
17196
+ * Updates the currently active option in the menu.
17197
+ * @param {HTMLElement} option - The option to set as active.
17314
17198
  */
17315
- addMenuOption(option) {
17316
- this._menuOptions.push(option);
17317
- this.notify({ type: 'optionsChange', options: this._menuOptions });
17318
-
17319
- if (this._pendingValue != null) {
17320
- this.queuePendingValue(this._pendingValue);
17321
- }
17199
+ updateActiveOption(option) {
17200
+ this.menuService.setHighlightedOption(option);
17322
17201
  }
17323
17202
 
17324
17203
  /**
17325
- * Removes a menu option from the service's list.
17326
- * @param {AuroMenuOption} option - the option to remove
17204
+ * Sets the internal value and manages update state.
17205
+ * @param {String|Array<String>} value - The value to set.
17206
+ * @protected
17327
17207
  */
17328
- removeMenuOption(option) {
17329
- this._menuOptions = this._menuOptions.filter(opt => opt !== option);
17330
- this.notify({ type: 'optionsChange', options: this._menuOptions });
17208
+ setInternalValue(value) {
17209
+ if (this.value !== value) {
17210
+ this.internalUpdateInProgress = true;
17211
+ this.value = value;
17331
17212
 
17332
- if (this._menuOptions.length === 0) {
17333
- this.clearPendingValue();
17213
+ setTimeout(() => {
17214
+ this.internalUpdateInProgress = false;
17215
+ });
17334
17216
  }
17335
17217
  }
17336
17218
 
17337
17219
  /**
17338
- * UTILITIES
17339
- */
17340
-
17341
- /**
17342
- * Normalizes a value or array of values into an array of strings for option selection.
17343
- * This function ensures that input values are consistently formatted for matching menu options.
17344
- *
17345
- * @param {string|number|Array<string|number>} value - The value(s) to normalize.
17346
- * @returns {Array<string>} An array of string values suitable for option matching.
17347
- * @throws {Error} If any value is not a string or number.
17220
+ * Handles changes from the menu service and updates component state.
17221
+ * @param {Object} event - The event object from the menu service.
17222
+ * @protected
17348
17223
  */
17349
- _getNormalizedValues(value) {
17350
- let values = value;
17351
-
17352
- // Handle JSON string and single value string input
17353
- if (!Array.isArray(values) && typeof values === 'string') {
17354
-
17355
- // Attempt to parse as JSON array
17356
- try {
17357
-
17358
- // Normalize single quotes to double quotes for JSON parsing
17359
- // This will not handle complex cases but will cover basic usage
17360
- const parseValue = values.replace(/'([^']*?)'/g, '"$1"');
17361
-
17362
- // Attempt parse
17363
- const parsed = JSON.parse(parseValue);
17364
-
17365
- // Ensure parsed value is an array
17366
- if (!Array.isArray(parsed)) throw new Error('Not an array');
17224
+ handleMenuChange(event) {
17225
+ if (event.type === 'valueChange') {
17367
17226
 
17368
- // Set values to parsed array
17369
- values = parsed;
17370
- } catch (err) {
17227
+ // New option is array value or first option with fallback to undefined for empty array in all cases
17228
+ const newOption = this.multiSelect && event.options.length ? event.options : event.options[0] || undefined;
17229
+ const newValue = event.stringValue;
17371
17230
 
17372
- // If parsing fails, treat as single value
17373
- values = [value];
17231
+ // Check if the option or value has actually changed
17232
+ if (this.optionSelected !== newOption || this.stringValue !== newValue) {
17233
+ this.optionSelected = newOption;
17234
+ this.setInternalValue(newValue);
17374
17235
  }
17375
- }
17376
17236
 
17377
- // Handle a single number being passed
17378
- if (typeof values === 'number') {
17379
- values = [String(values)];
17237
+ // Notify components of selection change
17238
+ this.notifySelectionChange(event);
17380
17239
  }
17381
17240
 
17382
- // Coerce each value to string and validate types
17383
- values.forEach((val, index) => {
17384
-
17385
- // Throw an error for invalid value types
17386
- if (typeof val !== 'string' && typeof val !== 'number') {
17387
- throw new Error('Value contains invalid value type. Supported types are string and number.');
17388
- }
17389
-
17390
- // Convert numbers to strings for consistency
17391
- if (typeof val === 'number') {
17392
- values[index] = String(val);
17393
- }
17394
- });
17241
+ if (event.type === 'highlightChange') {
17242
+ this.optionActive = event.option;
17243
+ this._index = event.index;
17244
+ }
17395
17245
 
17396
- // Return the resulting array of string values
17397
- return values;
17246
+ if (event.type === 'optionsChange') {
17247
+ this.options = event.options;
17248
+ this.dispatchEvent(new CustomEvent('auroMenu-optionsChange', {
17249
+ detail: {
17250
+ options: event.options
17251
+ }
17252
+ }));
17253
+ }
17398
17254
  }
17399
17255
 
17400
17256
  /**
17401
- * Returns whether two arrays of options contain the same elements.
17402
- * @param {AuroMenuOption[]} arr1 - First array of options.
17403
- * @param {AuroMenuOption[]} arr2 - Second array of options.
17404
- * @returns {boolean} True if arrays match, false otherwise.
17257
+ * Gets the currently selected options.
17258
+ * @returns {Array<HTMLElement>}
17405
17259
  */
17406
- optionsArraysMatch(arr1, arr2) {
17407
- if (arr1.length !== arr2.length) return false;
17408
-
17409
- const set1 = new Set(arr1);
17410
- const set2 = new Set(arr2);
17411
-
17412
- for (let item of set1) {
17413
- if (!set2.has(item)) {
17414
- return false;
17415
- }
17416
- }
17417
-
17418
- return true;
17260
+ get selectedOptions() {
17261
+ return this.menuService ? this.menuService.selectedOptions : [];
17419
17262
  }
17420
- }
17421
17263
 
17422
- const MenuContext = n('menu-context');
17264
+ /**
17265
+ * Gets the first selected option, or null if none.
17266
+ * @returns {HTMLElement|null}
17267
+ */
17268
+ get selectedOption() {
17269
+ return this.menuService ? this.menuService.selectedOptions[0] : null;
17270
+ }
17423
17271
 
17424
- /* eslint-disable no-underscore-dangle */
17425
- // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
17426
- // See LICENSE in the project root for license information.
17272
+ // Lifecycle Methods
17427
17273
 
17274
+ connectedCallback() {
17275
+ super.connectedCallback();
17428
17276
 
17277
+ this.provideContext();
17429
17278
 
17430
- /**
17431
- * The `auro-menu` element provides users a way to select from a list of options.
17432
- * @customElement auro-menu
17433
- *
17434
- * @event {CustomEvent<Element>} auroMenu-activatedOption - Notifies that a menuoption has been made `active`.
17435
- * @event {CustomEvent<any>} auroMenu-customEventFired - Notifies that a custom event has been fired.
17436
- * @event {CustomEvent<{ loading: boolean; hasLoadingPlaceholder: boolean; }>} auroMenu-loadingChange - Notifies when the loading attribute is changed.
17437
- * @event {CustomEvent<any>} auroMenu-selectValueFailure - Notifies that an attempt to select a menuoption by matching a value has failed.
17438
- * @event {CustomEvent<{ values: HTMLElement[] }>} auroMenu-deselectPrevented - Notifies that deselection was prevented and includes the affected options in `detail.values`.
17439
- * @event {CustomEvent<any>} auroMenu-selectValueReset - Notifies that the component value has been reset.
17440
- * @event {CustomEvent<any>} auroMenu-selectedOption - Notifies that a new menuoption selection has been made.
17441
- * @slot loadingText - Text to show while loading attribute is set
17442
- * @slot loadingIcon - Icon to show while loading attribute is set
17443
- * @slot - Slot for insertion of menu options.
17444
- */
17279
+ // this.addEventListener('keydown', this.handleKeyDown);
17280
+ this.addEventListener('auroMenuOption-click', this.handleMouseSelect);
17281
+ this.addEventListener('auroMenuOption-mouseover', this.handleOptionHover);
17282
+ this.addEventListener('slotchange', this.handleSlotChange);
17283
+ this.setTagAttribute("auro-menu");
17284
+ }
17445
17285
 
17446
- /* eslint-disable max-lines */
17286
+ disconnectedCallback() {
17287
+ // this.removeEventListener('keydown', this.handleKeyDown);
17288
+ this.removeEventListener('auroMenuOption-click', this.handleMouseSelect);
17289
+ this.removeEventListener('auroMenuOption-mouseover', this.handleOptionHover);
17290
+ this.removeEventListener('slotchange', this.handleSlotChange);
17447
17291
 
17448
- class AuroMenu extends AuroElement {
17292
+ super.disconnectedCallback();
17293
+ }
17449
17294
 
17450
- constructor() {
17451
- super();
17295
+ firstUpdated() {
17296
+ AuroLibraryRuntimeUtils$4.prototype.handleComponentTagRename(this, 'auro-menu');
17452
17297
 
17453
- // State properties (reactive)
17298
+ this.loadingSlots = this.querySelectorAll("[slot='loadingText'], [slot='loadingIcon']");
17299
+ this.initializeMenu();
17300
+ }
17454
17301
 
17455
- /**
17456
- * @private
17457
- */
17458
- this.shape = "box";
17459
17302
 
17460
- /**
17461
- * @private
17462
- */
17463
- this.size = "sm";
17303
+ updated(changedProperties) {
17304
+ super.updated(changedProperties);
17464
17305
 
17465
- // Value of the selected options
17466
- this.value = undefined;
17467
- // Currently selected option
17468
- this.optionSelected = undefined;
17469
- // String used for highlighting/filtering
17470
- this.matchWord = undefined;
17471
- // Hide the checkmark icon on selected options
17472
- this.noCheckmark = false;
17473
- // Currently active option
17474
- this.optionActive = undefined;
17475
- // Loading state
17476
- this.loading = false;
17477
- // Multi-select mode
17478
- this.multiSelect = false;
17479
- // Allow deselecting of menu options
17480
- this.allowDeselect = false;
17481
- // Select all matching options when setting value in multi-select mode
17482
- this.selectAllMatchingOptions = false;
17306
+ // Apply value selection synchronously so that static-HTML fixtures
17307
+ // resolve within a single update cycle. The refactored selectByValue
17308
+ // no longer calls reset() first, so the destructive intermediate-event
17309
+ // cascade that originally required deferral is eliminated. If option
17310
+ // keys are not yet resolved (framework mount-order race), selectByValue
17311
+ // queues a bounded retry automatically via queuePendingValue.
17312
+ if (changedProperties.has('value') && !this.internalUpdateInProgress) {
17313
+ this.menuService.selectByValue(this.value);
17314
+ }
17483
17315
 
17484
- // Event Bindings
17316
+ // Handle loading state changes
17317
+ if (changedProperties.has('loading')) {
17318
+ this.setLoadingState(this.loading);
17319
+ }
17485
17320
 
17486
- /**
17487
- * @private
17488
- */
17489
- this.handleSlotChange = this.handleSlotChange.bind(this);
17321
+ if (changedProperties.has('multiSelect') && this.rootMenu) {
17322
+ if (this.multiSelect) {
17323
+ this.setAttribute('aria-multiselectable', 'true');
17324
+ } else {
17325
+ this.removeAttribute('aria-multiselectable');
17326
+ }
17327
+ }
17328
+ }
17490
17329
 
17491
- // Instance properties (non-reactive)
17330
+ /**
17331
+ * Sets an attribute that matches the default tag name if the tag name is not the default.
17332
+ * @param {string} tagName - The tag name to set as an attribute.
17333
+ * @private
17334
+ */
17335
+ setTagAttribute(tagName) {
17336
+ if (this.tagName.toLowerCase() !== tagName) {
17337
+ this.setAttribute(tagName, true);
17338
+ }
17339
+ }
17492
17340
 
17493
- /**
17494
- * @private
17495
- */
17496
- Object.assign(this, {
17497
- // Root-level menu (true) or a nested submenu (false)
17498
- rootMenu: true,
17499
- // Currently focused/active menu item index
17500
- _index: -1,
17501
- // Nested menu spacer
17502
- nestingSpacer: '<span class="nestingSpacer"></span>',
17503
- // Loading indicator for slot elements
17504
- loadingSlots: null,
17341
+ /**
17342
+ * Sets the loading state and dispatches a loading change event.
17343
+ * @param {boolean} isLoading - Whether the menu is loading.
17344
+ * @protected
17345
+ */
17346
+ setLoadingState(isLoading) {
17347
+ this.setAttribute("aria-busy", isLoading);
17348
+ dispatchMenuEvent(this, "auroMenu-loadingChange", {
17349
+ loading: isLoading,
17350
+ hasLoadingPlaceholder: this.hasLoadingPlaceholder
17505
17351
  });
17506
17352
  }
17507
17353
 
17508
- static get properties() {
17509
- return {
17510
- ...super.properties,
17511
-
17512
- /**
17513
- * Allows deselecting an already selected option when clicked again in single-select mode.
17514
- */
17515
- allowDeselect: {
17516
- type: Boolean,
17517
- reflect: true,
17518
- },
17519
-
17520
- /**
17521
- * When true, the entire menu and all options are disabled.
17522
- */
17523
- disabled: {
17524
- type: Boolean,
17525
- reflect: true
17526
- },
17527
-
17528
- /**
17529
- * Indicates whether the menu has a loadingIcon or loadingText to render when in a loading state.
17530
- */
17531
- hasLoadingPlaceholder: {
17532
- type: Boolean
17533
- },
17354
+ // Init Methods
17534
17355
 
17535
- /**
17536
- * @private
17537
- */
17538
- layout: {
17539
- type: String
17540
- },
17356
+ /**
17357
+ * Initializes the menu's state and structure.
17358
+ * @private
17359
+ */
17360
+ initializeMenu() {
17361
+ if (this.rootMenu) {
17362
+ this.setAttribute('role', 'listbox');
17363
+ this.setAttribute('root', '');
17541
17364
 
17542
- /**
17543
- * Indent level for submenus.
17544
- * @private
17545
- */
17546
- level: {
17547
- type: Number,
17548
- reflect: false,
17549
- attribute: false
17550
- },
17365
+ if (this.multiSelect) {
17366
+ this.setAttribute('aria-multiselectable', 'true');
17367
+ }
17368
+ }
17551
17369
 
17552
- /**
17553
- * When true, displays a loading state using the loadingIcon and loadingText slots if provided.
17554
- */
17555
- loading: {
17556
- type: Boolean,
17557
- reflect: true
17558
- },
17370
+ this.handleNestedMenus(this);
17371
+ }
17559
17372
 
17560
- /**
17561
- * Specifies a string used to highlight matched string parts in options.
17562
- */
17563
- matchWord: {
17564
- type: String,
17565
- attribute: 'matchword'
17566
- },
17373
+ /**
17374
+ * Selects the currently highlighted option.
17375
+ * @protected
17376
+ */
17377
+ makeSelection() {
17378
+ this.menuService.selectHighlightedOption();
17379
+ }
17567
17380
 
17568
- /**
17569
- * When true, the selected option can be multiple options.
17570
- */
17571
- multiSelect: {
17572
- type: Boolean,
17573
- reflect: true,
17574
- attribute: 'multiselect'
17575
- },
17381
+ /**
17382
+ * Resets all options to their default state.
17383
+ * @private
17384
+ */
17385
+ clearSelection() {
17386
+ this.optionSelected = undefined;
17387
+ this.value = undefined;
17388
+ this._index = -1;
17389
+ }
17576
17390
 
17577
- /**
17578
- * When true, selected option will not show the checkmark.
17579
- */
17580
- noCheckmark: {
17581
- type: Boolean,
17582
- reflect: true,
17583
- attribute: 'nocheckmark'
17584
- },
17391
+ /**
17392
+ * Resets the menu to its initial state.
17393
+ * This is the only way to return value to undefined.
17394
+ * @public
17395
+ */
17396
+ reset() {
17397
+ this.menuService.reset();
17585
17398
 
17586
- /**
17587
- * Specifies the current active menuOption.
17588
- */
17589
- optionActive: {
17590
- type: Object,
17591
- attribute: 'optionactive'
17592
- },
17399
+ // Dispatch reset event
17400
+ dispatchMenuEvent(this, 'auroMenu-selectValueReset');
17401
+ }
17593
17402
 
17594
- /**
17595
- * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
17596
- */
17597
- optionSelected: {
17598
- // Allow HTMLElement, HTMLElement[] arrays and undefined
17599
- type: Object
17600
- },
17403
+ /**
17404
+ * Handles nested menu structure.
17405
+ * @private
17406
+ * @param {HTMLElement} menu - Root menu element.
17407
+ */
17408
+ handleNestedMenus(menu) {
17409
+ menu.level = menu.parentElement.level >= 0 ? menu.parentElement.level + 1 : 0;
17601
17410
 
17602
- /**
17603
- * Available menu options.
17604
- * @readonly
17605
- */
17606
- options: {
17607
- type: Array,
17608
- reflect: false,
17609
- attribute: false
17610
- },
17411
+ if (menu.level > 0) {
17412
+ menu.setAttribute('role', 'group');
17413
+ menu.removeAttribute("root");
17414
+ if (!menu.hasAttribute('aria-label')) {
17415
+ menu.setAttribute('aria-label', 'submenu');
17416
+ }
17417
+ }
17611
17418
 
17612
- /**
17613
- * Sets the size of the menu.
17614
- * @type {'sm' | 'md'}
17615
- * @default 'sm'
17616
- */
17617
- size: {
17618
- type: String,
17619
- reflect: true
17620
- },
17419
+ const options = menu.querySelectorAll(':scope > auro-menuoption, :scope > [auro-menuoption]');
17420
+ options.forEach((option) => {
17421
+ const regex = new RegExp(this.nestingSpacer, "gu");
17422
+ option.innerHTML = this.nestingSpacer.repeat(menu.level) + option.innerHTML.replace(regex, '');
17423
+ });
17424
+ }
17621
17425
 
17622
- /**
17623
- * When true, selects all options that match the provided value/key when setting value and multiselect is enabled.
17624
- */
17625
- selectAllMatchingOptions: {
17626
- type: Boolean,
17627
- reflect: true,
17628
- },
17426
+ /**
17427
+ * Navigates the menu options in the specified direction.
17428
+ * @param {'up'|'down'} direction - The direction to navigate.
17429
+ * @protected
17430
+ */
17431
+ navigateOptions(direction) {
17432
+ if (direction === 'up') {
17433
+ this.menuService.highlightPrevious();
17434
+ } else if (direction === 'down') {
17435
+ this.menuService.highlightNext();
17436
+ }
17437
+ }
17629
17438
 
17630
- /**
17631
- * Sets the shape of the menu.
17632
- * @type {'box' | 'round'}
17633
- * @default 'box'
17634
- */
17635
- shape: {
17636
- type: String,
17637
- reflect: true
17638
- },
17439
+ /**
17440
+ * Handles slot change events.
17441
+ * @private
17442
+ */
17443
+ handleSlotChange() {
17444
+ if (this.rootMenu) {
17445
+ this.initializeMenu();
17446
+ }
17447
+ }
17639
17448
 
17640
- /**
17641
- * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
17642
- */
17643
- value: {
17644
- type: String,
17645
- reflect: true,
17646
- attribute: 'value'
17647
- }
17648
- };
17449
+ /**
17450
+ * Handles custom events defined on options.
17451
+ * @private
17452
+ * @param {HTMLElement} option - Option with custom event.
17453
+ */
17454
+ handleCustomEvent(option) {
17455
+ const eventName = option.getAttribute('event');
17456
+ dispatchMenuEvent(this, eventName);
17457
+ dispatchMenuEvent(this, 'auroMenu-customEventFired');
17649
17458
  }
17650
17459
 
17651
- static get styles() {
17652
- return [
17653
- styleCss$1,
17654
- colorCss$1,
17655
- tokensCss
17656
- ];
17460
+ /**
17461
+ * Notifies selection change to parent components.
17462
+ * @param {any} source - The source that triggers this event.
17463
+ * @private
17464
+ */
17465
+ notifySelectionChange({value, stringValue, keys, options, reason} = {}) {
17466
+ dispatchMenuEvent(this, 'auroMenu-selectedOption', {
17467
+ value,
17468
+ stringValue,
17469
+ keys,
17470
+ options,
17471
+ reason
17472
+ });
17657
17473
  }
17658
17474
 
17659
17475
  /**
17660
- * @readonly
17661
- * @returns {string} - Returns the label of the currently selected option(s).
17476
+ * Checks if an option is currently selected.
17477
+ * @private
17478
+ * @param {HTMLElement} option - The option to check.
17479
+ * @returns {boolean}
17662
17480
  */
17663
- get currentLabel() {
17664
- return this.menuService.currentLabel;
17665
- };
17481
+ isOptionSelected(option) {
17482
+ if (!this.optionSelected) {
17483
+ return false;
17484
+ }
17485
+
17486
+ if (this.multiSelect) {
17487
+ // In multi-select mode, check if the option is in the selected array
17488
+ return Array.isArray(this.optionSelected) && this.optionSelected.some((selectedOption) => selectedOption === option);
17489
+ }
17490
+
17491
+ return this.optionSelected === option;
17492
+ }
17666
17493
 
17667
17494
  /**
17668
- * @readonly
17669
- * @returns {Array<HTMLElement>} - Returns the array of available menu options.
17670
- * @deprecated Use `options` property instead.
17495
+ * Getter for loading placeholder state.
17496
+ * @returns {boolean} - True if loading slots are present and non-empty.
17671
17497
  */
17672
- get items() {
17673
- return this.options;
17498
+ get hasLoadingPlaceholder() {
17499
+ return this.loadingSlots && this.loadingSlots.length > 0;
17674
17500
  }
17675
17501
 
17676
17502
  /**
17677
- * @returns {number} - Returns the index of the currently active option.
17503
+ * Getter for wrapper classes based on size.
17504
+ * @returns {Object} - Class map for the wrapper element.
17505
+ * @private
17678
17506
  */
17679
- get index() {
17680
- return this._index;
17507
+ get wrapperClasses() {
17508
+ return e$3({
17509
+ 'menuWrapper': true,
17510
+ [this.size]: true,
17511
+ });
17681
17512
  }
17682
17513
 
17683
17514
  /**
17684
- * @param {number} value - Sets the index of the currently active option.
17515
+ * Logic to determine the layout of the component.
17516
+ * @protected
17517
+ * @returns {void}
17685
17518
  */
17686
- set index(value) {
17687
- this.menuService.setHighlightedIndex(value);
17519
+ renderLayout() {
17520
+ if (this.loading) {
17521
+ return b$2`
17522
+ <div class="${this.wrapperClasses}">
17523
+ <auro-menuoption
17524
+ disabled
17525
+ loadingplaceholder
17526
+ class="${this.hasLoadingPlaceholder ? "" : "empty"}"
17527
+ >
17528
+ <div>
17529
+ <slot name="loadingIcon" class="body-lg"></slot>
17530
+ <slot name="loadingText"></slot>
17531
+ </div>
17532
+ </auro-menuoption>
17533
+ </div>
17534
+ `;
17535
+ }
17536
+
17537
+ return b$2`
17538
+ <div class="${this.wrapperClasses}">
17539
+ <slot @slotchange=${this.handleSlotChange}></slot>
17540
+ </div>
17541
+ `;
17688
17542
  }
17543
+ }
17544
+
17545
+ var styleCss = i$7`.body-default{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-default-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-default-font-size, 1rem);line-height:var(--wcss-body-default-line-height, 1.5rem)}.body-default-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-default-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-default-emphasized-font-size, 1rem);line-height:var(--wcss-body-default-emphasized-line-height, 1.5rem)}.body-lg{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-lg-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-lg-font-size, 1.125rem);line-height:var(--wcss-body-lg-line-height, 1.625rem)}.body-lg-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-lg-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-lg-emphasized-font-size, 1.125rem);line-height:var(--wcss-body-lg-emphasized-line-height, 1.625rem)}.body-sm{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-sm-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-sm-font-size, 0.875rem);line-height:var(--wcss-body-sm-line-height, 1.25rem)}.body-sm-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-sm-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-sm-emphasized-font-size, 0.875rem);line-height:var(--wcss-body-sm-emphasized-line-height, 1.25rem)}.body-xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-xs-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-xs-font-size, 0.75rem);line-height:var(--wcss-body-xs-line-height, 1rem)}.body-xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-xs-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-xs-emphasized-font-size, 0.75rem);line-height:var(--wcss-body-xs-emphasized-line-height, 1rem)}.body-2xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-2xs-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-2xs-font-size, 0.625rem);line-height:var(--wcss-body-2xs-line-height, 0.875rem)}.body-2xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-2xs-emphasized-weight, );letter-spacing:var(--wcss-body-letter-spacing, 0);font-size:var(--wcss-body-2xs-emphasized-font-size, 0.625rem);line-height:var(--wcss-body-2xs-emphasized-line-height, 0.875rem)}.display-2xl{font-family:var(--wcss-display-2xl-family, "AS Circular"),var(--wcss-display-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-2xl-weight, 300);line-height:var(--wcss-display-2xl-line-height, 1.3);font-size:var(--wcss-display-2xl-font-size, clamp(3.5rem, 6vw, 5.375rem));letter-spacing:var(--wcss-display-2xl-letter-spacing, 0)}.display-xl{font-family:var(--wcss-display-xl-family, "AS Circular"),var(--wcss-display-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-xl-weight, 300);line-height:var(--wcss-display-xl-line-height, 1.3);font-size:var(--wcss-display-xl-font-size, clamp(3rem, 5.3333333333vw, 4.5rem));letter-spacing:var(--wcss-display-xl-letter-spacing, 0)}.display-lg{font-family:var(--wcss-display-lg-family, "AS Circular"),var(--wcss-display-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-lg-weight, 300);line-height:var(--wcss-display-lg-line-height, 1.3);font-size:var(--wcss-display-lg-font-size, clamp(2.75rem, 4.6666666667vw, 4rem));letter-spacing:var(--wcss-display-lg-letter-spacing, 0)}.display-md{font-family:var(--wcss-display-md-family, "AS Circular"),var(--wcss-display-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-md-weight, 300);line-height:var(--wcss-display-md-line-height, 1.3);font-size:var(--wcss-display-md-font-size, clamp(2.5rem, 4vw, 3.5rem));letter-spacing:var(--wcss-display-md-letter-spacing, 0)}.display-sm{font-family:var(--wcss-display-sm-family, "AS Circular"),var(--wcss-display-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-sm-weight, 300);line-height:var(--wcss-display-sm-line-height, 1.3);font-size:var(--wcss-display-sm-font-size, clamp(2rem, 3.6666666667vw, 3rem));letter-spacing:var(--wcss-display-sm-letter-spacing, 0)}.display-xs{font-family:var(--wcss-display-xs-family, "AS Circular"),var(--wcss-display-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-display-xs-weight, 300);line-height:var(--wcss-display-xs-line-height, 1.3);font-size:var(--wcss-display-xs-font-size, clamp(1.75rem, 3vw, 2.375rem));letter-spacing:var(--wcss-display-xs-letter-spacing, 0)}.heading-xl{font-family:var(--wcss-heading-xl-family, "AS Circular"),var(--wcss-heading-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-xl-weight, 300);line-height:var(--wcss-heading-xl-line-height, 1.3);font-size:var(--wcss-heading-xl-font-size, clamp(2rem, 3vw, 2.5rem));letter-spacing:var(--wcss-heading-xl-letter-spacing, 0)}.heading-lg{font-family:var(--wcss-heading-lg-family, "AS Circular"),var(--wcss-heading-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-lg-weight, 300);line-height:var(--wcss-heading-lg-line-height, 1.3);font-size:var(--wcss-heading-lg-font-size, clamp(1.75rem, 2.6666666667vw, 2.25rem));letter-spacing:var(--wcss-heading-lg-letter-spacing, 0)}.heading-md{font-family:var(--wcss-heading-md-family, "AS Circular"),var(--wcss-heading-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-md-weight, 300);line-height:var(--wcss-heading-md-line-height, 1.3);font-size:var(--wcss-heading-md-font-size, clamp(1.625rem, 2.3333333333vw, 1.75rem));letter-spacing:var(--wcss-heading-md-letter-spacing, 0)}.heading-sm{font-family:var(--wcss-heading-sm-family, "AS Circular"),var(--wcss-heading-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-sm-weight, 300);line-height:var(--wcss-heading-sm-line-height, 1.3);font-size:var(--wcss-heading-sm-font-size, clamp(1.375rem, 2vw, 1.5rem));letter-spacing:var(--wcss-heading-sm-letter-spacing, 0)}.heading-xs{font-family:var(--wcss-heading-xs-family, "AS Circular"),var(--wcss-heading-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-xs-weight, 300);line-height:var(--wcss-heading-xs-line-height, 1.3);font-size:var(--wcss-heading-xs-font-size, clamp(1.25rem, 1.6666666667vw, 1.25rem));letter-spacing:var(--wcss-heading-xs-letter-spacing, 0)}.heading-2xs{font-family:var(--wcss-heading-2xs-family, "AS Circular"),var(--wcss-heading-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-heading-2xs-weight, 300);line-height:var(--wcss-heading-2xs-line-height, 1.3);font-size:var(--wcss-heading-2xs-font-size, clamp(1.125rem, 1.5vw, 1.125rem));letter-spacing:var(--wcss-heading-2xs-letter-spacing, 0)}.accent-2xl{font-family:var(--wcss-accent-2xl-family, "Good OT"),var(--wcss-accent-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-2xl-weight, 450);line-height:var(--wcss-accent-2xl-line-height, 1);font-size:var(--wcss-accent-2xl-font-size, clamp(2rem, 3.1666666667vw, 2.375rem));letter-spacing:var(--wcss-accent-2xl-letter-spacing, 0.05em);text-transform:uppercase}.accent-xl{font-family:var(--wcss-accent-xl-family, "Good OT"),var(--wcss-accent-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-xl-weight, 450);line-height:var(--wcss-accent-xl-line-height, 1.3);font-size:var(--wcss-accent-xl-font-size, clamp(1.625rem, 2.3333333333vw, 2rem));letter-spacing:var(--wcss-accent-xl-letter-spacing, 0.05em);text-transform:uppercase}.accent-lg{font-family:var(--wcss-accent-lg-family, "Good OT"),var(--wcss-accent-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-lg-weight, 450);line-height:var(--wcss-accent-lg-line-height, 1.3);font-size:var(--wcss-accent-lg-font-size, clamp(1.5rem, 2.1666666667vw, 1.75rem));letter-spacing:var(--wcss-accent-lg-letter-spacing, 0.05em);text-transform:uppercase}.accent-md{font-family:var(--wcss-accent-md-family, "Good OT"),var(--wcss-accent-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-md-weight, 500);line-height:var(--wcss-accent-md-line-height, 1.3);font-size:var(--wcss-accent-md-font-size, clamp(1.375rem, 1.8333333333vw, 1.5rem));letter-spacing:var(--wcss-accent-md-letter-spacing, 0.05em);text-transform:uppercase}.accent-sm{font-family:var(--wcss-accent-sm-family, "Good OT"),var(--wcss-accent-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-sm-weight, 500);line-height:var(--wcss-accent-sm-line-height, 1.3);font-size:var(--wcss-accent-sm-font-size, clamp(1.125rem, 1.5vw, 1.25rem));letter-spacing:var(--wcss-accent-sm-letter-spacing, 0.05em);text-transform:uppercase}.accent-xs{font-family:var(--wcss-accent-xs-family, "Good OT"),var(--wcss-accent-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-xs-weight, 500);line-height:var(--wcss-accent-xs-line-height, 1.3);font-size:var(--wcss-accent-xs-font-size, clamp(1rem, 1.3333333333vw, 1rem));letter-spacing:var(--wcss-accent-xs-letter-spacing, 0.1em);text-transform:uppercase}.accent-2xs{font-family:var(--wcss-accent-2xs-family, "Good OT"),var(--wcss-accent-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-weight:var(--wcss-accent-2xs-weight, 450);line-height:var(--wcss-accent-2xs-line-height, 1.3);font-size:var(--wcss-accent-2xs-font-size, clamp(0.875rem, 1.1666666667vw, 0.875rem));letter-spacing:var(--wcss-accent-2xs-letter-spacing, 0.1em);text-transform:uppercase}:host{cursor:pointer;user-select:none;text-overflow:ellipsis;max-width:100dvw}:host .wrapper{display:flex;align-items:center;height:var(--ds-size-400, 2rem);padding-right:var(--ds-size-200, 1rem);padding-left:calc(var(--ds-size-150, 0.75rem) + var(--ds-size-300, 1.5rem) + var(--ds-size-100, 0.5rem));border-radius:var(--ds-size-100, 0.5rem);-webkit-tap-highlight-color:transparent}:host .wrapper[class*=shape-box]{border-radius:unset}:host .wrapper[class*=shape-snowflake]{border-radius:unset;line-height:24px}:host .wrapper[class*=shape-pill]{border-radius:30px}:host .wrapper[class*=-lg]{padding-top:var(--ds-size-75, 0.375rem);padding-bottom:var(--ds-size-75, 0.375rem);padding-right:var(--ds-size-150, 0.75rem);line-height:26px}:host .wrapper[class*=-xl]{padding-top:var(--ds-size-100, 0.5rem);padding-bottom:var(--ds-size-100, 0.5rem);padding-right:var(--ds-size-200, 1rem);line-height:26px}:host slot{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}:host [auro-icon]{--ds-auro-icon-size: var(--ds-size-300, 1.5rem);margin-right:var(--ds-size-150, 0.75rem);margin-left:var(--ds-size-100, 0.5rem)}:host ::slotted(.nestingSpacer){display:inline-block;width:var(--ds-size-300, 1.5rem)}[slot=displayValue]{display:none}:host([loadingplaceholder]) .wrapper{padding-left:calc(var(--ds-size-150, 0.75rem) + var(--ds-size-300, 1.5rem) + var(--ds-size-100, 0.5rem))}:host([selected]) .wrapper{padding-left:0}:host([nocheckmark]) .wrapper{padding-left:var(--ds-size-150, 0.75rem)}:host([nocheckmark]) .wrapper[class*=-lg]{padding-left:var(--ds-size-150, 0.75rem)}:host([nocheckmark]) .wrapper[class*=-xl]{padding-left:var(--ds-size-200, 1rem)}:host([hidden]){display:none}:host([static]){pointer-events:none}:host([disabled]:hover){cursor:auto}:host([disabled]){user-select:none;pointer-events:none}`;
17546
+
17547
+ var colorCss = i$7`:host .wrapper{background-color:var(--ds-auro-menuoption-container-color, transparent);box-shadow:inset 0 0 0 1px var(--ds-auro-menuoption-container-border-color, transparent);color:var(--ds-auro-menuoption-text-color)}:host svg{fill:var(--ds-auro-menuoption-icon-color)}:host([disabled]){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-menuoption-text-color: var(--ds-basic-color-texticon-disabled, #d0d0d0);--ds-auro-menuoption-icon-color: var(--ds-basic-color-texticon-disabled, #d0d0d0)}:host(.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}@media(hover: hover){:host(:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}}:host(:focus){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}:host([selected]){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-subtle, #b4eff9);--ds-auro-menuoption-text-color: var(--ds-basic-color-texticon-default, #2a2a2a);--ds-auro-menuoption-icon-color: var(--ds-basic-color-texticon-default, #2a2a2a)}:host([selected].active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}@media(hover: hover){:host([selected]:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd)}}:host([selected]:focus){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-subtle, #b4eff9);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}:host(:focus.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}@media(hover: hover){:host(:focus:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}}:host([selected]:focus.active){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}@media(hover: hover){:host([selected]:focus:hover){--ds-auro-menuoption-container-color: var(--ds-basic-color-surface-accent1-muted, #ebfafd);--ds-auro-menuoption-container-border-color: var(--ds-basic-color-border-brand, #00274a)}}`;
17548
+
17549
+ class p{registerComponent(t,a){customElements.get(t)||customElements.define(t,class extends a{});}closestElement(t,a=this,e=(a,s=a&&a.closest(t))=>a&&a!==document&&a!==window?s||e(a.getRootNode().host):null){return e(a)}handleComponentTagRename(t,a){const e=a.toLowerCase();t.tagName.toLowerCase()!==e&&t.setAttribute(e,true);}elementMatch(t,a){const e=a.toLowerCase();return t.tagName.toLowerCase()===e||t.hasAttribute(e)}getSlotText(t,a){const e=t.shadowRoot?.querySelector(`slot[name="${a}"]`);return (e?.assignedNodes({flatten:true})||[]).map(t=>t.textContent?.trim()).join(" ").trim()||null}}var u='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-labelledby="error__desc" class="ico_squareLarge" data-deprecated="true" role="img" style="min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor" viewBox="0 0 24 24" part="svg"><title/><desc id="error__desc">Error alert indicator.</desc><path d="m13.047 5.599 6.786 11.586A1.207 1.207 0 0 1 18.786 19H5.214a1.207 1.207 0 0 1-1.047-1.815l6.786-11.586a1.214 1.214 0 0 1 2.094 0m-1.165.87a.23.23 0 0 0-.085.085L5.419 17.442a.232.232 0 0 0 .203.35h12.756a.234.234 0 0 0 .203-.35L12.203 6.554a.236.236 0 0 0-.321-.084M12 15.5a.75.75 0 1 1 0 1.5.75.75 0 0 1 0-1.5m-.024-6.22c.325 0 .589.261.589.583v4.434a.586.586 0 0 1-.589.583.586.586 0 0 1-.588-.583V9.863c0-.322.264-.583.588-.583"/></svg>',g='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" style="min-width:100%;height:auto;fill:currentColor" viewBox="0 0 142 138" part="svg"><title id="tail-DEFAULT__title">Airplane tail default image with light gray color</title><path fill="url(#paint0_linear_9225_2069)" fill-rule="evenodd" d="M124.093 14.665a3.26 3.26 0 0 1 3.208 3.829L101.097 119.85c-.47 1.087-2.211 3.398-5.692 3.471l-.106.001-80.65.013A659230 659230 0 0 1 99.08 17.412c1.214-1.497 3.275-2.722 5.372-2.747z" clip-rule="evenodd"/><defs><linearGradient id="paint0_linear_9225_2069" x1="114.823" x2="60.061" y1="14.665" y2="123.335" gradientUnits="userSpaceOnUse"><stop offset=".5" stop-color="#707984"/><stop offset=".5" stop-color="#646E7B"/></linearGradient></defs></svg>';class m extends i$4{static get properties(){return {hidden:{type:Boolean,reflect:true},hiddenVisually:{type:Boolean,reflect:true},hiddenAudible:{type:Boolean,reflect:true}}}hideAudible(t){return t?"true":"false"}}const f=new Map,w=(t,a={})=>{const e=a.responseParser||(t=>t.text());return f.has(t)||f.set(t,fetch(t).then(e)),f.get(t)};var z=i$7`:focus:not(:focus-visible){outline:3px solid transparent}.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock,:host{display:block}.util_displayFlex{display:flex}.util_displayHidden,:host([hidden]:not(:focus):not(:active)){display:none}.util_displayHiddenVisually,:host([hiddenVisually]:not(:focus):not(:active)){position:absolute;overflow:hidden;clip:rect(1px,1px,1px,1px);width:1px;height:1px;padding:0;border:0}.ico_squareLarge{fill:currentColor;height:var(--auro-size-lg, var(--ds-size-300, 1.5rem))}.ico_squareSmall{fill:currentColor;height:.6rem}.ico_squareMed{fill:currentColor;height:var(--auro-size-md, var(--ds-size-200, 1rem))}.ico_squareSml{fill:currentColor;height:var(--auro-size-sm, var(--ds-size-150, .75rem))}:host{color:currentColor;vertical-align:middle;display:inline-block}svg{min-width:var(--ds-auro-icon-size, 1.5rem)!important;width:var(--ds-auro-icon-size, 1.5rem)!important;height:var(--ds-auro-icon-size, 1.5rem)!important}.componentWrapper{display:flex;line-height:var(--ds-auro-icon-size)}.svgWrapper{height:var(--ds-auro-icon-size);width:var(--ds-auro-icon-size)}.svgWrapper [part=svg]{display:flex}.labelWrapper{margin-left:var(--ds-size-50, .25rem)}.labelWrapper ::slotted(*){line-height:inherit!important}
17550
+ `;class y extends m{constructor(){super(),this._initializeDefaults();}_initializeDefaults(){this.onDark=false,this.appearance="default";}static get properties(){return {...m.properties,onDark:{type:Boolean,reflect:true},appearance:{type:String,reflect:true},svg:{attribute:false,reflect:true}}}static get styles(){return z}async fetchIcon(t,a){let e="";e="logos"===t?await w(`${this.uri}/${t}/${a}.svg`):await w(`${this.uri}/icons/${t}/${a}.svg`);return (new DOMParser).parseFromString(e,"text/html").body.querySelector("svg")}async firstUpdated(){try{if(!this.customSvg){const t=await this.fetchIcon(this.category,this.name);if(t)this.svg=t;else if(!t){const t=this.name?.startsWith("tail-")?g:u;this.svg=(new DOMParser).parseFromString(t,"text/html").body.querySelector("svg");}}}catch(t){this.svg=void 0;}}}i$7`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock,:host{display:block}.util_displayFlex{display:flex}.util_displayHidden,:host([hidden]:not(:focus):not(:active)){display:none}.util_displayHiddenVisually,:host([hiddenVisually]:not(:focus):not(:active)){position:absolute;overflow:hidden;clip:rect(1px,1px,1px,1px);width:1px;height:1px;padding:0;border:0}:host{display:inline-block;--ds-auro-icon-size: 100%;width:100%;height:100%}:host .logo{color:var(--ds-auro-alaska-color)}:host([onDark]),:host([appearance=inverse]){--ds-auro-alaska-color: #FFF}
17551
+ `;var M=i$7`:host{--ds-auro-icon-color: var(--ds-basic-color-texticon-default, #2a2a2a);--ds-auro-alaska-color: #02426D;--ds-auro-icon-size: var(--ds-size-300, 1.5rem)}
17552
+ `;var _=i$7`:host{color:var(--ds-auro-icon-color)}:host([customColor]){color:inherit}:host(:not([onDark])[variant=accent1]),:host(:not([appearance=inverse])[variant=accent1]){--ds-auro-icon-color: var(--ds-basic-color-texticon-accent1, #265688)}:host(:not([onDark])[variant=disabled]),:host(:not([appearance=inverse])[variant=disabled]){--ds-auro-icon-color: var(--ds-basic-color-texticon-disabled, #d0d0d0)}:host(:not([onDark])[variant=muted]),:host(:not([appearance=inverse])[variant=muted]){--ds-auro-icon-color: var(--ds-basic-color-texticon-muted, #676767)}:host(:not([onDark])[variant=statusDefault]),:host(:not([appearance=inverse])[variant=statusDefault]){--ds-auro-icon-color: var(--ds-basic-color-status-default, #afb9c6)}:host(:not([onDark])[variant=statusInfo]),:host(:not([appearance=inverse])[variant=statusInfo]){--ds-auro-icon-color: var(--ds-basic-color-status-info, #01426a)}:host(:not([onDark])[variant=statusSuccess]),:host(:not([appearance=inverse])[variant=statusSuccess]){--ds-auro-icon-color: var(--ds-basic-color-status-success, #447a1f)}:host(:not([onDark])[variant=statusWarning]),:host(:not([appearance=inverse])[variant=statusWarning]){--ds-auro-icon-color: var(--ds-basic-color-status-warning, #fac200)}:host(:not([onDark])[variant=statusError]),:host(:not([appearance=inverse])[variant=statusError]){--ds-auro-icon-color: var(--ds-basic-color-status-error, #e31f26)}:host(:not([onDark])[variant=statusInfoSubtle]),:host(:not([appearance=inverse])[variant=statusInfoSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-info-subtle, #ebf3f9)}:host(:not([onDark])[variant=statusSuccessSubtle]),:host(:not([appearance=inverse])[variant=statusSuccessSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-success-subtle, #d6eac7)}:host(:not([onDark])[variant=statusWarningSubtle]),:host(:not([appearance=inverse])[variant=statusWarningSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-warning-subtle, #fff0b2)}:host(:not([onDark])[variant=statusErrorSubtle]),:host(:not([appearance=inverse])[variant=statusErrorSubtle]){--ds-auro-icon-color: var(--ds-basic-color-status-error-subtle, #fbc6c6)}:host(:not([onDark])[variant=fareBasicEconomy]),:host(:not([appearance=inverse])[variant=fareBasicEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-basiceconomy, #97eaf8)}:host(:not([onDark])[variant=fareBusiness]),:host(:not([appearance=inverse])[variant=fareBusiness]){--ds-auro-icon-color: var(--ds-basic-color-fare-business, #01426a)}:host(:not([onDark])[variant=fareEconomy]),:host(:not([appearance=inverse])[variant=fareEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-economy, #0074ca)}:host(:not([onDark])[variant=fareFirst]),:host(:not([appearance=inverse])[variant=fareFirst]){--ds-auro-icon-color: var(--ds-basic-color-fare-first, #00274a)}:host(:not([onDark])[variant=farePremiumEconomy]),:host(:not([appearance=inverse])[variant=farePremiumEconomy]){--ds-auro-icon-color: var(--ds-basic-color-fare-premiumeconomy, #005154)}:host(:not([onDark])[variant=tierOneWorldEmerald]),:host(:not([appearance=inverse])[variant=tierOneWorldEmerald]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-emerald, #139142)}:host(:not([onDark])[variant=tierOneWorldSapphire]),:host(:not([appearance=inverse])[variant=tierOneWorldSapphire]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-sapphire, #015daa)}:host(:not([onDark])[variant=tierOneWorldRuby]),:host(:not([appearance=inverse])[variant=tierOneWorldRuby]){--ds-auro-icon-color: var(--ds-basic-color-tier-program-oneworld-ruby, #a41d4a)}:host([onDark]),:host([appearance=inverse]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse, #ffffff)}:host([onDark][variant=disabled]),:host([appearance=inverse][variant=disabled]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse-disabled, #7e8894)}:host([onDark][variant=muted]),:host([appearance=inverse][variant=muted]){--ds-auro-icon-color: var(--ds-basic-color-texticon-inverse-muted, #ccd2db)}:host([onDark][variant=statusError]),:host([appearance=inverse][variant=statusError]){--ds-auro-icon-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8)}
17553
+ `;class k extends y{constructor(){super(),this._initializeDefaults();}_initializeDefaults(){this.variant=void 0,this.uri="https://cdn.jsdelivr.net/npm/@alaskaairux/icons@latest/dist",this.runtimeUtils=new p;}static get properties(){return {...y.properties,ariaHidden:{type:String,reflect:true},category:{type:String,reflect:true},customColor:{type:Boolean,reflect:true},customSvg:{type:Boolean},label:{type:Boolean,reflect:true},name:{type:String,reflect:true},variant:{type:String,reflect:true}}}static get styles(){return [y.styles,M,z,_]}static register(t="auro-icon"){p.prototype.registerComponent(t,k);}connectedCallback(){super.connectedCallback(),this.runtimeUtils.handleComponentTagRename(this,"auro-icon");}exposeCssParts(){this.setAttribute("exportparts","svg:iconSvg");}async firstUpdated(){if(await super.firstUpdated(),this.hasAttribute("ariaHidden")&&this.svg){const t=this.svg.querySelector("desc");t&&(t.remove(),this.svg.removeAttribute("aria-labelledby"));}}render(){const t={labelWrapper:true,util_displayHiddenVisually:!this.label};return b$2`
17554
+ <div class="componentWrapper">
17555
+ <div
17556
+ class="${e$3({svgWrapper:true})}"
17557
+ title="${o(this.title||void 0)}">
17558
+ <span aria-hidden="${o(this.ariaHidden||true)}" part="svg">
17559
+ ${this.customSvg?b$2`
17560
+ <slot name="svg"></slot>
17561
+ `:b$2`
17562
+ ${this.svg}
17563
+ `}
17564
+ </span>
17565
+ </div>
17566
+
17567
+ <div class="${e$3(t)}" part="label">
17568
+ <slot></slot>
17569
+ </div>
17570
+ </div>
17571
+ `}}
17572
+
17573
+ var iconVersion = '9.1.2';
17574
+
17575
+ var checkmarkIcon = {"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-labelledby=\"checkmark-sm__desc\" class=\"ico_squareLarge\" role=\"img\" style=\"min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor\" viewBox=\"0 0 24 24\" part=\"svg\"><title/><desc id=\"checkmark-sm__desc\">a small check mark.</desc><path d=\"M8.461 11.84a.625.625 0 1 0-.922.844l2.504 2.738c.247.27.674.27.922 0l5.496-6a.625.625 0 1 0-.922-.844l-5.035 5.496z\"/></svg>"};
17576
+
17577
+ // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
17578
+ // See LICENSE in the project root for license information.
17579
+
17580
+
17581
+ let menuOptionIdCounter = 0;
17582
+
17583
+ /**
17584
+ * The `auro-menuoption` element provides users a way to define a menu option.
17585
+ * @customElement auro-menuoption
17586
+ *
17587
+ * @slot default - The default slot for the menu option text.
17588
+ *
17589
+ * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
17590
+ */
17591
+ class AuroMenuOption extends AuroElement {
17689
17592
 
17690
17593
  /**
17691
17594
  * This will register this element with the browser.
17692
- * @param {string} [name="auro-menu"] - The name of the element that you want to register.
17595
+ * @param {string} [name="auro-menuoption"] - The name of the element that you want to register.
17693
17596
  *
17694
17597
  * @example
17695
- * AuroMenu.register("custom-menu") // this will register this element to <custom-menu/>
17598
+ * AuroMenuOption.register("custom-menuoption") // this will register this element to <custom-menuoption/>
17696
17599
  *
17697
17600
  */
17698
- static register(name = "auro-menu") {
17699
- AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroMenu);
17601
+ static register(name = "auro-menuoption") {
17602
+ AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroMenuOption);
17700
17603
  }
17701
17604
 
17702
17605
  /**
17703
- * Formatted value based on `multiSelect` state.
17704
- * Default type is `String`, changing to `Array<String>` when `multiSelect` is true.
17705
- * @private
17706
- * @returns {String|Array<String>}
17606
+ * Returns whether the menu option is currently active and selectable.
17607
+ * An option is considered active if it is not hidden, not disabled, and not static.
17608
+ * @returns {boolean} True if the option is active, false otherwise.
17707
17609
  */
17708
- get formattedValue() {
17709
- return this.menuService.currentValue;
17610
+ get isActive() {
17611
+ return !this.hasAttribute('hidden') &&
17612
+ !this.disabled &&
17613
+ !this.hasAttribute('static');
17710
17614
  }
17711
17615
 
17712
- /**
17713
- * Gets the current property values for the menu service.
17714
- * @private
17715
- * @returns {Object}
17716
- */
17717
- get propertyValues() {
17718
- return {
17719
- size: this.size,
17720
- shape: this.shape,
17721
- noCheckmark: this.nocheckmark,
17722
- disabled: this.disabled
17723
- };
17724
- }
17616
+ constructor() {
17617
+ super();
17725
17618
 
17726
- /**
17727
- * Provides the menu context to child components.
17728
- * Initializes the MenuService and subscribes to menu changes.
17729
- * @protected
17730
- */
17731
- provideContext() {
17732
- if (this.parentElement && this.parentElement.closest('auro-menu, [auro-menu]')) {
17733
- this.rootMenu = false;
17734
- this.menuService = this.parentElement.menuService;
17735
- this._contextProvider = this.parentElement._contextProvider;
17736
- return;
17737
- }
17619
+ this.bindEvents();
17738
17620
 
17739
- this.menuService = new MenuService({host: this});
17740
- this.menuService.setProperties(this.propertyValues);
17741
- this.menuService.subscribe(this.handleMenuChange.bind(this));
17742
- this._contextProvider = new i(this, {
17743
- context: MenuContext,
17744
- initialValue: this.menuService
17745
- });
17746
- }
17621
+ /**
17622
+ * @private
17623
+ */
17624
+ this.shape = undefined;
17747
17625
 
17748
- /**
17749
- * Updates the currently active option in the menu.
17750
- * @param {HTMLElement} option - The option to set as active.
17751
- */
17752
- updateActiveOption(option) {
17753
- this.menuService.setHighlightedOption(option);
17754
- }
17626
+ /**
17627
+ * @private
17628
+ */
17629
+ this.size = undefined;
17755
17630
 
17756
- /**
17757
- * Sets the internal value and manages update state.
17758
- * @param {String|Array<String>} value - The value to set.
17759
- * @protected
17760
- */
17761
- setInternalValue(value) {
17762
- if (this.value !== value) {
17763
- this.internalUpdateInProgress = true;
17764
- this.value = value;
17631
+ /**
17632
+ * Generate unique names for dependency components.
17633
+ */
17634
+ const versioning = new AuroDependencyVersioning$3();
17635
+ this.iconTag = versioning.generateTag('auro-formkit-menuoption-icon', iconVersion, k);
17765
17636
 
17766
- setTimeout(() => {
17767
- this.internalUpdateInProgress = false;
17768
- });
17769
- }
17637
+ this.selected = false;
17638
+ this.noCheckmark = false;
17639
+ this.disabled = false;
17640
+ this.noMatch = false;
17641
+
17642
+ /**
17643
+ * @private
17644
+ */
17645
+ this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
17646
+
17647
+ // Initialize context-related properties
17648
+ this.menuService = null;
17649
+ this.unsubscribe = null;
17650
+
17651
+ /**
17652
+ * @private
17653
+ */
17654
+ this.handleMenuChange = this.handleMenuChange.bind(this);
17770
17655
  }
17771
17656
 
17772
- /**
17773
- * Handles changes from the menu service and updates component state.
17774
- * @param {Object} event - The event object from the menu service.
17775
- * @protected
17776
- */
17777
- handleMenuChange(event) {
17778
- if (event.type === 'valueChange') {
17657
+ static get properties() {
17658
+ return {
17659
+ ...super.properties,
17660
+
17661
+ /**
17662
+ * When true, disables the menu option.
17663
+ */
17664
+ disabled: {
17665
+ type: Boolean,
17666
+ reflect: true
17667
+ },
17668
+
17669
+ /**
17670
+ * @private
17671
+ */
17672
+ event: {
17673
+ type: String,
17674
+ reflect: true
17675
+ },
17676
+
17677
+ /**
17678
+ * @private
17679
+ */
17680
+ layout: {
17681
+ type: String
17682
+ },
17683
+
17684
+ /**
17685
+ * Allows users to set a unique key for the menu option for specified option selection. If no key is provided, the value property will be used.
17686
+ */
17687
+ key: {
17688
+ type: String,
17689
+ reflect: true
17690
+ },
17691
+
17692
+ /**
17693
+ * @private
17694
+ */
17695
+ menuService: {
17696
+ type: Object,
17697
+ state: true
17698
+ },
17779
17699
 
17780
- // New option is array value or first option with fallback to undefined for empty array in all cases
17781
- const newOption = this.multiSelect && event.options.length ? event.options : event.options[0] || undefined;
17782
- const newValue = event.stringValue;
17700
+ /**
17701
+ * @private
17702
+ */
17703
+ matchWord: {
17704
+ type: String,
17705
+ state: true
17706
+ },
17783
17707
 
17784
- // Check if the option or value has actually changed
17785
- if (this.optionSelected !== newOption || this.stringValue !== newValue) {
17786
- this.optionSelected = newOption;
17787
- this.setInternalValue(newValue);
17788
- }
17708
+ /**
17709
+ * @private
17710
+ */
17711
+ noCheckmark: {
17712
+ type: Boolean,
17713
+ reflect: true
17714
+ },
17789
17715
 
17790
- // Notify components of selection change
17791
- this.notifySelectionChange(event);
17792
- }
17716
+ /**
17717
+ * When true, marks this option as the "no matching results" placeholder shown by combobox when the user's input does not match any available options. Enables distinct styling and prevents the option from being treated as a selectable match.
17718
+ */
17719
+ noMatch: {
17720
+ type: Boolean,
17721
+ reflect: true,
17722
+ attribute: 'nomatch'
17723
+ },
17793
17724
 
17794
- if (event.type === 'highlightChange') {
17795
- this.optionActive = event.option;
17796
- this._index = event.index;
17797
- }
17725
+ /**
17726
+ * Specifies that an option is selected.
17727
+ */
17728
+ selected: {
17729
+ type: Boolean,
17730
+ reflect: true
17731
+ },
17798
17732
 
17799
- if (event.type === 'optionsChange') {
17800
- this.options = event.options;
17801
- this.dispatchEvent(new CustomEvent('auroMenu-optionsChange', {
17802
- detail: {
17803
- options: event.options
17804
- }
17805
- }));
17806
- }
17807
- }
17733
+ /**
17734
+ * Specifies the tab index of the menu option.
17735
+ */
17736
+ tabIndex: {
17737
+ type: Number,
17738
+ reflect: true
17739
+ },
17808
17740
 
17809
- /**
17810
- * Gets the currently selected options.
17811
- * @returns {Array<HTMLElement>}
17812
- */
17813
- get selectedOptions() {
17814
- return this.menuService ? this.menuService.selectedOptions : [];
17741
+ /**
17742
+ * Specifies the value to be sent to a server.
17743
+ */
17744
+ value: {
17745
+ type: String,
17746
+ reflect: true
17747
+ },
17748
+ };
17815
17749
  }
17816
17750
 
17817
- /**
17818
- * Gets the first selected option, or null if none.
17819
- * @returns {HTMLElement|null}
17820
- */
17821
- get selectedOption() {
17822
- return this.menuService ? this.menuService.selectedOptions[0] : null;
17751
+ static get styles() {
17752
+ return [
17753
+ styleCss,
17754
+ colorCss,
17755
+ tokensCss
17756
+ ];
17823
17757
  }
17824
17758
 
17825
- // Lifecycle Methods
17826
-
17827
17759
  connectedCallback() {
17828
17760
  super.connectedCallback();
17829
17761
 
17830
- this.provideContext();
17831
-
17832
- // this.addEventListener('keydown', this.handleKeyDown);
17833
- this.addEventListener('auroMenuOption-click', this.handleMouseSelect);
17834
- this.addEventListener('auroMenuOption-mouseover', this.handleOptionHover);
17835
- this.addEventListener('slotchange', this.handleSlotChange);
17836
- this.setTagAttribute("auro-menu");
17837
- }
17762
+ // Add the tag name as an attribute if it is different than the component name
17763
+ // Add this step soon as this node gets attached to the DOM to avoid racing condition with menu's value setting logic.
17764
+ this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
17838
17765
 
17839
- disconnectedCallback() {
17840
- // this.removeEventListener('keydown', this.handleKeyDown);
17841
- this.removeEventListener('auroMenuOption-click', this.handleMouseSelect);
17842
- this.removeEventListener('auroMenuOption-mouseover', this.handleOptionHover);
17843
- this.removeEventListener('slotchange', this.handleSlotChange);
17766
+ // Set up context consumption in connectedCallback
17767
+ this._contextConsumer = new s$1(this, {
17768
+ context: MenuContext,
17769
+ callback: this.attachTo.bind(this),
17770
+ subscribe: true
17771
+ });
17844
17772
 
17845
- super.disconnectedCallback();
17773
+ // Establish the key property as early as possible.
17774
+ // When a framework (e.g. Svelte) inserts the element into the DOM before
17775
+ // setting its `value` property, both `getAttribute('value')` and
17776
+ // `getAttribute('key')` return null here. Setting `this.key = null`
17777
+ // would block the fallback in `updated()` that assigns key from the
17778
+ // value property (the guard checked `=== undefined`). Only assign key
17779
+ // if at least one source attribute is actually present so that the
17780
+ // `updated()` fallback can run when the value property arrives later.
17781
+ const valueAttr = this.getAttribute('value');
17782
+ const keyAttr = this.getAttribute('key');
17783
+ const resolvedKey = keyAttr !== null ? keyAttr : valueAttr;
17784
+ if (resolvedKey !== null) {
17785
+ this.key = resolvedKey;
17786
+ }
17846
17787
  }
17847
17788
 
17848
17789
  firstUpdated() {
17849
- AuroLibraryRuntimeUtils$4.prototype.handleComponentTagRename(this, 'auro-menu');
17790
+ // Add the tag name as an attribute if it is different than the component name
17791
+ this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
17850
17792
 
17851
- this.loadingSlots = this.querySelectorAll("[slot='loadingText'], [slot='loadingIcon']");
17852
- this.initializeMenu();
17853
- }
17793
+ // Generate unique ID if not already set (required for aria-activedescendant)
17794
+ if (!this.id) {
17795
+ menuOptionIdCounter += 1;
17796
+ this.id = `menuoption-${menuOptionIdCounter}`;
17797
+ }
17798
+
17799
+ this.setAttribute('role', 'option');
17800
+ this.setAttribute('aria-selected', 'false');
17854
17801
 
17802
+ this.addEventListener('mouseover', () => {
17803
+ this.dispatchEvent(new CustomEvent('auroMenuOption-mouseover', {
17804
+ bubbles: true,
17805
+ cancelable: false,
17806
+ composed: true,
17807
+ detail: this
17808
+ }));
17809
+ });
17810
+ }
17855
17811
 
17856
17812
  updated(changedProperties) {
17857
17813
  super.updated(changedProperties);
17858
17814
 
17859
- // Apply value selection synchronously so that static-HTML fixtures
17860
- // resolve within a single update cycle. The refactored selectByValue
17861
- // no longer calls reset() first, so the destructive intermediate-event
17862
- // cascade that originally required deferral is eliminated. If option
17863
- // keys are not yet resolved (framework mount-order race), selectByValue
17864
- // queues a bounded retry automatically via queuePendingValue.
17865
- if (changedProperties.has('value') && !this.internalUpdateInProgress) {
17866
- this.menuService.selectByValue(this.value);
17867
- }
17815
+ // Update aria-selected attribute if selected changed
17816
+ if (changedProperties.has('selected')) {
17868
17817
 
17869
- // Handle loading state changes
17870
- if (changedProperties.has('loading')) {
17871
- this.setLoadingState(this.loading);
17818
+ // Update aria-selected attribute
17819
+ this.setAttribute('aria-selected', this.selected.toString());
17820
+
17821
+ // Update menu service selection state if this isn't an internal update
17822
+ if (this.internalUpdateInProgress !== true && this.menuService) {
17823
+ this.menuService[this.selected ? 'selectOption' : 'deselectOption'](this);
17824
+ }
17872
17825
  }
17873
17826
 
17874
- if (changedProperties.has('multiSelect') && this.rootMenu) {
17875
- if (this.multiSelect) {
17876
- this.setAttribute('aria-multiselectable', 'true');
17827
+ if (changedProperties.has('disabled')) {
17828
+ if (this.disabled) {
17829
+ this.setAttribute('aria-disabled', 'true');
17877
17830
  } else {
17878
- this.removeAttribute('aria-multiselectable');
17831
+ this.removeAttribute('aria-disabled');
17879
17832
  }
17880
17833
  }
17834
+
17835
+ if (changedProperties.has('active')) {
17836
+ this.updateActiveClasses();
17837
+ }
17838
+
17839
+ // Update text highlight if matchWord changed
17840
+ if (changedProperties.has('matchWord')) {
17841
+ this.updateTextHighlight();
17842
+ }
17843
+
17844
+ // Set the key to be the passed value if no key is provided.
17845
+ // Loose equality (== null) is intentional: it catches both null AND
17846
+ // undefined. When a framework (e.g. Svelte, React) inserts the element
17847
+ // before setting its value property, connectedCallback skips key
17848
+ // assignment because both attributes are null at that point. The Lit
17849
+ // property default for `key` is undefined (not null), so strict
17850
+ // === null would miss the case and the fallback would never run.
17851
+ if (changedProperties.has('value') && this.key == null) { // eslint-disable-line eqeqeq, no-eq-null
17852
+ this.key = this.value;
17853
+ }
17854
+ }
17855
+
17856
+ disconnectedCallback() {
17857
+ if (this.menuService) {
17858
+ this.menuService.unsubscribe(this.handleMenuChange);
17859
+ this.menuService.removeMenuOption(this);
17860
+ }
17881
17861
  }
17882
17862
 
17883
17863
  /**
17884
- * Sets an attribute that matches the default tag name if the tag name is not the default.
17885
- * @param {string} tagName - The tag name to set as an attribute.
17886
- * @private
17864
+ * Sets up event listeners for user interaction with the menu option.
17865
+ * This function enables click and mouse enter events to trigger selection and highlighting logic.
17887
17866
  */
17888
- setTagAttribute(tagName) {
17889
- if (this.tagName.toLowerCase() !== tagName) {
17890
- this.setAttribute(tagName, true);
17891
- }
17867
+ bindEvents() {
17868
+ this.addEventListener('click', this.handleClick.bind(this));
17869
+ this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
17892
17870
  }
17893
17871
 
17894
17872
  /**
17895
- * Sets the loading state and dispatches a loading change event.
17896
- * @param {boolean} isLoading - Whether the menu is loading.
17897
- * @protected
17873
+ * Attaches this menu option to a menu service and subscribes to its events.
17874
+ * This method enables the option to participate in menu selection and highlighting logic.
17875
+ * @param {Object} service - The menu service instance to attach to.
17898
17876
  */
17899
- setLoadingState(isLoading) {
17900
- this.setAttribute("aria-busy", isLoading);
17901
- dispatchMenuEvent(this, "auroMenu-loadingChange", {
17902
- loading: isLoading,
17903
- hasLoadingPlaceholder: this.hasLoadingPlaceholder
17904
- });
17877
+ attachTo(service) {
17878
+ if (!service) {
17879
+ return;
17880
+ }
17881
+ this.menuService = service;
17882
+ this.menuService.addMenuOption(this);
17883
+ this.menuService.subscribe(this.handleMenuChange);
17905
17884
  }
17906
17885
 
17907
- // Init Methods
17886
+ /**
17887
+ * Handles changes from the menu service and updates the option's state.
17888
+ * This function synchronizes the option's properties and selection/highlight state with menu events.
17889
+ * @param {Object} event - The event object from the menu service.
17890
+ */
17891
+ handleMenuChange(event) {
17892
+
17893
+ // Ignore events without a type or property
17894
+ if (!event || (!event.type && !event.property)) {
17895
+ return;
17896
+ }
17897
+
17898
+ // Update reactive properties based on event type
17899
+ if (event.property && Object.keys(AuroMenuOption.properties).includes(event.property)) {
17900
+ this[event.property] = event.value;
17901
+ }
17902
+
17903
+ // Handle highlight changes
17904
+ if (event.type === 'highlightChange') {
17905
+ const isActive = event.option === this;
17906
+ this.active = isActive;
17907
+ this.updateActiveClasses();
17908
+ }
17909
+
17910
+ if (event.type === 'stateChange') {
17911
+ const isSelected = event.selectedOptions.includes(this);
17912
+ this.setInternalSelected(isSelected);
17913
+ }
17914
+ }
17908
17915
 
17909
17916
  /**
17910
- * Initializes the menu's state and structure.
17911
- * @private
17917
+ * Updates the internal selected state of the menu option bypassing 'updated' and triggers custom events if selected.
17918
+ * This function ensures the option's selection state is synchronized with menu logic and notifies listeners.
17919
+ * @param {boolean} isSelected - Whether the option should be marked as selected.
17912
17920
  */
17913
- initializeMenu() {
17914
- if (this.rootMenu) {
17915
- this.setAttribute('role', 'listbox');
17916
- this.setAttribute('root', '');
17921
+ setInternalSelected(isSelected) {
17922
+ this.internalUpdateInProgress = true;
17923
+ this.selected = isSelected;
17917
17924
 
17918
- if (this.multiSelect) {
17919
- this.setAttribute('aria-multiselectable', 'true');
17920
- }
17925
+ // Fire custom event if selected
17926
+ if (isSelected) {
17927
+ this.handleCustomEvent();
17921
17928
  }
17922
17929
 
17923
- this.handleNestedMenus(this);
17930
+ setTimeout(() => {
17931
+ this.internalUpdateInProgress = false;
17932
+ }, 0);
17924
17933
  }
17925
17934
 
17926
17935
  /**
17927
- * Selects the currently highlighted option.
17928
- * @protected
17936
+ * Sets the selected state of the menu option.
17937
+ * This function updates whether the option is currently selected.
17938
+ * @param {boolean} isSelected - Whether the option should be marked as selected.
17939
+ * @deprecated Simply modify the `selected` property directly instead.
17929
17940
  */
17930
- makeSelection() {
17931
- this.menuService.selectHighlightedOption();
17941
+ setSelected(isSelected) {
17942
+ this.selected = isSelected;
17932
17943
  }
17933
17944
 
17934
17945
  /**
17935
- * Resets all options to their default state.
17936
- * @private
17946
+ * Updates the active state and visual highlighting of the menu option.
17947
+ * This function toggles the option's active status and applies or removes the active CSS class.
17948
+ * @param {boolean} isActive - Whether the option should be marked as active.
17949
+ * @deprecated Simply modify the `active` property directly instead.
17937
17950
  */
17938
- clearSelection() {
17939
- this.optionSelected = undefined;
17940
- this.value = undefined;
17941
- this._index = -1;
17951
+ updateActive(isActive) {
17952
+
17953
+ // Set active state
17954
+ this.active = isActive;
17955
+ this.updateActiveClasses();
17942
17956
  }
17943
17957
 
17944
17958
  /**
17945
- * Resets the menu to its initial state.
17946
- * This is the only way to return value to undefined.
17947
- * @public
17959
+ * Updates the CSS class for the menu option based on its active state.
17960
+ * This function adds or removes the 'active' class to visually indicate the option's active status.
17961
+ * @private
17948
17962
  */
17949
- reset() {
17950
- this.menuService.reset();
17951
-
17952
- // Dispatch reset event
17953
- dispatchMenuEvent(this, 'auroMenu-selectValueReset');
17963
+ updateActiveClasses() {
17964
+ // Update class based on active state
17965
+ if (this.active) this.classList.add('active');
17966
+ else this.classList.remove('active');
17954
17967
  }
17955
17968
 
17969
+
17956
17970
  /**
17957
- * Handles nested menu structure.
17971
+ * Updates the visual highlighting of text within the menu option based on the current match word.
17972
+ * This function highlights matching text segments and manages nested spacers for display formatting.
17958
17973
  * @private
17959
- * @param {HTMLElement} menu - Root menu element.
17960
17974
  */
17961
- handleNestedMenus(menu) {
17962
- menu.level = menu.parentElement.level >= 0 ? menu.parentElement.level + 1 : 0;
17975
+ updateTextHighlight() {
17963
17976
 
17964
- if (menu.level > 0) {
17965
- menu.setAttribute('role', 'group');
17966
- menu.removeAttribute("root");
17967
- if (!menu.hasAttribute('aria-label')) {
17968
- menu.setAttribute('aria-label', 'submenu');
17969
- }
17977
+ // Regex for matchWord if needed
17978
+ let regexWord = null;
17979
+
17980
+ if (this.matchWord && this.matchWord.length) {
17981
+ const escapedWord = this.matchWord.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
17982
+ regexWord = new RegExp(escapedWord, 'giu');
17970
17983
  }
17971
17984
 
17972
- const options = menu.querySelectorAll(':scope > auro-menuoption, :scope > [auro-menuoption]');
17973
- options.forEach((option) => {
17974
- const regex = new RegExp(this.nestingSpacer, "gu");
17975
- option.innerHTML = this.nestingSpacer.repeat(menu.level) + option.innerHTML.replace(regex, '');
17976
- });
17977
- }
17985
+ // Update text highlighting if matchWord changed
17986
+ if (regexWord &&
17987
+ this.isActive && !this.hasAttribute('persistent')) {
17988
+ const nested = this.querySelectorAll('.nestingSpacer');
17978
17989
 
17979
- /**
17980
- * Navigates the menu options in the specified direction.
17981
- * @param {'up'|'down'} direction - The direction to navigate.
17982
- * @protected
17983
- */
17984
- navigateOptions(direction) {
17985
- if (direction === 'up') {
17986
- this.menuService.highlightPrevious();
17987
- } else if (direction === 'down') {
17988
- this.menuService.highlightNext();
17989
- }
17990
- }
17990
+ const displayValueEl = this.querySelector('[slot="displayValue"]');
17991
+ if (displayValueEl) {
17992
+ this.removeChild(displayValueEl);
17993
+ }
17991
17994
 
17992
- /**
17993
- * Handles slot change events.
17994
- * @private
17995
- */
17996
- handleSlotChange() {
17997
- if (this.rootMenu) {
17998
- this.initializeMenu();
17995
+ // Create nested spacers
17996
+ const nestingSpacerBundle = [...nested].map(() => this.nestingSpacer).join('');
17997
+
17998
+ // Update with spacers and matchWord
17999
+ this.innerHTML = nestingSpacerBundle +
18000
+ this.textContent.replace(
18001
+ regexWord,
18002
+ (match) => `<strong>${match}</strong>`
18003
+ );
18004
+ if (displayValueEl) {
18005
+ this.append(displayValueEl);
18006
+ }
17999
18007
  }
18000
18008
  }
18001
18009
 
18002
18010
  /**
18003
- * Handles custom events defined on options.
18011
+ * Handles click events on the menu option, toggling its selected state.
18012
+ * This function dispatches a click event and updates selection if the option is not disabled.
18004
18013
  * @private
18005
- * @param {HTMLElement} option - Option with custom event.
18006
18014
  */
18007
- handleCustomEvent(option) {
18008
- const eventName = option.getAttribute('event');
18009
- dispatchMenuEvent(this, eventName);
18010
- dispatchMenuEvent(this, 'auroMenu-customEventFired');
18015
+ handleClick() {
18016
+ if (!this.disabled && !this.menuService?.disabled) {
18017
+ this.dispatchClickEvent();
18018
+ this.selected = !this.selected;
18019
+ }
18011
18020
  }
18012
18021
 
18013
18022
  /**
18014
- * Notifies selection change to parent components.
18015
- * @param {any} source - The source that triggers this event.
18023
+ * Handles mouse enter events to highlight the menu option.
18024
+ * This function updates the menu service to set this option as the currently highlighted item if not disabled.
18016
18025
  * @private
18017
18026
  */
18018
- notifySelectionChange({value, stringValue, keys, options, reason} = {}) {
18019
- dispatchMenuEvent(this, 'auroMenu-selectedOption', {
18020
- value,
18021
- stringValue,
18022
- keys,
18023
- options,
18024
- reason
18025
- });
18027
+ handleMouseEnter() {
18028
+ if (!this.disabled) {
18029
+ this.menuService.setHighlightedOption(this);
18030
+ }
18026
18031
  }
18027
18032
 
18028
18033
  /**
18029
- * Checks if an option is currently selected.
18034
+ * Dispatches custom events defined for this menu option.
18035
+ * This function notifies listeners when a custom event is triggered by the option.
18030
18036
  * @private
18031
- * @param {HTMLElement} option - The option to check.
18032
- * @returns {boolean}
18033
18037
  */
18034
- isOptionSelected(option) {
18035
- if (!this.optionSelected) {
18036
- return false;
18037
- }
18038
-
18039
- if (this.multiSelect) {
18040
- // In multi-select mode, check if the option is in the selected array
18041
- return Array.isArray(this.optionSelected) && this.optionSelected.some((selectedOption) => selectedOption === option);
18038
+ handleCustomEvent() {
18039
+ if (this.event) {
18040
+ dispatchMenuEvent(this, this.event, { option: this });
18041
+ dispatchMenuEvent(this, 'auroMenu-customEventFired', { option: this });
18042
18042
  }
18043
-
18044
- return this.optionSelected === option;
18045
18043
  }
18046
18044
 
18047
18045
  /**
18048
- * Getter for loading placeholder state.
18049
- * @returns {boolean} - True if loading slots are present and non-empty.
18046
+ * Dispatches a click event for this menu option.
18047
+ * This function notifies listeners that the option has been clicked.
18048
+ * @private
18050
18049
  */
18051
- get hasLoadingPlaceholder() {
18052
- return this.loadingSlots && this.loadingSlots.length > 0;
18050
+ dispatchClickEvent() {
18051
+ this.dispatchEvent(new CustomEvent('auroMenuOption-click', {
18052
+ bubbles: true,
18053
+ cancelable: false,
18054
+ composed: true,
18055
+ detail: this
18056
+ }));
18053
18057
  }
18054
18058
 
18055
18059
  /**
18056
- * Getter for wrapper classes based on size.
18057
- * @returns {Object} - Class map for the wrapper element.
18060
+ * Generates an HTML element containing an SVG icon based on the provided `svgContent`.
18061
+ *
18058
18062
  * @private
18063
+ * @param {string} svgContent - The SVG content to be embedded.
18064
+ * @returns {Element} The HTML element containing the SVG icon.
18059
18065
  */
18060
- get wrapperClasses() {
18061
- return e$3({
18062
- 'menuWrapper': true,
18063
- [this.size]: true,
18064
- });
18066
+ generateIconHtml(svgContent) {
18067
+ const dom = new DOMParser().parseFromString(svgContent, 'text/html');
18068
+ const svg = dom.body.firstChild;
18069
+
18070
+ svg.setAttribute('slot', 'svg');
18071
+
18072
+ return u$7`<${this.iconTag} customColor customSvg>${svg}</${this.iconTag}>`;
18065
18073
  }
18066
18074
 
18067
18075
  /**
@@ -18070,26 +18078,26 @@ class AuroMenu extends AuroElement {
18070
18078
  * @returns {void}
18071
18079
  */
18072
18080
  renderLayout() {
18073
- if (this.loading) {
18074
- return b$2`
18075
- <div class="${this.wrapperClasses}">
18076
- <auro-menuoption
18077
- disabled
18078
- loadingplaceholder
18079
- class="${this.hasLoadingPlaceholder ? "" : "empty"}"
18080
- >
18081
- <div>
18082
- <slot name="loadingIcon" class="body-lg"></slot>
18083
- <slot name="loadingText"></slot>
18084
- </div>
18085
- </auro-menuoption>
18086
- </div>
18087
- `;
18088
- }
18089
18081
 
18090
- return b$2`
18091
- <div class="${this.wrapperClasses}">
18092
- <slot @slotchange=${this.handleSlotChange}></slot>
18082
+ const fontClassMap = {
18083
+ xs: 'body-sm',
18084
+ sm: 'body-default',
18085
+ md: 'body-default',
18086
+ lg: 'body-lg',
18087
+ xl: 'body-lg'
18088
+ };
18089
+
18090
+ const classes = e$3({
18091
+ 'wrapper': true,
18092
+ [this.size ? fontClassMap[this.size] : 'body-sm']: true,
18093
+ });
18094
+
18095
+ return u$7`
18096
+ <div class="${classes}">
18097
+ ${this.selected && !this.noCheckmark
18098
+ ? this.generateIconHtml(checkmarkIcon.svg)
18099
+ : undefined}
18100
+ <slot></slot>
18093
18101
  </div>
18094
18102
  `;
18095
18103
  }