@aurodesignsystem-dev/auro-formkit 0.0.0-pr1346.2 → 0.0.0-pr1346.4

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 (40) hide show
  1. package/components/checkbox/demo/api.min.js +1 -1
  2. package/components/checkbox/demo/index.min.js +1 -1
  3. package/components/checkbox/dist/index.js +1 -1
  4. package/components/checkbox/dist/registered.js +1 -1
  5. package/components/combobox/demo/api.min.js +252 -65
  6. package/components/combobox/demo/index.min.js +252 -65
  7. package/components/combobox/dist/auro-combobox.d.ts +2 -1
  8. package/components/combobox/dist/index.js +245 -62
  9. package/components/combobox/dist/registered.js +245 -62
  10. package/components/counter/demo/api.min.js +124 -12
  11. package/components/counter/demo/index.min.js +124 -12
  12. package/components/counter/dist/index.js +124 -12
  13. package/components/counter/dist/registered.js +124 -12
  14. package/components/datepicker/demo/api.min.js +161 -40
  15. package/components/datepicker/demo/index.min.js +161 -40
  16. package/components/datepicker/dist/index.js +161 -40
  17. package/components/datepicker/dist/registered.js +161 -40
  18. package/components/dropdown/demo/api.min.js +122 -10
  19. package/components/dropdown/demo/index.min.js +122 -10
  20. package/components/dropdown/dist/auro-dropdownBib.d.ts +18 -2
  21. package/components/dropdown/dist/index.js +122 -10
  22. package/components/dropdown/dist/registered.js +122 -10
  23. package/components/input/demo/api.min.js +38 -29
  24. package/components/input/demo/index.min.js +38 -29
  25. package/components/input/dist/index.js +38 -29
  26. package/components/input/dist/registered.js +38 -29
  27. package/components/menu/demo/api.min.js +7 -3
  28. package/components/menu/demo/index.min.js +7 -3
  29. package/components/menu/dist/index.js +7 -3
  30. package/components/menu/dist/registered.js +7 -3
  31. package/components/radio/demo/api.min.js +1 -1
  32. package/components/radio/demo/index.min.js +1 -1
  33. package/components/radio/dist/index.js +1 -1
  34. package/components/radio/dist/registered.js +1 -1
  35. package/components/select/demo/api.min.js +159 -17
  36. package/components/select/demo/index.min.js +159 -17
  37. package/components/select/dist/index.js +152 -14
  38. package/components/select/dist/registered.js +152 -14
  39. package/custom-elements.json +35 -10
  40. package/package.json +1 -1
