@aurodesignsystem-dev/auro-formkit 0.0.0-pr1488.2 → 0.0.0-pr1489.1
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/auro-bibtemplate.d.ts +7 -0
- package/components/bibtemplate/dist/index.js +9 -1
- package/components/bibtemplate/dist/registered.js +9 -1
- package/components/checkbox/demo/customize.min.js +1 -1
- package/components/checkbox/demo/getting-started.min.js +1 -1
- package/components/checkbox/demo/index.min.js +1 -1
- package/components/checkbox/demo/pages.json +1 -1
- package/components/checkbox/demo/why-checkbox.html +57 -0
- package/components/checkbox/demo/why-checkbox.md +86 -0
- package/components/checkbox/dist/index.js +1 -1
- package/components/checkbox/dist/registered.js +1 -1
- package/components/combobox/demo/customize.min.js +234 -16
- package/components/combobox/demo/getting-started.min.js +234 -16
- package/components/combobox/demo/index.min.js +234 -16
- package/components/combobox/demo/pages.json +1 -1
- package/components/combobox/demo/why-combobox.html +57 -0
- package/components/combobox/demo/why-combobox.md +113 -0
- package/components/combobox/dist/index.js +234 -16
- package/components/combobox/dist/registered.js +234 -16
- package/components/counter/demo/customize.min.js +233 -15
- package/components/counter/demo/index.min.js +233 -15
- package/components/counter/demo/keyboard-behavior.md +1 -0
- package/components/counter/demo/pages.json +1 -1
- package/components/counter/demo/why-counter.html +57 -0
- package/components/counter/demo/why-counter.md +108 -0
- package/components/counter/dist/index.js +22 -241
- package/components/counter/dist/registered.js +10 -2
- package/components/datepicker/demo/accessibility.md +51 -3
- package/components/datepicker/demo/api.md +11 -2
- package/components/datepicker/demo/customize.html +2 -0
- package/components/datepicker/demo/customize.js +19 -0
- package/components/datepicker/demo/customize.md +72 -8
- package/components/datepicker/demo/customize.min.js +26029 -0
- package/components/datepicker/demo/design.md +3 -1
- package/components/datepicker/demo/index.js +5 -1
- package/components/datepicker/demo/index.md +83 -2
- package/components/datepicker/demo/index.min.js +1564 -96
- package/components/datepicker/demo/keyboard-behavior.md +201 -2
- package/components/datepicker/demo/pages.json +1 -1
- package/components/datepicker/demo/voiceover.md +19 -12
- package/components/datepicker/demo/why-datepicker.html +57 -0
- package/components/datepicker/demo/why-datepicker.md +133 -0
- package/components/datepicker/dist/index.js +1489 -97
- package/components/datepicker/dist/registered.js +1489 -97
- package/components/datepicker/dist/src/auro-calendar-cell.d.ts +66 -1
- package/components/datepicker/dist/src/auro-calendar-month.d.ts +28 -0
- package/components/datepicker/dist/src/auro-calendar.d.ts +100 -0
- package/components/datepicker/dist/src/auro-datepicker.d.ts +88 -0
- package/components/datepicker/dist/src/datepickerKeyboardStrategy.d.ts +5 -3
- package/components/dropdown/demo/accessibility.md +11 -0
- package/components/dropdown/demo/api.md +1 -0
- package/components/dropdown/demo/customize.md +3 -0
- package/components/dropdown/demo/customize.min.js +223 -13
- package/components/dropdown/demo/getting-started.min.js +223 -13
- package/components/dropdown/demo/index.min.js +223 -13
- package/components/dropdown/demo/keyboard-behavior.md +1 -0
- package/components/dropdown/demo/pages.json +1 -1
- package/components/dropdown/demo/why-dropdown.html +57 -0
- package/components/dropdown/demo/why-dropdown.md +97 -0
- package/components/dropdown/dist/auro-dropdown.d.ts +33 -1
- package/components/dropdown/dist/index.js +223 -13
- package/components/dropdown/dist/registered.js +223 -13
- package/components/form/demo/customize.min.js +2461 -415
- package/components/form/demo/getting-started.min.js +2461 -415
- package/components/form/demo/index.min.js +2461 -415
- package/components/form/demo/pages.json +1 -1
- package/components/form/demo/registerDemoDeps.min.js +2457 -411
- package/components/form/demo/why-form.html +57 -0
- package/components/form/demo/why-form.md +101 -0
- package/components/input/demo/customize.min.js +1 -1
- package/components/input/demo/getting-started.min.js +1 -1
- package/components/input/demo/index.min.js +1 -1
- package/components/input/demo/pages.json +1 -1
- package/components/input/demo/why-input.html +57 -0
- package/components/input/demo/why-input.md +121 -0
- package/components/input/dist/index.js +1 -1
- package/components/input/dist/registered.js +1 -1
- package/components/menu/demo/pages.json +1 -1
- package/components/menu/demo/why-menu.html +57 -0
- package/components/menu/demo/why-menu.md +104 -0
- package/components/radio/demo/customize.min.js +2186 -0
- package/components/radio/demo/demo-support.min.js +55807 -0
- package/components/radio/demo/getting-started.js +1 -1
- package/components/radio/demo/getting-started.md +1 -1
- package/components/radio/demo/getting-started.min.js +2205 -0
- package/components/radio/demo/index.min.js +1 -1
- package/components/radio/demo/pages.json +1 -1
- package/components/radio/demo/why-radio.html +57 -0
- package/components/radio/demo/why-radio.md +92 -0
- package/components/radio/dist/index.js +1 -1
- package/components/radio/dist/registered.js +1 -1
- package/components/select/demo/customize.min.js +233 -15
- package/components/select/demo/getting-started.min.js +233 -15
- package/components/select/demo/index.min.js +233 -15
- package/components/select/demo/keyboard-behavior.md +1 -0
- package/components/select/demo/pages.json +1 -1
- package/components/select/demo/why-select.html +57 -0
- package/components/select/demo/why-select.md +128 -0
- package/components/select/dist/index.js +233 -15
- package/components/select/dist/registered.js +233 -15
- package/custom-elements.json +2183 -1466
- package/package.json +2 -2
|
@@ -3338,7 +3338,7 @@ function applyKeyboardStrategy(component, strategy, options = {}) {
|
|
|
3338
3338
|
});
|
|
3339
3339
|
}
|
|
3340
3340
|
|
|
3341
|
-
var styleCss$2 = i$2`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{width:auto;max-width:none;height:auto;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:relative;inset:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([isfullscreen]) .container::backdrop{background:var(--ds-color-background-primary, #fff)}:host(:popover-open){position:fixed;overflow:visible;padding:0;border:none;margin:0;background:transparent;inset:unset;outline:none}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}.util_displayHiddenVisually{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip-path:inset(50%);white-space:nowrap}`;
|
|
3341
|
+
var styleCss$2 = i$2`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{width:auto;max-width:none;height:auto;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:relative;inset:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}:host([desktopmodal]:popover-open)::backdrop{background:transparent}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([isfullscreen]) .container::backdrop{background:var(--ds-color-background-primary, #fff)}:host(:popover-open){position:fixed;overflow:visible;padding:0;border:none;margin:0;background:transparent;inset:unset;outline:none}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}.util_displayHiddenVisually{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip-path:inset(50%);white-space:nowrap}`;
|
|
3342
3342
|
|
|
3343
3343
|
var colorCss$2 = i$2`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
|
|
3344
3344
|
|
|
@@ -3969,7 +3969,7 @@ class AuroHelpText extends i {
|
|
|
3969
3969
|
}
|
|
3970
3970
|
}
|
|
3971
3971
|
|
|
3972
|
-
var formkitVersion = '
|
|
3972
|
+
var formkitVersion = '202606011911';
|
|
3973
3973
|
|
|
3974
3974
|
class AuroElement extends i {
|
|
3975
3975
|
static get properties() {
|
|
@@ -4149,6 +4149,7 @@ class AuroDropdown extends AuroElement {
|
|
|
4149
4149
|
_intializeDefaults() {
|
|
4150
4150
|
this.appearance = 'default';
|
|
4151
4151
|
this.chevron = false;
|
|
4152
|
+
this.desktopModal = false;
|
|
4152
4153
|
this.disabled = false;
|
|
4153
4154
|
this.disableKeyboardHandling = false;
|
|
4154
4155
|
this.error = false;
|
|
@@ -4329,6 +4330,14 @@ class AuroDropdown extends AuroElement {
|
|
|
4329
4330
|
reflect: true
|
|
4330
4331
|
},
|
|
4331
4332
|
|
|
4333
|
+
/**
|
|
4334
|
+
* If declared, the dropdown will behave as a modal dialog when in a desktop viewport size.
|
|
4335
|
+
*/
|
|
4336
|
+
desktopModal: {
|
|
4337
|
+
type: Boolean,
|
|
4338
|
+
reflect: true
|
|
4339
|
+
},
|
|
4340
|
+
|
|
4332
4341
|
/**
|
|
4333
4342
|
* If declared, the dropdown will only show by calling the API .show() public method.
|
|
4334
4343
|
*/
|
|
@@ -4616,6 +4625,15 @@ class AuroDropdown extends AuroElement {
|
|
|
4616
4625
|
|
|
4617
4626
|
disconnectedCallback() {
|
|
4618
4627
|
super.disconnectedCallback();
|
|
4628
|
+
this._clearPageInert();
|
|
4629
|
+
if (this._bibTabHandler) {
|
|
4630
|
+
this.removeEventListener('keydown', this._bibTabHandler);
|
|
4631
|
+
this._bibTabHandler = undefined;
|
|
4632
|
+
}
|
|
4633
|
+
if (this.focusTrap) {
|
|
4634
|
+
this.focusTrap.disconnect();
|
|
4635
|
+
this.focusTrap = undefined;
|
|
4636
|
+
}
|
|
4619
4637
|
if (this.floater) {
|
|
4620
4638
|
this.floater.hideBib('disconnect');
|
|
4621
4639
|
this.floater.disconnect();
|
|
@@ -4643,19 +4661,45 @@ class AuroDropdown extends AuroElement {
|
|
|
4643
4661
|
if (this.isPopoverVisible) {
|
|
4644
4662
|
// Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
|
|
4645
4663
|
// Desktop: use show() for Floating UI positioning + FocusTrap for focus management
|
|
4646
|
-
|
|
4647
|
-
this.
|
|
4664
|
+
this.bibElement.value.open(this.isBibFullscreen);
|
|
4665
|
+
this.updateFocusTrap();
|
|
4666
|
+
|
|
4667
|
+
// Desktop modal: make siblings inert so content outside is not interactive
|
|
4668
|
+
if (this.desktopModal && !this.isBibFullscreen) {
|
|
4669
|
+
this._setPageInert();
|
|
4670
|
+
}
|
|
4648
4671
|
} else {
|
|
4649
4672
|
this.bibElement.value.close();
|
|
4673
|
+
this._clearPageInert();
|
|
4650
4674
|
}
|
|
4651
4675
|
}
|
|
4652
4676
|
|
|
4653
4677
|
// When fullscreen strategy changes while open, re-open dialog with correct mode
|
|
4654
4678
|
// (e.g. resizing from desktop → mobile while dropdown is open)
|
|
4655
4679
|
if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
|
|
4656
|
-
const useModal = this.isBibFullscreen;
|
|
4657
4680
|
this.bibElement.value.close();
|
|
4658
|
-
this.bibElement.value.open(
|
|
4681
|
+
this.bibElement.value.open(this.isBibFullscreen);
|
|
4682
|
+
|
|
4683
|
+
// Re-initialize focus management for the new strategy
|
|
4684
|
+
this.updateFocusTrap();
|
|
4685
|
+
|
|
4686
|
+
// Toggle inert: desktop modal needs it, fullscreen showModal() handles it natively
|
|
4687
|
+
if (this.desktopModal && !this.isBibFullscreen) {
|
|
4688
|
+
this._setPageInert();
|
|
4689
|
+
} else {
|
|
4690
|
+
this._clearPageInert();
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
|
|
4694
|
+
// Handle desktopModal toggled while the dropdown is already open.
|
|
4695
|
+
// Re-initialize focus trapping and page inert state to match the new mode.
|
|
4696
|
+
if (changedProperties.has('desktopModal') && this.isPopoverVisible && !this.isBibFullscreen) {
|
|
4697
|
+
this.updateFocusTrap();
|
|
4698
|
+
if (this.desktopModal) {
|
|
4699
|
+
this._setPageInert();
|
|
4700
|
+
} else {
|
|
4701
|
+
this._clearPageInert();
|
|
4702
|
+
}
|
|
4659
4703
|
}
|
|
4660
4704
|
}
|
|
4661
4705
|
|
|
@@ -4665,8 +4709,14 @@ class AuroDropdown extends AuroElement {
|
|
|
4665
4709
|
* @param {CustomEvent} event - The custom event that contains the dropdown toggle information.
|
|
4666
4710
|
*/
|
|
4667
4711
|
handleDropdownToggle(event) {
|
|
4668
|
-
this.updateFocusTrap();
|
|
4669
4712
|
this.isPopoverVisible = event.detail.expanded;
|
|
4713
|
+
|
|
4714
|
+
// Tear down FocusTrap when closing. Creation happens in updated()
|
|
4715
|
+
// after the dialog is open so getFocusableElements can find content.
|
|
4716
|
+
if (!this.isPopoverVisible) {
|
|
4717
|
+
this.updateFocusTrap();
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4670
4720
|
const eventType = event.detail.eventType || "unknown";
|
|
4671
4721
|
if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
|
|
4672
4722
|
this.trigger.focus();
|
|
@@ -4765,19 +4815,178 @@ class AuroDropdown extends AuroElement {
|
|
|
4765
4815
|
* @private
|
|
4766
4816
|
*/
|
|
4767
4817
|
updateFocusTrap() {
|
|
4818
|
+
// Always clean up existing handlers/traps before setting up new ones
|
|
4819
|
+
// to prevent duplicate listeners on repeated calls.
|
|
4820
|
+
if (this._bibTabHandler) {
|
|
4821
|
+
this.removeEventListener('keydown', this._bibTabHandler);
|
|
4822
|
+
this._bibTabHandler = undefined;
|
|
4823
|
+
}
|
|
4824
|
+
|
|
4825
|
+
if (this.focusTrap) {
|
|
4826
|
+
this.focusTrap.disconnect();
|
|
4827
|
+
this.focusTrap = undefined;
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4768
4830
|
if (this.isPopoverVisible) {
|
|
4769
4831
|
if (!this.isBibFullscreen) {
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4832
|
+
if (this.desktopModal) {
|
|
4833
|
+
// Desktop modal: trap focus only within the bib content.
|
|
4834
|
+
// Can't use FocusTrap on the bib element because keydown events
|
|
4835
|
+
// from slotted content bubble through the dropdown host (light DOM),
|
|
4836
|
+
// not through the bib (shadow projection target). Using FocusTrap
|
|
4837
|
+
// on the dropdown would include the trigger in the tab cycle.
|
|
4838
|
+
// Instead, listen for Tab on the dropdown and manually wrap focus
|
|
4839
|
+
// within the bib's focusable elements.
|
|
4840
|
+
this._bibTabHandler = (event) => {
|
|
4841
|
+
if (event.key !== 'Tab') {
|
|
4842
|
+
return;
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
// Collect focusable elements from the bib content.
|
|
4846
|
+
const focusables = getFocusableElements(this.bibContent);
|
|
4847
|
+
|
|
4848
|
+
// Fallback: try from slotted content directly
|
|
4849
|
+
if (!focusables.length) {
|
|
4850
|
+
const slot = this.shadowRoot.querySelector('.slotContent slot');
|
|
4851
|
+
const assignedNodes = slot ? slot.assignedNodes({ flatten: true }) : [];
|
|
4852
|
+
|
|
4853
|
+
for (const node of assignedNodes) {
|
|
4854
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
4855
|
+
focusables.push(...getFocusableElements(node));
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
|
|
4860
|
+
if (!focusables.length) {
|
|
4861
|
+
return;
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4864
|
+
event.preventDefault();
|
|
4865
|
+
|
|
4866
|
+
const direction = event.shiftKey ? -1 : 1; // eslint-disable-line no-magic-numbers
|
|
4867
|
+
|
|
4868
|
+
// Walk the active element chain through shadow roots
|
|
4869
|
+
const actives = this._getActiveElements();
|
|
4870
|
+
|
|
4871
|
+
let idx = focusables.findIndex((el) => actives.includes(el));
|
|
4872
|
+
|
|
4873
|
+
if (idx === -1) { // eslint-disable-line no-magic-numbers
|
|
4874
|
+
// Focus is not on a known element — move to first/last
|
|
4875
|
+
idx = direction === 1 ? -1 : focusables.length; // eslint-disable-line no-magic-numbers
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
// Try each element in order, skipping any that can't receive focus
|
|
4879
|
+
// (e.g. hidden elements, elements in collapsed sections)
|
|
4880
|
+
for (let index = 0; index < focusables.length; index++) { // eslint-disable-line no-plusplus
|
|
4881
|
+
let nextIdx = idx + direction;
|
|
4882
|
+
|
|
4883
|
+
// Wrap around
|
|
4884
|
+
if (nextIdx < 0) {
|
|
4885
|
+
nextIdx = focusables.length - 1;
|
|
4886
|
+
} else if (nextIdx >= focusables.length) {
|
|
4887
|
+
nextIdx = 0;
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
focusables[nextIdx].focus();
|
|
4891
|
+
|
|
4892
|
+
// Verify focus actually moved to the target
|
|
4893
|
+
const newActives = this._getActiveElements();
|
|
4894
|
+
|
|
4895
|
+
if (newActives.includes(focusables[nextIdx])) {
|
|
4896
|
+
return;
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
// Focus didn't stick — skip this element and try the next
|
|
4900
|
+
idx = nextIdx;
|
|
4901
|
+
}
|
|
4902
|
+
};
|
|
4903
|
+
this.addEventListener('keydown', this._bibTabHandler);
|
|
4904
|
+
|
|
4905
|
+
// Move initial focus into the bib content, matching FocusTrap behavior
|
|
4906
|
+
requestAnimationFrame(() => {
|
|
4907
|
+
const focusables = getFocusableElements(this.bibContent);
|
|
4908
|
+
if (focusables.length) {
|
|
4909
|
+
focusables[0].focus();
|
|
4910
|
+
}
|
|
4911
|
+
});
|
|
4912
|
+
} else {
|
|
4913
|
+
// Normal desktop: use FocusTrap on the bib element
|
|
4914
|
+
this.focusTrap = new FocusTrap(this.bibContent);
|
|
4915
|
+
this.focusTrap.focusFirstElement();
|
|
4916
|
+
}
|
|
4773
4917
|
}
|
|
4774
4918
|
// Fullscreen: showModal() provides native focus trapping
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4922
|
+
/**
|
|
4923
|
+
* Returns the chain of active (focused) elements through shadow roots.
|
|
4924
|
+
* @private
|
|
4925
|
+
* @returns {Array<HTMLElement>}
|
|
4926
|
+
*/
|
|
4927
|
+
_getActiveElements() {
|
|
4928
|
+
let { activeElement } = document;
|
|
4929
|
+
const actives = [activeElement];
|
|
4930
|
+
|
|
4931
|
+
while (activeElement?.shadowRoot?.activeElement) {
|
|
4932
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
4933
|
+
actives.push(activeElement);
|
|
4934
|
+
}
|
|
4935
|
+
|
|
4936
|
+
return actives;
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
/**
|
|
4940
|
+
* Sets `inert` on sibling elements of the dropdown's top-level host
|
|
4941
|
+
* so that content outside the dropdown is not interactive while the modal is open.
|
|
4942
|
+
* Walks up through shadow DOM boundaries to find the outermost host element
|
|
4943
|
+
* in the light DOM, then sets `inert` on siblings at each ancestor level
|
|
4944
|
+
* to ensure all page content outside the host subtree is inert.
|
|
4945
|
+
* @private
|
|
4946
|
+
*/
|
|
4947
|
+
_setPageInert() {
|
|
4948
|
+
if (this._inertSiblings) {
|
|
4775
4949
|
return;
|
|
4776
4950
|
}
|
|
4777
4951
|
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4952
|
+
this._inertSiblings = [];
|
|
4953
|
+
|
|
4954
|
+
// Walk up through shadow DOM boundaries to find the topmost host
|
|
4955
|
+
// element in the light DOM. For example, if this dropdown is inside
|
|
4956
|
+
// auro-datepicker's shadow DOM, we walk up to the datepicker element
|
|
4957
|
+
// so we set inert on its siblings — not on the datepicker itself.
|
|
4958
|
+
let host = this;
|
|
4959
|
+
while (host.getRootNode() instanceof ShadowRoot) {
|
|
4960
|
+
host = host.getRootNode().host;
|
|
4961
|
+
}
|
|
4962
|
+
|
|
4963
|
+
// Walk up the ancestor chain, inerting siblings at each level
|
|
4964
|
+
// to ensure the entire page outside the host subtree is inert.
|
|
4965
|
+
let current = host;
|
|
4966
|
+
while (current.parentElement) {
|
|
4967
|
+
const parent = current.parentElement;
|
|
4968
|
+
for (const sibling of parent.children) {
|
|
4969
|
+
if (sibling !== current) {
|
|
4970
|
+
this._inertSiblings.push({ element: sibling, wasInert: sibling.inert });
|
|
4971
|
+
sibling.inert = true;
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
current = parent;
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
/**
|
|
4979
|
+
* Restores `inert` state on siblings that were tracked by `_setPageInert`.
|
|
4980
|
+
* Preserves the previous inert state so externally-inerted elements are
|
|
4981
|
+
* not inadvertently re-enabled.
|
|
4982
|
+
* @private
|
|
4983
|
+
*/
|
|
4984
|
+
_clearPageInert() {
|
|
4985
|
+
if (this._inertSiblings) {
|
|
4986
|
+
for (const entry of this._inertSiblings) {
|
|
4987
|
+
entry.element.inert = entry.wasInert;
|
|
4988
|
+
}
|
|
4989
|
+
this._inertSiblings = undefined;
|
|
4781
4990
|
}
|
|
4782
4991
|
}
|
|
4783
4992
|
|
|
@@ -5016,6 +5225,7 @@ class AuroDropdown extends AuroElement {
|
|
|
5016
5225
|
shape="${this.shape}"
|
|
5017
5226
|
?data-show="${this.isPopoverVisible}"
|
|
5018
5227
|
?isfullscreen="${this.isBibFullscreen}"
|
|
5228
|
+
?desktopmodal="${this.desktopModal}"
|
|
5019
5229
|
.dialogLabel="${this.bibDialogLabel}"
|
|
5020
5230
|
${n$2(this.bibElement)}
|
|
5021
5231
|
>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<p>The trigger is a focusable element and participates in the standard tab order, responding to <code>Tab</code> and <code>Shift+Tab</code> key events per <auro-hyperlink href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/tabindex">native browser behavior</auro-hyperlink>, i.e., these keys step through the browser tabindex sequence.</p>
|
|
6
6
|
<p>When the component is <code>disabled</code> it is removed from the <code>tabindex</code> sequence. VoiceOver's virtual cursor <em>(swipe navigation)</em> can still encounter the component, but standard keyboard <code>Tab</code> navigation skips it.</p>
|
|
7
7
|
<p>When the bib is collapsed, the bib content is excluded from the tab sequence. When <strong>expanded</strong>, focusable elements within the bib content are included in the natural tab order. In fullscreen mode, focus is trapped within the bib, and the tab sequence cycles through the bib content focusable elements until the bib is closed or the viewport no longer meets the fullscreen condition and is rendered as a popover.</p>
|
|
8
|
+
<p>When the <code>desktopModal</code> attribute is set, focus is also trapped within the bib on desktop viewports. All sibling elements on the page are marked <code>inert</code>, preventing interaction with content outside the dropdown until it is closed.</p>
|
|
8
9
|
<!-- AURO-GENERATED-CONTENT:END -->
|
|
9
10
|
<auro-header level="3" id="keyEvents">Key Events</auro-header>
|
|
10
11
|
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/keyEvents.md) -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
["accessibility.md","api.md","customize.md","design.md","getting-started.md","index.md","keyboard-behavior.md","voiceover.md","readme.md"]
|
|
1
|
+
["accessibility.md","api.md","customize.md","design.md","getting-started.md","index.md","keyboard-behavior.md","voiceover.md","why-dropdown.md","readme.md"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
3
|
+
See LICENSE in the project root for license information.
|
|
4
|
+
|
|
5
|
+
HTML in this document is standardized and NOT to be edited.
|
|
6
|
+
All demo code should be added/edited in ./demo/why-dropdown.md
|
|
7
|
+
|
|
8
|
+
With the exception of adding custom elements if needed for the demo.
|
|
9
|
+
|
|
10
|
+
----------------------- DO NOT EDIT -----------------------------
|
|
11
|
+
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
<!DOCTYPE html>
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head>
|
|
17
|
+
<meta charset="UTF-8" />
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
19
|
+
<title>Auro Web Component Demo | auro-dropdown | Why auro-dropdown</title>
|
|
20
|
+
|
|
21
|
+
<!-- highlight.js Stylesheet -->
|
|
22
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css"/>
|
|
23
|
+
|
|
24
|
+
<!-- Legacy reference is still needed to support auro-dropdown's use of legacy token values at this time -->
|
|
25
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@latest/dist/legacy/auro-classic/CSSCustomProperties.css"/>
|
|
26
|
+
|
|
27
|
+
<!-- Design Token Alaska Theme -->
|
|
28
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@latest/dist/themes/alaska/CSSCustomProperties--alaska.min.css"/>
|
|
29
|
+
|
|
30
|
+
<!-- Webcore Stylesheet Alaska Theme -->
|
|
31
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/bundled/themes/alaska.global.min.css" />
|
|
32
|
+
|
|
33
|
+
<!-- Demo Specific Styles -->
|
|
34
|
+
<link rel="stylesheet" type="text/css" href="./styles.min.css" />
|
|
35
|
+
<style>
|
|
36
|
+
table {
|
|
37
|
+
--ds-color-container-secondary-default: transparent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
tr:not(:last-of-type) {
|
|
41
|
+
border-bottom: 1px solid var(--ds-color-border-tertiary-default);
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body class="auro-markdown">
|
|
46
|
+
<main></main>
|
|
47
|
+
|
|
48
|
+
<script type="module">
|
|
49
|
+
import { renderPage } from './demo-support.min.js';
|
|
50
|
+
await renderPage('./why-dropdown.md');
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<!-- If additional elements are needed for the demo, add them here. -->
|
|
54
|
+
<script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-header@latest/+esm" type="module"></script>
|
|
55
|
+
<script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-hyperlink@latest/+esm" type="module"></script>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<auro-header level="1" id="overview">Why auro-dropdown?</auro-header>
|
|
2
|
+
<p>Native HTML has no dedicated dropdown/popover primitive that combines trigger management, content positioning, focus trapping, and responsive behavior. Building a dropdown from <code><details></code>/<code><summary></code> or custom <code><div></code> toggles requires significant work for accessibility and positioning. <code>auro-dropdown</code> provides a production-ready solution.</p>
|
|
3
|
+
<auro-header level="2" id="accessibility">Accessibility</auro-header>
|
|
4
|
+
<p>Custom dropdown implementations often break keyboard navigation, fail to trap focus, or leave background content interactive when the dropdown is open.</p>
|
|
5
|
+
<p><code>auro-dropdown</code> provides:</p>
|
|
6
|
+
<ul>
|
|
7
|
+
<li><strong>Focus trapping</strong> — In desktop modal mode, Tab and Shift+Tab cycle within the dropdown content. Background content is made inert. The implementation handles focus across shadow DOM boundaries.</li>
|
|
8
|
+
<li><strong>Native dialog semantics</strong> — On mobile, the dropdown opens via <code>showModal()</code>, which provides browser-native focus trapping and background inert behavior.</li>
|
|
9
|
+
<li><strong>ARIA attributes</strong> — The trigger carries <code>aria-expanded</code>, <code>aria-controls</code>, and a configurable <code>role</code> (default: <code>button</code>). The dropdown bib has an accessible dialog label.</li>
|
|
10
|
+
<li><strong>Focus delegation</strong> — <code>delegatesFocus: true</code> ensures clicks on the trigger container route focus to the correct interactive element.</li>
|
|
11
|
+
<li><strong>Escape to close</strong> — The dialog's cancel event is handled to close the dropdown on Escape.</li>
|
|
12
|
+
</ul>
|
|
13
|
+
<auro-header level="2" id="positioning">Positioning</auro-header>
|
|
14
|
+
<p>Positioning a dropdown relative to its trigger — accounting for viewport edges, scroll containers, and dynamic content — is a complex problem that native HTML does not solve.</p>
|
|
15
|
+
<p><code>auro-dropdown</code> integrates Floating UI with:</p>
|
|
16
|
+
<ul>
|
|
17
|
+
<li>Configurable <code>placement</code> (12 positions: top/bottom/left/right with start/center/end)</li>
|
|
18
|
+
<li><code>offset</code> control for gap between trigger and content</li>
|
|
19
|
+
<li><code>noFlip</code> to prevent automatic repositioning</li>
|
|
20
|
+
<li><code>shift</code> to slide the dropdown along the edge to avoid clipping</li>
|
|
21
|
+
<li><code>autoPlacement</code> to let the library choose the best position</li>
|
|
22
|
+
<li><code>matchWidth</code> to size the dropdown to the trigger width</li>
|
|
23
|
+
<li>Layout containment escape so the dropdown is not clipped by ancestor <code>overflow</code> or <code><dialog></code> elements</li>
|
|
24
|
+
</ul>
|
|
25
|
+
<auro-header level="2" id="responsiveBehavior">Responsive behavior</auro-header>
|
|
26
|
+
<p>Native disclosure elements have no concept of viewport-aware presentation.</p>
|
|
27
|
+
<p><code>auro-dropdown</code> adapts to the viewport:</p>
|
|
28
|
+
<ul>
|
|
29
|
+
<li><strong>Desktop</strong> — Content appears as a positioned panel with optional modal behavior</li>
|
|
30
|
+
<li><strong>Mobile</strong> — Content opens as a fullscreen dialog below a configurable breakpoint (<code>fullscreenBreakpoint</code>: xs, sm, md, lg, xl, or disabled)</li>
|
|
31
|
+
<li><strong>Desktop modal</strong> — <code>desktopModal</code> makes background siblings inert, traps focus within the dropdown, and prevents interaction with page content — all without going fullscreen</li>
|
|
32
|
+
</ul>
|
|
33
|
+
<auro-header level="2" id="contentagnostic">Content-agnostic</auro-header>
|
|
34
|
+
<p>Unlike <code><select></code>, which only accepts <code><option></code> elements, <code>auro-dropdown</code> accepts any HTML content in its default slot. This makes it the foundation for composed components like <code>auro-select</code>, <code>auro-combobox</code>, and <code>auro-counter-group</code>.</p>
|
|
35
|
+
<auro-header level="2" id="triggerFlexibility">Trigger flexibility</auro-header>
|
|
36
|
+
<p>The <code>trigger</code> slot accepts any content — plain text, buttons, inputs, or complex custom elements. The component detects whether the trigger content is focusable and adjusts tabindex and ARIA attributes accordingly.</p>
|
|
37
|
+
<auro-header level="2" id="designSystemIntegration">Design system integration</auro-header>
|
|
38
|
+
<p><code>auro-dropdown</code> is built with the Auro Design System:</p>
|
|
39
|
+
<ul>
|
|
40
|
+
<li>Three layout options: classic, emphasized, snowflake</li>
|
|
41
|
+
<li>Light and dark theme support (<code>appearance</code>)</li>
|
|
42
|
+
<li>Chevron indicator for expand/collapse state</li>
|
|
43
|
+
<li>CSS <code>::part()</code> selectors (<code>trigger</code>, <code>chevron</code>, <code>size</code>, <code>helpText</code>)</li>
|
|
44
|
+
<li>Help text slot with error message support</li>
|
|
45
|
+
</ul>
|
|
46
|
+
<auro-header level="2" id="summary">Summary</auro-header>
|
|
47
|
+
<table>
|
|
48
|
+
<thead>
|
|
49
|
+
<tr>
|
|
50
|
+
<th>Capability</th>
|
|
51
|
+
<th><code><details></code> / custom div</th>
|
|
52
|
+
<th><code>auro-dropdown</code></th>
|
|
53
|
+
</tr>
|
|
54
|
+
</thead>
|
|
55
|
+
<tbody>
|
|
56
|
+
<tr>
|
|
57
|
+
<td>Viewport-aware positioning</td>
|
|
58
|
+
<td>No</td>
|
|
59
|
+
<td>Floating UI with flip/shift/auto</td>
|
|
60
|
+
</tr>
|
|
61
|
+
<tr>
|
|
62
|
+
<td>Focus trapping</td>
|
|
63
|
+
<td>No</td>
|
|
64
|
+
<td>Desktop modal + fullscreen dialog</td>
|
|
65
|
+
</tr>
|
|
66
|
+
<tr>
|
|
67
|
+
<td>Background inert</td>
|
|
68
|
+
<td>No</td>
|
|
69
|
+
<td>Automatic in modal modes</td>
|
|
70
|
+
</tr>
|
|
71
|
+
<tr>
|
|
72
|
+
<td>Keyboard close (Escape)</td>
|
|
73
|
+
<td>No</td>
|
|
74
|
+
<td>Built-in</td>
|
|
75
|
+
</tr>
|
|
76
|
+
<tr>
|
|
77
|
+
<td>Mobile fullscreen</td>
|
|
78
|
+
<td>No</td>
|
|
79
|
+
<td>Automatic at breakpoint</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td>Any HTML content</td>
|
|
83
|
+
<td>Yes</td>
|
|
84
|
+
<td>Yes</td>
|
|
85
|
+
</tr>
|
|
86
|
+
<tr>
|
|
87
|
+
<td>Trigger detection</td>
|
|
88
|
+
<td>No</td>
|
|
89
|
+
<td>Auto-detects focusable content</td>
|
|
90
|
+
</tr>
|
|
91
|
+
<tr>
|
|
92
|
+
<td>Theming</td>
|
|
93
|
+
<td>No</td>
|
|
94
|
+
<td>Three layouts + appearance modes</td>
|
|
95
|
+
</tr>
|
|
96
|
+
</tbody>
|
|
97
|
+
</table>
|
|
@@ -44,6 +44,13 @@ export class AuroDropdown extends AuroElement {
|
|
|
44
44
|
type: BooleanConstructor;
|
|
45
45
|
reflect: boolean;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* If declared, the dropdown will behave as a modal dialog when in a desktop viewport size.
|
|
49
|
+
*/
|
|
50
|
+
desktopModal: {
|
|
51
|
+
type: BooleanConstructor;
|
|
52
|
+
reflect: boolean;
|
|
53
|
+
};
|
|
47
54
|
/**
|
|
48
55
|
* If declared, the dropdown will only show by calling the API .show() public method.
|
|
49
56
|
*/
|
|
@@ -285,6 +292,7 @@ export class AuroDropdown extends AuroElement {
|
|
|
285
292
|
private _intializeDefaults;
|
|
286
293
|
appearance: string | undefined;
|
|
287
294
|
chevron: boolean | undefined;
|
|
295
|
+
desktopModal: boolean | undefined;
|
|
288
296
|
disabled: boolean | undefined;
|
|
289
297
|
disableKeyboardHandling: boolean | undefined;
|
|
290
298
|
error: boolean | undefined;
|
|
@@ -373,6 +381,8 @@ export class AuroDropdown extends AuroElement {
|
|
|
373
381
|
* @returns {string}
|
|
374
382
|
*/
|
|
375
383
|
private get focusableEntityQuery();
|
|
384
|
+
_bibTabHandler: ((event: any) => void) | undefined;
|
|
385
|
+
focusTrap: any;
|
|
376
386
|
updated(changedProperties: any): void;
|
|
377
387
|
firstUpdated(): void;
|
|
378
388
|
dropdownId: any;
|
|
@@ -400,7 +410,29 @@ export class AuroDropdown extends AuroElement {
|
|
|
400
410
|
* @private
|
|
401
411
|
*/
|
|
402
412
|
private updateFocusTrap;
|
|
403
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Returns the chain of active (focused) elements through shadow roots.
|
|
415
|
+
* @private
|
|
416
|
+
* @returns {Array<HTMLElement>}
|
|
417
|
+
*/
|
|
418
|
+
private _getActiveElements;
|
|
419
|
+
/**
|
|
420
|
+
* Sets `inert` on sibling elements of the dropdown's top-level host
|
|
421
|
+
* so that content outside the dropdown is not interactive while the modal is open.
|
|
422
|
+
* Walks up through shadow DOM boundaries to find the outermost host element
|
|
423
|
+
* in the light DOM, then sets `inert` on siblings at each ancestor level
|
|
424
|
+
* to ensure all page content outside the host subtree is inert.
|
|
425
|
+
* @private
|
|
426
|
+
*/
|
|
427
|
+
private _setPageInert;
|
|
428
|
+
_inertSiblings: any[] | undefined;
|
|
429
|
+
/**
|
|
430
|
+
* Restores `inert` state on siblings that were tracked by `_setPageInert`.
|
|
431
|
+
* Preserves the previous inert state so externally-inerted elements are
|
|
432
|
+
* not inadvertently re-enabled.
|
|
433
|
+
* @private
|
|
434
|
+
*/
|
|
435
|
+
private _clearPageInert;
|
|
404
436
|
/**
|
|
405
437
|
* Function to support @focusout event.
|
|
406
438
|
* @private
|