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