@aurodesignsystem-dev/auro-formkit 0.0.0-pr1452.0 → 0.0.0-pr1456.0

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