@aurodesignsystem-dev/auro-formkit 0.0.0-pr1474.3 → 0.0.0-pr1475.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/components/bibtemplate/dist/auro-bibtemplate.d.ts +7 -0
  2. package/components/bibtemplate/dist/index.js +9 -1
  3. package/components/bibtemplate/dist/registered.js +9 -1
  4. package/components/checkbox/demo/customize.html +2 -1
  5. package/components/checkbox/demo/customize.min.js +1 -1
  6. package/components/checkbox/demo/getting-started.min.js +1 -1
  7. package/components/checkbox/demo/index.min.js +1 -1
  8. package/components/checkbox/dist/index.js +1 -1
  9. package/components/checkbox/dist/registered.js +1 -1
  10. package/components/combobox/demo/customize.html +2 -1
  11. package/components/combobox/demo/customize.md +130 -106
  12. package/components/combobox/demo/customize.min.js +209 -16
  13. package/components/combobox/demo/getting-started.min.js +209 -16
  14. package/components/combobox/demo/index.min.js +209 -16
  15. package/components/combobox/dist/index.js +209 -16
  16. package/components/combobox/dist/registered.js +209 -16
  17. package/components/counter/demo/customize.min.js +208 -15
  18. package/components/counter/demo/index.min.js +208 -15
  19. package/components/counter/demo/keyboard-behavior.md +1 -0
  20. package/components/counter/dist/index.js +10 -2
  21. package/components/counter/dist/registered.js +10 -2
  22. package/components/datepicker/demo/accessibility.md +51 -3
  23. package/components/datepicker/demo/api.md +9 -0
  24. package/components/datepicker/demo/customize.html +2 -0
  25. package/components/datepicker/demo/customize.js +19 -0
  26. package/components/datepicker/demo/customize.md +72 -8
  27. package/components/datepicker/demo/customize.min.js +25690 -0
  28. package/components/datepicker/demo/design.md +3 -1
  29. package/components/datepicker/demo/index.js +2 -1
  30. package/components/datepicker/demo/index.md +81 -1
  31. package/components/datepicker/demo/index.min.js +1223 -101
  32. package/components/datepicker/demo/keyboard-behavior.md +201 -2
  33. package/components/datepicker/demo/voiceover.md +19 -12
  34. package/components/datepicker/dist/index.js +1155 -104
  35. package/components/datepicker/dist/registered.js +1155 -104
  36. package/components/datepicker/dist/src/auro-calendar-cell.d.ts +59 -0
  37. package/components/datepicker/dist/src/auro-calendar-month.d.ts +28 -0
  38. package/components/datepicker/dist/src/auro-calendar.d.ts +84 -0
  39. package/components/datepicker/dist/src/auro-datepicker.d.ts +80 -0
  40. package/components/datepicker/dist/src/datepickerKeyboardStrategy.d.ts +5 -3
  41. package/components/dropdown/demo/accessibility.md +11 -0
  42. package/components/dropdown/demo/api.md +1 -0
  43. package/components/dropdown/demo/customize.md +3 -0
  44. package/components/dropdown/demo/customize.min.js +198 -13
  45. package/components/dropdown/demo/getting-started.min.js +198 -13
  46. package/components/dropdown/demo/index.min.js +198 -13
  47. package/components/dropdown/demo/keyboard-behavior.md +1 -0
  48. package/components/dropdown/dist/auro-dropdown.d.ts +30 -1
  49. package/components/dropdown/dist/index.js +198 -13
  50. package/components/dropdown/dist/registered.js +198 -13
  51. package/components/form/demo/customize.html +6 -6
  52. package/components/form/demo/customize.js +0 -17
  53. package/components/form/demo/customize.md +51 -125
  54. package/components/form/demo/customize.min.js +1776 -327
  55. package/components/form/demo/getting-started.min.js +1776 -291
  56. package/components/form/demo/index.min.js +1776 -291
  57. package/components/form/demo/registerDemoDeps.min.js +1769 -139
  58. package/components/form/dist/auro-form.d.ts +5 -45
  59. package/components/form/dist/index.js +7 -152
  60. package/components/form/dist/registered.js +7 -152
  61. package/components/input/demo/customize.html +2 -1
  62. package/components/input/demo/customize.min.js +1 -1
  63. package/components/input/demo/getting-started.min.js +1 -1
  64. package/components/input/demo/index.min.js +1 -1
  65. package/components/input/dist/index.js +1 -1
  66. package/components/input/dist/registered.js +1 -1
  67. package/components/radio/demo/customize.min.js +2186 -0
  68. package/components/radio/demo/demo-support.min.js +55807 -0
  69. package/components/radio/demo/getting-started.js +1 -1
  70. package/components/radio/demo/getting-started.md +1 -1
  71. package/components/radio/demo/getting-started.min.js +2205 -0
  72. package/components/radio/demo/index.min.js +1 -1
  73. package/components/radio/dist/index.js +1 -1
  74. package/components/radio/dist/registered.js +1 -1
  75. package/components/select/demo/customize.html +2 -2
  76. package/components/select/demo/customize.min.js +208 -15
  77. package/components/select/demo/getting-started.min.js +208 -15
  78. package/components/select/demo/index.min.js +208 -15
  79. package/components/select/demo/keyboard-behavior.md +1 -0
  80. package/components/select/dist/index.js +208 -15
  81. package/components/select/dist/registered.js +208 -15
  82. package/custom-elements.json +2130 -1518
  83. package/package.json +2 -2
