@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
@@ -167,1533 +167,1674 @@ let s$1 = class s{get value(){return this.o}set value(s){this.setValue(s);}setVa
167
167
  * SPDX-License-Identifier: BSD-3-Clause
168
168
  */let e$2 = class e extends Event{constructor(t,s){super("context-provider",{bubbles:true,composed:true}),this.context=t,this.contextTarget=s;}};let i$2 = class i extends s$1{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$3(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$2(this.context,this.host));}};
169
169
 
170
- /**
171
- * @license
172
- * Copyright 2020 Google LLC
173
- * SPDX-License-Identifier: BSD-3-Clause
174
- */
175
- const a=Symbol.for(""),o$1=t=>{if(t?.r===a)return t?._$litStatic$},s=t=>({_$litStatic$:t,r:a}),i$1=(t,...r)=>({_$litStatic$:r.reduce((r,e,a)=>r+(t=>{if(void 0!==t._$litStatic$)return t._$litStatic$;throw Error(`Value passed to 'literal' function must be a 'literal' result: ${t}. Use 'unsafeStatic' to pass non-literal values, but\n take care to ensure page security.`)})(e)+t[a+1],t[0]),r:a}),l=new Map,n=t=>(r,...e)=>{const a=e.length;let s,i;const n=[],u=[];let c,$=0,f=false;for(;$<a;){for(c=r[$];$<a&&void 0!==(i=e[$],s=o$1(i));)c+=s+r[++$],f=true;$!==a&&u.push(i),n.push(c),$++;}if($===a&&n.push(r[a]),f){const t=n.join("$$lit$$");void 0===(r=l.get(t))&&(n.raw=n,l.set(t,r=n)),e=u;}return t(r,...e)},u$1=n(b);
176
-
177
- var styleCss = i$6`.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}`;
178
-
179
- var colorCss = i$6`: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)}}`;
180
-
181
- // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
182
- // See LICENSE in the project root for license information.
170
+ /* eslint-disable */
183
171
 