@@ -2906,7 +2906,7 @@ class p{registerComponent(t,a){customElements.get(t)||customElements.define(t,cl
2906
2906
 
2907
2907
  var iconVersion = '9.1.2';
2908
2908
 
2909
- var styleCss$2 = i$2`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2909
+ var styleCss$2 = i$2`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2910
2910
 
2911
2911
  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)}`;
2912
2912
 
@@ -2914,6 +2914,8 @@ var tokensCss$1 = i$2`:host(:not([ondark])),:host(:not([appearance=inverse])){--
2914
2914
 
2915
2915
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
2916
2916
  // See LICENSE in the project root for license information.
2917
+ /* eslint-disable max-lines */
2918
+ // ---------------------------------------------------------------------
2917
2919
 
2918
2920
 
2919
2921
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
@@ -3089,22 +3091,63 @@ class AuroDropdownBib extends i {
3089
3091
  // Handle ESC key via dialog's cancel event
3090
3092
  const dialog = this.shadowRoot.querySelector('dialog');
3091
3093
  dialog.addEventListener('cancel', (event) => {
3092
- event.preventDefault(); // Let parent handle closing
3094
+ // Let parent handle closing
3095
+ event.preventDefault();
3093
3096
  this.dispatchEvent(new CustomEvent('auro-bib-cancel', {
3094
3097
  bubbles: true,
3095
3098
  composed: true
3096
3099
  }));
3097
3100
  });
3098
3101
 
3099
- // Re-dispatch navigation keyboard events so they cross the shadow DOM
3100
- // boundary and reach the combobox/select key handlers.
3101
- // Only intercept keys used for menu navigation let all other keys
3102
- // (characters, Backspace, etc.) through so typing in inputs works.
3103
- const navKeys = new Set(['ArrowUp', 'ArrowDown', 'Enter', 'Escape']);
3102
+ // showModal() creates a closed focus scope keyboard events inside
3103
+ // the dialog's shadow DOM do NOT bubble out to the combobox/select
3104
+ // keydown handlers in the parent shadow DOM. This handler bridges
3105
+ // that gap by re-dispatching navigation keys so they cross the
3106
+ // shadow boundary and reach the menu navigation logic in the parent
3107
+ // component.
3108
+ //
3109
+ // The trade-off: intercepting these keys means native keyboard
3110
+ // behaviors that would normally "just work" must be manually
3111
+ // re-implemented here:
3112
+ //
3113
+ // - Enter on buttons: Custom elements (auro-button) don't get the
3114
+ // native Enter→click that <button> provides, so we call .click()
3115
+ // directly when Enter is pressed on a button-like element.
3116
+ //
3117
+ // - Tab: NOT intercepted — left to the browser's native focus trap
3118
+ // provided by showModal(), which cycles Tab between focusable
3119
+ // elements inside the dialog (e.g. the input and close button).
3120
+ // Intercepting Tab would kill the native focus trap and break
3121
+ // focus management inside the dialog.
3122
+ //
3123
+ // - Escape: The native <dialog> fires a `cancel` event on ESC
3124
+ // (handled above), so the re-dispatched Escape is a secondary
3125
+ // path for parent components that also listen for Escape keydown.
3126
+ const navKeys = new Set([
3127
+ 'ArrowUp',
3128
+ 'ArrowDown',
3129
+ 'Enter',
3130
+ 'Escape'
3131
+ ]);
3104
3132
  dialog.addEventListener('keydown', (event) => {
3105
3133
  if (!navKeys.has(event.key)) {
3106
3134
  return;
3107
3135
  }
3136
+
3137
+ // Custom elements (auro-button) don't get the native Enter→click
3138
+ // behavior that <button> has. Find the button in the composed path
3139
+ // and click it directly.
3140
+ if (event.key === 'Enter') {
3141
+ const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
3142
+ const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
3143
+ if (btn) {
3144
+ event.preventDefault();
3145
+ event.stopPropagation();
3146
+ btn.click();
3147
+ return;
3148
+ }
3149
+ }
3150
+
3108
3151
  event.preventDefault();
3109
3152
  event.stopPropagation();
3110
3153
  const newEvent = new KeyboardEvent('keydown', {
@@ -3128,9 +3171,55 @@ class AuroDropdownBib extends i {
3128
3171
  }
3129
3172
 
3130
3173
  /**
3131
- * Opens the dialog using showModal() for accessibility.
3132
- * @param {boolean} modal - If true, uses showModal() (default). If false, uses show().
3174
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
3175
+ *
3176
+ * The showModal() function places the dialog in the browser's **top layer**,
3177
+ * which is a separate rendering layer above the normal DOM. On mobile, the
3178
+ * compositor processes visual-viewport panning before top-layer touch
3179
+ * handling. This means the entire viewport — including the top-layer dialog
3180
+ * — can be panned by a touch gesture, causing the page behind the dialog to
3181
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
3182
+ * the event if the touch started outside the dialog or any scrollable child within it.
3183
+ *
3184
+ * @private
3185
+ */
3186
+ _lockTouchScroll() {
3187
+ const dialog = this.shadowRoot.querySelector('dialog');
3188
+
3189
+ this._touchMoveHandler = (event) => {
3190
+ // Walk the composed path (which crosses shadow DOM boundaries) to
3191
+ // check whether the touch started inside a scrollable element that
3192
+ // lives within the dialog. If so, allow the scroll.
3193
+ for (const el of event.composedPath()) {
3194
+ if (el === dialog) {
3195
+ // Reached the dialog boundary without finding a scrollable child.
3196
+ break;
3197
+ }
3198
+ if (el instanceof HTMLElement && el.scrollHeight > el.clientHeight) {
3199
+ const { overflowY } = getComputedStyle(el);
3200
+ if (overflowY === 'auto' || overflowY === 'scroll') {
3201
+ return;
3202
+ }
3203
+ }
3204
+ }
3205
+
3206
+ event.preventDefault();
3207
+ };
3208
+
3209
+ document.addEventListener('touchmove', this._touchMoveHandler, { passive: false });
3210
+ }
3211
+
3212
+ /**
3213
+ * Removes the touchmove listener added by _lockTouchScroll().
3214
+ * @private
3133
3215
  */
3216
+ _unlockTouchScroll() {
3217
+ if (this._touchMoveHandler) {
3218
+ document.removeEventListener('touchmove', this._touchMoveHandler);
3219
+ this._touchMoveHandler = undefined;
3220
+ }
3221
+ }
3222
+
3134
3223
  open(modal = true) {
3135
3224
  const dialog = this.shadowRoot.querySelector('dialog');
3136
3225
  if (dialog && !dialog.open) {
@@ -3147,6 +3236,8 @@ class AuroDropdownBib extends i {
3147
3236
 
3148
3237
  documentElement.style.overflow = prevOverflow;
3149
3238
 
3239
+ this._lockTouchScroll();
3240
+
3150
3241
  } else {
3151
3242
  // Use setAttribute instead of dialog.show() to avoid the dialog
3152
3243
  // focusing steps which steal focus from the trigger and cause
@@ -3162,6 +3253,7 @@ class AuroDropdownBib extends i {
3162
3253
  close() {
3163
3254
  const dialog = this.shadowRoot.querySelector('dialog');
3164
3255
  if (dialog && dialog.open) {
3256
+ this._unlockTouchScroll();
3165
3257
  dialog.close();
3166
3258
  }
3167
3259
  }
@@ -3427,7 +3519,7 @@ class AuroHelpText extends i {
3427
3519
  }
3428
3520
  }
3429
3521
 
3430
- var formkitVersion = '202602201708';
3522
+ var formkitVersion = '202602260152';
3431
3523
 
3432
3524
  class AuroElement extends i {
3433
3525
  static get properties() {
@@ -3700,6 +3792,18 @@ class AuroDropdown extends AuroElement {
3700
3792
  */
3701
3793
  show() {
3702
3794
  this.floater.showBib();
3795
+
3796
+ // Open dialog synchronously so callers remain in the user gesture
3797
+ // chain. This is critical for mobile browsers (iOS Safari) to keep
3798
+ // the virtual keyboard open when transitioning from the trigger
3799
+ // input to an input inside the fullscreen dialog. Without this,
3800
+ // showModal() fires asynchronously via Lit's update cycle, which
3801
+ // falls outside the user activation window and causes iOS to
3802
+ // dismiss the keyboard.
3803
+ if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
3804
+ const useModal = !this.disableFocusTrap;
3805
+ this.bibElement.value.open(useModal);
3806
+ }
3703
3807
  }
3704
3808
 
3705
3809
  /**
@@ -4086,6 +4190,14 @@ class AuroDropdown extends AuroElement {
4086
4190
  this.bibElement.value.close();
4087
4191
  }
4088
4192
  }
4193
+
4194
+ // When fullscreen strategy changes while open, re-open dialog with correct mode
4195
+ // (e.g. resizing from desktop → mobile while dropdown is open)
4196
+ if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
4197
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
4198
+ this.bibElement.value.close();
4199
+ this.bibElement.value.open(useModal);
4200
+ }
4089
4201
  }
4090
4202
 
4091
4203
  /**
@@ -74,9 +74,25 @@ export class AuroDropdownBib extends LitElement {
74
74
  bibTemplate: any;
75
75
  firstUpdated(changedProperties: any): void;
76
76
  /**
77
- * Opens the dialog using showModal() for accessibility.
78
- * @param {boolean} modal - If true, uses showModal() (default). If false, uses show().
77
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
78
+ *
79
+ * The showModal() function places the dialog in the browser's **top layer**,
80
+ * which is a separate rendering layer above the normal DOM. On mobile, the
81
+ * compositor processes visual-viewport panning before top-layer touch
82
+ * handling. This means the entire viewport — including the top-layer dialog
83
+ * — can be panned by a touch gesture, causing the page behind the dialog to
84
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
85
+ * the event if the touch started outside the dialog or any scrollable child within it.
86
+ *
87
+ * @private
88
+ */
89
+ private _lockTouchScroll;
90
+ _touchMoveHandler: (event: any) => void;
91
+ /**
92
+ * Removes the touchmove listener added by _lockTouchScroll().
93
+ * @private
79
94
  */
95
+ private _unlockTouchScroll;
80
96
  open(modal?: boolean): void;
81
97
  /**
82
98
  * Closes the dialog.
@@ -2815,7 +2815,7 @@ class p{registerComponent(t,a){customElements.get(t)||customElements.define(t,cl
2815
2815
 
2816
2816
  var iconVersion = '9.1.2';
2817
2817
 
2818
- var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2818
+ var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2819
2819
 
2820
2820
  var colorCss$2 = css`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
2821
2821
 
@@ -2823,6 +2823,8 @@ var tokensCss$1 = css`:host(:not([ondark])),:host(:not([appearance=inverse])){--
2823
2823
 
2824
2824
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
2825
2825
  // See LICENSE in the project root for license information.
2826
+ /* eslint-disable max-lines */
2827
+ // ---------------------------------------------------------------------
2826
2828
 
2827
2829
 
2828
2830
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
@@ -2998,22 +3000,63 @@ class AuroDropdownBib extends LitElement {
2998
3000
  // Handle ESC key via dialog's cancel event
2999
3001
  const dialog = this.shadowRoot.querySelector('dialog');
3000
3002
  dialog.addEventListener('cancel', (event) => {
3001
- event.preventDefault(); // Let parent handle closing
3003
+ // Let parent handle closing
3004
+ event.preventDefault();
3002
3005
  this.dispatchEvent(new CustomEvent('auro-bib-cancel', {
3003
3006
  bubbles: true,
3004
3007
  composed: true
3005
3008
  }));
3006
3009
  });
3007
3010
 
3008
- // Re-dispatch navigation keyboard events so they cross the shadow DOM
3009
- // boundary and reach the combobox/select key handlers.
3010
- // Only intercept keys used for menu navigation let all other keys
3011
- // (characters, Backspace, etc.) through so typing in inputs works.
3012
- const navKeys = new Set(['ArrowUp', 'ArrowDown', 'Enter', 'Escape']);
3011
+ // showModal() creates a closed focus scope keyboard events inside
3012
+ // the dialog's shadow DOM do NOT bubble out to the combobox/select
3013
+ // keydown handlers in the parent shadow DOM. This handler bridges
3014
+ // that gap by re-dispatching navigation keys so they cross the
3015
+ // shadow boundary and reach the menu navigation logic in the parent
3016
+ // component.
3017
+ //
3018
+ // The trade-off: intercepting these keys means native keyboard
3019
+ // behaviors that would normally "just work" must be manually
3020
+ // re-implemented here:
3021
+ //
3022
+ // - Enter on buttons: Custom elements (auro-button) don't get the
3023
+ // native Enter→click that <button> provides, so we call .click()
3024
+ // directly when Enter is pressed on a button-like element.
3025
+ //
3026
+ // - Tab: NOT intercepted — left to the browser's native focus trap
3027
+ // provided by showModal(), which cycles Tab between focusable
3028
+ // elements inside the dialog (e.g. the input and close button).
3029
+ // Intercepting Tab would kill the native focus trap and break
3030
+ // focus management inside the dialog.
3031
+ //
3032
+ // - Escape: The native <dialog> fires a `cancel` event on ESC
3033
+ // (handled above), so the re-dispatched Escape is a secondary
3034
+ // path for parent components that also listen for Escape keydown.
3035
+ const navKeys = new Set([
3036
+ 'ArrowUp',
3037
+ 'ArrowDown',
3038
+ 'Enter',
3039
+ 'Escape'
3040
+ ]);
3013
3041
  dialog.addEventListener('keydown', (event) => {
3014
3042
  if (!navKeys.has(event.key)) {
3015
3043
  return;
3016
3044
  }
3045
+
3046
+ // Custom elements (auro-button) don't get the native Enter→click
3047
+ // behavior that <button> has. Find the button in the composed path
3048
+ // and click it directly.
3049
+ if (event.key === 'Enter') {
3050
+ const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
3051
+ const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
3052
+ if (btn) {
3053
+ event.preventDefault();
3054
+ event.stopPropagation();
3055
+ btn.click();
3056
+ return;
3057
+ }
3058
+ }
3059
+
3017
3060
  event.preventDefault();
3018
3061
  event.stopPropagation();
3019
3062
  const newEvent = new KeyboardEvent('keydown', {
@@ -3037,9 +3080,55 @@ class AuroDropdownBib extends LitElement {
3037
3080
  }
3038
3081
 
3039
3082
  /**
3040
- * Opens the dialog using showModal() for accessibility.
3041
- * @param {boolean} modal - If true, uses showModal() (default). If false, uses show().
3083
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
3084
+ *
3085
+ * The showModal() function places the dialog in the browser's **top layer**,
3086
+ * which is a separate rendering layer above the normal DOM. On mobile, the
3087
+ * compositor processes visual-viewport panning before top-layer touch
3088
+ * handling. This means the entire viewport — including the top-layer dialog
3089
+ * — can be panned by a touch gesture, causing the page behind the dialog to
3090
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
3091
+ * the event if the touch started outside the dialog or any scrollable child within it.
3092
+ *
3093
+ * @private
3094
+ */
3095
+ _lockTouchScroll() {
3096
+ const dialog = this.shadowRoot.querySelector('dialog');
3097
+
3098
+ this._touchMoveHandler = (event) => {
3099
+ // Walk the composed path (which crosses shadow DOM boundaries) to
3100
+ // check whether the touch started inside a scrollable element that
3101
+ // lives within the dialog. If so, allow the scroll.
3102
+ for (const el of event.composedPath()) {
3103
+ if (el === dialog) {
3104
+ // Reached the dialog boundary without finding a scrollable child.
3105
+ break;
3106
+ }
3107
+ if (el instanceof HTMLElement && el.scrollHeight > el.clientHeight) {
3108
+ const { overflowY } = getComputedStyle(el);
3109
+ if (overflowY === 'auto' || overflowY === 'scroll') {
3110
+ return;
3111
+ }
3112
+ }
3113
+ }
3114
+
3115
+ event.preventDefault();
3116
+ };
3117
+
3118
+ document.addEventListener('touchmove', this._touchMoveHandler, { passive: false });
3119
+ }
3120
+
3121
+ /**
3122
+ * Removes the touchmove listener added by _lockTouchScroll().
3123
+ * @private
3042
3124
  */
3125
+ _unlockTouchScroll() {
3126
+ if (this._touchMoveHandler) {
3127
+ document.removeEventListener('touchmove', this._touchMoveHandler);
3128
+ this._touchMoveHandler = undefined;
3129
+ }
3130
+ }
3131
+
3043
3132
  open(modal = true) {
3044
3133
  const dialog = this.shadowRoot.querySelector('dialog');
3045
3134
  if (dialog && !dialog.open) {
@@ -3056,6 +3145,8 @@ class AuroDropdownBib extends LitElement {
3056
3145
 
3057
3146
  documentElement.style.overflow = prevOverflow;
3058
3147
 
3148
+ this._lockTouchScroll();
3149
+
3059
3150
  } else {
3060
3151
  // Use setAttribute instead of dialog.show() to avoid the dialog
3061
3152
  // focusing steps which steal focus from the trigger and cause
@@ -3071,6 +3162,7 @@ class AuroDropdownBib extends LitElement {
3071
3162
  close() {
3072
3163
  const dialog = this.shadowRoot.querySelector('dialog');
3073
3164
  if (dialog && dialog.open) {
3165
+ this._unlockTouchScroll();
3074
3166
  dialog.close();
3075
3167
  }
3076
3168
  }
@@ -3336,7 +3428,7 @@ class AuroHelpText extends LitElement {
3336
3428
  }
3337
3429
  }
3338
3430
 
3339
- var formkitVersion = '202602201708';
3431
+ var formkitVersion = '202602260152';
3340
3432
 
3341
3433
  class AuroElement extends LitElement {
3342
3434
  static get properties() {
@@ -3609,6 +3701,18 @@ class AuroDropdown extends AuroElement {
3609
3701
  */
3610
3702
  show() {
3611
3703
  this.floater.showBib();
3704
+
3705
+ // Open dialog synchronously so callers remain in the user gesture
3706
+ // chain. This is critical for mobile browsers (iOS Safari) to keep
3707
+ // the virtual keyboard open when transitioning from the trigger
3708
+ // input to an input inside the fullscreen dialog. Without this,
3709
+ // showModal() fires asynchronously via Lit's update cycle, which
3710
+ // falls outside the user activation window and causes iOS to
3711
+ // dismiss the keyboard.
3712
+ if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
3713
+ const useModal = !this.disableFocusTrap;
3714
+ this.bibElement.value.open(useModal);
3715
+ }
3612
3716
  }
3613
3717
 
3614
3718
  /**
@@ -3995,6 +4099,14 @@ class AuroDropdown extends AuroElement {
3995
4099
  this.bibElement.value.close();
3996
4100
  }
3997
4101
  }
4102
+
4103
+ // When fullscreen strategy changes while open, re-open dialog with correct mode
4104
+ // (e.g. resizing from desktop → mobile while dropdown is open)
4105
+ if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
4106
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
4107
+ this.bibElement.value.close();
4108
+ this.bibElement.value.open(useModal);
4109
+ }
3998
4110
  }
3999
4111
 
4000
4112
  /**
@@ -2815,7 +2815,7 @@ class p{registerComponent(t,a){customElements.get(t)||customElements.define(t,cl
2815
2815
 
2816
2816
  var iconVersion = '9.1.2';
2817
2817
 
2818
- var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2818
+ var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;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([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}`;
2819
2819
 
2820
2820
  var colorCss$2 = css`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
2821
2821
 
@@ -2823,6 +2823,8 @@ var tokensCss$1 = css`:host(:not([ondark])),:host(:not([appearance=inverse])){--
2823
2823
 
2824
2824
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
2825
2825
  // See LICENSE in the project root for license information.
2826
+ /* eslint-disable max-lines */
2827
+ // ---------------------------------------------------------------------
2826
2828
 
2827
2829
 
2828
2830
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
@@ -2998,22 +3000,63 @@ class AuroDropdownBib extends LitElement {
2998
3000
  // Handle ESC key via dialog's cancel event
2999
3001
  const dialog = this.shadowRoot.querySelector('dialog');
3000
3002
  dialog.addEventListener('cancel', (event) => {
3001
- event.preventDefault(); // Let parent handle closing
3003
+ // Let parent handle closing
3004
+ event.preventDefault();
3002
3005
  this.dispatchEvent(new CustomEvent('auro-bib-cancel', {
3003
3006
  bubbles: true,
3004
3007
  composed: true
3005
3008
  }));
3006
3009
  });
3007
3010
 
3008
- // Re-dispatch navigation keyboard events so they cross the shadow DOM
3009
- // boundary and reach the combobox/select key handlers.
3010
- // Only intercept keys used for menu navigation let all other keys
3011
- // (characters, Backspace, etc.) through so typing in inputs works.
3012
- const navKeys = new Set(['ArrowUp', 'ArrowDown', 'Enter', 'Escape']);
3011
+ // showModal() creates a closed focus scope keyboard events inside
3012
+ // the dialog's shadow DOM do NOT bubble out to the combobox/select
3013
+ // keydown handlers in the parent shadow DOM. This handler bridges
3014
+ // that gap by re-dispatching navigation keys so they cross the
3015
+ // shadow boundary and reach the menu navigation logic in the parent
3016
+ // component.
3017
+ //
3018
+ // The trade-off: intercepting these keys means native keyboard
3019
+ // behaviors that would normally "just work" must be manually
3020
+ // re-implemented here:
3021
+ //
3022
+ // - Enter on buttons: Custom elements (auro-button) don't get the
3023
+ // native Enter→click that <button> provides, so we call .click()
3024
+ // directly when Enter is pressed on a button-like element.
3025
+ //
3026
+ // - Tab: NOT intercepted — left to the browser's native focus trap
3027
+ // provided by showModal(), which cycles Tab between focusable
3028
+ // elements inside the dialog (e.g. the input and close button).
3029
+ // Intercepting Tab would kill the native focus trap and break
3030
+ // focus management inside the dialog.
3031
+ //
3032
+ // - Escape: The native <dialog> fires a `cancel` event on ESC
3033
+ // (handled above), so the re-dispatched Escape is a secondary
3034
+ // path for parent components that also listen for Escape keydown.
3035
+ const navKeys = new Set([
3036
+ 'ArrowUp',
3037
+ 'ArrowDown',
3038
+ 'Enter',
3039
+ 'Escape'
3040
+ ]);
3013
3041
  dialog.addEventListener('keydown', (event) => {
3014
3042
  if (!navKeys.has(event.key)) {
3015
3043
  return;
3016
3044
  }
3045
+
3046
+ // Custom elements (auro-button) don't get the native Enter→click
3047
+ // behavior that <button> has. Find the button in the composed path
3048
+ // and click it directly.
3049
+ if (event.key === 'Enter') {
3050
+ const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
3051
+ const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
3052
+ if (btn) {
3053
+ event.preventDefault();
3054
+ event.stopPropagation();
3055
+ btn.click();
3056
+ return;
3057
+ }
3058
+ }
3059
+
3017
3060
  event.preventDefault();
3018
3061
  event.stopPropagation();
3019
3062
  const newEvent = new KeyboardEvent('keydown', {
@@ -3037,9 +3080,55 @@ class AuroDropdownBib extends LitElement {
3037
3080
  }
3038
3081
 
3039
3082
  /**
3040
- * Opens the dialog using showModal() for accessibility.
3041
- * @param {boolean} modal - If true, uses showModal() (default). If false, uses show().
3083
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
3084
+ *
3085
+ * The showModal() function places the dialog in the browser's **top layer**,
3086
+ * which is a separate rendering layer above the normal DOM. On mobile, the
3087
+ * compositor processes visual-viewport panning before top-layer touch
3088
+ * handling. This means the entire viewport — including the top-layer dialog
3089
+ * — can be panned by a touch gesture, causing the page behind the dialog to
3090
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
3091
+ * the event if the touch started outside the dialog or any scrollable child within it.
3092
+ *
3093
+ * @private
3094
+ */
3095
+ _lockTouchScroll() {
3096
+ const dialog = this.shadowRoot.querySelector('dialog');
3097
+
3098
+ this._touchMoveHandler = (event) => {
3099
+ // Walk the composed path (which crosses shadow DOM boundaries) to
3100
+ // check whether the touch started inside a scrollable element that
3101
+ // lives within the dialog. If so, allow the scroll.
3102
+ for (const el of event.composedPath()) {
3103
+ if (el === dialog) {
3104
+ // Reached the dialog boundary without finding a scrollable child.
3105
+ break;
3106
+ }
3107
+ if (el instanceof HTMLElement && el.scrollHeight > el.clientHeight) {
3108
+ const { overflowY } = getComputedStyle(el);
3109
+ if (overflowY === 'auto' || overflowY === 'scroll') {
3110
+ return;
3111
+ }
3112
+ }
3113
+ }
3114
+
3115
+ event.preventDefault();
3116
+ };
3117
+
3118
+ document.addEventListener('touchmove', this._touchMoveHandler, { passive: false });
3119
+ }
3120
+
3121
+ /**
3122
+ * Removes the touchmove listener added by _lockTouchScroll().
3123
+ * @private
3042
3124
  */
3125
+ _unlockTouchScroll() {
3126
+ if (this._touchMoveHandler) {
3127
+ document.removeEventListener('touchmove', this._touchMoveHandler);
3128
+ this._touchMoveHandler = undefined;
3129
+ }
3130
+ }
3131
+
3043
3132
  open(modal = true) {
3044
3133
  const dialog = this.shadowRoot.querySelector('dialog');
3045
3134
  if (dialog && !dialog.open) {
@@ -3056,6 +3145,8 @@ class AuroDropdownBib extends LitElement {
3056
3145
 
3057
3146
  documentElement.style.overflow = prevOverflow;
3058
3147
 
3148
+ this._lockTouchScroll();
3149
+
3059
3150
  } else {
3060
3151
  // Use setAttribute instead of dialog.show() to avoid the dialog
3061
3152
  // focusing steps which steal focus from the trigger and cause
@@ -3071,6 +3162,7 @@ class AuroDropdownBib extends LitElement {
3071
3162
  close() {
3072
3163
  const dialog = this.shadowRoot.querySelector('dialog');
3073
3164
  if (dialog && dialog.open) {
3165
+ this._unlockTouchScroll();
3074
3166
  dialog.close();
3075
3167
  }
3076
3168
  }
@@ -3336,7 +3428,7 @@ class AuroHelpText extends LitElement {
3336
3428
  }
3337
3429
  }
3338
3430
 
3339
- var formkitVersion = '202602201708';
3431
+ var formkitVersion = '202602260152';
3340
3432
 
3341
3433
  class AuroElement extends LitElement {
3342
3434
  static get properties() {
@@ -3609,6 +3701,18 @@ class AuroDropdown extends AuroElement {
3609
3701
  */
3610
3702
  show() {
3611
3703
  this.floater.showBib();
3704
+
3705
+ // Open dialog synchronously so callers remain in the user gesture
3706
+ // chain. This is critical for mobile browsers (iOS Safari) to keep
3707
+ // the virtual keyboard open when transitioning from the trigger
3708
+ // input to an input inside the fullscreen dialog. Without this,
3709
+ // showModal() fires asynchronously via Lit's update cycle, which
3710
+ // falls outside the user activation window and causes iOS to
3711
+ // dismiss the keyboard.
3712
+ if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
3713
+ const useModal = !this.disableFocusTrap;
3714
+ this.bibElement.value.open(useModal);
3715
+ }
3612
3716
  }
3613
3717
 
3614
3718
  /**
@@ -3995,6 +4099,14 @@ class AuroDropdown extends AuroElement {
3995
4099
  this.bibElement.value.close();
3996
4100
  }
3997
4101
  }
4102
+
4103
+ // When fullscreen strategy changes while open, re-open dialog with correct mode
4104
+ // (e.g. resizing from desktop → mobile while dropdown is open)
4105
+ if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
4106
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
4107
+ this.bibElement.value.close();
4108
+ this.bibElement.value.open(useModal);
4109
+ }
3998
4110
  }
3999
4111
 
4000
4112
  /**