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