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