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