@aurodesignsystem-dev/auro-formkit 0.0.0-pr624.16 → 0.0.0-pr624.18
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/bibtemplate/dist/buttonVersion.d.ts +1 -1
- package/components/bibtemplate/dist/index.js +61 -10
- package/components/bibtemplate/dist/registered.js +61 -10
- package/components/combobox/demo/api.min.js +134 -20
- package/components/combobox/demo/index.min.js +134 -20
- package/components/combobox/dist/index.js +122 -20
- package/components/combobox/dist/registered.js +122 -20
- package/components/counter/demo/api.min.js +478 -93
- package/components/counter/demo/index.min.js +478 -93
- package/components/counter/dist/auro-counter-group.d.ts +16 -8
- package/components/counter/dist/auro-counter.d.ts +6 -0
- package/components/counter/dist/index.js +478 -93
- package/components/counter/dist/registered.js +478 -93
- package/components/datepicker/demo/api.min.js +181 -28
- package/components/datepicker/demo/index.min.js +181 -28
- package/components/datepicker/dist/index.js +181 -28
- package/components/datepicker/dist/registered.js +181 -28
- package/components/input/demo/api.min.js +60 -9
- package/components/input/demo/index.min.js +60 -9
- package/components/input/dist/index.js +60 -9
- package/components/input/dist/registered.js +60 -9
- package/components/menu/demo/api.min.js +12 -0
- package/components/menu/demo/index.min.js +12 -0
- package/components/menu/dist/auro-menu.d.ts +6 -0
- package/components/menu/dist/index.js +12 -0
- package/components/menu/dist/registered.js +12 -0
- package/components/select/demo/api.min.js +94 -17
- package/components/select/demo/index.min.js +94 -17
- package/components/select/dist/auro-select.d.ts +7 -0
- package/components/select/dist/index.js +82 -17
- package/components/select/dist/registered.js +82 -17
- package/package.json +3 -3
|
@@ -419,7 +419,6 @@ let AuroElement$5 = class AuroElement extends i$2 {
|
|
|
419
419
|
* @private
|
|
420
420
|
*/
|
|
421
421
|
wrapper: {
|
|
422
|
-
type: HTMLElement,
|
|
423
422
|
attribute: false,
|
|
424
423
|
reflect: false
|
|
425
424
|
}
|
|
@@ -857,7 +856,7 @@ let AuroLoader$1 = class AuroLoader extends i$2 {
|
|
|
857
856
|
|
|
858
857
|
var loaderVersion$1 = '5.0.0';
|
|
859
858
|
|
|
860
|
-
/* eslint-disable max-lines, curly */
|
|
859
|
+
/* eslint-disable max-lines, curly, jsdoc/no-undefined-types */
|
|
861
860
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
862
861
|
// See LICENSE in the project root for license information.
|
|
863
862
|
|
|
@@ -924,6 +923,21 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
924
923
|
* @private
|
|
925
924
|
*/
|
|
926
925
|
this.loaderTag = versioning.generateTag('auro-loader', loaderVersion$1, AuroLoader$1);
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* @private
|
|
929
|
+
*/
|
|
930
|
+
this.buttonHref = undefined;
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* @private
|
|
934
|
+
*/
|
|
935
|
+
this.buttonTarget = undefined;
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* @private
|
|
939
|
+
*/
|
|
940
|
+
this.buttonRel = undefined;
|
|
927
941
|
}
|
|
928
942
|
|
|
929
943
|
static get styles() {
|
|
@@ -990,13 +1004,23 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
990
1004
|
},
|
|
991
1005
|
|
|
992
1006
|
/**
|
|
993
|
-
* Populates `
|
|
1007
|
+
* Populates `tabindex` to define the focusable sequence in keyboard navigation.
|
|
994
1008
|
*/
|
|
995
1009
|
tIndex: {
|
|
996
1010
|
type: String,
|
|
997
1011
|
reflect: true
|
|
998
1012
|
},
|
|
999
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* Populates `tabindex` to define the focusable sequence in keyboard navigation.
|
|
1016
|
+
* Must be used with "." to ensure the host element does not retain a reference to the `tabindex` attribute.
|
|
1017
|
+
* Example: `<auro-button .tabindex="${this.disabled ? '-1' : '0'}"></auro-button>`
|
|
1018
|
+
*/
|
|
1019
|
+
tabindex: {
|
|
1020
|
+
type: String,
|
|
1021
|
+
reflect: false
|
|
1022
|
+
},
|
|
1023
|
+
|
|
1000
1024
|
/**
|
|
1001
1025
|
* Sets title attribute. The information is most often shown as a tooltip text when the mouse moves over the element.
|
|
1002
1026
|
*/
|
|
@@ -1029,6 +1053,27 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
1029
1053
|
type: String,
|
|
1030
1054
|
reflect: true
|
|
1031
1055
|
},
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* @private
|
|
1059
|
+
*/
|
|
1060
|
+
buttonHref: {
|
|
1061
|
+
type: String,
|
|
1062
|
+
},
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* @private
|
|
1066
|
+
*/
|
|
1067
|
+
buttonTarget: {
|
|
1068
|
+
type: String,
|
|
1069
|
+
},
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* @private
|
|
1073
|
+
*/
|
|
1074
|
+
buttonRel: {
|
|
1075
|
+
type: String,
|
|
1076
|
+
},
|
|
1032
1077
|
};
|
|
1033
1078
|
}
|
|
1034
1079
|
|
|
@@ -1119,14 +1164,17 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
1119
1164
|
loading: this.loading,
|
|
1120
1165
|
};
|
|
1121
1166
|
|
|
1167
|
+
const tag = this.buttonHref ? i$1`a` : i$1`button`;
|
|
1168
|
+
const part = this.buttonHref ? 'link' : 'button';
|
|
1169
|
+
|
|
1122
1170
|
return u`
|
|
1123
|
-
|
|
1124
|
-
part="
|
|
1171
|
+
<${tag}
|
|
1172
|
+
part="${part}"
|
|
1125
1173
|
aria-label="${o(this.loading ? this.loadingText : this.currentAriaLabel || undefined)}"
|
|
1126
1174
|
aria-labelledby="${o(this.loading ? undefined : this.currentAriaLabelledBy || undefined)}"
|
|
1127
|
-
|
|
1175
|
+
tabindex="${o(this.tIndex || this.tabindex)}"
|
|
1128
1176
|
?autofocus="${this.autofocus}"
|
|
1129
|
-
class
|
|
1177
|
+
class=${e(classes)}
|
|
1130
1178
|
?disabled="${this.disabled || this.loading}"
|
|
1131
1179
|
?onDark="${this.onDark}"
|
|
1132
1180
|
title="${o(this.title ? this.title : undefined)}"
|
|
@@ -1135,6 +1183,9 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
1135
1183
|
variant="${o(this.variant ? this.variant : undefined)}"
|
|
1136
1184
|
.value="${o(this.value ? this.value : undefined)}"
|
|
1137
1185
|
@click="${this.type === 'submit' ? this.surfaceSubmitEvent : undefined}"
|
|
1186
|
+
href="${o(this.buttonHref || undefined)}"
|
|
1187
|
+
target="${o(this.buttonTarget || undefined)}"
|
|
1188
|
+
rel="${o(this.buttonRel || undefined)}"
|
|
1138
1189
|
>
|
|
1139
1190
|
${o(this.loading ? u`<${this.loaderTag} pulse part="loader"></${this.loaderTag}>` : undefined)}
|
|
1140
1191
|
|
|
@@ -1143,12 +1194,12 @@ let AuroButton$1 = class AuroButton extends AuroElement$5 {
|
|
|
1143
1194
|
<slot></slot>
|
|
1144
1195
|
</span>
|
|
1145
1196
|
</span>
|
|
1146
|
-
|
|
1197
|
+
</${tag}>
|
|
1147
1198
|
`;
|
|
1148
1199
|
}
|
|
1149
1200
|
|
|
1150
1201
|
/**
|
|
1151
|
-
* Renders the layout of the button
|
|
1202
|
+
* Renders the layout of the button.
|
|
1152
1203
|
* @returns {TemplateResult}
|
|
1153
1204
|
* @private
|
|
1154
1205
|
*/
|
|
@@ -2662,6 +2713,18 @@ class AuroCounter extends i$2 {
|
|
|
2662
2713
|
|
|
2663
2714
|
firstUpdated() {
|
|
2664
2715
|
this.initValue();
|
|
2716
|
+
this.setTagAttribute("auro-counter");
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
/**
|
|
2720
|
+
* Sets an attribute that matches the default tag name if the tag name is not the default.
|
|
2721
|
+
* @param {string} tagName - The tag name to set as an attribute.
|
|
2722
|
+
* @private
|
|
2723
|
+
*/
|
|
2724
|
+
setTagAttribute(tagName) {
|
|
2725
|
+
if (this.tagName.toLowerCase() !== tagName) {
|
|
2726
|
+
this.setAttribute(tagName, true);
|
|
2727
|
+
}
|
|
2665
2728
|
}
|
|
2666
2729
|
|
|
2667
2730
|
/**
|
|
@@ -2702,14 +2765,24 @@ class AuroCounter extends i$2 {
|
|
|
2702
2765
|
<label id="counter-label" class="label"><slot @slotchange="${this.onDefaultSlotChange}" ></slot></label>
|
|
2703
2766
|
<slot id="counter-description" name="description"></slot>
|
|
2704
2767
|
</div>
|
|
2705
|
-
<div
|
|
2768
|
+
<div
|
|
2769
|
+
part="counterControl"
|
|
2770
|
+
aria-labelledby="counter-label"
|
|
2771
|
+
aria-describedby="counter-description"
|
|
2772
|
+
tabindex="${this.disabled ? '-1' : '0'}"
|
|
2773
|
+
role="spinbutton"
|
|
2774
|
+
aria-valuemin="${this.min}"
|
|
2775
|
+
aria-valuemax="${this.max}"
|
|
2776
|
+
aria-valuenow="${this.value}"
|
|
2777
|
+
aria-valuetext="${this.value !== undefined ? this.value : this.min}"
|
|
2778
|
+
>
|
|
2706
2779
|
<auro-counter-button
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2780
|
+
aria-hidden="true"
|
|
2781
|
+
.tabindex="${'-1'}"
|
|
2782
|
+
part="controlMinus"
|
|
2783
|
+
@click="${() => this.decrement()}"
|
|
2784
|
+
?onDark="${this.onDark}"
|
|
2785
|
+
?disabled="${this.disabled || this.disableMin || this.isIncrementDisabled(this.min)}"
|
|
2713
2786
|
>
|
|
2714
2787
|
<${this.iconTag} class="controlIcon" customSvg> ${IconUtil.generateSvgHtml(minusIcon)} </${this.iconTag}>
|
|
2715
2788
|
</auro-counter-button>
|
|
@@ -2719,12 +2792,12 @@ class AuroCounter extends i$2 {
|
|
|
2719
2792
|
</div>
|
|
2720
2793
|
|
|
2721
2794
|
<auro-counter-button
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2795
|
+
aria-hidden="true"
|
|
2796
|
+
.tabindex="${'-1'}"
|
|
2797
|
+
part="controlPlus"
|
|
2798
|
+
@click="${() => this.increment()}"
|
|
2799
|
+
?onDark="${this.onDark}"
|
|
2800
|
+
?disabled="${this.disabled || this.disableMax || this.isIncrementDisabled(this.max)}"
|
|
2728
2801
|
>
|
|
2729
2802
|
<${this.iconTag} class="controlIcon" customSvg> ${IconUtil.generateSvgHtml(plusIcon)} </${this.iconTag}>
|
|
2730
2803
|
</auro-counter-button>
|
|
@@ -7351,7 +7424,6 @@ let AuroElement$1 = class AuroElement extends i$2 {
|
|
|
7351
7424
|
* @private
|
|
7352
7425
|
*/
|
|
7353
7426
|
wrapper: {
|
|
7354
|
-
type: HTMLElement,
|
|
7355
7427
|
attribute: false,
|
|
7356
7428
|
reflect: false
|
|
7357
7429
|
}
|
|
@@ -7660,7 +7732,7 @@ class AuroLoader extends i$2 {
|
|
|
7660
7732
|
|
|
7661
7733
|
var loaderVersion = '5.0.0';
|
|
7662
7734
|
|
|
7663
|
-
/* eslint-disable max-lines, curly */
|
|
7735
|
+
/* eslint-disable max-lines, curly, jsdoc/no-undefined-types */
|
|
7664
7736
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
7665
7737
|
// See LICENSE in the project root for license information.
|
|
7666
7738
|
|
|
@@ -7727,6 +7799,21 @@ class AuroButton extends AuroElement$1 {
|
|
|
7727
7799
|
* @private
|
|
7728
7800
|
*/
|
|
7729
7801
|
this.loaderTag = versioning.generateTag('auro-loader', loaderVersion, AuroLoader);
|
|
7802
|
+
|
|
7803
|
+
/**
|
|
7804
|
+
* @private
|
|
7805
|
+
*/
|
|
7806
|
+
this.buttonHref = undefined;
|
|
7807
|
+
|
|
7808
|
+
/**
|
|
7809
|
+
* @private
|
|
7810
|
+
*/
|
|
7811
|
+
this.buttonTarget = undefined;
|
|
7812
|
+
|
|
7813
|
+
/**
|
|
7814
|
+
* @private
|
|
7815
|
+
*/
|
|
7816
|
+
this.buttonRel = undefined;
|
|
7730
7817
|
}
|
|
7731
7818
|
|
|
7732
7819
|
static get styles() {
|
|
@@ -7793,13 +7880,23 @@ class AuroButton extends AuroElement$1 {
|
|
|
7793
7880
|
},
|
|
7794
7881
|
|
|
7795
7882
|
/**
|
|
7796
|
-
* Populates `
|
|
7883
|
+
* Populates `tabindex` to define the focusable sequence in keyboard navigation.
|
|
7797
7884
|
*/
|
|
7798
7885
|
tIndex: {
|
|
7799
7886
|
type: String,
|
|
7800
7887
|
reflect: true
|
|
7801
7888
|
},
|
|
7802
7889
|
|
|
7890
|
+
/**
|
|
7891
|
+
* Populates `tabindex` to define the focusable sequence in keyboard navigation.
|
|
7892
|
+
* Must be used with "." to ensure the host element does not retain a reference to the `tabindex` attribute.
|
|
7893
|
+
* Example: `<auro-button .tabindex="${this.disabled ? '-1' : '0'}"></auro-button>`
|
|
7894
|
+
*/
|
|
7895
|
+
tabindex: {
|
|
7896
|
+
type: String,
|
|
7897
|
+
reflect: false
|
|
7898
|
+
},
|
|
7899
|
+
|
|
7803
7900
|
/**
|
|
7804
7901
|
* Sets title attribute. The information is most often shown as a tooltip text when the mouse moves over the element.
|
|
7805
7902
|
*/
|
|
@@ -7832,6 +7929,27 @@ class AuroButton extends AuroElement$1 {
|
|
|
7832
7929
|
type: String,
|
|
7833
7930
|
reflect: true
|
|
7834
7931
|
},
|
|
7932
|
+
|
|
7933
|
+
/**
|
|
7934
|
+
* @private
|
|
7935
|
+
*/
|
|
7936
|
+
buttonHref: {
|
|
7937
|
+
type: String,
|
|
7938
|
+
},
|
|
7939
|
+
|
|
7940
|
+
/**
|
|
7941
|
+
* @private
|
|
7942
|
+
*/
|
|
7943
|
+
buttonTarget: {
|
|
7944
|
+
type: String,
|
|
7945
|
+
},
|
|
7946
|
+
|
|
7947
|
+
/**
|
|
7948
|
+
* @private
|
|
7949
|
+
*/
|
|
7950
|
+
buttonRel: {
|
|
7951
|
+
type: String,
|
|
7952
|
+
},
|
|
7835
7953
|
};
|
|
7836
7954
|
}
|
|
7837
7955
|
|
|
@@ -7922,14 +8040,17 @@ class AuroButton extends AuroElement$1 {
|
|
|
7922
8040
|
loading: this.loading,
|
|
7923
8041
|
};
|
|
7924
8042
|
|
|
8043
|
+
const tag = this.buttonHref ? i$1`a` : i$1`button`;
|
|
8044
|
+
const part = this.buttonHref ? 'link' : 'button';
|
|
8045
|
+
|
|
7925
8046
|
return u`
|
|
7926
|
-
|
|
7927
|
-
part="
|
|
8047
|
+
<${tag}
|
|
8048
|
+
part="${part}"
|
|
7928
8049
|
aria-label="${o(this.loading ? this.loadingText : this.currentAriaLabel || undefined)}"
|
|
7929
8050
|
aria-labelledby="${o(this.loading ? undefined : this.currentAriaLabelledBy || undefined)}"
|
|
7930
|
-
|
|
8051
|
+
tabindex="${o(this.tIndex || this.tabindex)}"
|
|
7931
8052
|
?autofocus="${this.autofocus}"
|
|
7932
|
-
class
|
|
8053
|
+
class=${e(classes)}
|
|
7933
8054
|
?disabled="${this.disabled || this.loading}"
|
|
7934
8055
|
?onDark="${this.onDark}"
|
|
7935
8056
|
title="${o(this.title ? this.title : undefined)}"
|
|
@@ -7938,6 +8059,9 @@ class AuroButton extends AuroElement$1 {
|
|
|
7938
8059
|
variant="${o(this.variant ? this.variant : undefined)}"
|
|
7939
8060
|
.value="${o(this.value ? this.value : undefined)}"
|
|
7940
8061
|
@click="${this.type === 'submit' ? this.surfaceSubmitEvent : undefined}"
|
|
8062
|
+
href="${o(this.buttonHref || undefined)}"
|
|
8063
|
+
target="${o(this.buttonTarget || undefined)}"
|
|
8064
|
+
rel="${o(this.buttonRel || undefined)}"
|
|
7941
8065
|
>
|
|
7942
8066
|
${o(this.loading ? u`<${this.loaderTag} pulse part="loader"></${this.loaderTag}>` : undefined)}
|
|
7943
8067
|
|
|
@@ -7946,12 +8070,12 @@ class AuroButton extends AuroElement$1 {
|
|
|
7946
8070
|
<slot></slot>
|
|
7947
8071
|
</span>
|
|
7948
8072
|
</span>
|
|
7949
|
-
|
|
8073
|
+
</${tag}>
|
|
7950
8074
|
`;
|
|
7951
8075
|
}
|
|
7952
8076
|
|
|
7953
8077
|
/**
|
|
7954
|
-
* Renders the layout of the button
|
|
8078
|
+
* Renders the layout of the button.
|
|
7955
8079
|
* @returns {TemplateResult}
|
|
7956
8080
|
* @private
|
|
7957
8081
|
*/
|
|
@@ -7960,7 +8084,7 @@ class AuroButton extends AuroElement$1 {
|
|
|
7960
8084
|
}
|
|
7961
8085
|
}
|
|
7962
8086
|
|
|
7963
|
-
var buttonVersion = '11.
|
|
8087
|
+
var buttonVersion = '11.2.1';
|
|
7964
8088
|
|
|
7965
8089
|
// Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
7966
8090
|
// See LICENSE in the project root for license information.
|
|
@@ -9073,7 +9197,266 @@ class AuroElement extends i$2 {
|
|
|
9073
9197
|
}
|
|
9074
9198
|
}
|
|
9075
9199
|
|
|
9076
|
-
|
|
9200
|
+
// Selectors for focusable elements
|
|
9201
|
+
const FOCUSABLE_SELECTORS = [
|
|
9202
|
+
'a[href]',
|
|
9203
|
+
'button:not([disabled])',
|
|
9204
|
+
'textarea:not([disabled])',
|
|
9205
|
+
'input:not([disabled])',
|
|
9206
|
+
'select:not([disabled])',
|
|
9207
|
+
'[role="tab"]:not([disabled])',
|
|
9208
|
+
'[role="link"]:not([disabled])',
|
|
9209
|
+
'[role="button"]:not([disabled])',
|
|
9210
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
9211
|
+
'[contenteditable]:not([contenteditable="false"])'
|
|
9212
|
+
];
|
|
9213
|
+
|
|
9214
|
+
// List of custom components that are known to be focusable
|
|
9215
|
+
const FOCUSABLE_COMPONENTS = [
|
|
9216
|
+
'auro-checkbox',
|
|
9217
|
+
'auro-radio',
|
|
9218
|
+
'auro-dropdown',
|
|
9219
|
+
'auro-button',
|
|
9220
|
+
'auro-combobox',
|
|
9221
|
+
'auro-input',
|
|
9222
|
+
'auro-counter',
|
|
9223
|
+
'auro-menu',
|
|
9224
|
+
'auro-select',
|
|
9225
|
+
'auro-datepicker',
|
|
9226
|
+
'auro-hyperlink',
|
|
9227
|
+
'auro-accordion',
|
|
9228
|
+
];
|
|
9229
|
+
|
|
9230
|
+
/**
|
|
9231
|
+
* Determines if a given element is a custom focusable component.
|
|
9232
|
+
* Returns true if the element matches a known focusable component and is not disabled.
|
|
9233
|
+
*
|
|
9234
|
+
* @param {HTMLElement} element The element to check for focusability.
|
|
9235
|
+
* @returns {boolean} True if the element is a focusable custom component, false otherwise.
|
|
9236
|
+
*/
|
|
9237
|
+
function isFocusableComponent(element) {
|
|
9238
|
+
const componentName = element.tagName.toLowerCase();
|
|
9239
|
+
|
|
9240
|
+
// Guard Clause: Element is a focusable component
|
|
9241
|
+
if (!FOCUSABLE_COMPONENTS.some((name) => element.hasAttribute(name) || componentName === name)) return false;
|
|
9242
|
+
|
|
9243
|
+
// Guard Clause: Element is not disabled
|
|
9244
|
+
if (element.hasAttribute('disabled')) return false;
|
|
9245
|
+
|
|
9246
|
+
// Guard Clause: The element is a hyperlink and has no href attribute
|
|
9247
|
+
if (componentName.match("hyperlink") && !element.hasAttribute('href')) return false;
|
|
9248
|
+
|
|
9249
|
+
// If all guard clauses pass, the element is a focusable component
|
|
9250
|
+
return true;
|
|
9251
|
+
}
|
|
9252
|
+
|
|
9253
|
+
/**
|
|
9254
|
+
* Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
|
|
9255
|
+
* Returns a unique, ordered array of elements that can receive focus.
|
|
9256
|
+
*
|
|
9257
|
+
* @param {HTMLElement} container The container to search within
|
|
9258
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
9259
|
+
*/
|
|
9260
|
+
function getFocusableElements(container) {
|
|
9261
|
+
// Get elements in DOM order by walking the tree
|
|
9262
|
+
const orderedFocusableElements = [];
|
|
9263
|
+
|
|
9264
|
+
// Define a recursive function to collect focusable elements in DOM order
|
|
9265
|
+
const collectFocusableElements = (root) => {
|
|
9266
|
+
// Check if current element is focusable
|
|
9267
|
+
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
9268
|
+
// Check if this is a custom component that is focusable
|
|
9269
|
+
const isComponentFocusable = isFocusableComponent(root);
|
|
9270
|
+
|
|
9271
|
+
if (isComponentFocusable) {
|
|
9272
|
+
// Add the component itself as a focusable element and don't traverse its shadow DOM
|
|
9273
|
+
orderedFocusableElements.push(root);
|
|
9274
|
+
return; // Skip traversing inside this component
|
|
9275
|
+
}
|
|
9276
|
+
|
|
9277
|
+
// Check if the element itself matches any selector
|
|
9278
|
+
for (const selector of FOCUSABLE_SELECTORS) {
|
|
9279
|
+
if (root.matches?.(selector)) {
|
|
9280
|
+
orderedFocusableElements.push(root);
|
|
9281
|
+
break; // Once we know it's focusable, no need to check other selectors
|
|
9282
|
+
}
|
|
9283
|
+
}
|
|
9284
|
+
|
|
9285
|
+
// Process shadow DOM only for non-Auro components
|
|
9286
|
+
if (root.shadowRoot) {
|
|
9287
|
+
// Process shadow DOM children in order
|
|
9288
|
+
if (root.shadowRoot.children) {
|
|
9289
|
+
Array.from(root.shadowRoot.children).forEach(child => {
|
|
9290
|
+
collectFocusableElements(child);
|
|
9291
|
+
});
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
|
|
9295
|
+
// Process slots and their assigned nodes in order
|
|
9296
|
+
if (root.tagName === 'SLOT') {
|
|
9297
|
+
const assignedNodes = root.assignedNodes({ flatten: true });
|
|
9298
|
+
for (const node of assignedNodes) {
|
|
9299
|
+
collectFocusableElements(node);
|
|
9300
|
+
}
|
|
9301
|
+
} else {
|
|
9302
|
+
// Process light DOM children in order
|
|
9303
|
+
if (root.children) {
|
|
9304
|
+
Array.from(root.children).forEach(child => {
|
|
9305
|
+
collectFocusableElements(child);
|
|
9306
|
+
});
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
}
|
|
9310
|
+
};
|
|
9311
|
+
|
|
9312
|
+
// Start the traversal from the container
|
|
9313
|
+
collectFocusableElements(container);
|
|
9314
|
+
|
|
9315
|
+
// Remove duplicates that might have been collected through different paths
|
|
9316
|
+
// while preserving order
|
|
9317
|
+
const uniqueElements = [];
|
|
9318
|
+
const seen = new Set();
|
|
9319
|
+
|
|
9320
|
+
for (const element of orderedFocusableElements) {
|
|
9321
|
+
if (!seen.has(element)) {
|
|
9322
|
+
seen.add(element);
|
|
9323
|
+
uniqueElements.push(element);
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
|
|
9327
|
+
return uniqueElements;
|
|
9328
|
+
}
|
|
9329
|
+
|
|
9330
|
+
/**
|
|
9331
|
+
* FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
|
|
9332
|
+
* It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
|
|
9333
|
+
*/
|
|
9334
|
+
class FocusTrap {
|
|
9335
|
+
/**
|
|
9336
|
+
* Creates a new FocusTrap instance for the given container element.
|
|
9337
|
+
* Initializes event listeners and prepares the container for focus management.
|
|
9338
|
+
*
|
|
9339
|
+
* @param {HTMLElement} container The DOM element to trap focus within.
|
|
9340
|
+
* @throws {Error} If the provided container is not a valid HTMLElement.
|
|
9341
|
+
*/
|
|
9342
|
+
constructor(container) {
|
|
9343
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
9344
|
+
throw new Error("FocusTrap requires a valid HTMLElement.");
|
|
9345
|
+
}
|
|
9346
|
+
|
|
9347
|
+
this.container = container;
|
|
9348
|
+
this.tabDirection = 'forward'; // or 'backward'
|
|
9349
|
+
|
|
9350
|
+
this._init();
|
|
9351
|
+
}
|
|
9352
|
+
|
|
9353
|
+
/**
|
|
9354
|
+
* Initializes the focus trap by setting up event listeners and attributes on the container.
|
|
9355
|
+
* Prepares the container for focus management, including support for shadow DOM and inert attributes.
|
|
9356
|
+
*
|
|
9357
|
+
* @private
|
|
9358
|
+
*/
|
|
9359
|
+
_init() {
|
|
9360
|
+
|
|
9361
|
+
// Add inert attribute to prevent focusing programmatically as well (if supported)
|
|
9362
|
+
if ('inert' in HTMLElement.prototype) {
|
|
9363
|
+
this.container.inert = false; // Ensure the container isn't inert
|
|
9364
|
+
this.container.setAttribute('data-focus-trap-container', true); // Mark for identification
|
|
9365
|
+
}
|
|
9366
|
+
|
|
9367
|
+
// Track tab direction
|
|
9368
|
+
this.container.addEventListener('keydown', this._onKeydown);
|
|
9369
|
+
}
|
|
9370
|
+
|
|
9371
|
+
/**
|
|
9372
|
+
* Handles keydown events to manage tab navigation within the container.
|
|
9373
|
+
* Ensures that focus wraps around when reaching the first or last focusable element.
|
|
9374
|
+
*
|
|
9375
|
+
* @param {KeyboardEvent} e The keyboard event triggered by user interaction.
|
|
9376
|
+
* @private
|
|
9377
|
+
*/
|
|
9378
|
+
_onKeydown = (e) => {
|
|
9379
|
+
|
|
9380
|
+
if (e.key === 'Tab') {
|
|
9381
|
+
|
|
9382
|
+
// Set the tab direction based on the key pressed
|
|
9383
|
+
this.tabDirection = e.shiftKey ? 'backward' : 'forward';
|
|
9384
|
+
|
|
9385
|
+
// Get the active element(s) in the document and shadow root
|
|
9386
|
+
// This will include the active element in the shadow DOM if it exists
|
|
9387
|
+
// Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
|
|
9388
|
+
const actives = [
|
|
9389
|
+
document.activeElement,
|
|
9390
|
+
...document.activeElement.shadowRoot && [document.activeElement.shadowRoot.activeElement] || []
|
|
9391
|
+
];
|
|
9392
|
+
|
|
9393
|
+
// Update the focusable elements
|
|
9394
|
+
const focusables = this._getFocusableElements();
|
|
9395
|
+
|
|
9396
|
+
// If we're at either end of the focusable elements, wrap around to the other end
|
|
9397
|
+
const focusIndex =
|
|
9398
|
+
(actives.includes(focusables[0]) || actives.includes(this.container)) && this.tabDirection === 'backward'
|
|
9399
|
+
? focusables.length - 1
|
|
9400
|
+
: actives.includes(focusables[focusables.length - 1]) && this.tabDirection === 'forward'
|
|
9401
|
+
? 0
|
|
9402
|
+
: null;
|
|
9403
|
+
|
|
9404
|
+
if (focusIndex !== null) {
|
|
9405
|
+
focusables[focusIndex].focus();
|
|
9406
|
+
e.preventDefault(); // Prevent default tab behavior
|
|
9407
|
+
e.stopPropagation(); // Stop the event from bubbling up
|
|
9408
|
+
}
|
|
9409
|
+
}
|
|
9410
|
+
};
|
|
9411
|
+
|
|
9412
|
+
/**
|
|
9413
|
+
* Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
|
|
9414
|
+
* Returns a unique, ordered array of elements that can receive focus.
|
|
9415
|
+
*
|
|
9416
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
9417
|
+
* @private
|
|
9418
|
+
*/
|
|
9419
|
+
_getFocusableElements() {
|
|
9420
|
+
// Use the imported utility function to get focusable elements
|
|
9421
|
+
const elements = getFocusableElements(this.container);
|
|
9422
|
+
|
|
9423
|
+
// Filter out any elements with the 'focus-bookend' class
|
|
9424
|
+
return elements;
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
/**
|
|
9428
|
+
* Moves focus to the first focusable element within the container.
|
|
9429
|
+
* Useful for setting initial focus when activating the focus trap.
|
|
9430
|
+
*/
|
|
9431
|
+
focusFirstElement() {
|
|
9432
|
+
const focusables = this._getFocusableElements();
|
|
9433
|
+
if (focusables.length) focusables[0].focus();
|
|
9434
|
+
}
|
|
9435
|
+
|
|
9436
|
+
/**
|
|
9437
|
+
* Moves focus to the last focusable element within the container.
|
|
9438
|
+
* Useful for setting focus when deactivating or cycling focus in reverse.
|
|
9439
|
+
*/
|
|
9440
|
+
focusLastElement() {
|
|
9441
|
+
const focusables = this._getFocusableElements();
|
|
9442
|
+
if (focusables.length) focusables[focusables.length - 1].focus();
|
|
9443
|
+
}
|
|
9444
|
+
|
|
9445
|
+
/**
|
|
9446
|
+
* Removes event listeners and attributes added by the focus trap.
|
|
9447
|
+
* Call this method to clean up when the focus trap is no longer needed.
|
|
9448
|
+
*/
|
|
9449
|
+
disconnect() {
|
|
9450
|
+
|
|
9451
|
+
if (this.container.hasAttribute('data-focus-trap-container')) {
|
|
9452
|
+
this.container.removeAttribute('data-focus-trap-container');
|
|
9453
|
+
}
|
|
9454
|
+
|
|
9455
|
+
this.container.removeEventListener('keydown', this._onKeydown);
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
9458
|
+
|
|
9459
|
+
/* eslint-disable lit/no-invalid-html, lit/binding-positions, max-lines, prefer-destructuring, no-underscore-dangle, arrow-parens, no-confusing-arrow, curly, no-unused-expressions */
|
|
9077
9460
|
|
|
9078
9461
|
|
|
9079
9462
|
/**
|
|
@@ -9132,6 +9515,11 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9132
9515
|
*/
|
|
9133
9516
|
this.validation = new AuroFormValidation();
|
|
9134
9517
|
|
|
9518
|
+
// Bind callback methods since we can't use arrow functions in class properties
|
|
9519
|
+
|
|
9520
|
+
/** @private */
|
|
9521
|
+
this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
|
|
9522
|
+
|
|
9135
9523
|
/**
|
|
9136
9524
|
* Generate unique names for dependency components.
|
|
9137
9525
|
* @private
|
|
@@ -9171,6 +9559,8 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9171
9559
|
static get properties() {
|
|
9172
9560
|
return {
|
|
9173
9561
|
|
|
9562
|
+
...super.properties,
|
|
9563
|
+
|
|
9174
9564
|
/**
|
|
9175
9565
|
* If declared, bib's position will be automatically calculated where to appear.
|
|
9176
9566
|
* @default false
|
|
@@ -9309,51 +9699,6 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9309
9699
|
};
|
|
9310
9700
|
}
|
|
9311
9701
|
|
|
9312
|
-
/**
|
|
9313
|
-
* Traps keyboard tab interactions within dropdown when open.
|
|
9314
|
-
* @private
|
|
9315
|
-
* @param {KeyboardEvent} event - The keyboard event.
|
|
9316
|
-
* @param {NodeList} counters - The list of counter elements.
|
|
9317
|
-
*/
|
|
9318
|
-
trapKeyboard(event, counters) {
|
|
9319
|
-
if (!this.dropdown.isPopoverVisible) {
|
|
9320
|
-
return;
|
|
9321
|
-
}
|
|
9322
|
-
|
|
9323
|
-
event.stopPropagation();
|
|
9324
|
-
event.preventDefault();
|
|
9325
|
-
|
|
9326
|
-
const firstFocusable = counters[0];
|
|
9327
|
-
const lastFocusable = counters[counters.length - 1];
|
|
9328
|
-
|
|
9329
|
-
if (event.key === 'Enter') {
|
|
9330
|
-
firstFocusable.focus();
|
|
9331
|
-
}
|
|
9332
|
-
|
|
9333
|
-
if (event.key === 'Escape') {
|
|
9334
|
-
this.dropdown.hide();
|
|
9335
|
-
}
|
|
9336
|
-
|
|
9337
|
-
if (event.key === 'Tab' && this.dropdown && event.target.offsetParent === this.dropdown.bib) {
|
|
9338
|
-
this.dropdown.noHideOnThisFocusLoss = true;
|
|
9339
|
-
|
|
9340
|
-
const currentIndex = Array.from(counters).indexOf(document.activeElement);
|
|
9341
|
-
|
|
9342
|
-
if (event.shiftKey) {
|
|
9343
|
-
if (currentIndex === 0) {
|
|
9344
|
-
lastFocusable.focus();
|
|
9345
|
-
} else {
|
|
9346
|
-
counters[currentIndex - 1].focus();
|
|
9347
|
-
}
|
|
9348
|
-
} else if (currentIndex === counters.length - 1) {
|
|
9349
|
-
firstFocusable.focus();
|
|
9350
|
-
} else {
|
|
9351
|
-
counters[currentIndex + 1].focus();
|
|
9352
|
-
}
|
|
9353
|
-
|
|
9354
|
-
}
|
|
9355
|
-
}
|
|
9356
|
-
|
|
9357
9702
|
/**
|
|
9358
9703
|
* Dynamically disables increment/decrement buttons on a counter based on group value.
|
|
9359
9704
|
* This method checks the total aggregated value against the group's min and max properties.
|
|
@@ -9387,6 +9732,52 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9387
9732
|
});
|
|
9388
9733
|
}
|
|
9389
9734
|
|
|
9735
|
+
/**
|
|
9736
|
+
* Performs state updates that should happen when the dropdown is toggled.
|
|
9737
|
+
* @returns {void}
|
|
9738
|
+
* @private
|
|
9739
|
+
*/
|
|
9740
|
+
handleDropdownToggle() {
|
|
9741
|
+
|
|
9742
|
+
// Check if the dropdown is open
|
|
9743
|
+
const dropdownIsOpen = this.dropdown.isPopoverVisible;
|
|
9744
|
+
|
|
9745
|
+
// Adds and removes the focus trap based on the dropdown state
|
|
9746
|
+
this.updateFocusTrap(dropdownIsOpen);
|
|
9747
|
+
|
|
9748
|
+
// Tasks to perform if the dropdown is closed
|
|
9749
|
+
if (!dropdownIsOpen) {
|
|
9750
|
+
|
|
9751
|
+
// Shift focus to the dropdown trigger
|
|
9752
|
+
this.dropdown.trigger.focus();
|
|
9753
|
+
}
|
|
9754
|
+
}
|
|
9755
|
+
|
|
9756
|
+
/**
|
|
9757
|
+
* Updates the focus trap based on whether the dropdown is open or closed.
|
|
9758
|
+
* If the dropdown is open, it creates a new focus trap and focuses the first element
|
|
9759
|
+
* If the dropdown is closed, it disconnects the focus trap if it exists to prevent memory leaks and disable focus trapping.
|
|
9760
|
+
* @param {boolean} dropdownIsOpen - Indicates whether the dropdown is currently open.
|
|
9761
|
+
* @returns {void}
|
|
9762
|
+
* @private
|
|
9763
|
+
*/
|
|
9764
|
+
updateFocusTrap(dropdownIsOpen) {
|
|
9765
|
+
|
|
9766
|
+
// If the dropdown is open, create a focus trap and focus the first element
|
|
9767
|
+
if (dropdownIsOpen) {
|
|
9768
|
+
this.dropdownFocusTrap = new FocusTrap(this.dropdown.bibContent);
|
|
9769
|
+
this.dropdownFocusTrap.focusFirstElement();
|
|
9770
|
+
return;
|
|
9771
|
+
}
|
|
9772
|
+
|
|
9773
|
+
// Guard Clause: Ensure there is a focus trap currently active before continuing
|
|
9774
|
+
if (!this.dropdownFocusTrap) return;
|
|
9775
|
+
|
|
9776
|
+
// If the dropdown is not open, disconnect the focus trap if it exists
|
|
9777
|
+
this.dropdownFocusTrap.disconnect();
|
|
9778
|
+
this.dropdownFocusTrap = undefined;
|
|
9779
|
+
}
|
|
9780
|
+
|
|
9390
9781
|
/**
|
|
9391
9782
|
* Configures the dropdown counters by selecting all `auro-counter` elements,
|
|
9392
9783
|
* appending them to the `auro-counter-wrapper` element within the shadow DOM,
|
|
@@ -9395,28 +9786,14 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9395
9786
|
*/
|
|
9396
9787
|
configureDropdownCounters() {
|
|
9397
9788
|
this.dropdown = this.shadowRoot.querySelector(this.dropdownTag._$litStatic$);
|
|
9398
|
-
this.dropdown.addEventListener('keydown', (event) => this.trapKeyboard(event, this.counters, 'dropdown'));
|
|
9399
|
-
// notify dropdown to reconfigure as the trigger text is updated
|
|
9400
9789
|
this.dropdown.requestUpdate();
|
|
9401
9790
|
|
|
9402
|
-
this.addEventListener(
|
|
9403
|
-
if (!this.dropdown.isPopoverVisible) {
|
|
9404
|
-
this.dropdown.focus();
|
|
9405
|
-
}
|
|
9406
|
-
});
|
|
9791
|
+
this.dropdown.addEventListener("auroDropdown-toggled", this.handleDropdownToggle);
|
|
9407
9792
|
|
|
9408
9793
|
const counterWrapper = this.shadowRoot.querySelector('auro-counter-wrapper');
|
|
9409
9794
|
const counterSlot = counterWrapper.querySelector('slot');
|
|
9410
9795
|
this.counters = counterSlot.assignedElements().filter(el => el.tagName.toLowerCase() === 'auro-counter' || el.hasAttribute('auro-counter'));
|
|
9411
9796
|
|
|
9412
|
-
if (this.keydownHandler) {
|
|
9413
|
-
counterWrapper.removeEventListener('keydown', this.keydownHandler);
|
|
9414
|
-
}
|
|
9415
|
-
this.keydownHandler = (keydownEvent) => {
|
|
9416
|
-
this.trapKeyboard(keydownEvent, this.counters);
|
|
9417
|
-
};
|
|
9418
|
-
counterWrapper.addEventListener('keydown', this.keydownHandler);
|
|
9419
|
-
|
|
9420
9797
|
this.counters.forEach((counter) => {
|
|
9421
9798
|
counter.addEventListener("input", () => this.updateValue());
|
|
9422
9799
|
});
|
|
@@ -9558,6 +9935,13 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9558
9935
|
this.updateValueText();
|
|
9559
9936
|
}
|
|
9560
9937
|
|
|
9938
|
+
disconnectedCallback() {
|
|
9939
|
+
super.disconnectedCallback();
|
|
9940
|
+
|
|
9941
|
+
// Remove the event listener for dropdown toggling
|
|
9942
|
+
this.removeEventListener("auroDropdown-toggled", this.handleDropdownToggle);
|
|
9943
|
+
}
|
|
9944
|
+
|
|
9561
9945
|
/**
|
|
9562
9946
|
* Registers the custom element with the browser.
|
|
9563
9947
|
* @param {string} [name="auro-counter-group"] - Custom element name to register.
|
|
@@ -9576,6 +9960,7 @@ class AuroCounterGroup extends AuroElement {
|
|
|
9576
9960
|
renderCounterDropdown() {
|
|
9577
9961
|
return u`
|
|
9578
9962
|
<${this.dropdownTag}
|
|
9963
|
+
noHideOnThisFocusLoss
|
|
9579
9964
|
chevron common fluid
|
|
9580
9965
|
part="dropdown"
|
|
9581
9966
|
?autoPlacement="${this.autoPlacement}"
|