@aurodesignsystem-dev/auro-formkit 0.0.0-pr1460.2 → 0.0.0-pr1462.0

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