@@ -3311,7 +3311,7 @@ function applyKeyboardStrategy(component, strategy, options = {}) {
3311
3311
  });
3312
3312
  }
3313
3313
 
3314
- 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}`;
3314
+ 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}`;
3315
3315
 
3316
3316
  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)}`;
3317
3317
 
@@ -3942,7 +3942,7 @@ class AuroHelpText extends i {
3942
3942
  }
3943
3943
  }
3944
3944
 
3945
- var formkitVersion = '202605122144';
3945
+ var formkitVersion = '202605182147';
3946
3946
 
3947
3947
  class AuroElement extends i {
3948
3948
  static get properties() {
@@ -4122,6 +4122,7 @@ class AuroDropdown extends AuroElement {
4122
4122
  _intializeDefaults() {
4123
4123
  this.appearance = 'default';
4124
4124
  this.chevron = false;
4125
+ this.desktopModal = false;
4125
4126
  this.disabled = false;
4126
4127
  this.disableKeyboardHandling = false;
4127
4128
  this.error = false;
@@ -4302,6 +4303,14 @@ class AuroDropdown extends AuroElement {
4302
4303
  reflect: true
4303
4304
  },
4304
4305
 
4306
+ /**
4307
+ * If declared, the dropdown will behave as a modal dialog when in a desktop viewport size.
4308
+ */
4309
+ desktopModal: {
4310
+ type: Boolean,
4311
+ reflect: true
4312
+ },
4313
+
4305
4314
  /**
4306
4315
  * If declared, the dropdown will only show by calling the API .show() public method.
4307
4316
  */
@@ -4589,6 +4598,15 @@ class AuroDropdown extends AuroElement {
4589
4598
 
4590
4599
  disconnectedCallback() {
4591
4600
  super.disconnectedCallback();
4601
+ this._clearPageInert();
4602
+ if (this._bibTabHandler) {
4603
+ this.removeEventListener('keydown', this._bibTabHandler);
4604
+ this._bibTabHandler = undefined;
4605
+ }
4606
+ if (this.focusTrap) {
4607
+ this.focusTrap.disconnect();
4608
+ this.focusTrap = undefined;
4609
+ }
4592
4610
  if (this.floater) {
4593
4611
  this.floater.hideBib('disconnect');
4594
4612
  this.floater.disconnect();
@@ -4616,19 +4634,34 @@ class AuroDropdown extends AuroElement {
4616
4634
  if (this.isPopoverVisible) {
4617
4635
  // Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
4618
4636
  // Desktop: use show() for Floating UI positioning + FocusTrap for focus management
4619
- const useModal = this.isBibFullscreen;
4620
- this.bibElement.value.open(useModal);
4637
+ this.bibElement.value.open(this.isBibFullscreen);
4638
+ this.updateFocusTrap();
4639
+
4640
+ // Desktop modal: make siblings inert so content outside is not interactive
4641
+ if (this.desktopModal && !this.isBibFullscreen) {
4642
+ this._setPageInert();
4643
+ }
4621
4644
  } else {
4622
4645
  this.bibElement.value.close();
4646
+ this._clearPageInert();
4623
4647
  }
4624
4648
  }
4625
4649
 
4626
4650
  // When fullscreen strategy changes while open, re-open dialog with correct mode
4627
4651
  // (e.g. resizing from desktop → mobile while dropdown is open)
4628
4652
  if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
4629
- const useModal = this.isBibFullscreen;
4630
4653
  this.bibElement.value.close();
4631
- this.bibElement.value.open(useModal);
4654
+ this.bibElement.value.open(this.isBibFullscreen);
4655
+
4656
+ // Re-initialize focus management for the new strategy
4657
+ this.updateFocusTrap();
4658
+
4659
+ // Toggle inert: desktop modal needs it, fullscreen showModal() handles it natively
4660
+ if (this.desktopModal && !this.isBibFullscreen) {
4661
+ this._setPageInert();
4662
+ } else {
4663
+ this._clearPageInert();
4664
+ }
4632
4665
  }
4633
4666
  }
4634
4667
 
@@ -4638,8 +4671,14 @@ class AuroDropdown extends AuroElement {
4638
4671
  * @param {CustomEvent} event - The custom event that contains the dropdown toggle information.
4639
4672
  */
4640
4673
  handleDropdownToggle(event) {
4641
- this.updateFocusTrap();
4642
4674
  this.isPopoverVisible = event.detail.expanded;
4675
+
4676
+ // Tear down FocusTrap when closing. Creation happens in updated()
4677
+ // after the dialog is open so getFocusableElements can find content.
4678
+ if (!this.isPopoverVisible) {
4679
+ this.updateFocusTrap();
4680
+ }
4681
+
4643
4682
  const eventType = event.detail.eventType || "unknown";
4644
4683
  if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
4645
4684
  this.trigger.focus();
@@ -4738,19 +4777,164 @@ class AuroDropdown extends AuroElement {
4738
4777
  * @private
4739
4778
  */
4740
4779
  updateFocusTrap() {
4780
+ // Always clean up existing handlers/traps before setting up new ones
4781
+ // to prevent duplicate listeners on repeated calls.
4782
+ if (this._bibTabHandler) {
4783
+ this.removeEventListener('keydown', this._bibTabHandler);
4784
+ this._bibTabHandler = undefined;
4785
+ }
4786
+
4787
+ if (this.focusTrap) {
4788
+ this.focusTrap.disconnect();
4789
+ this.focusTrap = undefined;
4790
+ }
4791
+
4741
4792
  if (this.isPopoverVisible) {
4742
4793
  if (!this.isBibFullscreen) {
4743
- // Desktop: show() doesn't trap focus, so use FocusTrap
4744
- this.focusTrap = new FocusTrap(this.bibContent);
4745
- this.focusTrap.focusFirstElement();
4794
+ if (this.desktopModal) {
4795
+ // Desktop modal: trap focus only within the bib content.
4796
+ // Can't use FocusTrap on the bib element because keydown events
4797
+ // from slotted content bubble through the dropdown host (light DOM),
4798
+ // not through the bib (shadow projection target). Using FocusTrap
4799
+ // on the dropdown would include the trigger in the tab cycle.
4800
+ // Instead, listen for Tab on the dropdown and manually wrap focus
4801
+ // within the bib's focusable elements.
4802
+ this._bibTabHandler = (event) => {
4803
+ if (event.key !== 'Tab') {
4804
+ return;
4805
+ }
4806
+
4807
+ // Collect focusable elements from the bib content.
4808
+ const focusables = getFocusableElements(this.bibContent);
4809
+
4810
+ // Fallback: try from slotted content directly
4811
+ if (!focusables.length) {
4812
+ const slot = this.shadowRoot.querySelector('.slotContent slot');
4813
+ const assignedNodes = slot ? slot.assignedNodes({ flatten: true }) : [];
4814
+
4815
+ for (const node of assignedNodes) {
4816
+ if (node.nodeType === Node.ELEMENT_NODE) {
4817
+ focusables.push(...getFocusableElements(node));
4818
+ }
4819
+ }
4820
+ }
4821
+
4822
+ if (!focusables.length) {
4823
+ return;
4824
+ }
4825
+
4826
+ event.preventDefault();
4827
+
4828
+ const direction = event.shiftKey ? -1 : 1; // eslint-disable-line no-magic-numbers
4829
+
4830
+ // Walk the active element chain through shadow roots
4831
+ const actives = this._getActiveElements();
4832
+
4833
+ let idx = focusables.findIndex((el) => actives.includes(el));
4834
+
4835
+ if (idx === -1) { // eslint-disable-line no-magic-numbers
4836
+ // Focus is not on a known element — move to first/last
4837
+ idx = direction === 1 ? -1 : focusables.length; // eslint-disable-line no-magic-numbers
4838
+ }
4839
+
4840
+ // Try each element in order, skipping any that can't receive focus
4841
+ // (e.g. hidden elements, elements in collapsed sections)
4842
+ for (let index = 0; index < focusables.length; index++) { // eslint-disable-line no-plusplus
4843
+ let nextIdx = idx + direction;
4844
+
4845
+ // Wrap around
4846
+ if (nextIdx < 0) {
4847
+ nextIdx = focusables.length - 1;
4848
+ } else if (nextIdx >= focusables.length) {
4849
+ nextIdx = 0;
4850
+ }
4851
+
4852
+ focusables[nextIdx].focus();
4853
+
4854
+ // Verify focus actually moved to the target
4855
+ const newActives = this._getActiveElements();
4856
+
4857
+ if (newActives.includes(focusables[nextIdx])) {
4858
+ return;
4859
+ }
4860
+
4861
+ // Focus didn't stick — skip this element and try the next
4862
+ idx = nextIdx;
4863
+ }
4864
+ };
4865
+ this.addEventListener('keydown', this._bibTabHandler);
4866
+ } else {
4867
+ // Normal desktop: use FocusTrap on the bib element
4868
+ this.focusTrap = new FocusTrap(this.bibContent);
4869
+ this.focusTrap.focusFirstElement();
4870
+ }
4746
4871
  }
4747
4872
  // Fullscreen: showModal() provides native focus trapping
4873
+ }
4874
+ }
4875
+
4876
+ /**
4877
+ * Returns the chain of active (focused) elements through shadow roots.
4878
+ * @private
4879
+ * @returns {Array<HTMLElement>}
4880
+ */
4881
+ _getActiveElements() {
4882
+ let { activeElement } = document;
4883
+ const actives = [activeElement];
4884
+
4885
+ while (activeElement?.shadowRoot?.activeElement) {
4886
+ activeElement = activeElement.shadowRoot.activeElement;
4887
+ actives.push(activeElement);
4888
+ }
4889
+
4890
+ return actives;
4891
+ }
4892
+
4893
+ /**
4894
+ * Sets `inert` on sibling elements of the dropdown's top-level host
4895
+ * so that content outside the dropdown is not interactive while the modal is open.
4896
+ * Walks up through shadow DOM boundaries to find the outermost host element
4897
+ * in the light DOM, then sets `inert` on that element's siblings.
4898
+ * @private
4899
+ */
4900
+ _setPageInert() {
4901
+ if (this._inertSiblings) {
4748
4902
  return;
4749
4903
  }
4750
4904
 
4751
- if (this.focusTrap) {
4752
- this.focusTrap.disconnect();
4753
- this.focusTrap = undefined;
4905
+ this._inertSiblings = [];
4906
+
4907
+ // Walk up through shadow DOM boundaries to find the topmost host
4908
+ // element in the light DOM. For example, if this dropdown is inside
4909
+ // auro-datepicker's shadow DOM, we walk up to the datepicker element
4910
+ // so we set inert on its siblings — not on the datepicker itself.
4911
+ let host = this;
4912
+ while (host.getRootNode() instanceof ShadowRoot) {
4913
+ host = host.getRootNode().host;
4914
+ }
4915
+
4916
+ const parent = host.parentElement;
4917
+
4918
+ if (parent) {
4919
+ for (const sibling of parent.children) {
4920
+ if (sibling !== host && !sibling.inert) {
4921
+ sibling.inert = true;
4922
+ this._inertSiblings.push(sibling);
4923
+ }
4924
+ }
4925
+ }
4926
+ }
4927
+
4928
+ /**
4929
+ * Restores `inert` state on siblings that were made inert by `_setPageInert`.
4930
+ * @private
4931
+ */
4932
+ _clearPageInert() {
4933
+ if (this._inertSiblings) {
4934
+ for (const sibling of this._inertSiblings) {
4935
+ sibling.inert = false;
4936
+ }
4937
+ this._inertSiblings = undefined;
4754
4938
  }
4755
4939
  }
4756
4940
 
@@ -4989,6 +5173,7 @@ class AuroDropdown extends AuroElement {
4989
5173
  shape="${this.shape}"
4990
5174
  ?data-show="${this.isPopoverVisible}"
4991
5175
  ?isfullscreen="${this.isBibFullscreen}"
5176
+ ?desktopmodal="${this.desktopModal}"
4992
5177
  .dialogLabel="${this.bibDialogLabel}"
4993
5178
  ${n$2(this.bibElement)}
4994
5179
  >
@@ -3311,7 +3311,7 @@ function applyKeyboardStrategy(component, strategy, options = {}) {
3311
3311
  });
3312
3312
  }
3313
3313
 
3314
- 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}`;
3314
+ 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}`;
3315
3315
 
3316
3316
  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)}`;