184
- // ---------------------------------------------------------------------
172
+ class MenuService {
185
173
 
186
- /* eslint-disable line-comment-position, no-inline-comments, no-confusing-arrow, no-nested-ternary, implicit-arrow-linebreak */
174
+ /**
175
+ * PROPERTIES AND GETTERS
176
+ */
187
177
 
188
- class AuroLibraryRuntimeUtils {
178
+ /**
179
+ * Gets the list of registered menu options.
180
+ * @returns {AuroMenuOption[]}
181
+ */
182
+ get menuOptions() {
183
+ return this._menuOptions;
184
+ }
189
185
 
190
- /* eslint-disable jsdoc/require-param */
186
+ /**
187
+ * Gets the currently highlighted option.
188
+ * @returns {AuroMenuOption|null}
189
+ */
190
+ get highlightedOption() {
191
+ return this._menuOptions[this.highlightedIndex] || null;
192
+ }
191
193
 
192
194
  /**
193
- * This will register a new custom element with the browser.
194
- * @param {String} name - The name of the custom element.
195
- * @param {Object} componentClass - The class to register as a custom element.
196
- * @returns {void}
195
+ * Gets the current value(s) of the selected option(s).
196
+ * @returns {string|string[]|undefined}
197
197
  */
198
- registerComponent(name, componentClass) {
199
- if (!customElements.get(name)) {
200
- customElements.define(name, class extends componentClass {});
201
- }
198
+ get currentValue() {
199
+ const values = (this.selectedOptions || []).map(option => option.value);
200
+ return this.multiSelect ? values : values[0];
202
201
  }
203
202
 
204
203
  /**
205
- * Finds and returns the closest HTML Element based on a selector.
206
- * @returns {void}
204
+ * Gets the label(s) of the currently selected option(s).
205
+ * @returns {string}
207
206
  */
208
- closestElement(
209
- selector, // selector like in .closest()
210
- base = this, // extra functionality to skip a parent
211
- __Closest = (el, found = el && el.closest(selector)) =>
212
- !el || el === document || el === window
213
- ? null // standard .closest() returns null for non-found selectors also
214
- : found
215
- ? found // found a selector INside this element
216
- : __Closest(el.getRootNode().host) // recursion!! break out to parent DOM
217
- ) {
218
- return __Closest(base);
207
+ get currentLabel() {
208
+ const labels = (this.selectedOptions || []).map(option => option.textContent);
209
+ return this.multiSelect ? labels.join(", ") : labels[0] || '';
219
210
  }
220
- /* eslint-enable jsdoc/require-param */
221
211
 
222
212
  /**
223
- * If the element passed is registered with a different tag name than what is passed in, the tag name is added as an attribute to the element.
224
- * @param {Object} elem - The element to check.
225
- * @param {String} tagName - The name of the Auro component to check for or add as an attribute.
226
- * @returns {void}
213
+ * Gets the string representation of the current value(s).
214
+ * For multi-select, this is a JSON stringified array.
215
+ * @returns {string|undefined}
227
216
  */
228
- handleComponentTagRename(elem, tagName) {
229
- const tag = tagName.toLowerCase();
230
- const elemTag = elem.tagName.toLowerCase();
217
+ get stringValue() {
218
+ const { currentValue } = this;
231
219
 
232
- if (elemTag !== tag) {
233
- elem.setAttribute(tag, true);
220
+ if (Array.isArray(currentValue)) {
221
+ if (currentValue.length > 0) {
222
+ return JSON.stringify(currentValue);
223
+ }
224
+ return undefined;
225
+ }
226
+
227
+ if (typeof currentValue === 'string') {
228
+ if (currentValue.length > 0) {
229
+ return currentValue;
230
+ }
231
+ return undefined;
234
232
  }
233
+
234
+ // Future: handle other types here (e.g., number, object, etc.)
235
+ return undefined;
235
236
  }
236
237
 
237
238
  /**
238
- * Validates if an element is a specific Auro component.
239
- * @param {Object} elem - The element to validate.
240
- * @param {String} tagName - The name of the Auro component to check against.
241
- * @returns {Boolean} - Returns true if the element is the specified Auro component.
239
+ * Gets the key(s) of the currently selected option(s).
240
+ * @returns {string|string[]|undefined}
242
241
  */
243
- elementMatch(elem, tagName) {
244
- const tag = tagName.toLowerCase();
245
- const elemTag = elem.tagName.toLowerCase();
246
-
247
- return elemTag === tag || elem.hasAttribute(tag);
242
+ get currentKeys() {
243
+ const keys = (this.selectedOptions || []).map(option => option.key);
244
+ return this.multiSelect ? keys : keys[0];
248
245
  }
249
246
 
250
247
  /**
251
- * Gets the text content of a named slot.
252
- * @returns {String}
253
- * @private
248
+ * CONSTRUCTOR
254
249
  */
255
- getSlotText(elem, name) {
256
- const slot = elem.shadowRoot?.querySelector(`slot[name="${name}"]`);
257
- const nodes = slot?.assignedNodes({ flatten: true }) || [];
258
- const text = nodes.map(n => n.textContent?.trim()).join(' ').trim();
259
250
 
260
- return text || null;
261
- }
262
- }
251
+ /**
252
+ * Creates a new MenuService instance.
253
+ * @param {Object} options - The options object.
254
+ * @param {AuroMenu} options.host - The host element that this service will control. Required.
255
+ * @throws {Error} If the host is not provided.
256
+ */
257
+ constructor({ host } = {}) {
263
258
 
264
- // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
265
- // See LICENSE in the project root for license information.
259
+ // Ensure a host was passed
260
+ if (!host) {
261
+ throw new Error("MenuService requires a host element.");
262
+ }
266
263
 
264
+ // Attach the service to the host
265
+ this.host = host;
266
+ this.host.addController(this);
267
267
 
268
- class AuroDependencyVersioning {
268
+ // Set default properties
269
+ this.size = undefined;
270
+ this.shape = undefined;
271
+ this.noCheckmark = undefined;
272
+ this.disabled = undefined;
273
+ this.matchWord = undefined;
274
+ this.multiSelect = undefined;
275
+ this.allowDeselect = undefined;
276
+ this.selectAllMatchingOptions = undefined;
277
+
278
+ this.highlightedIndex = -1;
279
+
280
+ this._menuOptions = [];
281
+ this._subscribers = [];
282
+ this.internalUpdateInProgress = false;
283
+ this.selectedOptions = [];
284
+ this._pendingValue = null;
285
+ this._pendingRetryScheduled = false;
286
+ this._pendingRetryCount = 0;
287
+ }
269
288
 
270
289
  /**
271
- * Generates a unique string to be used for child auro element naming.
272
- * @private
273
- * @param {string} baseName - Defines the first part of the unique element name.
274
- * @param {string} version - Version of the component that will be appended to the baseName.
275
- * @returns {string} - Unique string to be used for naming.
290
+ * PROPERTY SYNCING
276
291
  */
277
- generateElementName(baseName, version) {
278
- let result = baseName;
279
292
 
280
- result += '-';
281
- result += version.replace(/[.]/g, '_');
293
+ /**
294
+ * Handles host updates.
295
+ * This is a lit reactive lifecycle method.
296
+ * This comes from the Lit controller interface provided by adding this service as a controller to the host.
297
+ * See constructor for `this.host.addController(this)`
298
+ * You can read more about Lit reactive controllers here: https://lit.dev/docs/composition/controllers/
299
+ */
300
+ hostUpdated() {
282
301
 
283
- return result;
302
+ // Reset selection if multiSelect mode changes
303
+ if (this.host.multiSelect !== this.multiSelect) {
304
+ this.selectedOptions = [];
305
+ }
306
+
307
+ // Update properties on host update
308
+ this.setProperties({
309
+ size: this.host.size,
310
+ shape: this.host.shape,
311
+ noCheckmark: this.host.noCheckmark,
312
+ disabled: this.host.disabled,
313
+ matchWord: this.host.matchWord,
314
+ multiSelect: this.host.multiSelect,
315
+ allowDeselect: this.host.allowDeselect,
316
+ selectAllMatchingOptions: this.host.selectAllMatchingOptions
317
+ });
284
318
  }
285
319
 
286
320
  /**
287
- * Generates a unique string to be used for child auro element naming.
288
- * @param {string} baseName - Defines the first part of the unique element name.
289
- * @param {string} version - Version of the component that will be appended to the baseName.
290
- * @returns {string} - Unique string to be used for naming.
321
+ * Handles host disconnection and memory cleanup.
291
322
  */
292
- generateTag(baseName, version, tagClass) {
293
- const elementName = this.generateElementName(baseName, version);
294
- const tag = i$1`${s(elementName)}`;
323
+ hostDisconnected() {
324
+ this._subscribers = [];
325
+ this._menuOptions = [];
326
+ this._pendingValue = null;
327
+ this._pendingRetryScheduled = false;
328
+ this._pendingRetryCount = 0;
329
+ }
295
330
 
296
- if (!customElements.get(elementName)) {
297
- customElements.define(elementName, class extends tagClass {});
331
+ /**
332
+ * Sets a property value if it exists on the instance and the value has changed.
333
+ * @param {string} property
334
+ * @param {any} value
335
+ */
336
+ setProperty(property, value) {
337
+
338
+ // Only update if we are tracking the property in this service
339
+ if (this.hasOwnProperty(property)) {
340
+
341
+ // Check if the value has changed
342
+ const valueChanged = this[property] !== value;
343
+
344
+ // Update and notify if changed
345
+ if (valueChanged) {
346
+ this[property] = value;
347
+ this.notify({ property, value });
348
+ }
298
349
  }
350
+ }
299
351
 
300
- return tag;
352
+ /**
353
+ * Sets multiple properties on the instance.
354
+ * @param {Object} properties - Key-value pairs of properties to set.
355
+ */
356
+ setProperties(properties) {
357
+ for (const [key, value] of Object.entries(properties)) {
358
+ this.setProperty(key, value);
359
+ }
301
360
  }
302
- }
303
361
 
304
- /**
305
- * @license
306
- * Copyright 2017 Google LLC
307
- * SPDX-License-Identifier: BSD-3-Clause
308
- */
309
- const t={ATTRIBUTE:1},e$1=t=>(...e)=>({_$litDirective$:t,values:e});class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i;}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}
362
+ /**
363
+ * MENU OPTION HIGHLIGHTING
364
+ */
310
365
 
311
- /**
312
- * @license
313
- * Copyright 2018 Google LLC
314
- * SPDX-License-Identifier: BSD-3-Clause
315
- */const e=e$1(class extends i{constructor(t$1){if(super(t$1),t$1.type!==t.ATTRIBUTE||"class"!==t$1.name||t$1.strings?.length>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return " "+Object.keys(t).filter(s=>t[s]).join(" ")+" "}update(s,[i]){if(void 0===this.st){this.st=new Set,void 0!==s.strings&&(this.nt=new Set(s.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in i)i[t]&&!this.nt?.has(t)&&this.st.add(t);return this.render(i)}const r=s.element.classList;for(const t of this.st)t in i||(r.remove(t),this.st.delete(t));for(const t in i){const s=!!i[t];s===this.st.has(t)||this.nt?.has(t)||(s?(r.add(t),this.st.add(t)):(r.remove(t),this.st.delete(t)));}return E}});
366
+ /**
367
+ * Highlights the next active option in the menu.
368
+ */
369
+ highlightNext() {
370
+ this.moveHighlightedOption("next");
371
+ }
316
372
 
317
- /**
318
- * @license
319
- * Copyright 2018 Google LLC
320
- * SPDX-License-Identifier: BSD-3-Clause
321
- */const o=o=>o??A;
322
-
323
- 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$3{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$6`: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}
324
- `;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$6`.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}
325
- `;var M=i$6`: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)}
326
- `;var _=i$6`: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)}
327
- `;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`
328
- <div class="componentWrapper">
329
- <div
330
- class="${e({svgWrapper:true})}"
331
- title="${o(this.title||void 0)}">
332
- <span aria-hidden="${o(this.ariaHidden||true)}" part="svg">
333
- ${this.customSvg?b`
334
- <slot name="svg"></slot>
335
- `:b`
336
- ${this.svg}
337
- `}
338
- </span>
339
- </div>
340
-
341
- <div class="${e(t)}" part="label">
342
- <slot></slot>
343
- </div>
344
- </div>
345
- `}}
373
+ /**
374
+ * Highlights the previous active option in the menu.
375
+ */
376
+ highlightPrevious() {
377
+ this.moveHighlightedOption("previous");
378
+ }
346
379
 
347
- var iconVersion = '9.1.2';
380
+ /**
381
+ * Moves the highlighted option in the specified direction.
382
+ * @param {string} direction - The direction to move the highlight ("next" or "previous").
383
+ */
384
+ moveHighlightedOption(direction) {
348
385
 
349
- 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>"};
386
+ // Get the active options
387
+ const activeOptions = this._menuOptions.filter(option => option.isActive);
350
388
 
351
- // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
352
- // See LICENSE in the project root for license information.
389
+ // Get the currently active option
390
+ const currentActiveOption = activeOptions[activeOptions.indexOf(this.highlightedOption)];
353
391
 
392
+ // Determine the new index based on the currently active option and direction
393
+ let newIndex = currentActiveOption
394
+ ? direction === "previous"
395
+ ? activeOptions.indexOf(currentActiveOption) - 1
396
+ : activeOptions.indexOf(currentActiveOption) + 1
397
+ : direction === "previous"
398
+ ? activeOptions.length - 1
399
+ : 0;
354
400
 
355
- /**
356
- * Helper method to dispatch custom events.
357
- * @param {HTMLElement} element - Element to dispatch event from.
358
- * @param {string} eventName - Name of the event to dispatch.
359
- * @param {Object} [detail] - Optional detail object to include with the event.
360
- */
361
- function dispatchMenuEvent(element, eventName, detail = null) {
362
- const eventConfig = {
363
- bubbles: true,
364
- cancelable: false,
365
- composed: true
366
- };
401
+ // Wrap around the index if needed
402
+ newIndex = newIndex < 0 ? activeOptions.length - 1 : newIndex >= activeOptions.length ? 0 : newIndex;
367
403
 
368
- if (detail !== null) {
369
- eventConfig.detail = detail;
404
+ // Get the new active option and set it as highlighted
405
+ const newActiveOption = activeOptions[newIndex];
406
+ this.setHighlightedOption(newActiveOption);
370
407
  }
371
408
 
372
- element.dispatchEvent(new CustomEvent(eventName, eventConfig));
373
- }
409
+ /**
410
+ * Sets the highlighted index to the specified option.
411
+ * @param {AuroMenuOption} option - The option to highlight.
412
+ */
413
+ setHighlightedOption(option) {
374
414
 
375
- // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
376
- // See LICENSE in the project root for license information.
415
+ if (!option) return;
377
416
 
417
+ // Get the index of the option to highlight
418
+ const index = this._menuOptions.indexOf(option);
378
419
 
379
- let menuOptionIdCounter = 0;
420
+ // Update highlighted index
421
+ this.highlightedIndex = index;
380
422
 
381
- /**
382
- * The `auro-menuoption` element provides users a way to define a menu option.
383
- * @customElement auro-menuoption
384
- *
385
- * @slot default - The default slot for the menu option text.
386
- *
387
- * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
388
- */
389
- class AuroMenuOption extends AuroElement {
423
+ // Notify subscribers of highlight change
424
+ this.notify({ type: 'highlightChange', option, index: this.highlightedIndex });
425
+
426
+ // Dispatch the change event
427
+ this.dispatchChangeEvent('auroMenu-activatedOption', option);
428
+ }
390
429
 
391
430
  /**
392
- * This will register this element with the browser.
393
- * @param {string} [name="auro-menuoption"] - The name of the element that you want to register.
394
- *
395
- * @example
396
- * AuroMenuOption.register("custom-menuoption") // this will register this element to <custom-menuoption/>
397
- *
431
+ * Sets the highlighted option to the option at the specified index if it exists.
432
+ * @param {number} index
398
433
  */
399
- static register(name = "auro-menuoption") {
400
- AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroMenuOption);
434
+ setHighlightedIndex(index) {
435
+ const option = this._menuOptions[index] || null;
436
+ this.setHighlightedOption(option);
401
437
  }
402
438
 
403
439
  /**
404
- * Returns whether the menu option is currently active and selectable.
405
- * An option is considered active if it is not hidden, not disabled, and not static.
406
- * @returns {boolean} True if the option is active, false otherwise.
440
+ * Selects the currently highlighted option.
407
441
  */
408
- get isActive() {
409
- return !this.hasAttribute('hidden') &&
410
- !this.disabled &&
411
- !this.hasAttribute('static');
442
+ selectHighlightedOption() {
443
+ if (this.highlightedOption) {
444
+ this.toggleOption(this.highlightedOption);
445
+ }
412
446
  }
413
447
 
414
- constructor() {
415
- super();
448
+ /**
449
+ * SELECTION AND DESELECTION METHODS
450
+ */
416
451
 
417
- this.bindEvents();
452
+ /**
453
+ * Selects one or more options in a batch operation
454
+ * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to select
455
+ */
456
+ selectOptions(options) {
457
+ let optionsToSelect = Array.isArray(options) ? options : [options];
418
458
 
419
- /**
420
- * @private
421
- */
422
- this.shape = undefined;
459
+ // Filter out options that are inactive
460
+ optionsToSelect = optionsToSelect.filter(option => option.isActive);
423
461
 
424
- /**
425
- * @private
426
- */
427
- this.size = undefined;
462
+ if (!optionsToSelect.length) return;
428
463
 
429
- /**
430
- * Generate unique names for dependency components.
431
- */
432
- const versioning = new AuroDependencyVersioning();
433
- this.iconTag = versioning.generateTag('auro-formkit-menuoption-icon', iconVersion, k);
464
+ if (this.multiSelect) {
465
+ this.selectedOptions = [...(this.selectedOptions || []), ...optionsToSelect];
466
+ } else {
467
+ // In single select mode, only take the last option
468
+ this.selectedOptions = [optionsToSelect[optionsToSelect.length - 1]];
469
+ }
434
470
 
435
- this.selected = false;
436
- this.noCheckmark = false;
437
- this.disabled = false;
438
- this.noMatch = false;
471
+ this.stageUpdate();
472
+ }
439
473
 
440
- /**
441
- * @private
442
- */
443
- this.runtimeUtils = new AuroLibraryRuntimeUtils();
474
+ /**
475
+ * Deselects one or more options in a batch operation
476
+ * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to deselect
477
+ */
478
+ deselectOptions(options) {
479
+ const optionsToDeselect = Array.isArray(options) ? options : [options];
444
480
 
445
- // Initialize context-related properties
446
- this.menuService = null;
447
- this.unsubscribe = null;
481
+ if (!optionsToDeselect.length) return;
448
482
 
449
- /**
450
- * @private
451
- */
452
- this.handleMenuChange = this.handleMenuChange.bind(this);
453
- }
483
+ // Check if deselection should be prevented
484
+ const shouldPreventDeselect = !this.allowDeselect && !this.multiSelect;
485
+ const isOnlySelectedOption = this.selectedOptions.length === 1 && optionsToDeselect.includes(this.selectedOptions[0]);
454
486
 
455
- static get properties() {
456
- return {
457
- ...super.properties,
487
+ // Prevent deselecting the only selected option if not allowed
488
+ if (shouldPreventDeselect && isOnlySelectedOption) {
489
+ optionsToDeselect.forEach(option => {
490
+ option.selected = true;
491
+ });
492
+ this.dispatchChangeEvent('auroMenu-deselectPrevented', {
493
+ values: optionsToDeselect
494
+ });
495
+ return;
496
+ }
458
497
 
459
- /**
460
- * When true, disables the menu option.
461
- */
462
- disabled: {
463
- type: Boolean,
464
- reflect: true
465
- },
498
+ const optionsSet = new Set(optionsToDeselect);
499
+ this.selectedOptions = (this.selectedOptions || [])
500
+ .filter(opt => !optionsSet.has(opt));
466
501
 
467
- /**
468
- * @private
469
- */
470
- event: {
471
- type: String,
472
- reflect: true
473
- },
502
+ this.stageUpdate();
503
+ }
474
504
 
475
- /**
476
- * @private
477
- */
478
- layout: {
479
- type: String
480
- },
505
+ /**
506
+ * Selects a single option.
507
+ * @param {AuroMenuOption} option
508
+ */
509
+ selectOption(option) {
510
+ this.selectOptions(option);
511
+ }
481
512
 
482
- /**
483
- * 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.
484
- */
485
- key: {
486
- type: String,
487
- reflect: true
488
- },
513
+ /**
514
+ * Deselects a single option.
515
+ * @param {AuroMenuOption} option
516
+ */
517
+ deselectOption(option) {
518
+ this.deselectOptions(option);
519
+ }
489
520
 
490
- /**
491
- * @private
492
- */
493
- menuService: {
494
- type: Object,
495
- state: true
496
- },
497
-
498
- /**
499
- * @private
500
- */
501
- matchWord: {
502
- type: String,
503
- state: true
504
- },
505
-
506
- /**
507
- * @private
508
- */
509
- noCheckmark: {
510
- type: Boolean,
511
- reflect: true
512
- },
513
-
514
- /**
515
- * 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.
516
- */
517
- noMatch: {
518
- type: Boolean,
519
- reflect: true,
520
- attribute: 'nomatch'
521
- },
522
-
523
- /**
524
- * Specifies that an option is selected.
525
- */
526
- selected: {
527
- type: Boolean,
528
- reflect: true
529
- },
530
-
531
- /**
532
- * Specifies the tab index of the menu option.
533
- */
534
- tabIndex: {
535
- type: Number,
536
- reflect: true
537
- },
538
-
539
- /**
540
- * Specifies the value to be sent to a server.
541
- */
542
- value: {
543
- type: String,
544
- reflect: true
545
- },
546
- };
521
+ /**
522
+ * Toggles the selection state of a single option.
523
+ * @param {AuroMenuOption} option
524
+ */
525
+ toggleOption(option) {
526
+ if (option.selected) {
527
+ this.deselectOption(option);
528
+ } else {
529
+ this.selectOption(option);
530
+ }
547
531
  }
548
532
 
549
- static get styles() {
550
- return [
551
- styleCss,
552
- colorCss,
553
- tokensCss
554
- ];
555
- }
533
+ /**
534
+ * Selects options based on their value(s) when compared to a passed value or values.
535
+ * Value or values are normalized to an array of strings that can be matched to option keys.
536
+ * @param {string|number|Array<string|number>} value - The value(s) to select.
537
+ */
538
+ selectByValue(value) {
539
+ const isEmptyValue = value === undefined ||
540
+ value === null ||
541
+ (Array.isArray(value) && value.length === 0) ||
542
+ (typeof value === 'string' && value.trim() === '');
556
543
 
557
- connectedCallback() {
558
- super.connectedCallback();
544
+ // Early exit for invalid/empty values
545
+ if (isEmptyValue) {
546
+ this.selectedOptions.forEach(opt => opt.selected = false);
547
+ this.selectedOptions = [];
548
+ return;
549
+ }
559
550
 
560
- // Add the tag name as an attribute if it is different than the component name
561
- // Add this step soon as this node gets attached to the DOM to avoid racing condition with menu's value setting logic.
562
- this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
551
+ // If an internal update cycle is still in progress, defer value application
552
+ // rather than dropping it.
553
+ if (this.internalUpdateInProgress || this.host.internalUpdateInProgress) {
554
+ this.queuePendingValue(value);
555
+ return;
556
+ }
563
557
 
564
- // Set up context consumption in connectedCallback
565
- this._contextConsumer = new s$2(this, {
566
- context: MenuContext,
567
- callback: this.attachTo.bind(this),
568
- subscribe: true
569
- });
558
+ // Normalize values to array of strings
559
+ const normalizedValues = this._getNormalizedValues(value);
570
560
 
571
- // Establish the key property as early as possible.
572
- // When a framework (e.g. Svelte) inserts the element into the DOM before
573
- // setting its `value` property, both `getAttribute('value')` and
574
- // `getAttribute('key')` return null here. Setting `this.key = null`
575
- // would block the fallback in `updated()` that assigns key from the
576
- // value property (the guard checked `=== undefined`). Only assign key
577
- // if at least one source attribute is actually present so that the
578
- // `updated()` fallback can run when the value property arrives later.
579
- const valueAttr = this.getAttribute('value');
580
- const keyAttr = this.getAttribute('key');
581
- const resolvedKey = keyAttr !== null ? keyAttr : valueAttr;
582
- if (resolvedKey !== null) {
583
- this.key = resolvedKey;
561
+ // Validate for single-select mode
562
+ let validatedValues = normalizedValues;
563
+ if (normalizedValues.length > 1 && !this.multiSelect) {
564
+ console.warn("MenuService - Multiple values provided for single-select menu. Only the first value will be selected.");
565
+ validatedValues = [normalizedValues[0]];
584
566
  }
585
- }
586
-
587
- firstUpdated() {
588
- // Add the tag name as an attribute if it is different than the component name
589
- this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
590
567
 
591
- // Generate unique ID if not already set (required for aria-activedescendant)
592
- if (!this.id) {
593
- menuOptionIdCounter += 1;
594
- this.id = `menuoption-${menuOptionIdCounter}`;
568
+ if (this._menuOptions.length === 0) {
569
+ this.queuePendingValue(value);
570
+ return;
595
571
  }
596
572
 
597
- this.setAttribute('role', 'option');
598
- this.setAttribute('aria-selected', 'false');
573
+ // Find matching options by comparing available options to validated values
574
+ const trackedKeys = new Set();
575
+ const optionsToSelect = this._menuOptions.filter(option => {
576
+ const passesFilter = validatedValues.includes(option.key);
577
+ const alreadyTracked = trackedKeys.has(option.key);
578
+ const isActive = option.isActive;
599
579
 
600
- this.addEventListener('mouseover', () => {
601
- this.dispatchEvent(new CustomEvent('auroMenuOption-mouseover', {
602
- bubbles: true,
603
- cancelable: false,
604
- composed: true,
605
- detail: this
606
- }));
580
+ trackedKeys.add(option.key);
581
+
582
+ // Include the option in the options to be selected if it passes the filter check and
583
+ // either hasn't been tracked yet or selectAllMatchingOptions is true
584
+ return isActive && passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
607
585
  });
608
- }
609
586
 
610
- updated(changedProperties) {
611
- super.updated(changedProperties);
587
+ // Handle no matches: clear existing selection, but do not dispatch an intermediate
588
+ // undefined value that can overwrite the host value in parent components.
589
+ if (!optionsToSelect.length) {
590
+ const hasUnresolvedKeys = this._menuOptions.some((option) => option.isActive && option.key == null);
612
591
 
613
- // Update aria-selected attribute if selected changed
614
- if (changedProperties.has('selected')) {
592
+ if (hasUnresolvedKeys) {
593
+ this.queuePendingValue(value);
594
+ return;
595
+ }
615
596
 
616
- // Update aria-selected attribute
617
- this.setAttribute('aria-selected', this.selected.toString());
597
+ this.clearPendingValue();
618
598
 
619
- // Update menu service selection state if this isn't an internal update
620
- if (this.internalUpdateInProgress !== true && this.menuService) {
621
- this.menuService[this.selected ? 'selectOption' : 'deselectOption'](this);
599
+ if (this.selectedOptions.length > 0) {
600
+ this.selectedOptions = [];
622
601
  }
623
- }
624
602
 
625
- if (changedProperties.has('disabled')) {
626
- if (this.disabled) {
627
- this.setAttribute('aria-disabled', 'true');
628
- } else {
629
- this.removeAttribute('aria-disabled');
603
+ // Always notify so the host resets any stale invalid value, even when
604
+ // selectedOptions was already empty (e.g. double-clicking set-invalid).
605
+ this.stageUpdate({ reason: 'no-match' });
606
+
607
+ // Dispatch failure event if no matches found
608
+ if (validatedValues.length) {
609
+ this.dispatchChangeEvent('auroMenu-selectValueFailure', {
610
+ message: 'No matching options found for the provided value(s).',
611
+ values: validatedValues
612
+ });
630
613
  }
631
- }
632
614
 
633
- if (changedProperties.has('active')) {
634
- this.updateActiveClasses();
615
+ return;
635
616
  }
636
617
 
637
- // Update text highlight if matchWord changed
638
- if (changedProperties.has('matchWord')) {
639
- this.updateTextHighlight();
640
- }
618
+ this.clearPendingValue();
641
619
 
642
- // Set the key to be the passed value if no key is provided.
643
- // Loose equality (== null) is intentional: it catches both null AND
644
- // undefined. When a framework (e.g. Svelte, React) inserts the element
645
- // before setting its value property, connectedCallback skips key
646
- // assignment because both attributes are null at that point. The Lit
647
- // property default for `key` is undefined (not null), so strict
648
- // === null would miss the case and the fallback would never run.
649
- if (changedProperties.has('value') && this.key == null) { // eslint-disable-line eqeqeq, no-eq-null
650
- this.key = this.value;
620
+ if (this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
621
+ return;
651
622
  }
652
- }
653
623
 
654
- disconnectedCallback() {
655
- if (this.menuService) {
656
- this.menuService.unsubscribe(this.handleMenuChange);
657
- this.menuService.removeMenuOption(this);
658
- }
624
+ // Apply programmatic selection as a single transaction and emit one final state.
625
+ this.selectedOptions = optionsToSelect;
626
+ this.stageUpdate();
659
627
  }
660
628
 
661
629
  /**
662
- * Sets up event listeners for user interaction with the menu option.
663
- * This function enables click and mouse enter events to trigger selection and highlighting logic.
630
+ * Queues a pending value and schedules a bounded retry.
631
+ * @param {string|number|Array<string|number>} value - The value to retry.
664
632
  */
665
- bindEvents() {
666
- this.addEventListener('click', this.handleClick.bind(this));
667
- this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
668
- }
633
+ queuePendingValue(value) {
634
+ this._pendingValue = value;
669
635
 
670
- /**
671
- * Attaches this menu option to a menu service and subscribes to its events.
672
- * This method enables the option to participate in menu selection and highlighting logic.
673
- * @param {Object} service - The menu service instance to attach to.
674
- */
675
- attachTo(service) {
676
- if (!service) {
636
+ if (this._pendingRetryScheduled || this._pendingRetryCount >= 5) {
677
637
  return;
678
638
  }
679
- this.menuService = service;
680
- this.menuService.addMenuOption(this);
681
- this.menuService.subscribe(this.handleMenuChange);
682
- }
683
639
 
684
- /**
685
- * Handles changes from the menu service and updates the option's state.
686
- * This function synchronizes the option's properties and selection/highlight state with menu events.
687
- * @param {Object} event - The event object from the menu service.
688
- */
689
- handleMenuChange(event) {
640
+ this._pendingRetryScheduled = true;
641
+ this._pendingRetryCount += 1;
690
642
 
691
- // Ignore events without a type or property
692
- if (!event || (!event.type && !event.property)) {
693
- return;
694
- }
643
+ setTimeout(() => {
644
+ this._pendingRetryScheduled = false;
695
645
 
696
- // Update reactive properties based on event type
697
- if (event.property && Object.keys(AuroMenuOption.properties).includes(event.property)) {
698
- this[event.property] = event.value;
699
- }
646
+ if (this._pendingValue == null) {
647
+ return;
648
+ }
700
649
 
701
- // Handle highlight changes
702
- if (event.type === 'highlightChange') {
703
- const isActive = event.option === this;
704
- this.active = isActive;
705
- this.updateActiveClasses();
706
- }
650
+ const pendingValue = this._pendingValue;
651
+ this.selectByValue(pendingValue);
652
+ }, 0);
653
+ }
707
654
 
708
- if (event.type === 'stateChange') {
709
- const isSelected = event.selectedOptions.includes(this);
710
- this.setInternalSelected(isSelected);
711
- }
655
+ /**
656
+ * Clears pending retry state.
657
+ */
658
+ clearPendingValue() {
659
+ this._pendingValue = null;
660
+ this._pendingRetryScheduled = false;
661
+ this._pendingRetryCount = 0;
712
662
  }
713
663
 
714
664
  /**
715
- * Updates the internal selected state of the menu option bypassing 'updated' and triggers custom events if selected.
716
- * This function ensures the option's selection state is synchronized with menu logic and notifies listeners.
717
- * @param {boolean} isSelected - Whether the option should be marked as selected.
665
+ * Resets the selected options to an empty array.
718
666
  */
719
- setInternalSelected(isSelected) {
720
- this.internalUpdateInProgress = true;
721
- this.selected = isSelected;
667
+ reset() {
668
+ const previousOptions = [...this.selectedOptions];
669
+ previousOptions.forEach(opt => opt.selected = false);
670
+ this.selectedOptions = [];
722
671
 
723
- // Fire custom event if selected
724
- if (isSelected) {
725
- this.handleCustomEvent();
672
+ // Single update after clearing all
673
+ if (previousOptions.length) {
674
+ this.stageUpdate();
726
675
  }
727
-
728
- setTimeout(() => {
729
- this.internalUpdateInProgress = false;
730
- }, 0);
731
676
  }
732
677
 
733
678
  /**
734
- * Sets the selected state of the menu option.
735
- * This function updates whether the option is currently selected.
736
- * @param {boolean} isSelected - Whether the option should be marked as selected.
737
- * @deprecated Simply modify the `selected` property directly instead.
679
+ * SUBSCRIPTION, NOTIFICATION AND EVENT DISPATCH METHODS
738
680
  */
739
- setSelected(isSelected) {
740
- this.selected = isSelected;
741
- }
742
681
 
743
682
  /**
744
- * Updates the active state and visual highlighting of the menu option.
745
- * This function toggles the option's active status and applies or removes the active CSS class.
746
- * @param {boolean} isActive - Whether the option should be marked as active.
747
- * @deprecated Simply modify the `active` property directly instead.
683
+ * Subscribes a callback to menu service events.
684
+ * @param {Function} callback - The callback to invoke on events.
748
685
  */
749
- updateActive(isActive) {
750
-
751
- // Set active state
752
- this.active = isActive;
753
- this.updateActiveClasses();
686
+ subscribe(callback) {
687
+ this._subscribers.push(callback);
754
688
  }
755
689
 
756
690
  /**
757
- * Updates the CSS class for the menu option based on its active state.
758
- * This function adds or removes the 'active' class to visually indicate the option's active status.
759
- * @private
691
+ * Remove a previously subscribed callback from menu service events.
692
+ * @param {Function} callback
760
693
  */
761
- updateActiveClasses() {
762
- // Update class based on active state
763
- if (this.active) this.classList.add('active');
764
- else this.classList.remove('active');
694
+ unsubscribe(callback) {
695
+ this._subscribers = this._subscribers.filter(cb => cb !== callback);
765
696
  }
766
697
 
767
-
768
698
  /**
769
- * Updates the visual highlighting of text within the menu option based on the current match word.
770
- * This function highlights matching text segments and manages nested spacers for display formatting.
771
- * @private
699
+ * Stages an update to notify subscribers of state and value changes.
772
700
  */
773
- updateTextHighlight() {
774
-
775
- // Regex for matchWord if needed
776
- let regexWord = null;
777
-
778
- if (this.matchWord && this.matchWord.length) {
779
- const escapedWord = this.matchWord.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
780
- regexWord = new RegExp(escapedWord, 'giu');
781
- }
782
-
783
- // Update text highlighting if matchWord changed
784
- if (regexWord &&
785
- this.isActive && !this.hasAttribute('persistent')) {
786
- const nested = this.querySelectorAll('.nestingSpacer');
787
-
788
- const displayValueEl = this.querySelector('[slot="displayValue"]');
789
- if (displayValueEl) {
790
- this.removeChild(displayValueEl);
791
- }
792
-
793
- // Create nested spacers
794
- const nestingSpacerBundle = [...nested].map(() => this.nestingSpacer).join('');
795
-
796
- // Update with spacers and matchWord
797
- this.innerHTML = nestingSpacerBundle +
798
- this.textContent.replace(
799
- regexWord,
800
- (match) => `<strong>${match}</strong>`
801
- );
802
- if (displayValueEl) {
803
- this.append(displayValueEl);
804
- }
805
- }
701
+ stageUpdate(meta = {}) {
702
+ this.notifyStateChange(meta);
703
+ this.notifyValueChange(meta);
806
704
  }
807
705
 
808
706
  /**
809
- * Handles click events on the menu option, toggling its selected state.
810
- * This function dispatches a click event and updates selection if the option is not disabled.
811
- * @private
707
+ * Notifies subscribers of a menu service event.
708
+ * All notifications are sent to all subscribers.
709
+ * @param {string} event - The event to send to subscribers.
812
710
  */
813
- handleClick() {
814
- if (!this.disabled) {
815
- this.dispatchClickEvent();
816
- this.selected = !this.selected;
817
- }
711
+ notify(event) {
712
+ this._subscribers.forEach(callback => callback(event));
818
713
  }
819
714
 
820
715
  /**
821
- * Handles mouse enter events to highlight the menu option.
822
- * This function updates the menu service to set this option as the currently highlighted item if not disabled.
823
- * @private
716
+ * Notifies subscribers of a state change (selected options has changed).
824
717
  */
825
- handleMouseEnter() {
826
- if (!this.disabled) {
827
- this.menuService.setHighlightedOption(this);
828
- }
718
+ notifyStateChange(meta = {}) {
719
+ this.notify({
720
+ type: 'stateChange',
721
+ selectedOptions: this.selectedOptions,
722
+ ...meta
723
+ });
829
724
  }
830
725
 
831
726
  /**
832
- * Dispatches custom events defined for this menu option.
833
- * This function notifies listeners when a custom event is triggered by the option.
834
- * @private
727
+ * Notifies subscribers of a value change (current value has changed).
835
728
  */
836
- handleCustomEvent() {
837
- if (this.event) {
838
- dispatchMenuEvent(this, this.event, { option: this });
839
- dispatchMenuEvent(this, 'auroMenu-customEventFired', { option: this });
840
- }
729
+ notifyValueChange(meta = {}) {
730
+
731
+ // Prepare details for the event
732
+ const details = {
733
+ value: this.currentValue,
734
+ stringValue: this.stringValue,
735
+ keys: this.currentKeys,
736
+ options: this.selectedOptions,
737
+ label: this.currentLabel
738
+ };
739
+
740
+ // If only one option is selected, include its index
741
+ if (this.selectedOptions.length === 1) details.index = this._menuOptions.indexOf(this.selectedOptions[0]);
742
+
743
+ this.notify({
744
+ type: 'valueChange',
745
+ ...meta,
746
+ ...details
747
+ });
841
748
  }
842
749
 
843
750
  /**
844
- * Dispatches a click event for this menu option.
845
- * This function notifies listeners that the option has been clicked.
846
- * @private
751
+ * Dispatches a custom event from the host element.
752
+ * @param {string} eventName
753
+ * @param {any} detail
847
754
  */
848
- dispatchClickEvent() {
849
- this.dispatchEvent(new CustomEvent('auroMenuOption-click', {
755
+ dispatchChangeEvent(eventName, detail) {
756
+ this.host.dispatchEvent(new CustomEvent(eventName, {
850
757
  bubbles: true,
851
758
  cancelable: false,
852
759
  composed: true,
853
- detail: this
760
+ detail
854
761
  }));
855
762
  }
856
763
 
857
764
  /**
858
- * Generates an HTML element containing an SVG icon based on the provided `svgContent`.
859
- *
860
- * @private
861
- * @param {string} svgContent - The SVG content to be embedded.
862
- * @returns {Element} The HTML element containing the SVG icon.
765
+ * MENU OPTION MANAGEMENT METHODS
863
766
  */
864
- generateIconHtml(svgContent) {
865
- const dom = new DOMParser().parseFromString(svgContent, 'text/html');
866
- const svg = dom.body.firstChild;
867
767
 
868
- svg.setAttribute('slot', 'svg');
768
+ /**
769
+ * Adds a menu option to the service's list.
770
+ * @param {AuroMenuOption} option - the option to track
771
+ */
772
+ addMenuOption(option) {
773
+ this._menuOptions.push(option);
774
+ this.notify({ type: 'optionsChange', options: this._menuOptions });
869
775
 
870
- return u$1`<${this.iconTag} customColor customSvg>${svg}</${this.iconTag}>`;
776
+ if (this._pendingValue != null) {
777
+ this.queuePendingValue(this._pendingValue);
778
+ }
871
779
  }
872
780
 
873
781
  /**
874
- * Logic to determine the layout of the component.
875
- * @protected
876
- * @returns {void}
782
+ * Removes a menu option from the service's list.
783
+ * @param {AuroMenuOption} option - the option to remove
877
784
  */
878
- renderLayout() {
879
-
880
- const fontClassMap = {
881
- xs: 'body-sm',
882
- sm: 'body-default',
883
- md: 'body-default',
884
- lg: 'body-lg',
885
- xl: 'body-lg'
886
- };
887
-
888
- const classes = e({
889
- 'wrapper': true,
890
- [this.size ? fontClassMap[this.size] : 'body-sm']: true,
891
- });
785
+ removeMenuOption(option) {
786
+ this._menuOptions = this._menuOptions.filter(opt => opt !== option);
787
+ this.notify({ type: 'optionsChange', options: this._menuOptions });
892
788
 
893
- return u$1`
894
- <div class="${classes}">
895
- ${this.selected && !this.noCheckmark
896
- ? this.generateIconHtml(checkmarkIcon.svg)
897
- : undefined}
898
- <slot></slot>
899
- </div>
900
- `;
789
+ if (this._menuOptions.length === 0) {
790
+ this.clearPendingValue();
791
+ }
901
792
  }
902
- }
903
-
904
- /* eslint-disable */
905
-
906
- class MenuService {
907
793
 
908
794
  /**
909
- * PROPERTIES AND GETTERS
795
+ * UTILITIES
910
796
  */
911
797
 
912
798
  /**
913
- * Gets the list of registered menu options.
914
- * @returns {AuroMenuOption[]}
915
- */
916
- get menuOptions() {
917
- return this._menuOptions;
918
- }
919
-
920
- /**
921
- * Gets the currently highlighted option.
922
- * @returns {AuroMenuOption|null}
799
+ * Normalizes a value or array of values into an array of strings for option selection.
800
+ * This function ensures that input values are consistently formatted for matching menu options.
801
+ *
802
+ * @param {string|number|Array<string|number>} value - The value(s) to normalize.
803
+ * @returns {Array<string>} An array of string values suitable for option matching.
804
+ * @throws {Error} If any value is not a string or number.
923
805
  */
924
- get highlightedOption() {
925
- return this._menuOptions[this.highlightedIndex] || null;
926
- }
806
+ _getNormalizedValues(value) {
807
+ let values = value;
927
808
 
928
- /**
929
- * Gets the current value(s) of the selected option(s).
930
- * @returns {string|string[]|undefined}
931
- */
932
- get currentValue() {
933
- const values = (this.selectedOptions || []).map(option => option.value);
934
- return this.multiSelect ? values : values[0];
935
- }
809
+ // Handle JSON string and single value string input
810
+ if (!Array.isArray(values) && typeof values === 'string') {
936
811
 
937
- /**
938
- * Gets the label(s) of the currently selected option(s).
939
- * @returns {string}
940
- */
941
- get currentLabel() {
942
- const labels = (this.selectedOptions || []).map(option => option.textContent);
943
- return this.multiSelect ? labels.join(", ") : labels[0] || '';
944
- }
812
+ // Attempt to parse as JSON array
813
+ try {
945
814
 
946
- /**
947
- * Gets the string representation of the current value(s).
948
- * For multi-select, this is a JSON stringified array.
949
- * @returns {string|undefined}
950
- */
951
- get stringValue() {
952
- const { currentValue } = this;
815
+ // Normalize single quotes to double quotes for JSON parsing
816
+ // This will not handle complex cases but will cover basic usage
817
+ const parseValue = values.replace(/'([^']*?)'/g, '"$1"');
953
818
 
954
- if (Array.isArray(currentValue)) {
955
- if (currentValue.length > 0) {
956
- return JSON.stringify(currentValue);
819
+ // Attempt parse
820
+ const parsed = JSON.parse(parseValue);
821
+
822
+ // Ensure parsed value is an array
823
+ if (!Array.isArray(parsed)) throw new Error('Not an array');
824
+
825
+ // Set values to parsed array
826
+ values = parsed;
827
+ } catch (err) {
828
+
829
+ // If parsing fails, treat as single value
830
+ values = [value];
957
831
  }
958
- return undefined;
959
832
  }
960
833
 
961
- if (typeof currentValue === 'string') {
962
- if (currentValue.length > 0) {
963
- return currentValue;
964
- }
965
- return undefined;
834
+ // Handle a single number being passed
835
+ if (typeof values === 'number') {
836
+ values = [String(values)];
966
837
  }
967
838
 
968
- // Future: handle other types here (e.g., number, object, etc.)
969
- return undefined;
970
- }
839
+ // Coerce each value to string and validate types
840
+ values.forEach((val, index) => {
971
841
 
972
- /**
973
- * Gets the key(s) of the currently selected option(s).
974
- * @returns {string|string[]|undefined}
975
- */
976
- get currentKeys() {
977
- const keys = (this.selectedOptions || []).map(option => option.key);
978
- return this.multiSelect ? keys : keys[0];
842
+ // Throw an error for invalid value types
843
+ if (typeof val !== 'string' && typeof val !== 'number') {
844
+ throw new Error('Value contains invalid value type. Supported types are string and number.');
845
+ }
846
+
847
+ // Convert numbers to strings for consistency
848
+ if (typeof val === 'number') {
849
+ values[index] = String(val);
850
+ }
851
+ });
852
+
853
+ // Return the resulting array of string values
854
+ return values;
979
855
  }
980
856
 
981
857
  /**
982
- * CONSTRUCTOR
858
+ * Returns whether two arrays of options contain the same elements.
859
+ * @param {AuroMenuOption[]} arr1 - First array of options.
860
+ * @param {AuroMenuOption[]} arr2 - Second array of options.
861
+ * @returns {boolean} True if arrays match, false otherwise.
983
862
  */
863
+ optionsArraysMatch(arr1, arr2) {
864
+ if (arr1.length !== arr2.length) return false;
984
865
 
985
- /**
986
- * Creates a new MenuService instance.
987
- * @param {Object} options - The options object.
988
- * @param {AuroMenu} options.host - The host element that this service will control. Required.
989
- * @throws {Error} If the host is not provided.
990
- */
991
- constructor({ host } = {}) {
866
+ const set1 = new Set(arr1);
867
+ const set2 = new Set(arr2);
992
868
 
993
- // Ensure a host was passed
994
- if (!host) {
995
- throw new Error("MenuService requires a host element.");
869
+ for (let item of set1) {
870
+ if (!set2.has(item)) {
871
+ return false;
872
+ }
996
873
  }
997
874
 
998
- // Attach the service to the host
999
- this.host = host;
1000
- this.host.addController(this);
875
+ return true;
876
+ }
877
+ }
1001
878
 
1002
- // Set default properties
1003
- this.size = undefined;
1004
- this.shape = undefined;
1005
- this.noCheckmark = undefined;
1006
- this.disabled = undefined;
1007
- this.matchWord = undefined;
1008
- this.multiSelect = undefined;
1009
- this.allowDeselect = undefined;
1010
- this.selectAllMatchingOptions = undefined;
879
+ const MenuContext = n$1('menu-context');
1011
880
 
1012
- this.highlightedIndex = -1;
881
+ // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
882
+ // See LICENSE in the project root for license information.
1013
883
 
1014
- this._menuOptions = [];
1015
- this._subscribers = [];
1016
- this.internalUpdateInProgress = false;
1017
- this.selectedOptions = [];
1018
- this._pendingValue = null;
1019
- this._pendingRetryScheduled = false;
1020
- this._pendingRetryCount = 0;
1021
- }
884
+ // ---------------------------------------------------------------------
1022
885
 
1023
- /**
1024
- * PROPERTY SYNCING
1025
- */
886
+ /* eslint-disable line-comment-position, no-inline-comments, no-confusing-arrow, no-nested-ternary, implicit-arrow-linebreak */
887
+
888
+ class AuroLibraryRuntimeUtils {
889
+
890
+ /* eslint-disable jsdoc/require-param */
1026
891
 
1027
892
  /**
1028
- * Handles host updates.
1029
- * This is a lit reactive lifecycle method.
1030
- * This comes from the Lit controller interface provided by adding this service as a controller to the host.
1031
- * See constructor for `this.host.addController(this)`
1032
- * You can read more about Lit reactive controllers here: https://lit.dev/docs/composition/controllers/
893
+ * This will register a new custom element with the browser.
894
+ * @param {String} name - The name of the custom element.
895
+ * @param {Object} componentClass - The class to register as a custom element.
896
+ * @returns {void}
1033
897
  */
1034
- hostUpdated() {
1035
-
1036
- // Reset selection if multiSelect mode changes
1037
- if (this.host.multiSelect !== this.multiSelect) {
1038
- this.selectedOptions = [];
898
+ registerComponent(name, componentClass) {
899
+ if (!customElements.get(name)) {
900
+ customElements.define(name, class extends componentClass {});
1039
901
  }
1040
-
1041
- // Update properties on host update
1042
- this.setProperties({
1043
- size: this.host.size,
1044
- shape: this.host.shape,
1045
- noCheckmark: this.host.noCheckmark,
1046
- disabled: this.host.disabled,
1047
- matchWord: this.host.matchWord,
1048
- multiSelect: this.host.multiSelect,
1049
- allowDeselect: this.host.allowDeselect,
1050
- selectAllMatchingOptions: this.host.selectAllMatchingOptions
1051
- });
1052
902
  }
1053
903
 
1054
904
  /**
1055
- * Handles host disconnection and memory cleanup.
905
+ * Finds and returns the closest HTML Element based on a selector.
906
+ * @returns {void}
1056
907
  */
1057
- hostDisconnected() {
1058
- this._subscribers = [];
1059
- this._menuOptions = [];
1060
- this._pendingValue = null;
1061
- this._pendingRetryScheduled = false;
1062
- this._pendingRetryCount = 0;
908
+ closestElement(
909
+ selector, // selector like in .closest()
910
+ base = this, // extra functionality to skip a parent
911
+ __Closest = (el, found = el && el.closest(selector)) =>
912
+ !el || el === document || el === window
913
+ ? null // standard .closest() returns null for non-found selectors also
914
+ : found
915
+ ? found // found a selector INside this element
916
+ : __Closest(el.getRootNode().host) // recursion!! break out to parent DOM
917
+ ) {
918
+ return __Closest(base);
1063
919
  }
920
+ /* eslint-enable jsdoc/require-param */
1064
921
 
1065
922
  /**
1066
- * Sets a property value if it exists on the instance and the value has changed.
1067
- * @param {string} property
1068
- * @param {any} value
923
+ * If the element passed is registered with a different tag name than what is passed in, the tag name is added as an attribute to the element.
924
+ * @param {Object} elem - The element to check.
925
+ * @param {String} tagName - The name of the Auro component to check for or add as an attribute.
926
+ * @returns {void}
1069
927
  */
1070
- setProperty(property, value) {
1071
-
1072
- // Only update if we are tracking the property in this service
1073
- if (this.hasOwnProperty(property)) {
1074
-
1075
- // Check if the value has changed
1076
- const valueChanged = this[property] !== value;
928
+ handleComponentTagRename(elem, tagName) {
929
+ const tag = tagName.toLowerCase();
930
+ const elemTag = elem.tagName.toLowerCase();
1077
931
 
1078
- // Update and notify if changed
1079
- if (valueChanged) {
1080
- this[property] = value;
1081
- this.notify({ property, value });
1082
- }
932
+ if (elemTag !== tag) {
933
+ elem.setAttribute(tag, true);
1083
934
  }
1084
935
  }
1085
936
 
1086
937
  /**
1087
- * Sets multiple properties on the instance.
1088
- * @param {Object} properties - Key-value pairs of properties to set.
938
+ * Validates if an element is a specific Auro component.
939
+ * @param {Object} elem - The element to validate.
940
+ * @param {String} tagName - The name of the Auro component to check against.
941
+ * @returns {Boolean} - Returns true if the element is the specified Auro component.
1089
942
  */
1090
- setProperties(properties) {
1091
- for (const [key, value] of Object.entries(properties)) {
1092
- this.setProperty(key, value);
1093
- }
943
+ elementMatch(elem, tagName) {
944
+ const tag = tagName.toLowerCase();
945
+ const elemTag = elem.tagName.toLowerCase();
946
+
947
+ return elemTag === tag || elem.hasAttribute(tag);
1094
948
  }
1095
949
 
1096
950
  /**
1097
- * MENU OPTION HIGHLIGHTING
951
+ * Gets the text content of a named slot.
952
+ * @returns {String}
953
+ * @private
1098
954
  */
955
+ getSlotText(elem, name) {
956
+ const slot = elem.shadowRoot?.querySelector(`slot[name="${name}"]`);
957
+ const nodes = slot?.assignedNodes({ flatten: true }) || [];
958
+ const text = nodes.map(n => n.textContent?.trim()).join(' ').trim();
1099
959
 
1100
- /**
1101
- * Highlights the next active option in the menu.
1102
- */
1103
- highlightNext() {
1104
- this.moveHighlightedOption("next");
960
+ return text || null;
1105
961
  }
962
+ }
1106
963
 
1107
- /**
1108
- * Highlights the previous active option in the menu.
1109
- */
1110
- highlightPrevious() {
1111
- this.moveHighlightedOption("previous");
964
+ // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
965
+ // See LICENSE in the project root for license information.
966
+
967
+
968
+ /**
969
+ * Helper method to dispatch custom events.
970
+ * @param {HTMLElement} element - Element to dispatch event from.
971
+ * @param {string} eventName - Name of the event to dispatch.
972
+ * @param {Object} [detail] - Optional detail object to include with the event.
973
+ */
974
+ function dispatchMenuEvent(element, eventName, detail = null) {
975
+ const eventConfig = {
976
+ bubbles: true,
977
+ cancelable: false,
978
+ composed: true
979
+ };
980
+
981
+ if (detail !== null) {
982
+ eventConfig.detail = detail;
1112
983
  }
1113
984
 
1114
- /**
1115
- * Moves the highlighted option in the specified direction.
1116
- * @param {string} direction - The direction to move the highlight ("next" or "previous").
1117
- */
1118
- moveHighlightedOption(direction) {
985
+ element.dispatchEvent(new CustomEvent(eventName, eventConfig));
986
+ }
1119
987
 
1120
- // Get the active options
1121
- const activeOptions = this._menuOptions.filter(option => option.isActive);
988
+ /**
989
+ * @license
990
+ * Copyright 2017 Google LLC
991
+ * SPDX-License-Identifier: BSD-3-Clause
992
+ */
993
+ const t={ATTRIBUTE:1},e$1=t=>(...e)=>({_$litDirective$:t,values:e});let i$1 = class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i;}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};
1122
994
 
1123
- // Get the currently active option
1124
- const currentActiveOption = activeOptions[activeOptions.indexOf(this.highlightedOption)];
995
+ /**
996
+ * @license
997
+ * Copyright 2018 Google LLC
998
+ * SPDX-License-Identifier: BSD-3-Clause
999
+ */const e=e$1(class extends i$1{constructor(t$1){if(super(t$1),t$1.type!==t.ATTRIBUTE||"class"!==t$1.name||t$1.strings?.length>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return " "+Object.keys(t).filter(s=>t[s]).join(" ")+" "}update(s,[i]){if(void 0===this.st){this.st=new Set,void 0!==s.strings&&(this.nt=new Set(s.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in i)i[t]&&!this.nt?.has(t)&&this.st.add(t);return this.render(i)}const r=s.element.classList;for(const t of this.st)t in i||(r.remove(t),this.st.delete(t));for(const t in i){const s=!!i[t];s===this.st.has(t)||this.nt?.has(t)||(s?(r.add(t),this.st.add(t)):(r.remove(t),this.st.delete(t)));}return E}});
1125
1000
 
1126
- // Determine the new index based on the currently active option and direction
1127
- let newIndex = currentActiveOption
1128
- ? direction === "previous"
1129
- ? activeOptions.indexOf(currentActiveOption) - 1
1130
- : activeOptions.indexOf(currentActiveOption) + 1
1131
- : direction === "previous"
1132
- ? activeOptions.length - 1
1133
- : 0;
1001
+ /* eslint-disable no-underscore-dangle */
1002
+ // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1003
+ // See LICENSE in the project root for license information.
1134
1004
 
1135
- // Wrap around the index if needed
1136
- newIndex = newIndex < 0 ? activeOptions.length - 1 : newIndex >= activeOptions.length ? 0 : newIndex;
1137
1005
 
1138
- // Get the new active option and set it as highlighted
1139
- const newActiveOption = activeOptions[newIndex];
1140
- this.setHighlightedOption(newActiveOption);
1006
+
1007
+ /**
1008
+ * The `auro-menu` element provides users a way to select from a list of options.
1009
+ * @customElement auro-menu
1010
+ *
1011
+ * @event {CustomEvent<Element>} auroMenu-activatedOption - Notifies that a menuoption has been made `active`.
1012
+ * @event {CustomEvent<any>} auroMenu-customEventFired - Notifies that a custom event has been fired.
1013
+ * @event {CustomEvent<{ loading: boolean; hasLoadingPlaceholder: boolean; }>} auroMenu-loadingChange - Notifies when the loading attribute is changed.
1014
+ * @event {CustomEvent<any>} auroMenu-selectValueFailure - Notifies that an attempt to select a menuoption by matching a value has failed.
1015
+ * @event {CustomEvent<{ values: HTMLElement[] }>} auroMenu-deselectPrevented - Notifies that deselection was prevented and includes the affected options in `detail.values`.
1016
+ * @event {CustomEvent<any>} auroMenu-selectValueReset - Notifies that the component value has been reset.
1017
+ * @event {CustomEvent<any>} auroMenu-selectedOption - Notifies that a new menuoption selection has been made.
1018
+ * @slot loadingText - Text to show while loading attribute is set
1019
+ * @slot loadingIcon - Icon to show while loading attribute is set
1020
+ * @slot - Slot for insertion of menu options.
1021
+ */
1022
+
1023
+ /* eslint-disable max-lines */
1024
+
1025
+ class AuroMenu extends AuroElement {
1026
+
1027
+ constructor() {
1028
+ super();
1029
+
1030
+ // State properties (reactive)
1031
+
1032
+ /**
1033
+ * @private
1034
+ */
1035
+ this.shape = "box";
1036
+
1037
+ /**
1038
+ * @private
1039
+ */
1040
+ this.size = "sm";
1041
+
1042
+ // Value of the selected options
1043
+ this.value = undefined;
1044
+ // Currently selected option
1045
+ this.optionSelected = undefined;
1046
+ // String used for highlighting/filtering
1047
+ this.matchWord = undefined;
1048
+ // Hide the checkmark icon on selected options
1049
+ this.noCheckmark = false;
1050
+ // Currently active option
1051
+ this.optionActive = undefined;
1052
+ // Loading state
1053
+ this.loading = false;
1054
+ // Multi-select mode
1055
+ this.multiSelect = false;
1056
+ // Allow deselecting of menu options
1057
+ this.allowDeselect = false;
1058
+ // Select all matching options when setting value in multi-select mode
1059
+ this.selectAllMatchingOptions = false;
1060
+
1061
+ // Event Bindings
1062
+
1063
+ /**
1064
+ * @private
1065
+ */
1066
+ this.handleSlotChange = this.handleSlotChange.bind(this);
1067
+
1068
+ // Instance properties (non-reactive)
1069
+
1070
+ /**
1071
+ * @private
1072
+ */
1073
+ Object.assign(this, {
1074
+ // Root-level menu (true) or a nested submenu (false)
1075
+ rootMenu: true,
1076
+ // Currently focused/active menu item index
1077
+ _index: -1,
1078
+ // Nested menu spacer
1079
+ nestingSpacer: '<span class="nestingSpacer"></span>',
1080
+ // Loading indicator for slot elements
1081
+ loadingSlots: null,
1082
+ });
1141
1083
  }
1142
1084
 
1143
- /**
1144
- * Sets the highlighted index to the specified option.
1145
- * @param {AuroMenuOption} option - The option to highlight.
1146
- */
1147
- setHighlightedOption(option) {
1085
+ static get properties() {
1086
+ return {
1087
+ ...super.properties,
1148
1088
 
1149
- if (!option) return;
1089
+ /**
1090
+ * Allows deselecting an already selected option when clicked again in single-select mode.
1091
+ */
1092
+ allowDeselect: {
1093
+ type: Boolean,
1094
+ reflect: true,
1095
+ },
1150
1096
 
1151
- // Get the index of the option to highlight
1152
- const index = this._menuOptions.indexOf(option);
1097
+ /**
1098
+ * When true, the entire menu and all options are disabled.
1099
+ */
1100
+ disabled: {
1101
+ type: Boolean,
1102
+ reflect: true
1103
+ },
1153
1104
 
1154
- // Update highlighted index
1155
- this.highlightedIndex = index;
1105
+ /**
1106
+ * Indicates whether the menu has a loadingIcon or loadingText to render when in a loading state.
1107
+ */
1108
+ hasLoadingPlaceholder: {
1109
+ type: Boolean
1110
+ },
1156
1111
 
1157
- // Notify subscribers of highlight change
1158
- this.notify({ type: 'highlightChange', option, index: this.highlightedIndex });
1112
+ /**
1113
+ * @private
1114
+ */
1115
+ layout: {
1116
+ type: String
1117
+ },
1159
1118
 
1160
- // Dispatch the change event
1161
- this.dispatchChangeEvent('auroMenu-activatedOption', option);
1119
+ /**
1120
+ * Indent level for submenus.
1121
+ * @private
1122
+ */
1123
+ level: {
1124
+ type: Number,
1125
+ reflect: false,
1126
+ attribute: false
1127
+ },
1128
+
1129
+ /**
1130
+ * When true, displays a loading state using the loadingIcon and loadingText slots if provided.
1131
+ */
1132
+ loading: {
1133
+ type: Boolean,
1134
+ reflect: true
1135
+ },
1136
+
1137
+ /**
1138
+ * Specifies a string used to highlight matched string parts in options.
1139
+ */
1140
+ matchWord: {
1141
+ type: String,
1142
+ attribute: 'matchword'
1143
+ },
1144
+
1145
+ /**
1146
+ * When true, the selected option can be multiple options.
1147
+ */
1148
+ multiSelect: {
1149
+ type: Boolean,
1150
+ reflect: true,
1151
+ attribute: 'multiselect'
1152
+ },
1153
+
1154
+ /**
1155
+ * When true, selected option will not show the checkmark.
1156
+ */
1157
+ noCheckmark: {
1158
+ type: Boolean,
1159
+ reflect: true,
1160
+ attribute: 'nocheckmark'
1161
+ },
1162
+
1163
+ /**
1164
+ * Specifies the current active menuOption.
1165
+ */
1166
+ optionActive: {
1167
+ type: Object,
1168
+ attribute: 'optionactive'
1169
+ },
1170
+
1171
+ /**
1172
+ * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
1173
+ */
1174
+ optionSelected: {
1175
+ // Allow HTMLElement, HTMLElement[] arrays and undefined
1176
+ type: Object
1177
+ },
1178
+
1179
+ /**
1180
+ * Available menu options.
1181
+ * @readonly
1182
+ */
1183
+ options: {
1184
+ type: Array,
1185
+ reflect: false,
1186
+ attribute: false
1187
+ },
1188
+
1189
+ /**
1190
+ * Sets the size of the menu.
1191
+ * @type {'sm' | 'md'}
1192
+ * @default 'sm'
1193
+ */
1194
+ size: {
1195
+ type: String,
1196
+ reflect: true
1197
+ },
1198
+
1199
+ /**
1200
+ * When true, selects all options that match the provided value/key when setting value and multiselect is enabled.
1201
+ */
1202
+ selectAllMatchingOptions: {
1203
+ type: Boolean,
1204
+ reflect: true,
1205
+ },
1206
+
1207
+ /**
1208
+ * Sets the shape of the menu.
1209
+ * @type {'box' | 'round'}
1210
+ * @default 'box'
1211
+ */
1212
+ shape: {
1213
+ type: String,
1214
+ reflect: true
1215
+ },
1216
+
1217
+ /**
1218
+ * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
1219
+ */
1220
+ value: {
1221
+ type: String,
1222
+ reflect: true,
1223
+ attribute: 'value'
1224
+ }
1225
+ };
1226
+ }
1227
+
1228
+ static get styles() {
1229
+ return [
1230
+ styleCss$1,
1231
+ colorCss$1,
1232
+ tokensCss
1233
+ ];
1234
+ }
1235
+
1236
+ /**
1237
+ * @readonly
1238
+ * @returns {string} - Returns the label of the currently selected option(s).
1239
+ */
1240
+ get currentLabel() {
1241
+ return this.menuService.currentLabel;
1242
+ };
1243
+
1244
+ /**
1245
+ * @readonly
1246
+ * @returns {Array<HTMLElement>} - Returns the array of available menu options.
1247
+ * @deprecated Use `options` property instead.
1248
+ */
1249
+ get items() {
1250
+ return this.options;
1251
+ }
1252
+
1253
+ /**
1254
+ * @returns {number} - Returns the index of the currently active option.
1255
+ */
1256
+ get index() {
1257
+ return this._index;
1162
1258
  }
1163
1259
 
1164
1260
  /**
1165
- * Sets the highlighted option to the option at the specified index if it exists.
1166
- * @param {number} index
1261
+ * @param {number} value - Sets the index of the currently active option.
1167
1262
  */
1168
- setHighlightedIndex(index) {
1169
- const option = this._menuOptions[index] || null;
1170
- this.setHighlightedOption(option);
1263
+ set index(value) {
1264
+ this.menuService.setHighlightedIndex(value);
1171
1265
  }
1172
1266
 
1173
1267
  /**
1174
- * Selects the currently highlighted option.
1268
+ * This will register this element with the browser.
1269
+ * @param {string} [name="auro-menu"] - The name of the element that you want to register.
1270
+ *
1271
+ * @example
1272
+ * AuroMenu.register("custom-menu") // this will register this element to <custom-menu/>
1273
+ *
1175
1274
  */
1176
- selectHighlightedOption() {
1177
- if (this.highlightedOption) {
1178
- this.toggleOption(this.highlightedOption);
1179
- }
1275
+ static register(name = "auro-menu") {
1276
+ AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroMenu);
1180
1277
  }
1181
1278
 
1182
1279
  /**
1183
- * SELECTION AND DESELECTION METHODS
1280
+ * Formatted value based on `multiSelect` state.
1281
+ * Default type is `String`, changing to `Array<String>` when `multiSelect` is true.
1282
+ * @private
1283
+ * @returns {String|Array<String>}
1184
1284
  */
1285
+ get formattedValue() {
1286
+ return this.menuService.currentValue;
1287
+ }
1185
1288
 
1186
1289
  /**
1187
- * Selects one or more options in a batch operation
1188
- * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to select
1290
+ * Gets the current property values for the menu service.
1291
+ * @private
1292
+ * @returns {Object}
1189
1293
  */
1190
- selectOptions(options) {
1191
- let optionsToSelect = Array.isArray(options) ? options : [options];
1192
-
1193
- // Filter out options that are inactive
1194
- optionsToSelect = optionsToSelect.filter(option => option.isActive);
1195
-
1196
- if (!optionsToSelect.length) return;
1197
-
1198
- if (this.multiSelect) {
1199
- this.selectedOptions = [...(this.selectedOptions || []), ...optionsToSelect];
1200
- } else {
1201
- // In single select mode, only take the last option
1202
- this.selectedOptions = [optionsToSelect[optionsToSelect.length - 1]];
1203
- }
1204
-
1205
- this.stageUpdate();
1294
+ get propertyValues() {
1295
+ return {
1296
+ size: this.size,
1297
+ shape: this.shape,
1298
+ noCheckmark: this.nocheckmark,
1299
+ disabled: this.disabled
1300
+ };
1206
1301
  }
1207
1302
 
1208
1303
  /**
1209
- * Deselects one or more options in a batch operation
1210
- * @param {AuroMenuOption|AuroMenuOption[]} options - Single option or array of options to deselect
1304
+ * Provides the menu context to child components.
1305
+ * Initializes the MenuService and subscribes to menu changes.
1306
+ * @protected
1211
1307
  */
1212
- deselectOptions(options) {
1213
- const optionsToDeselect = Array.isArray(options) ? options : [options];
1214
-
1215
- if (!optionsToDeselect.length) return;
1216
-
1217
- // Check if deselection should be prevented
1218
- const shouldPreventDeselect = !this.allowDeselect && !this.multiSelect;
1219
- const isOnlySelectedOption = this.selectedOptions.length === 1 && optionsToDeselect.includes(this.selectedOptions[0]);
1220
-
1221
- // Prevent deselecting the only selected option if not allowed
1222
- if (shouldPreventDeselect && isOnlySelectedOption) {
1223
- optionsToDeselect.forEach(option => {
1224
- option.selected = true;
1225
- });
1226
- this.dispatchChangeEvent('auroMenu-deselectPrevented', {
1227
- values: optionsToDeselect
1228
- });
1308
+ provideContext() {
1309
+ if (this.parentElement && this.parentElement.closest('auro-menu, [auro-menu]')) {
1310
+ this.rootMenu = false;
1311
+ this.menuService = this.parentElement.menuService;
1312
+ this._contextProvider = this.parentElement._contextProvider;
1229
1313
  return;
1230
1314
  }
1231
1315
 
1232
- const optionsSet = new Set(optionsToDeselect);
1233
- this.selectedOptions = (this.selectedOptions || [])
1234
- .filter(opt => !optionsSet.has(opt));
1235
-
1236
- this.stageUpdate();
1316
+ this.menuService = new MenuService({host: this});
1317
+ this.menuService.setProperties(this.propertyValues);
1318
+ this.menuService.subscribe(this.handleMenuChange.bind(this));
1319
+ this._contextProvider = new i$2(this, {
1320
+ context: MenuContext,
1321
+ initialValue: this.menuService
1322
+ });
1237
1323
  }
1238
1324
 
1239
1325
  /**
1240
- * Selects a single option.
1241
- * @param {AuroMenuOption} option
1326
+ * Updates the currently active option in the menu.
1327
+ * @param {HTMLElement} option - The option to set as active.
1242
1328
  */
1243
- selectOption(option) {
1244
- this.selectOptions(option);
1329
+ updateActiveOption(option) {
1330
+ this.menuService.setHighlightedOption(option);
1245
1331
  }
1246
1332
 
1247
1333
  /**
1248
- * Deselects a single option.
1249
- * @param {AuroMenuOption} option
1334
+ * Sets the internal value and manages update state.
1335
+ * @param {String|Array<String>} value - The value to set.
1336
+ * @protected
1250
1337
  */
1251
- deselectOption(option) {
1252
- this.deselectOptions(option);
1253
- }
1338
+ setInternalValue(value) {
1339
+ if (this.value !== value) {
1340
+ this.internalUpdateInProgress = true;
1341
+ this.value = value;
1254
1342
 
1255
- /**
1256
- * Toggles the selection state of a single option.
1257
- * @param {AuroMenuOption} option
1258
- */
1259
- toggleOption(option) {
1260
- if (option.selected) {
1261
- this.deselectOption(option);
1262
- } else {
1263
- this.selectOption(option);
1343
+ setTimeout(() => {
1344
+ this.internalUpdateInProgress = false;
1345
+ });
1264
1346
  }
1265
1347
  }
1266
1348
 
1267
1349
  /**
1268
- * Selects options based on their value(s) when compared to a passed value or values.
1269
- * Value or values are normalized to an array of strings that can be matched to option keys.
1270
- * @param {string|number|Array<string|number>} value - The value(s) to select.
1350
+ * Handles changes from the menu service and updates component state.
1351
+ * @param {Object} event - The event object from the menu service.
1352
+ * @protected
1271
1353
  */
1272
- selectByValue(value) {
1273
- const isEmptyValue = value === undefined ||
1274
- value === null ||
1275
- (Array.isArray(value) && value.length === 0) ||
1276
- (typeof value === 'string' && value.trim() === '');
1354
+ handleMenuChange(event) {
1355
+ if (event.type === 'valueChange') {
1277
1356
 
1278
- // Early exit for invalid/empty values
1279
- if (isEmptyValue) {
1280
- this.selectedOptions.forEach(opt => opt.selected = false);
1281
- this.selectedOptions = [];
1282
- return;
1283
- }
1357
+ // New option is array value or first option with fallback to undefined for empty array in all cases
1358
+ const newOption = this.multiSelect && event.options.length ? event.options : event.options[0] || undefined;
1359
+ const newValue = event.stringValue;
1284
1360
 
1285
- // If an internal update cycle is still in progress, defer value application
1286
- // rather than dropping it.
1287
- if (this.internalUpdateInProgress || this.host.internalUpdateInProgress) {
1288
- this.queuePendingValue(value);
1289
- return;
1290
- }
1361
+ // Check if the option or value has actually changed
1362
+ if (this.optionSelected !== newOption || this.stringValue !== newValue) {
1363
+ this.optionSelected = newOption;
1364
+ this.setInternalValue(newValue);
1365
+ }
1291
1366
 
1292
- // Normalize values to array of strings
1293
- const normalizedValues = this._getNormalizedValues(value);
1367
+ // Notify components of selection change
1368
+ this.notifySelectionChange(event);
1369
+ }
1294
1370
 
1295
- // Validate for single-select mode
1296
- let validatedValues = normalizedValues;
1297
- if (normalizedValues.length > 1 && !this.multiSelect) {
1298
- console.warn("MenuService - Multiple values provided for single-select menu. Only the first value will be selected.");
1299
- validatedValues = [normalizedValues[0]];
1371
+ if (event.type === 'highlightChange') {
1372
+ this.optionActive = event.option;
1373
+ this._index = event.index;
1300
1374
  }
1301
1375
 
1302
- if (this._menuOptions.length === 0) {
1303
- this.queuePendingValue(value);
1304
- return;
1376
+ if (event.type === 'optionsChange') {
1377
+ this.options = event.options;
1378
+ this.dispatchEvent(new CustomEvent('auroMenu-optionsChange', {
1379
+ detail: {
1380
+ options: event.options
1381
+ }
1382
+ }));
1305
1383
  }
1384
+ }
1306
1385
 
1307
- // Find matching options by comparing available options to validated values
1308
- const trackedKeys = new Set();
1309
- const optionsToSelect = this._menuOptions.filter(option => {
1310
- const passesFilter = validatedValues.includes(option.key);
1311
- const alreadyTracked = trackedKeys.has(option.key);
1312
- const isActive = option.isActive;
1386
+ /**
1387
+ * Gets the currently selected options.
1388
+ * @returns {Array<HTMLElement>}
1389
+ */
1390
+ get selectedOptions() {
1391
+ return this.menuService ? this.menuService.selectedOptions : [];
1392
+ }
1313
1393
 
1314
- trackedKeys.add(option.key);
1394
+ /**
1395
+ * Gets the first selected option, or null if none.
1396
+ * @returns {HTMLElement|null}
1397
+ */
1398
+ get selectedOption() {
1399
+ return this.menuService ? this.menuService.selectedOptions[0] : null;
1400
+ }
1315
1401
 
1316
- // Include the option in the options to be selected if it passes the filter check and
1317
- // either hasn't been tracked yet or selectAllMatchingOptions is true
1318
- return isActive && passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
1319
- });
1402
+ // Lifecycle Methods
1320
1403
 
1321
- // Handle no matches: clear existing selection, but do not dispatch an intermediate
1322
- // undefined value that can overwrite the host value in parent components.
1323
- if (!optionsToSelect.length) {
1324
- const hasUnresolvedKeys = this._menuOptions.some((option) => option.isActive && option.key == null);
1404
+ connectedCallback() {
1405
+ super.connectedCallback();
1325
1406
 
1326
- if (hasUnresolvedKeys) {
1327
- this.queuePendingValue(value);
1328
- return;
1329
- }
1407
+ this.provideContext();
1330
1408
 
1331
- this.clearPendingValue();
1409
+ // this.addEventListener('keydown', this.handleKeyDown);
1410
+ this.addEventListener('auroMenuOption-click', this.handleMouseSelect);
1411
+ this.addEventListener('auroMenuOption-mouseover', this.handleOptionHover);
1412
+ this.addEventListener('slotchange', this.handleSlotChange);
1413
+ this.setTagAttribute("auro-menu");
1414
+ }
1332
1415
 
1333
- if (this.selectedOptions.length > 0) {
1334
- this.selectedOptions = [];
1335
- }
1416
+ disconnectedCallback() {
1417
+ // this.removeEventListener('keydown', this.handleKeyDown);
1418
+ this.removeEventListener('auroMenuOption-click', this.handleMouseSelect);
1419
+ this.removeEventListener('auroMenuOption-mouseover', this.handleOptionHover);
1420
+ this.removeEventListener('slotchange', this.handleSlotChange);
1336
1421
 
1337
- // Always notify so the host resets any stale invalid value, even when
1338
- // selectedOptions was already empty (e.g. double-clicking set-invalid).
1339
- this.stageUpdate({ reason: 'no-match' });
1422
+ super.disconnectedCallback();
1423
+ }
1424
+
1425
+ firstUpdated() {
1426
+ AuroLibraryRuntimeUtils.prototype.handleComponentTagRename(this, 'auro-menu');
1427
+
1428
+ this.loadingSlots = this.querySelectorAll("[slot='loadingText'], [slot='loadingIcon']");
1429
+ this.initializeMenu();
1430
+ }
1340
1431
 
1341
- // Dispatch failure event if no matches found
1342
- if (validatedValues.length) {
1343
- this.dispatchChangeEvent('auroMenu-selectValueFailure', {
1344
- message: 'No matching options found for the provided value(s).',
1345
- values: validatedValues
1346
- });
1347
- }
1348
1432
 
1349
- return;
1350
- }
1433
+ updated(changedProperties) {
1434
+ super.updated(changedProperties);
1351
1435
 
1352
- this.clearPendingValue();
1436
+ // Apply value selection synchronously so that static-HTML fixtures
1437
+ // resolve within a single update cycle. The refactored selectByValue
1438
+ // no longer calls reset() first, so the destructive intermediate-event
1439
+ // cascade that originally required deferral is eliminated. If option
1440
+ // keys are not yet resolved (framework mount-order race), selectByValue
1441
+ // queues a bounded retry automatically via queuePendingValue.
1442
+ if (changedProperties.has('value') && !this.internalUpdateInProgress) {
1443
+ this.menuService.selectByValue(this.value);
1444
+ }
1353
1445
 
1354
- if (this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
1355
- return;
1446
+ // Handle loading state changes
1447
+ if (changedProperties.has('loading')) {
1448
+ this.setLoadingState(this.loading);
1356
1449
  }
1357
1450
 
1358
- // Apply programmatic selection as a single transaction and emit one final state.
1359
- this.selectedOptions = optionsToSelect;
1360
- this.stageUpdate();
1451
+ if (changedProperties.has('multiSelect') && this.rootMenu) {
1452
+ if (this.multiSelect) {
1453
+ this.setAttribute('aria-multiselectable', 'true');
1454
+ } else {
1455
+ this.removeAttribute('aria-multiselectable');
1456
+ }
1457
+ }
1361
1458
  }
1362
1459
 
1363
1460
  /**
1364
- * Queues a pending value and schedules a bounded retry.
1365
- * @param {string|number|Array<string|number>} value - The value to retry.
1461
+ * Sets an attribute that matches the default tag name if the tag name is not the default.
1462
+ * @param {string} tagName - The tag name to set as an attribute.
1463
+ * @private
1366
1464
  */
1367
- queuePendingValue(value) {
1368
- this._pendingValue = value;
1369
-
1370
- if (this._pendingRetryScheduled || this._pendingRetryCount >= 5) {
1371
- return;
1465
+ setTagAttribute(tagName) {
1466
+ if (this.tagName.toLowerCase() !== tagName) {
1467
+ this.setAttribute(tagName, true);
1372
1468
  }
1373
-
1374
- this._pendingRetryScheduled = true;
1375
- this._pendingRetryCount += 1;
1376
-
1377
- setTimeout(() => {
1378
- this._pendingRetryScheduled = false;
1379
-
1380
- if (this._pendingValue == null) {
1381
- return;
1382
- }
1383
-
1384
- const pendingValue = this._pendingValue;
1385
- this.selectByValue(pendingValue);
1386
- }, 0);
1387
1469
  }
1388
1470
 
1389
1471
  /**
1390
- * Clears pending retry state.
1472
+ * Sets the loading state and dispatches a loading change event.
1473
+ * @param {boolean} isLoading - Whether the menu is loading.
1474
+ * @protected
1391
1475
  */
1392
- clearPendingValue() {
1393
- this._pendingValue = null;
1394
- this._pendingRetryScheduled = false;
1395
- this._pendingRetryCount = 0;
1476
+ setLoadingState(isLoading) {
1477
+ this.setAttribute("aria-busy", isLoading);
1478
+ dispatchMenuEvent(this, "auroMenu-loadingChange", {
1479
+ loading: isLoading,
1480
+ hasLoadingPlaceholder: this.hasLoadingPlaceholder
1481
+ });
1396
1482
  }
1397
1483
 
1484
+ // Init Methods
1485
+
1398
1486
  /**
1399
- * Resets the selected options to an empty array.
1487
+ * Initializes the menu's state and structure.
1488
+ * @private
1400
1489
  */
1401
- reset() {
1402
- const previousOptions = [...this.selectedOptions];
1403
- previousOptions.forEach(opt => opt.selected = false);
1404
- this.selectedOptions = [];
1490
+ initializeMenu() {
1491
+ if (this.rootMenu) {
1492
+ this.setAttribute('role', 'listbox');
1493
+ this.setAttribute('root', '');
1405
1494
 
1406
- // Single update after clearing all
1407
- if (previousOptions.length) {
1408
- this.stageUpdate();
1495
+ if (this.multiSelect) {
1496
+ this.setAttribute('aria-multiselectable', 'true');
1497
+ }
1409
1498
  }
1410
- }
1411
1499
 
1412
- /**
1413
- * SUBSCRIPTION, NOTIFICATION AND EVENT DISPATCH METHODS
1414
- */
1500
+ this.handleNestedMenus(this);
1501
+ }
1415
1502
 
1416
1503
  /**
1417
- * Subscribes a callback to menu service events.
1418
- * @param {Function} callback - The callback to invoke on events.
1504
+ * Selects the currently highlighted option.
1505
+ * @protected
1419
1506
  */
1420
- subscribe(callback) {
1421
- this._subscribers.push(callback);
1507
+ makeSelection() {
1508
+ this.menuService.selectHighlightedOption();
1422
1509
  }
1423
1510
 
1424
1511
  /**
1425
- * Remove a previously subscribed callback from menu service events.
1426
- * @param {Function} callback
1512
+ * Resets all options to their default state.
1513
+ * @private
1427
1514
  */
1428
- unsubscribe(callback) {
1429
- this._subscribers = this._subscribers.filter(cb => cb !== callback);
1515
+ clearSelection() {
1516
+ this.optionSelected = undefined;
1517
+ this.value = undefined;
1518
+ this._index = -1;
1430
1519
  }
1431
1520
 
1432
1521
  /**
1433
- * Stages an update to notify subscribers of state and value changes.
1522
+ * Resets the menu to its initial state.
1523
+ * This is the only way to return value to undefined.
1524
+ * @public
1434
1525
  */
1435
- stageUpdate(meta = {}) {
1436
- this.notifyStateChange(meta);
1437
- this.notifyValueChange(meta);
1526
+ reset() {
1527
+ this.menuService.reset();
1528
+
1529
+ // Dispatch reset event
1530
+ dispatchMenuEvent(this, 'auroMenu-selectValueReset');
1438
1531
  }
1439
1532
 
1440
1533
  /**
1441
- * Notifies subscribers of a menu service event.
1442
- * All notifications are sent to all subscribers.
1443
- * @param {string} event - The event to send to subscribers.
1534
+ * Handles nested menu structure.
1535
+ * @private
1536
+ * @param {HTMLElement} menu - Root menu element.
1444
1537
  */
1445
- notify(event) {
1446
- this._subscribers.forEach(callback => callback(event));
1538
+ handleNestedMenus(menu) {
1539
+ menu.level = menu.parentElement.level >= 0 ? menu.parentElement.level + 1 : 0;
1540
+
1541
+ if (menu.level > 0) {
1542
+ menu.setAttribute('role', 'group');
1543
+ menu.removeAttribute("root");
1544
+ if (!menu.hasAttribute('aria-label')) {
1545
+ menu.setAttribute('aria-label', 'submenu');
1546
+ }
1547
+ }
1548
+
1549
+ const options = menu.querySelectorAll(':scope > auro-menuoption, :scope > [auro-menuoption]');
1550
+ options.forEach((option) => {
1551
+ const regex = new RegExp(this.nestingSpacer, "gu");
1552
+ option.innerHTML = this.nestingSpacer.repeat(menu.level) + option.innerHTML.replace(regex, '');
1553
+ });
1447
1554
  }
1448
1555
 
1449
1556
  /**
1450
- * Notifies subscribers of a state change (selected options has changed).
1557
+ * Navigates the menu options in the specified direction.
1558
+ * @param {'up'|'down'} direction - The direction to navigate.
1559
+ * @protected
1451
1560
  */
1452
- notifyStateChange(meta = {}) {
1453
- this.notify({
1454
- type: 'stateChange',
1455
- selectedOptions: this.selectedOptions,
1456
- ...meta
1457
- });
1561
+ navigateOptions(direction) {
1562
+ if (direction === 'up') {
1563
+ this.menuService.highlightPrevious();
1564
+ } else if (direction === 'down') {
1565
+ this.menuService.highlightNext();
1566
+ }
1458
1567
  }
1459
1568
 
1460
1569
  /**
1461
- * Notifies subscribers of a value change (current value has changed).
1570
+ * Handles slot change events.
1571
+ * @private
1462
1572
  */
1463
- notifyValueChange(meta = {}) {
1464
-
1465
- // Prepare details for the event
1466
- const details = {
1467
- value: this.currentValue,
1468
- stringValue: this.stringValue,
1469
- keys: this.currentKeys,
1470
- options: this.selectedOptions,
1471
- label: this.currentLabel
1472
- };
1473
-
1474
- // If only one option is selected, include its index
1475
- if (this.selectedOptions.length === 1) details.index = this._menuOptions.indexOf(this.selectedOptions[0]);
1476
-
1477
- this.notify({
1478
- type: 'valueChange',
1479
- ...meta,
1480
- ...details
1481
- });
1573
+ handleSlotChange() {
1574
+ if (this.rootMenu) {
1575
+ this.initializeMenu();
1576
+ }
1482
1577
  }
1483
1578
 
1484
1579
  /**
1485
- * Dispatches a custom event from the host element.
1486
- * @param {string} eventName
1487
- * @param {any} detail
1580
+ * Handles custom events defined on options.
1581
+ * @private
1582
+ * @param {HTMLElement} option - Option with custom event.
1488
1583
  */
1489
- dispatchChangeEvent(eventName, detail) {
1490
- this.host.dispatchEvent(new CustomEvent(eventName, {
1491
- bubbles: true,
1492
- cancelable: false,
1493
- composed: true,
1494
- detail
1495
- }));
1584
+ handleCustomEvent(option) {
1585
+ const eventName = option.getAttribute('event');
1586
+ dispatchMenuEvent(this, eventName);
1587
+ dispatchMenuEvent(this, 'auroMenu-customEventFired');
1496
1588
  }
1497
1589
 
1498
1590
  /**
1499
- * MENU OPTION MANAGEMENT METHODS
1591
+ * Notifies selection change to parent components.
1592
+ * @param {any} source - The source that triggers this event.
1593
+ * @private
1500
1594
  */
1595
+ notifySelectionChange({value, stringValue, keys, options, reason} = {}) {
1596
+ dispatchMenuEvent(this, 'auroMenu-selectedOption', {
1597
+ value,
1598
+ stringValue,
1599
+ keys,
1600
+ options,
1601
+ reason
1602
+ });
1603
+ }
1501
1604
 
1502
1605
  /**
1503
- * Adds a menu option to the service's list.
1504
- * @param {AuroMenuOption} option - the option to track
1606
+ * Checks if an option is currently selected.
1607
+ * @private
1608
+ * @param {HTMLElement} option - The option to check.
1609
+ * @returns {boolean}
1505
1610
  */
1506
- addMenuOption(option) {
1507
- this._menuOptions.push(option);
1508
- this.notify({ type: 'optionsChange', options: this._menuOptions });
1611
+ isOptionSelected(option) {
1612
+ if (!this.optionSelected) {
1613
+ return false;
1614
+ }
1509
1615
 
1510
- if (this._pendingValue != null) {
1511
- this.queuePendingValue(this._pendingValue);
1616
+ if (this.multiSelect) {
1617
+ // In multi-select mode, check if the option is in the selected array
1618
+ return Array.isArray(this.optionSelected) && this.optionSelected.some((selectedOption) => selectedOption === option);
1512
1619
  }
1620
+
1621
+ return this.optionSelected === option;
1513
1622
  }
1514
1623
 
1515
1624
  /**
1516
- * Removes a menu option from the service's list.
1517
- * @param {AuroMenuOption} option - the option to remove
1625
+ * Getter for loading placeholder state.
1626
+ * @returns {boolean} - True if loading slots are present and non-empty.
1518
1627
  */
1519
- removeMenuOption(option) {
1520
- this._menuOptions = this._menuOptions.filter(opt => opt !== option);
1521
- this.notify({ type: 'optionsChange', options: this._menuOptions });
1522
-
1523
- if (this._menuOptions.length === 0) {
1524
- this.clearPendingValue();
1525
- }
1628
+ get hasLoadingPlaceholder() {
1629
+ return this.loadingSlots && this.loadingSlots.length > 0;
1526
1630
  }
1527
1631
 
1528
1632
  /**
1529
- * UTILITIES
1633
+ * Getter for wrapper classes based on size.
1634
+ * @returns {Object} - Class map for the wrapper element.
1635
+ * @private
1530
1636
  */
1637
+ get wrapperClasses() {
1638
+ return e({
1639
+ 'menuWrapper': true,
1640
+ [this.size]: true,
1641
+ });
1642
+ }
1531
1643
 
1532
1644
  /**
1533
- * Normalizes a value or array of values into an array of strings for option selection.
1534
- * This function ensures that input values are consistently formatted for matching menu options.
1535
- *
1536
- * @param {string|number|Array<string|number>} value - The value(s) to normalize.
1537
- * @returns {Array<string>} An array of string values suitable for option matching.
1538
- * @throws {Error} If any value is not a string or number.
1645
+ * Logic to determine the layout of the component.
1646
+ * @protected
1647
+ * @returns {void}
1539
1648
  */
1540
- _getNormalizedValues(value) {
1541
- let values = value;
1542
-
1543
- // Handle JSON string and single value string input
1544
- if (!Array.isArray(values) && typeof values === 'string') {
1545
-
1546
- // Attempt to parse as JSON array
1547
- try {
1649
+ renderLayout() {
1650
+ if (this.loading) {
1651
+ return b`
1652
+ <div class="${this.wrapperClasses}">
1653
+ <auro-menuoption
1654
+ disabled
1655
+ loadingplaceholder
1656
+ class="${this.hasLoadingPlaceholder ? "" : "empty"}"
1657
+ >
1658
+ <div>
1659
+ <slot name="loadingIcon" class="body-lg"></slot>
1660
+ <slot name="loadingText"></slot>
1661
+ </div>
1662
+ </auro-menuoption>
1663
+ </div>
1664
+ `;
1665
+ }
1548
1666
 
1549
- // Normalize single quotes to double quotes for JSON parsing
1550
- // This will not handle complex cases but will cover basic usage
1551
- const parseValue = values.replace(/'([^']*?)'/g, '"$1"');
1667
+ return b`
1668
+ <div class="${this.wrapperClasses}">
1669
+ <slot @slotchange=${this.handleSlotChange}></slot>
1670
+ </div>
1671
+ `;
1672
+ }
1673
+ }
1552
1674
 
1553
- // Attempt parse
1554
- const parsed = JSON.parse(parseValue);
1675
+ /**
1676
+ * @license
1677
+ * Copyright 2020 Google LLC
1678
+ * SPDX-License-Identifier: BSD-3-Clause
1679
+ */
1680
+ const a=Symbol.for(""),o$1=t=>{if(t?.r===a)return t?._$litStatic$},s=t=>({_$litStatic$:t,r:a}),i=(t,...r)=>({_$litStatic$:r.reduce((r,e,a)=>r+(t=>{if(void 0!==t._$litStatic$)return t._$litStatic$;throw Error(`Value passed to 'literal' function must be a 'literal' result: ${t}. Use 'unsafeStatic' to pass non-literal values, but\n take care to ensure page security.`)})(e)+t[a+1],t[0]),r:a}),l=new Map,n=t=>(r,...e)=>{const a=e.length;let s,i;const n=[],u=[];let c,$=0,f=false;for(;$<a;){for(c=r[$];$<a&&void 0!==(i=e[$],s=o$1(i));)c+=s+r[++$],f=true;$!==a&&u.push(i),n.push(c),$++;}if($===a&&n.push(r[a]),f){const t=n.join("$$lit$$");void 0===(r=l.get(t))&&(n.raw=n,l.set(t,r=n)),e=u;}return t(r,...e)},u$1=n(b);
1555
1681
 
1556
- // Ensure parsed value is an array
1557
- if (!Array.isArray(parsed)) throw new Error('Not an array');
1682
+ var styleCss = i$6`.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}`;
1558
1683
 
1559
- // Set values to parsed array
1560
- values = parsed;
1561
- } catch (err) {
1684
+ var colorCss = i$6`: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)}}`;
1562
1685
 
1563
- // If parsing fails, treat as single value
1564
- values = [value];
1565
- }
1566
- }
1686
+ // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
1687
+ // See LICENSE in the project root for license information.
1567
1688
 
1568
- // Handle a single number being passed
1569
- if (typeof values === 'number') {
1570
- values = [String(values)];
1571
- }
1572
1689
 
1573
- // Coerce each value to string and validate types
1574
- values.forEach((val, index) => {
1690
+ class AuroDependencyVersioning {
1575
1691
 
1576
- // Throw an error for invalid value types
1577
- if (typeof val !== 'string' && typeof val !== 'number') {
1578
- throw new Error('Value contains invalid value type. Supported types are string and number.');
1579
- }
1692
+ /**
1693
+ * Generates a unique string to be used for child auro element naming.
1694
+ * @private
1695
+ * @param {string} baseName - Defines the first part of the unique element name.
1696
+ * @param {string} version - Version of the component that will be appended to the baseName.
1697
+ * @returns {string} - Unique string to be used for naming.
1698
+ */
1699
+ generateElementName(baseName, version) {
1700
+ let result = baseName;
1580
1701
 
1581
- // Convert numbers to strings for consistency
1582
- if (typeof val === 'number') {
1583
- values[index] = String(val);
1584
- }
1585
- });
1702
+ result += '-';
1703
+ result += version.replace(/[.]/g, '_');
1586
1704
 
1587
- // Return the resulting array of string values
1588
- return values;
1705
+ return result;
1589
1706
  }
1590
1707
 
1591
1708
  /**
1592
- * Returns whether two arrays of options contain the same elements.
1593
- * @param {AuroMenuOption[]} arr1 - First array of options.
1594
- * @param {AuroMenuOption[]} arr2 - Second array of options.
1595
- * @returns {boolean} True if arrays match, false otherwise.
1709
+ * Generates a unique string to be used for child auro element naming.
1710
+ * @param {string} baseName - Defines the first part of the unique element name.
1711
+ * @param {string} version - Version of the component that will be appended to the baseName.
1712
+ * @returns {string} - Unique string to be used for naming.
1596
1713
  */
1597
- optionsArraysMatch(arr1, arr2) {
1598
- if (arr1.length !== arr2.length) return false;
1599
-
1600
- const set1 = new Set(arr1);
1601
- const set2 = new Set(arr2);
1714
+ generateTag(baseName, version, tagClass) {
1715
+ const elementName = this.generateElementName(baseName, version);
1716
+ const tag = i`${s(elementName)}`;
1602
1717
 
1603
- for (let item of set1) {
1604
- if (!set2.has(item)) {
1605
- return false;
1606
- }
1718
+ if (!customElements.get(elementName)) {
1719
+ customElements.define(elementName, class extends tagClass {});
1607
1720
  }
1608
1721
 
1609
- return true;
1722
+ return tag;
1610
1723
  }
1611
1724
  }
1612
1725
 
1613
- const MenuContext = n$1('menu-context');
1726
+ /**
1727
+ * @license
1728
+ * Copyright 2018 Google LLC
1729
+ * SPDX-License-Identifier: BSD-3-Clause
1730
+ */const o=o=>o??A;
1614
1731
 
1615
- /* eslint-disable no-underscore-dangle */
1616
- // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1732
+ 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$3{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$6`: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}
1733
+ `;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$6`.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}
1734
+ `;var M=i$6`: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)}
1735
+ `;var _=i$6`: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)}
1736
+ `;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`
1737
+ <div class="componentWrapper">
1738
+ <div
1739
+ class="${e({svgWrapper:true})}"
1740
+ title="${o(this.title||void 0)}">
1741
+ <span aria-hidden="${o(this.ariaHidden||true)}" part="svg">
1742
+ ${this.customSvg?b`
1743
+ <slot name="svg"></slot>
1744
+ `:b`
1745
+ ${this.svg}
1746
+ `}
1747
+ </span>
1748
+ </div>
1749
+
1750
+ <div class="${e(t)}" part="label">
1751
+ <slot></slot>
1752
+ </div>
1753
+ </div>
1754
+ `}}
1755
+
1756
+ var iconVersion = '9.1.2';
1757
+
1758
+ 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>"};
1759
+
1760
+ // Copyright (c) 2021 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1617
1761
  // See LICENSE in the project root for license information.
1618
1762
 
1619
1763
 
1764
+ let menuOptionIdCounter = 0;
1620
1765
 
1621
1766
  /**
1622
- * The `auro-menu` element provides users a way to select from a list of options.
1623
- * @customElement auro-menu
1767
+ * The `auro-menuoption` element provides users a way to define a menu option.
1768
+ * @customElement auro-menuoption
1624
1769
  *
1625
- * @event {CustomEvent<Element>} auroMenu-activatedOption - Notifies that a menuoption has been made `active`.
1626
- * @event {CustomEvent<any>} auroMenu-customEventFired - Notifies that a custom event has been fired.
1627
- * @event {CustomEvent<{ loading: boolean; hasLoadingPlaceholder: boolean; }>} auroMenu-loadingChange - Notifies when the loading attribute is changed.
1628
- * @event {CustomEvent<any>} auroMenu-selectValueFailure - Notifies that an attempt to select a menuoption by matching a value has failed.
1629
- * @event {CustomEvent<{ values: HTMLElement[] }>} auroMenu-deselectPrevented - Notifies that deselection was prevented and includes the affected options in `detail.values`.
1630
- * @event {CustomEvent<any>} auroMenu-selectValueReset - Notifies that the component value has been reset.
1631
- * @event {CustomEvent<any>} auroMenu-selectedOption - Notifies that a new menuoption selection has been made.
1632
- * @slot loadingText - Text to show while loading attribute is set
1633
- * @slot loadingIcon - Icon to show while loading attribute is set
1634
- * @slot - Slot for insertion of menu options.
1770
+ * @slot default - The default slot for the menu option text.
1771
+ *
1772
+ * @event auroMenuOption-mouseover - Notifies that this option has been hovered over.
1635
1773
  */
1774
+ class AuroMenuOption extends AuroElement {
1636
1775
 
1637
- /* eslint-disable max-lines */
1776
+ /**
1777
+ * This will register this element with the browser.
1778
+ * @param {string} [name="auro-menuoption"] - The name of the element that you want to register.
1779
+ *
1780
+ * @example
1781
+ * AuroMenuOption.register("custom-menuoption") // this will register this element to <custom-menuoption/>
1782
+ *
1783
+ */
1784
+ static register(name = "auro-menuoption") {
1785
+ AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroMenuOption);
1786
+ }
1638
1787
 
1639
- class AuroMenu extends AuroElement {
1788
+ /**
1789
+ * Returns whether the menu option is currently active and selectable.
1790
+ * An option is considered active if it is not hidden, not disabled, and not static.
1791
+ * @returns {boolean} True if the option is active, false otherwise.
1792
+ */
1793
+ get isActive() {
1794
+ return !this.hasAttribute('hidden') &&
1795
+ !this.disabled &&
1796
+ !this.hasAttribute('static');
1797
+ }
1640
1798
 
1641
1799
  constructor() {
1642
1800
  super();
1643
1801
 
1644
- // State properties (reactive)
1802
+ this.bindEvents();
1645
1803
 
1646
1804
  /**
1647
1805
  * @private
1648
1806
  */
1649
- this.shape = "box";
1807
+ this.shape = undefined;
1650
1808
 
1651
1809
  /**
1652
1810
  * @private
1653
1811
  */
1654
- this.size = "sm";
1655
-
1656
- // Value of the selected options
1657
- this.value = undefined;
1658
- // Currently selected option
1659
- this.optionSelected = undefined;
1660
- // String used for highlighting/filtering
1661
- this.matchWord = undefined;
1662
- // Hide the checkmark icon on selected options
1663
- this.noCheckmark = false;
1664
- // Currently active option
1665
- this.optionActive = undefined;
1666
- // Loading state
1667
- this.loading = false;
1668
- // Multi-select mode
1669
- this.multiSelect = false;
1670
- // Allow deselecting of menu options
1671
- this.allowDeselect = false;
1672
- // Select all matching options when setting value in multi-select mode
1673
- this.selectAllMatchingOptions = false;
1674
-
1675
- // Event Bindings
1812
+ this.size = undefined;
1676
1813
 
1677
1814
  /**
1678
- * @private
1815
+ * Generate unique names for dependency components.
1679
1816
  */
1680
- this.handleSlotChange = this.handleSlotChange.bind(this);
1817
+ const versioning = new AuroDependencyVersioning();
1818
+ this.iconTag = versioning.generateTag('auro-formkit-menuoption-icon', iconVersion, k);
1681
1819
 
1682
- // Instance properties (non-reactive)
1820
+ this.selected = false;
1821
+ this.noCheckmark = false;
1822
+ this.disabled = false;
1823
+ this.noMatch = false;
1683
1824
 
1684
1825
  /**
1685
1826
  * @private
1686
1827
  */
1687
- Object.assign(this, {
1688
- // Root-level menu (true) or a nested submenu (false)
1689
- rootMenu: true,
1690
- // Currently focused/active menu item index
1691
- _index: -1,
1692
- // Nested menu spacer
1693
- nestingSpacer: '<span class="nestingSpacer"></span>',
1694
- // Loading indicator for slot elements
1695
- loadingSlots: null,
1696
- });
1828
+ this.runtimeUtils = new AuroLibraryRuntimeUtils();
1829
+
1830
+ // Initialize context-related properties
1831
+ this.menuService = null;
1832
+ this.unsubscribe = null;
1833
+
1834
+ /**
1835
+ * @private
1836
+ */
1837
+ this.handleMenuChange = this.handleMenuChange.bind(this);
1697
1838
  }
1698
1839
 
1699
1840
  static get properties() {
@@ -1701,15 +1842,7 @@ class AuroMenu extends AuroElement {
1701
1842
  ...super.properties,
1702
1843
 
1703
1844
  /**
1704
- * Allows deselecting an already selected option when clicked again in single-select mode.
1705
- */
1706
- allowDeselect: {
1707
- type: Boolean,
1708
- reflect: true,
1709
- },
1710
-
1711
- /**
1712
- * When true, the entire menu and all options are disabled.
1845
+ * When true, disables the menu option.
1713
1846
  */
1714
1847
  disabled: {
1715
1848
  type: Boolean,
@@ -1717,542 +1850,409 @@ class AuroMenu extends AuroElement {
1717
1850
  },
1718
1851
 
1719
1852
  /**
1720
- * Indicates whether the menu has a loadingIcon or loadingText to render when in a loading state.
1721
- */
1722
- hasLoadingPlaceholder: {
1723
- type: Boolean
1724
- },
1725
-
1726
- /**
1727
- * @private
1728
- */
1729
- layout: {
1730
- type: String
1731
- },
1732
-
1733
- /**
1734
- * Indent level for submenus.
1735
1853
  * @private
1736
1854
  */
1737
- level: {
1738
- type: Number,
1739
- reflect: false,
1740
- attribute: false
1741
- },
1742
-
1743
- /**
1744
- * When true, displays a loading state using the loadingIcon and loadingText slots if provided.
1745
- */
1746
- loading: {
1747
- type: Boolean,
1748
- reflect: true
1749
- },
1750
-
1751
- /**
1752
- * Specifies a string used to highlight matched string parts in options.
1753
- */
1754
- matchWord: {
1755
- type: String,
1756
- attribute: 'matchword'
1757
- },
1758
-
1759
- /**
1760
- * When true, the selected option can be multiple options.
1761
- */
1762
- multiSelect: {
1763
- type: Boolean,
1764
- reflect: true,
1765
- attribute: 'multiselect'
1766
- },
1767
-
1768
- /**
1769
- * When true, selected option will not show the checkmark.
1770
- */
1771
- noCheckmark: {
1772
- type: Boolean,
1773
- reflect: true,
1774
- attribute: 'nocheckmark'
1775
- },
1776
-
1777
- /**
1778
- * Specifies the current active menuOption.
1779
- */
1780
- optionActive: {
1781
- type: Object,
1782
- attribute: 'optionactive'
1783
- },
1784
-
1785
- /**
1786
- * An array of currently selected menu options, type `HTMLElement` by default. In multi-select mode, `optionSelected` is an array of HTML elements.
1787
- */
1788
- optionSelected: {
1789
- // Allow HTMLElement, HTMLElement[] arrays and undefined
1790
- type: Object
1791
- },
1792
-
1793
- /**
1794
- * Available menu options.
1795
- * @readonly
1796
- */
1797
- options: {
1798
- type: Array,
1799
- reflect: false,
1800
- attribute: false
1801
- },
1802
-
1803
- /**
1804
- * Sets the size of the menu.
1805
- * @type {'sm' | 'md'}
1806
- * @default 'sm'
1807
- */
1808
- size: {
1855
+ event: {
1809
1856
  type: String,
1810
1857
  reflect: true
1811
1858
  },
1812
1859
 
1813
1860
  /**
1814
- * When true, selects all options that match the provided value/key when setting value and multiselect is enabled.
1861
+ * @private
1815
1862
  */
1816
- selectAllMatchingOptions: {
1817
- type: Boolean,
1818
- reflect: true,
1863
+ layout: {
1864
+ type: String
1819
1865
  },
1820
1866
 
1821
1867
  /**
1822
- * Sets the shape of the menu.
1823
- * @type {'box' | 'round'}
1824
- * @default 'box'
1868
+ * 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.
1825
1869
  */
1826
- shape: {
1870
+ key: {
1827
1871
  type: String,
1828
1872
  reflect: true
1829
1873
  },
1830
1874
 
1831
1875
  /**
1832
- * The value of the selected option. In multi-select mode, this is a JSON stringified array of selected option values.
1876
+ * @private
1833
1877
  */
1834
- value: {
1835
- type: String,
1836
- reflect: true,
1837
- attribute: 'value'
1838
- }
1839
- };
1840
- }
1841
-
1842
- static get styles() {
1843
- return [
1844
- styleCss$1,
1845
- colorCss$1,
1846
- tokensCss
1847
- ];
1848
- }
1849
-
1850
- /**
1851
- * @readonly
1852
- * @returns {string} - Returns the label of the currently selected option(s).
1853
- */
1854
- get currentLabel() {
1855
- return this.menuService.currentLabel;
1856
- };
1857
-
1858
- /**
1859
- * @readonly
1860
- * @returns {Array<HTMLElement>} - Returns the array of available menu options.
1861
- * @deprecated Use `options` property instead.
1862
- */
1863
- get items() {
1864
- return this.options;
1865
- }
1866
-
1867
- /**
1868
- * @returns {number} - Returns the index of the currently active option.
1869
- */
1870
- get index() {
1871
- return this._index;
1872
- }
1873
-
1874
- /**
1875
- * @param {number} value - Sets the index of the currently active option.
1876
- */
1877
- set index(value) {
1878
- this.menuService.setHighlightedIndex(value);
1879
- }
1880
-
1881
- /**
1882
- * This will register this element with the browser.
1883
- * @param {string} [name="auro-menu"] - The name of the element that you want to register.
1884
- *
1885
- * @example
1886
- * AuroMenu.register("custom-menu") // this will register this element to <custom-menu/>
1887
- *
1888
- */
1889
- static register(name = "auro-menu") {
1890
- AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroMenu);
1891
- }
1892
-
1893
- /**
1894
- * Formatted value based on `multiSelect` state.
1895
- * Default type is `String`, changing to `Array<String>` when `multiSelect` is true.
1896
- * @private
1897
- * @returns {String|Array<String>}
1898
- */
1899
- get formattedValue() {
1900
- return this.menuService.currentValue;
1901
- }
1902
-
1903
- /**
1904
- * Gets the current property values for the menu service.
1905
- * @private
1906
- * @returns {Object}
1907
- */
1908
- get propertyValues() {
1909
- return {
1910
- size: this.size,
1911
- shape: this.shape,
1912
- noCheckmark: this.nocheckmark,
1913
- disabled: this.disabled
1914
- };
1915
- }
1916
-
1917
- /**
1918
- * Provides the menu context to child components.
1919
- * Initializes the MenuService and subscribes to menu changes.
1920
- * @protected
1921
- */
1922
- provideContext() {
1923
- if (this.parentElement && this.parentElement.closest('auro-menu, [auro-menu]')) {
1924
- this.rootMenu = false;
1925
- this.menuService = this.parentElement.menuService;
1926
- this._contextProvider = this.parentElement._contextProvider;
1927
- return;
1928
- }
1929
-
1930
- this.menuService = new MenuService({host: this});
1931
- this.menuService.setProperties(this.propertyValues);
1932
- this.menuService.subscribe(this.handleMenuChange.bind(this));
1933
- this._contextProvider = new i$2(this, {
1934
- context: MenuContext,
1935
- initialValue: this.menuService
1936
- });
1937
- }
1938
-
1939
- /**
1940
- * Updates the currently active option in the menu.
1941
- * @param {HTMLElement} option - The option to set as active.
1942
- */
1943
- updateActiveOption(option) {
1944
- this.menuService.setHighlightedOption(option);
1945
- }
1946
-
1947
- /**
1948
- * Sets the internal value and manages update state.
1949
- * @param {String|Array<String>} value - The value to set.
1950
- * @protected
1951
- */
1952
- setInternalValue(value) {
1953
- if (this.value !== value) {
1954
- this.internalUpdateInProgress = true;
1955
- this.value = value;
1956
-
1957
- setTimeout(() => {
1958
- this.internalUpdateInProgress = false;
1959
- });
1960
- }
1961
- }
1962
-
1963
- /**
1964
- * Handles changes from the menu service and updates component state.
1965
- * @param {Object} event - The event object from the menu service.
1966
- * @protected
1967
- */
1968
- handleMenuChange(event) {
1969
- if (event.type === 'valueChange') {
1878
+ menuService: {
1879
+ type: Object,
1880
+ state: true
1881
+ },
1970
1882
 
1971
- // New option is array value or first option with fallback to undefined for empty array in all cases
1972
- const newOption = this.multiSelect && event.options.length ? event.options : event.options[0] || undefined;
1973
- const newValue = event.stringValue;
1883
+ /**
1884
+ * @private
1885
+ */
1886
+ matchWord: {
1887
+ type: String,
1888
+ state: true
1889
+ },
1974
1890
 
1975
- // Check if the option or value has actually changed
1976
- if (this.optionSelected !== newOption || this.stringValue !== newValue) {
1977
- this.optionSelected = newOption;
1978
- this.setInternalValue(newValue);
1979
- }
1891
+ /**
1892
+ * @private
1893
+ */
1894
+ noCheckmark: {
1895
+ type: Boolean,
1896
+ reflect: true
1897
+ },
1980
1898
 
1981
- // Notify components of selection change
1982
- this.notifySelectionChange(event);
1983
- }
1899
+ /**
1900
+ * 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.
1901
+ */
1902
+ noMatch: {
1903
+ type: Boolean,
1904
+ reflect: true,
1905
+ attribute: 'nomatch'
1906
+ },
1984
1907
 
1985
- if (event.type === 'highlightChange') {
1986
- this.optionActive = event.option;
1987
- this._index = event.index;
1988
- }
1908
+ /**
1909
+ * Specifies that an option is selected.
1910
+ */
1911
+ selected: {
1912
+ type: Boolean,
1913
+ reflect: true
1914
+ },
1989
1915
 
1990
- if (event.type === 'optionsChange') {
1991
- this.options = event.options;
1992
- this.dispatchEvent(new CustomEvent('auroMenu-optionsChange', {
1993
- detail: {
1994
- options: event.options
1995
- }
1996
- }));
1997
- }
1998
- }
1916
+ /**
1917
+ * Specifies the tab index of the menu option.
1918
+ */
1919
+ tabIndex: {
1920
+ type: Number,
1921
+ reflect: true
1922
+ },
1999
1923
 
2000
- /**
2001
- * Gets the currently selected options.
2002
- * @returns {Array<HTMLElement>}
2003
- */
2004
- get selectedOptions() {
2005
- return this.menuService ? this.menuService.selectedOptions : [];
1924
+ /**
1925
+ * Specifies the value to be sent to a server.
1926
+ */
1927
+ value: {
1928
+ type: String,
1929
+ reflect: true
1930
+ },
1931
+ };
2006
1932
  }
2007
1933
 
2008
- /**
2009
- * Gets the first selected option, or null if none.
2010
- * @returns {HTMLElement|null}
2011
- */
2012
- get selectedOption() {
2013
- return this.menuService ? this.menuService.selectedOptions[0] : null;
1934
+ static get styles() {
1935
+ return [
1936
+ styleCss,
1937
+ colorCss,
1938
+ tokensCss
1939
+ ];
2014
1940
  }
2015
1941
 
2016
- // Lifecycle Methods
2017
-
2018
1942
  connectedCallback() {
2019
1943
  super.connectedCallback();
2020
1944
 
2021
- this.provideContext();
2022
-
2023
- // this.addEventListener('keydown', this.handleKeyDown);
2024
- this.addEventListener('auroMenuOption-click', this.handleMouseSelect);
2025
- this.addEventListener('auroMenuOption-mouseover', this.handleOptionHover);
2026
- this.addEventListener('slotchange', this.handleSlotChange);
2027
- this.setTagAttribute("auro-menu");
2028
- }
1945
+ // Add the tag name as an attribute if it is different than the component name
1946
+ // Add this step soon as this node gets attached to the DOM to avoid racing condition with menu's value setting logic.
1947
+ this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
2029
1948
 
2030
- disconnectedCallback() {
2031
- // this.removeEventListener('keydown', this.handleKeyDown);
2032
- this.removeEventListener('auroMenuOption-click', this.handleMouseSelect);
2033
- this.removeEventListener('auroMenuOption-mouseover', this.handleOptionHover);
2034
- this.removeEventListener('slotchange', this.handleSlotChange);
1949
+ // Set up context consumption in connectedCallback
1950
+ this._contextConsumer = new s$2(this, {
1951
+ context: MenuContext,
1952
+ callback: this.attachTo.bind(this),
1953
+ subscribe: true
1954
+ });
2035
1955
 
2036
- super.disconnectedCallback();
1956
+ // Establish the key property as early as possible.
1957
+ // When a framework (e.g. Svelte) inserts the element into the DOM before
1958
+ // setting its `value` property, both `getAttribute('value')` and
1959
+ // `getAttribute('key')` return null here. Setting `this.key = null`
1960
+ // would block the fallback in `updated()` that assigns key from the
1961
+ // value property (the guard checked `=== undefined`). Only assign key
1962
+ // if at least one source attribute is actually present so that the
1963
+ // `updated()` fallback can run when the value property arrives later.
1964
+ const valueAttr = this.getAttribute('value');
1965
+ const keyAttr = this.getAttribute('key');
1966
+ const resolvedKey = keyAttr !== null ? keyAttr : valueAttr;
1967
+ if (resolvedKey !== null) {
1968
+ this.key = resolvedKey;
1969
+ }
2037
1970
  }
2038
1971
 
2039
1972
  firstUpdated() {
2040
- AuroLibraryRuntimeUtils.prototype.handleComponentTagRename(this, 'auro-menu');
1973
+ // Add the tag name as an attribute if it is different than the component name
1974
+ this.runtimeUtils.handleComponentTagRename(this, 'auro-menuoption');
2041
1975
 
2042
- this.loadingSlots = this.querySelectorAll("[slot='loadingText'], [slot='loadingIcon']");
2043
- this.initializeMenu();
2044
- }
1976
+ // Generate unique ID if not already set (required for aria-activedescendant)
1977
+ if (!this.id) {
1978
+ menuOptionIdCounter += 1;
1979
+ this.id = `menuoption-${menuOptionIdCounter}`;
1980
+ }
1981
+
1982
+ this.setAttribute('role', 'option');
1983
+ this.setAttribute('aria-selected', 'false');
2045
1984
 
1985
+ this.addEventListener('mouseover', () => {
1986
+ this.dispatchEvent(new CustomEvent('auroMenuOption-mouseover', {
1987
+ bubbles: true,
1988
+ cancelable: false,
1989
+ composed: true,
1990
+ detail: this
1991
+ }));
1992
+ });
1993
+ }
2046
1994
 
2047
1995
  updated(changedProperties) {
2048
1996
  super.updated(changedProperties);
2049
1997
 
2050
- // Apply value selection synchronously so that static-HTML fixtures
2051
- // resolve within a single update cycle. The refactored selectByValue
2052
- // no longer calls reset() first, so the destructive intermediate-event
2053
- // cascade that originally required deferral is eliminated. If option
2054
- // keys are not yet resolved (framework mount-order race), selectByValue
2055
- // queues a bounded retry automatically via queuePendingValue.
2056
- if (changedProperties.has('value') && !this.internalUpdateInProgress) {
2057
- this.menuService.selectByValue(this.value);
2058
- }
1998
+ // Update aria-selected attribute if selected changed
1999
+ if (changedProperties.has('selected')) {
2059
2000
 
2060
- // Handle loading state changes
2061
- if (changedProperties.has('loading')) {
2062
- this.setLoadingState(this.loading);
2001
+ // Update aria-selected attribute
2002
+ this.setAttribute('aria-selected', this.selected.toString());
2003
+
2004
+ // Update menu service selection state if this isn't an internal update
2005
+ if (this.internalUpdateInProgress !== true && this.menuService) {
2006
+ this.menuService[this.selected ? 'selectOption' : 'deselectOption'](this);
2007
+ }
2063
2008
  }
2064
2009
 
2065
- if (changedProperties.has('multiSelect') && this.rootMenu) {
2066
- if (this.multiSelect) {
2067
- this.setAttribute('aria-multiselectable', 'true');
2010
+ if (changedProperties.has('disabled')) {
2011
+ if (this.disabled) {
2012
+ this.setAttribute('aria-disabled', 'true');
2068
2013
  } else {
2069
- this.removeAttribute('aria-multiselectable');
2014
+ this.removeAttribute('aria-disabled');
2070
2015
  }
2071
2016
  }
2017
+
2018
+ if (changedProperties.has('active')) {
2019
+ this.updateActiveClasses();
2020
+ }
2021
+
2022
+ // Update text highlight if matchWord changed
2023
+ if (changedProperties.has('matchWord')) {
2024
+ this.updateTextHighlight();
2025
+ }
2026
+
2027
+ // Set the key to be the passed value if no key is provided.
2028
+ // Loose equality (== null) is intentional: it catches both null AND
2029
+ // undefined. When a framework (e.g. Svelte, React) inserts the element
2030
+ // before setting its value property, connectedCallback skips key
2031
+ // assignment because both attributes are null at that point. The Lit
2032
+ // property default for `key` is undefined (not null), so strict
2033
+ // === null would miss the case and the fallback would never run.
2034
+ if (changedProperties.has('value') && this.key == null) { // eslint-disable-line eqeqeq, no-eq-null
2035
+ this.key = this.value;
2036
+ }
2037
+ }
2038
+
2039
+ disconnectedCallback() {
2040
+ if (this.menuService) {
2041
+ this.menuService.unsubscribe(this.handleMenuChange);
2042
+ this.menuService.removeMenuOption(this);
2043
+ }
2072
2044
  }
2073
2045
 
2074
2046
  /**
2075
- * Sets an attribute that matches the default tag name if the tag name is not the default.
2076
- * @param {string} tagName - The tag name to set as an attribute.
2077
- * @private
2047
+ * Sets up event listeners for user interaction with the menu option.
2048
+ * This function enables click and mouse enter events to trigger selection and highlighting logic.
2078
2049
  */
2079
- setTagAttribute(tagName) {
2080
- if (this.tagName.toLowerCase() !== tagName) {
2081
- this.setAttribute(tagName, true);
2082
- }
2050
+ bindEvents() {
2051
+ this.addEventListener('click', this.handleClick.bind(this));
2052
+ this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
2083
2053
  }
2084
2054
 
2085
2055
  /**
2086
- * Sets the loading state and dispatches a loading change event.
2087
- * @param {boolean} isLoading - Whether the menu is loading.
2088
- * @protected
2056
+ * Attaches this menu option to a menu service and subscribes to its events.
2057
+ * This method enables the option to participate in menu selection and highlighting logic.
2058
+ * @param {Object} service - The menu service instance to attach to.
2089
2059
  */
2090
- setLoadingState(isLoading) {
2091
- this.setAttribute("aria-busy", isLoading);
2092
- dispatchMenuEvent(this, "auroMenu-loadingChange", {
2093
- loading: isLoading,
2094
- hasLoadingPlaceholder: this.hasLoadingPlaceholder
2095
- });
2060
+ attachTo(service) {
2061
+ if (!service) {
2062
+ return;
2063
+ }
2064
+ this.menuService = service;
2065
+ this.menuService.addMenuOption(this);
2066
+ this.menuService.subscribe(this.handleMenuChange);
2096
2067
  }
2097
2068
 
2098
- // Init Methods
2069
+ /**
2070
+ * Handles changes from the menu service and updates the option's state.
2071
+ * This function synchronizes the option's properties and selection/highlight state with menu events.
2072
+ * @param {Object} event - The event object from the menu service.
2073
+ */
2074
+ handleMenuChange(event) {
2075
+
2076
+ // Ignore events without a type or property
2077
+ if (!event || (!event.type && !event.property)) {
2078
+ return;
2079
+ }
2080
+
2081
+ // Update reactive properties based on event type
2082
+ if (event.property && Object.keys(AuroMenuOption.properties).includes(event.property)) {
2083
+ this[event.property] = event.value;
2084
+ }
2085
+
2086
+ // Handle highlight changes
2087
+ if (event.type === 'highlightChange') {
2088
+ const isActive = event.option === this;
2089
+ this.active = isActive;
2090
+ this.updateActiveClasses();
2091
+ }
2092
+
2093
+ if (event.type === 'stateChange') {
2094
+ const isSelected = event.selectedOptions.includes(this);
2095
+ this.setInternalSelected(isSelected);
2096
+ }
2097
+ }
2099
2098
 
2100
2099
  /**
2101
- * Initializes the menu's state and structure.
2102
- * @private
2100
+ * Updates the internal selected state of the menu option bypassing 'updated' and triggers custom events if selected.
2101
+ * This function ensures the option's selection state is synchronized with menu logic and notifies listeners.
2102
+ * @param {boolean} isSelected - Whether the option should be marked as selected.
2103
2103
  */
2104
- initializeMenu() {
2105
- if (this.rootMenu) {
2106
- this.setAttribute('role', 'listbox');
2107
- this.setAttribute('root', '');
2104
+ setInternalSelected(isSelected) {
2105
+ this.internalUpdateInProgress = true;
2106
+ this.selected = isSelected;
2108
2107
 
2109
- if (this.multiSelect) {
2110
- this.setAttribute('aria-multiselectable', 'true');
2111
- }
2108
+ // Fire custom event if selected
2109
+ if (isSelected) {
2110
+ this.handleCustomEvent();
2112
2111
  }
2113
2112
 
2114
- this.handleNestedMenus(this);
2113
+ setTimeout(() => {
2114
+ this.internalUpdateInProgress = false;
2115
+ }, 0);
2115
2116
  }
2116
2117
 
2117
2118
  /**
2118
- * Selects the currently highlighted option.
2119
- * @protected
2119
+ * Sets the selected state of the menu option.
2120
+ * This function updates whether the option is currently selected.
2121
+ * @param {boolean} isSelected - Whether the option should be marked as selected.
2122
+ * @deprecated Simply modify the `selected` property directly instead.
2120
2123
  */
2121
- makeSelection() {
2122
- this.menuService.selectHighlightedOption();
2124
+ setSelected(isSelected) {
2125
+ this.selected = isSelected;
2123
2126
  }
2124
2127
 
2125
2128
  /**
2126
- * Resets all options to their default state.
2127
- * @private
2129
+ * Updates the active state and visual highlighting of the menu option.
2130
+ * This function toggles the option's active status and applies or removes the active CSS class.
2131
+ * @param {boolean} isActive - Whether the option should be marked as active.
2132
+ * @deprecated Simply modify the `active` property directly instead.
2128
2133
  */
2129
- clearSelection() {
2130
- this.optionSelected = undefined;
2131
- this.value = undefined;
2132
- this._index = -1;
2134
+ updateActive(isActive) {
2135
+
2136
+ // Set active state
2137
+ this.active = isActive;
2138
+ this.updateActiveClasses();
2133
2139
  }
2134
2140
 
2135
2141
  /**
2136
- * Resets the menu to its initial state.
2137
- * This is the only way to return value to undefined.
2138
- * @public
2142
+ * Updates the CSS class for the menu option based on its active state.
2143
+ * This function adds or removes the 'active' class to visually indicate the option's active status.
2144
+ * @private
2139
2145
  */
2140
- reset() {
2141
- this.menuService.reset();
2142
-
2143
- // Dispatch reset event
2144
- dispatchMenuEvent(this, 'auroMenu-selectValueReset');
2146
+ updateActiveClasses() {
2147
+ // Update class based on active state
2148
+ if (this.active) this.classList.add('active');
2149
+ else this.classList.remove('active');
2145
2150
  }
2146
2151
 
2152
+
2147
2153
  /**
2148
- * Handles nested menu structure.
2154
+ * Updates the visual highlighting of text within the menu option based on the current match word.
2155
+ * This function highlights matching text segments and manages nested spacers for display formatting.
2149
2156
  * @private
2150
- * @param {HTMLElement} menu - Root menu element.
2151
2157
  */
2152
- handleNestedMenus(menu) {
2153
- menu.level = menu.parentElement.level >= 0 ? menu.parentElement.level + 1 : 0;
2158
+ updateTextHighlight() {
2154
2159
 
2155
- if (menu.level > 0) {
2156
- menu.setAttribute('role', 'group');
2157
- menu.removeAttribute("root");
2158
- if (!menu.hasAttribute('aria-label')) {
2159
- menu.setAttribute('aria-label', 'submenu');
2160
- }
2160
+ // Regex for matchWord if needed
2161
+ let regexWord = null;
2162
+
2163
+ if (this.matchWord && this.matchWord.length) {
2164
+ const escapedWord = this.matchWord.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
2165
+ regexWord = new RegExp(escapedWord, 'giu');
2161
2166
  }
2162
2167
 
2163
- const options = menu.querySelectorAll(':scope > auro-menuoption, :scope > [auro-menuoption]');
2164
- options.forEach((option) => {
2165
- const regex = new RegExp(this.nestingSpacer, "gu");
2166
- option.innerHTML = this.nestingSpacer.repeat(menu.level) + option.innerHTML.replace(regex, '');
2167
- });
2168
- }
2168
+ // Update text highlighting if matchWord changed
2169
+ if (regexWord &&
2170
+ this.isActive && !this.hasAttribute('persistent')) {
2171
+ const nested = this.querySelectorAll('.nestingSpacer');
2169
2172
 
2170
- /**
2171
- * Navigates the menu options in the specified direction.
2172
- * @param {'up'|'down'} direction - The direction to navigate.
2173
- * @protected
2174
- */
2175
- navigateOptions(direction) {
2176
- if (direction === 'up') {
2177
- this.menuService.highlightPrevious();
2178
- } else if (direction === 'down') {
2179
- this.menuService.highlightNext();
2180
- }
2181
- }
2173
+ const displayValueEl = this.querySelector('[slot="displayValue"]');
2174
+ if (displayValueEl) {
2175
+ this.removeChild(displayValueEl);
2176
+ }
2182
2177
 
2183
- /**
2184
- * Handles slot change events.
2185
- * @private
2186
- */
2187
- handleSlotChange() {
2188
- if (this.rootMenu) {
2189
- this.initializeMenu();
2178
+ // Create nested spacers
2179
+ const nestingSpacerBundle = [...nested].map(() => this.nestingSpacer).join('');
2180
+
2181
+ // Update with spacers and matchWord
2182
+ this.innerHTML = nestingSpacerBundle +
2183
+ this.textContent.replace(
2184
+ regexWord,
2185
+ (match) => `<strong>${match}</strong>`
2186
+ );
2187
+ if (displayValueEl) {
2188
+ this.append(displayValueEl);
2189
+ }
2190
2190
  }
2191
2191
  }
2192
2192
 
2193
2193
  /**
2194
- * Handles custom events defined on options.
2194
+ * Handles click events on the menu option, toggling its selected state.
2195
+ * This function dispatches a click event and updates selection if the option is not disabled.
2195
2196
  * @private
2196
- * @param {HTMLElement} option - Option with custom event.
2197
2197
  */
2198
- handleCustomEvent(option) {
2199
- const eventName = option.getAttribute('event');
2200
- dispatchMenuEvent(this, eventName);
2201
- dispatchMenuEvent(this, 'auroMenu-customEventFired');
2198
+ handleClick() {
2199
+ if (!this.disabled) {
2200
+ this.dispatchClickEvent();
2201
+ this.selected = !this.selected;
2202
+ }
2202
2203
  }
2203
2204
 
2204
2205
  /**
2205
- * Notifies selection change to parent components.
2206
- * @param {any} source - The source that triggers this event.
2206
+ * Handles mouse enter events to highlight the menu option.
2207
+ * This function updates the menu service to set this option as the currently highlighted item if not disabled.
2207
2208
  * @private
2208
2209
  */
2209
- notifySelectionChange({value, stringValue, keys, options, reason} = {}) {
2210
- dispatchMenuEvent(this, 'auroMenu-selectedOption', {
2211
- value,
2212
- stringValue,
2213
- keys,
2214
- options,
2215
- reason
2216
- });
2210
+ handleMouseEnter() {
2211
+ if (!this.disabled) {
2212
+ this.menuService.setHighlightedOption(this);
2213
+ }
2217
2214
  }
2218
2215
 
2219
2216
  /**
2220
- * Checks if an option is currently selected.
2217
+ * Dispatches custom events defined for this menu option.
2218
+ * This function notifies listeners when a custom event is triggered by the option.
2221
2219
  * @private
2222
- * @param {HTMLElement} option - The option to check.
2223
- * @returns {boolean}
2224
2220
  */
2225
- isOptionSelected(option) {
2226
- if (!this.optionSelected) {
2227
- return false;
2228
- }
2229
-
2230
- if (this.multiSelect) {
2231
- // In multi-select mode, check if the option is in the selected array
2232
- return Array.isArray(this.optionSelected) && this.optionSelected.some((selectedOption) => selectedOption === option);
2221
+ handleCustomEvent() {
2222
+ if (this.event) {
2223
+ dispatchMenuEvent(this, this.event, { option: this });
2224
+ dispatchMenuEvent(this, 'auroMenu-customEventFired', { option: this });
2233
2225
  }
2234
-
2235
- return this.optionSelected === option;
2236
2226
  }
2237
2227
 
2238
2228
  /**
2239
- * Getter for loading placeholder state.
2240
- * @returns {boolean} - True if loading slots are present and non-empty.
2229
+ * Dispatches a click event for this menu option.
2230
+ * This function notifies listeners that the option has been clicked.
2231
+ * @private
2241
2232
  */
2242
- get hasLoadingPlaceholder() {
2243
- return this.loadingSlots && this.loadingSlots.length > 0;
2233
+ dispatchClickEvent() {
2234
+ this.dispatchEvent(new CustomEvent('auroMenuOption-click', {
2235
+ bubbles: true,
2236
+ cancelable: false,
2237
+ composed: true,
2238
+ detail: this
2239
+ }));
2244
2240
  }
2245
2241
 
2246
2242
  /**
2247
- * Getter for wrapper classes based on size.
2248
- * @returns {Object} - Class map for the wrapper element.
2243
+ * Generates an HTML element containing an SVG icon based on the provided `svgContent`.
2244
+ *
2249
2245
  * @private
2246
+ * @param {string} svgContent - The SVG content to be embedded.
2247
+ * @returns {Element} The HTML element containing the SVG icon.
2250
2248
  */
2251
- get wrapperClasses() {
2252
- return e({
2253
- 'menuWrapper': true,
2254
- [this.size]: true,
2255
- });
2249
+ generateIconHtml(svgContent) {
2250
+ const dom = new DOMParser().parseFromString(svgContent, 'text/html');
2251
+ const svg = dom.body.firstChild;
2252
+
2253
+ svg.setAttribute('slot', 'svg');
2254
+
2255
+ return u$1`<${this.iconTag} customColor customSvg>${svg}</${this.iconTag}>`;
2256
2256
  }
2257
2257
 
2258
2258
  /**
@@ -2261,26 +2261,26 @@ class AuroMenu extends AuroElement {
2261
2261
  * @returns {void}
2262
2262
  */
2263
2263
  renderLayout() {
2264
- if (this.loading) {
2265
- return b`
2266
- <div class="${this.wrapperClasses}">
2267
- <auro-menuoption
2268
- disabled
2269
- loadingplaceholder
2270
- class="${this.hasLoadingPlaceholder ? "" : "empty"}"
2271
- >
2272
- <div>
2273
- <slot name="loadingIcon" class="body-lg"></slot>
2274
- <slot name="loadingText"></slot>
2275
- </div>
2276
- </auro-menuoption>
2277
- </div>
2278
- `;
2279
- }
2280
2264
 
2281
- return b`
2282
- <div class="${this.wrapperClasses}">
2283
- <slot @slotchange=${this.handleSlotChange}></slot>
2265
+ const fontClassMap = {
2266
+ xs: 'body-sm',
2267
+ sm: 'body-default',
2268
+ md: 'body-default',
2269
+ lg: 'body-lg',
2270
+ xl: 'body-lg'
2271
+ };
2272
+
2273
+ const classes = e({
2274
+ 'wrapper': true,
2275
+ [this.size ? fontClassMap[this.size] : 'body-sm']: true,
2276
+ });
2277
+
2278
+ return u$1`
2279
+ <div class="${classes}">
2280
+ ${this.selected && !this.noCheckmark
2281
+ ? this.generateIconHtml(checkmarkIcon.svg)
2282
+ : undefined}
2283
+ <slot></slot>
2284
2284
  </div>
2285
2285
  `;
2286
2286
  }