3317
3317
 
@@ -3942,7 +3942,7 @@ class AuroHelpText extends i {
3942
3942
  }
3943
3943
  }
3944
3944
 
3945
- var formkitVersion = '202605122144';
3945
+ var formkitVersion = '202605182147';
3946
3946
 
3947
3947
  class AuroElement extends i {
3948
3948
  static get properties() {
@@ -4122,6 +4122,7 @@ class AuroDropdown extends AuroElement {
4122
4122
  _intializeDefaults() {
4123
4123
  this.appearance = 'default';
4124
4124
  this.chevron = false;
4125
+ this.desktopModal = false;
4125
4126
  this.disabled = false;
4126
4127
  this.disableKeyboardHandling = false;
4127
4128
  this.error = false;
@@ -4302,6 +4303,14 @@ class AuroDropdown extends AuroElement {
4302
4303
  reflect: true
4303
4304
  },
4304
4305
 
4306
+ /**
4307
+ * If declared, the dropdown will behave as a modal dialog when in a desktop viewport size.
4308
+ */
4309
+ desktopModal: {
4310
+ type: Boolean,
4311
+ reflect: true
4312
+ },
4313
+
4305
4314
  /**
4306
4315
  * If declared, the dropdown will only show by calling the API .show() public method.
4307
4316
  */
@@ -4589,6 +4598,15 @@ class AuroDropdown extends AuroElement {
4589
4598
 
4590
4599
  disconnectedCallback() {
4591
4600
  super.disconnectedCallback();
4601
+ this._clearPageInert();
4602
+ if (this._bibTabHandler) {
4603
+ this.removeEventListener('keydown', this._bibTabHandler);
4604
+ this._bibTabHandler = undefined;
4605
+ }
4606
+ if (this.focusTrap) {
4607
+ this.focusTrap.disconnect();
4608
+ this.focusTrap = undefined;
4609
+ }
4592
4610
  if (this.floater) {
4593
4611
  this.floater.hideBib('disconnect');
4594
4612
  this.floater.disconnect();
@@ -4616,19 +4634,34 @@ class AuroDropdown extends AuroElement {
4616
4634
  if (this.isPopoverVisible) {
4617
4635
  // Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
4618
4636
  // Desktop: use show() for Floating UI positioning + FocusTrap for focus management
4619
- const useModal = this.isBibFullscreen;
4620
- this.bibElement.value.open(useModal);
4637
+ this.bibElement.value.open(this.isBibFullscreen);
4638
+ this.updateFocusTrap();
4639
+
4640
+ // Desktop modal: make siblings inert so content outside is not interactive
4641
+ if (this.desktopModal && !this.isBibFullscreen) {
4642
+ this._setPageInert();
4643
+ }
4621
4644
  } else {
4622
4645
  this.bibElement.value.close();
4646
+ this._clearPageInert();
4623
4647
  }
4624
4648
  }
4625
4649
 
4626
4650
  // When fullscreen strategy changes while open, re-open dialog with correct mode
4627
4651
  // (e.g. resizing from desktop → mobile while dropdown is open)
4628
4652
  if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
4629
- const useModal = this.isBibFullscreen;
4630
4653
  this.bibElement.value.close();
4631
- this.bibElement.value.open(useModal);
4654
+ this.bibElement.value.open(this.isBibFullscreen);
4655
+
4656
+ // Re-initialize focus management for the new strategy
4657
+ this.updateFocusTrap();
4658
+
4659
+ // Toggle inert: desktop modal needs it, fullscreen showModal() handles it natively
4660
+ if (this.desktopModal && !this.isBibFullscreen) {
4661
+ this._setPageInert();
4662
+ } else {
4663
+ this._clearPageInert();
4664
+ }
4632
4665
  }
4633
4666
  }
4634
4667
 
@@ -4638,8 +4671,14 @@ class AuroDropdown extends AuroElement {
4638
4671
  * @param {CustomEvent} event - The custom event that contains the dropdown toggle information.
4639
4672
  */
4640
4673
  handleDropdownToggle(event) {
4641
- this.updateFocusTrap();
4642
4674
  this.isPopoverVisible = event.detail.expanded;
4675
+
4676
+ // Tear down FocusTrap when closing. Creation happens in updated()
4677
+ // after the dialog is open so getFocusableElements can find content.
4678
+ if (!this.isPopoverVisible) {
4679
+ this.updateFocusTrap();
4680
+ }
4681
+
4643
4682
  const eventType = event.detail.eventType || "unknown";
4644
4683
  if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
4645
4684
  this.trigger.focus();
@@ -4738,19 +4777,164 @@ class AuroDropdown extends AuroElement {
4738
4777
  * @private
4739
4778
  */
4740
4779
  updateFocusTrap() {
4780
+ // Always clean up existing handlers/traps before setting up new ones
4781
+ // to prevent duplicate listeners on repeated calls.
4782
+ if (this._bibTabHandler) {
4783
+ this.removeEventListener('keydown', this._bibTabHandler);
4784
+ this._bibTabHandler = undefined;
4785
+ }
4786
+
4787
+ if (this.focusTrap) {
4788
+ this.focusTrap.disconnect();
4789
+ this.focusTrap = undefined;
4790
+ }
4791
+
4741
4792
  if (this.isPopoverVisible) {
4742
4793
  if (!this.isBibFullscreen) {
4743
- // Desktop: show() doesn't trap focus, so use FocusTrap
4744
- this.focusTrap = new FocusTrap(this.bibContent);
4745
- this.focusTrap.focusFirstElement();
4794
+ if (this.desktopModal) {
4795
+ // Desktop modal: trap focus only within the bib content.
4796
+ // Can't use FocusTrap on the bib element because keydown events
4797
+ // from slotted content bubble through the dropdown host (light DOM),
4798
+ // not through the bib (shadow projection target). Using FocusTrap
4799
+ // on the dropdown would include the trigger in the tab cycle.
4800
+ // Instead, listen for Tab on the dropdown and manually wrap focus
4801
+ // within the bib's focusable elements.
4802
+ this._bibTabHandler = (event) => {
4803
+ if (event.key !== 'Tab') {
4804
+ return;
4805
+ }
4806
+
4807
+ // Collect focusable elements from the bib content.
4808
+ const focusables = getFocusableElements(this.bibContent);
4809
+
4810
+ // Fallback: try from slotted content directly
4811
+ if (!focusables.length) {
4812
+ const slot = this.shadowRoot.querySelector('.slotContent slot');
4813
+ const assignedNodes = slot ? slot.assignedNodes({ flatten: true }) : [];
4814
+
4815
+ for (const node of assignedNodes) {
4816
+ if (node.nodeType === Node.ELEMENT_NODE) {
4817
+ focusables.push(...getFocusableElements(node));
4818
+ }
4819
+ }
4820
+ }
4821
+
4822
+ if (!focusables.length) {
4823
+ return;
4824
+ }
4825
+
4826
+ event.preventDefault();
4827
+
4828
+ const direction = event.shiftKey ? -1 : 1; // eslint-disable-line no-magic-numbers
4829
+
4830
+ // Walk the active element chain through shadow roots
4831
+ const actives = this._getActiveElements();
4832
+
4833
+ let idx = focusables.findIndex((el) => actives.includes(el));
4834
+
4835
+ if (idx === -1) { // eslint-disable-line no-magic-numbers
4836
+ // Focus is not on a known element — move to first/last
4837
+ idx = direction === 1 ? -1 : focusables.length; // eslint-disable-line no-magic-numbers
4838
+ }
4839
+
4840
+ // Try each element in order, skipping any that can't receive focus
4841
+ // (e.g. hidden elements, elements in collapsed sections)
4842
+ for (let index = 0; index < focusables.length; index++) { // eslint-disable-line no-plusplus
4843
+ let nextIdx = idx + direction;
4844
+
4845
+ // Wrap around
4846
+ if (nextIdx < 0) {
4847
+ nextIdx = focusables.length - 1;
4848
+ } else if (nextIdx >= focusables.length) {
4849
+ nextIdx = 0;
4850
+ }
4851
+
4852
+ focusables[nextIdx].focus();
4853
+
4854
+ // Verify focus actually moved to the target
4855
+ const newActives = this._getActiveElements();
4856
+
4857
+ if (newActives.includes(focusables[nextIdx])) {
4858
+ return;
4859
+ }
4860
+
4861
+ // Focus didn't stick — skip this element and try the next
4862
+ idx = nextIdx;
4863
+ }
4864
+ };
4865
+ this.addEventListener('keydown', this._bibTabHandler);
4866
+ } else {
4867
+ // Normal desktop: use FocusTrap on the bib element
4868
+ this.focusTrap = new FocusTrap(this.bibContent);
4869
+ this.focusTrap.focusFirstElement();
4870
+ }
4746
4871
  }
4747
4872
  // Fullscreen: showModal() provides native focus trapping
4873
+ }
4874
+ }
4875
+
4876
+ /**
4877
+ * Returns the chain of active (focused) elements through shadow roots.
4878
+ * @private
4879
+ * @returns {Array<HTMLElement>}
4880
+ */
4881
+ _getActiveElements() {
4882
+ let { activeElement } = document;
4883
+ const actives = [activeElement];
4884
+
4885
+ while (activeElement?.shadowRoot?.activeElement) {
4886
+ activeElement = activeElement.shadowRoot.activeElement;
4887
+ actives.push(activeElement);
4888
+ }
4889
+
4890
+ return actives;
4891
+ }
4892
+
4893
+ /**
4894
+ * Sets `inert` on sibling elements of the dropdown's top-level host
4895
+ * so that content outside the dropdown is not interactive while the modal is open.
4896
+ * Walks up through shadow DOM boundaries to find the outermost host element
4897
+ * in the light DOM, then sets `inert` on that element's siblings.
4898
+ * @private
4899
+ */
4900
+ _setPageInert() {
4901
+ if (this._inertSiblings) {
4748
4902
  return;
4749
4903
  }
4750
4904
 
4751
- if (this.focusTrap) {
4752
- this.focusTrap.disconnect();
4753
- this.focusTrap = undefined;
4905
+ this._inertSiblings = [];
4906
+
4907
+ // Walk up through shadow DOM boundaries to find the topmost host
4908
+ // element in the light DOM. For example, if this dropdown is inside
4909
+ // auro-datepicker's shadow DOM, we walk up to the datepicker element
4910
+ // so we set inert on its siblings — not on the datepicker itself.
4911
+ let host = this;
4912
+ while (host.getRootNode() instanceof ShadowRoot) {
4913
+ host = host.getRootNode().host;
4914
+ }
4915
+
4916
+ const parent = host.parentElement;
4917
+
4918
+ if (parent) {
4919
+ for (const sibling of parent.children) {
4920
+ if (sibling !== host && !sibling.inert) {
4921
+ sibling.inert = true;
4922
+ this._inertSiblings.push(sibling);
4923
+ }
4924
+ }
4925
+ }
4926
+ }
4927
+
4928
+ /**
4929
+ * Restores `inert` state on siblings that were made inert by `_setPageInert`.
4930
+ * @private
4931
+ */
4932
+ _clearPageInert() {
4933
+ if (this._inertSiblings) {
4934
+ for (const sibling of this._inertSiblings) {
4935
+ sibling.inert = false;
4936
+ }
4937
+ this._inertSiblings = undefined;
4754
4938
  }
4755
4939
  }
4756
4940
 
@@ -4989,6 +5173,7 @@ class AuroDropdown extends AuroElement {
4989
5173
  shape="${this.shape}"
4990
5174
  ?data-show="${this.isPopoverVisible}"
4991
5175
  ?isfullscreen="${this.isBibFullscreen}"
5176
+ ?desktopmodal="${this.desktopModal}"
4992
5177
  .dialogLabel="${this.bibDialogLabel}"
4993
5178
  ${n$2(this.bibElement)}
4994
5179
  >