@ionic/core 8.8.7-dev.11779385275.161a641b → 8.8.7-dev.11779467048.1641d05e

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 (72) hide show
  1. package/components/index.js +1 -1
  2. package/components/ion-action-sheet.js +1 -1
  3. package/components/ion-alert.js +1 -1
  4. package/components/ion-datetime.js +1 -1
  5. package/components/ion-gallery.js +1 -1
  6. package/components/ion-loading.js +1 -1
  7. package/components/ion-menu.js +1 -1
  8. package/components/ion-modal.js +1 -1
  9. package/components/ion-picker-legacy.js +1 -1
  10. package/components/ion-popover.js +1 -1
  11. package/components/ion-select-modal.js +1 -1
  12. package/components/ion-select-popover.js +1 -1
  13. package/components/ion-select.js +1 -1
  14. package/components/ion-toast.js +1 -1
  15. package/components/{p-B2rpt1JV.js → p-C38HUpU5.js} +1 -1
  16. package/components/{p-Dmuy6xyk.js → p-C4G6C9fP.js} +1 -1
  17. package/components/{p-B6zr9RZN.js → p-CVRxImH6.js} +1 -1
  18. package/components/{p-B71c6yUH.js → p-CoFDGTFO.js} +1 -1
  19. package/components/{p-DAv9P_LE.js → p-CykCvfXQ.js} +1 -1
  20. package/components/p-DHTe6lDL.js +4 -0
  21. package/components/{p-Di5rHO3q.js → p-qZr7hBPz.js} +1 -1
  22. package/dist/cjs/index.cjs.js +1 -1
  23. package/dist/cjs/ion-action-sheet.cjs.entry.js +1 -1
  24. package/dist/cjs/ion-alert.cjs.entry.js +1 -1
  25. package/dist/cjs/ion-datetime_3.cjs.entry.js +1 -1
  26. package/dist/cjs/ion-gallery.cjs.entry.js +7 -28
  27. package/dist/cjs/ion-loading.cjs.entry.js +1 -1
  28. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  29. package/dist/cjs/ion-modal.cjs.entry.js +1 -1
  30. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  31. package/dist/cjs/ion-select-modal.cjs.entry.js +1 -1
  32. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  33. package/dist/cjs/ion-toast.cjs.entry.js +1 -1
  34. package/dist/cjs/{overlays-Hci_7vw_.js → overlays-C54DhaTC.js} +187 -10
  35. package/dist/collection/components/gallery/gallery.js +7 -28
  36. package/dist/collection/utils/overlays.js +187 -10
  37. package/dist/docs.json +1 -1
  38. package/dist/esm/index.js +1 -1
  39. package/dist/esm/ion-action-sheet.entry.js +1 -1
  40. package/dist/esm/ion-alert.entry.js +1 -1
  41. package/dist/esm/ion-datetime_3.entry.js +1 -1
  42. package/dist/esm/ion-gallery.entry.js +7 -28
  43. package/dist/esm/ion-loading.entry.js +1 -1
  44. package/dist/esm/ion-menu_3.entry.js +1 -1
  45. package/dist/esm/ion-modal.entry.js +1 -1
  46. package/dist/esm/ion-popover.entry.js +1 -1
  47. package/dist/esm/ion-select-modal.entry.js +1 -1
  48. package/dist/esm/ion-select_3.entry.js +1 -1
  49. package/dist/esm/ion-toast.entry.js +1 -1
  50. package/dist/esm/{overlays-rwDDzEs4.js → overlays-ttYCMKRp.js} +187 -10
  51. package/dist/ionic/index.esm.js +1 -1
  52. package/dist/ionic/ionic.esm.js +1 -1
  53. package/dist/ionic/p-06bd033b.entry.js +4 -0
  54. package/dist/ionic/{p-c10fa162.entry.js → p-1f74b8d4.entry.js} +1 -1
  55. package/dist/ionic/{p-a9fb086b.entry.js → p-2f8aa0ac.entry.js} +1 -1
  56. package/dist/ionic/{p-2f0073af.entry.js → p-3331cfa9.entry.js} +1 -1
  57. package/dist/ionic/{p-35b144f5.entry.js → p-33c34361.entry.js} +1 -1
  58. package/dist/ionic/{p-15e3e8f5.entry.js → p-5061a8d4.entry.js} +1 -1
  59. package/dist/ionic/{p-4a0260e6.entry.js → p-8f04bd89.entry.js} +1 -1
  60. package/dist/ionic/{p-bf972309.entry.js → p-967576f8.entry.js} +1 -1
  61. package/dist/ionic/p-DdyNaGpi.js +4 -0
  62. package/dist/ionic/{p-71b6014c.entry.js → p-bb898d47.entry.js} +1 -1
  63. package/dist/ionic/p-dea52cb3.entry.js +4 -0
  64. package/dist/ionic/{p-432c5888.entry.js → p-fc796d48.entry.js} +1 -1
  65. package/dist/types/components/gallery/gallery.d.ts +2 -5
  66. package/hydrate/index.js +194 -38
  67. package/hydrate/index.mjs +194 -38
  68. package/package.json +1 -1
  69. package/components/p-CtiqM786.js +0 -4
  70. package/dist/ionic/p-0f3b4262.entry.js +0 -4
  71. package/dist/ionic/p-4079cee3.entry.js +0 -4
  72. package/dist/ionic/p-C4uUM9DM.js +0 -4
@@ -54,7 +54,6 @@ const BREAKPOINT_ORDER = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
54
54
  const Gallery = class {
55
55
  constructor(hostRef) {
56
56
  registerInstance(this, hostRef);
57
- this.itemWrapperSelector = '[data-gallery-group]';
58
57
  // Keep track of whether we've warned about invalid columns, invalid gap,
59
58
  // and unused order properties to avoid duplicate warnings on screen resize.
60
59
  this.hasWarnedInvalidColumns = false;
@@ -109,9 +108,8 @@ const Gallery = class {
109
108
  const styles = getComputedStyle(this.el);
110
109
  const rowHeight = parseFloat(styles.getPropertyValue('grid-auto-rows')) || 0;
111
110
  const rowGap = parseFloat(styles.getPropertyValue('row-gap')) || parseFloat(styles.getPropertyValue('gap')) || 0;
112
- const itemGap = parseFloat(styles.getPropertyValue('column-gap')) || parseFloat(styles.getPropertyValue('gap')) || 0;
113
111
  const items = this.getItems();
114
- this.layoutMasonry(items, rowHeight, rowGap, itemGap, columns);
112
+ this.layoutMasonry(items, rowHeight, rowGap, columns);
115
113
  };
116
114
  }
117
115
  onColumnsOrGapChanged() {
@@ -368,28 +366,12 @@ const Gallery = class {
368
366
  const gap = this.getGapForWidth(width);
369
367
  this.el.style.setProperty('--internal-gallery-gap', `${gap}`);
370
368
  }
371
- isGalleryItemElement(element) {
372
- var _a;
373
- return typeof ((_a = element.style) === null || _a === void 0 ? void 0 : _a.setProperty) === 'function';
374
- }
375
369
  /**
376
- * Return all gallery items that can be grid items with inline placement styles.
377
- * Direct children marked with `data-gallery-group` are ignored and replaced
378
- * with their element children.
370
+ * Return all directly slotted children of the gallery that can be grid items
371
+ * with inline placement styles (HTML elements and SVG elements).
379
372
  */
380
373
  getItems() {
381
- const items = Array.from(this.el.children).filter((child) => this.isGalleryItemElement(child));
382
- const flattenedItems = [];
383
- items.forEach((itemEl) => {
384
- if (!itemEl.matches(this.itemWrapperSelector)) {
385
- flattenedItems.push(itemEl);
386
- return;
387
- }
388
- itemEl.style.display = 'contents';
389
- const wrappedItems = Array.from(itemEl.children).filter((child) => this.isGalleryItemElement(child));
390
- flattenedItems.push(...wrappedItems);
391
- });
392
- return flattenedItems;
374
+ return Array.from(this.el.children).filter((child) => { var _a; return typeof ((_a = child.style) === null || _a === void 0 ? void 0 : _a.setProperty) === 'function'; });
393
375
  }
394
376
  /**
395
377
  * Clear the item styles for the given item element.
@@ -446,14 +428,11 @@ const Gallery = class {
446
428
  /**
447
429
  * Apply masonry placement by assigning each item a column and row span.
448
430
  */
449
- layoutMasonry(items, rowHeight, rowGap, itemGap, columns) {
431
+ layoutMasonry(items, rowHeight, rowGap, columns) {
450
432
  const columnHeights = new Array(columns).fill(0);
451
433
  const lastItemsByColumn = new Array(columns).fill(undefined);
452
434
  items.forEach((itemEl, i) => {
453
435
  itemEl.style.marginBottom = '';
454
- if (itemEl.parentElement !== this.el) {
455
- itemEl.style.marginBottom = `${itemGap}px`;
456
- }
457
436
  const span = this.calculateRowSpan(itemEl, rowHeight, rowGap);
458
437
  if (span === undefined) {
459
438
  this.clearItemStyles(itemEl);
@@ -502,11 +481,11 @@ const Gallery = class {
502
481
  const { layout } = this;
503
482
  const order = this.getOrder();
504
483
  const theme = getIonTheme(this);
505
- return (h(Host, { key: '10b550a9cc0c6b6994a86ec95bc6dbfadb3e8c58', class: {
484
+ return (h(Host, { key: '1bf2973d22835c0dbddf3214b602f8c08b95e421', class: {
506
485
  [theme]: true,
507
486
  [`gallery-layout-${layout}`]: true,
508
487
  [`gallery-order-${order}`]: layout === 'masonry' && order !== undefined,
509
- } }, h("slot", { key: '1ac472f867053973aa90975cd61901a2e8ff20aa', onSlotchange: this.onSlotChange })));
488
+ } }, h("slot", { key: '0dea31f609f6afdb1d73ecb2d873909ffe49203f', onSlotchange: this.onSlotChange })));
510
489
  }
511
490
  get el() { return getElement(this); }
512
491
  static get watchers() { return {
@@ -5,7 +5,7 @@ import { r as registerInstance, c as createEvent, e as config, h, d as Host, g a
5
5
  import { E as ENABLE_HTML_CONTENT_DEFAULT } from './config-BwKpO3Is.js';
6
6
  import { r as raf } from './helpers-Do7zwvM1.js';
7
7
  import { c as createLockController } from './lock-controller-B-hirT0v.js';
8
- import { d as createDelegateController, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-rwDDzEs4.js';
8
+ import { d as createDelegateController, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-ttYCMKRp.js';
9
9
  import { s as sanitizeDOMString } from './index-D4ugF_sT.js';
10
10
  import { g as getClassMap } from './theme-DaJxRxSQ.js';
11
11
  import { c as getIonTheme } from './ionic-global-CAZb-5i-.js';
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, e as config, j as printIonError, h, d as Host, g as getElement } from './index-Omi_TcwW.js';
5
5
  import { g as getTimeGivenProgression } from './cubic-bezier-hHmYLOfE.js';
6
- import { o as getPresentedOverlay, B as BACKDROP, n as focusFirstDescendant, q as focusLastDescendant, G as GESTURE } from './overlays-rwDDzEs4.js';
6
+ import { o as getPresentedOverlay, B as BACKDROP, n as focusFirstDescendant, q as focusLastDescendant, G as GESTURE } from './overlays-ttYCMKRp.js';
7
7
  import { G as GESTURE_CONTROLLER } from './gesture-controller-BTEOs1at.js';
8
8
  import { b as getIonMode, a as isPlatform, c as getIonTheme, s as shouldUseCloseWatcher } from './ionic-global-CAZb-5i-.js';
9
9
  import { p as isEndSide, i as inheritAriaAttributes, l as assert, e as clamp } from './helpers-Do7zwvM1.js';
@@ -7,7 +7,7 @@ import { C as CoreDelegate, a as attachComponent, d as detachComponent } from '.
7
7
  import { g as getElementRoot, e as clamp, r as raf, b as inheritAttributes, h as hasLazyBuild } from './helpers-Do7zwvM1.js';
8
8
  import { c as createLockController } from './lock-controller-B-hirT0v.js';
9
9
  import { g as getCapacitor } from './capacitor-C4lYa1nV.js';
10
- import { G as GESTURE, O as OVERLAY_GESTURE_PRIORITY, F as FOCUS_TRAP_DISABLE_CLASS, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-rwDDzEs4.js';
10
+ import { G as GESTURE, O as OVERLAY_GESTURE_PRIORITY, F as FOCUS_TRAP_DISABLE_CLASS, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-ttYCMKRp.js';
11
11
  import { g as getClassMap } from './theme-DaJxRxSQ.js';
12
12
  import { e as deepReady, w as waitForMount } from './index-CGthURny.js';
13
13
  import { w as win, b as getIonMode, c as getIonTheme } from './ionic-global-CAZb-5i-.js';
@@ -2,7 +2,7 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, f as printIonWarning, h, d as Host, g as getElement } from './index-Omi_TcwW.js';
5
- import { B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, n as focusFirstDescendant, g as dismiss, h as eventMethod, F as FOCUS_TRAP_DISABLE_CLASS } from './overlays-rwDDzEs4.js';
5
+ import { B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, n as focusFirstDescendant, g as dismiss, h as eventMethod, F as FOCUS_TRAP_DISABLE_CLASS } from './overlays-ttYCMKRp.js';
6
6
  import { C as CoreDelegate, a as attachComponent, d as detachComponent } from './framework-delegate-CjVwn_KZ.js';
7
7
  import { g as getElementRoot, r as raf, f as addEventListener, h as hasLazyBuild } from './helpers-Do7zwvM1.js';
8
8
  import { c as createLockController } from './lock-controller-B-hirT0v.js';
@@ -4,7 +4,7 @@
4
4
  import { r as registerInstance, h, i as forceUpdate, d as Host, g as getElement } from './index-Omi_TcwW.js';
5
5
  import { c as getIonTheme, b as getIonMode } from './ionic-global-CAZb-5i-.js';
6
6
  import { x as xSvg } from './x-BDqjX7Z_.js';
7
- import { s as safeCall } from './overlays-rwDDzEs4.js';
7
+ import { s as safeCall } from './overlays-ttYCMKRp.js';
8
8
  import { r as renderOptionLabel } from './select-option-render-B2qc5ZP7.js';
9
9
  import { h as hostContext, g as getClassMap } from './theme-DaJxRxSQ.js';
10
10
  import { b as closeSharp, z as closeOutline } from './index-D2tu5BUg.js';
@@ -8,7 +8,7 @@ import { c as createNotchController } from './notch-controller-DiBq57w8.js';
8
8
  import { i as isOptionSelected, c as compareOptions } from './compare-with-utils-sObYyvOy.js';
9
9
  import { c as checkInvalidState } from './validity-BjW8SOqw.js';
10
10
  import { b as inheritAttributes, a as renderHiddenInput, o as focusVisibleElement } from './helpers-Do7zwvM1.js';
11
- import { c as popoverController, b as actionSheetController, a as alertController, m as modalController, s as safeCall } from './overlays-rwDDzEs4.js';
11
+ import { c as popoverController, b as actionSheetController, a as alertController, m as modalController, s as safeCall } from './overlays-ttYCMKRp.js';
12
12
  import { i as isRTL } from './dir-C53feagD.js';
13
13
  import { s as sanitizeDOMString } from './index-D4ugF_sT.js';
14
14
  import { h as hostContext, c as createColorClasses, g as getClassMap } from './theme-DaJxRxSQ.js';
@@ -5,7 +5,7 @@ import { f as printIonWarning, r as registerInstance, c as createEvent, e as con
5
5
  import { E as ENABLE_HTML_CONTENT_DEFAULT } from './config-BwKpO3Is.js';
6
6
  import { g as getElementRoot, r as raf } from './helpers-Do7zwvM1.js';
7
7
  import { c as createLockController } from './lock-controller-B-hirT0v.js';
8
- import { O as OVERLAY_GESTURE_PRIORITY, d as createDelegateController, e as createTriggerController, i as isCancel, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod, s as safeCall, G as GESTURE } from './overlays-rwDDzEs4.js';
8
+ import { O as OVERLAY_GESTURE_PRIORITY, d as createDelegateController, e as createTriggerController, i as isCancel, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod, s as safeCall, G as GESTURE } from './overlays-ttYCMKRp.js';
9
9
  import { s as sanitizeDOMString } from './index-D4ugF_sT.js';
10
10
  import { c as createColorClasses, g as getClassMap } from './theme-DaJxRxSQ.js';
11
11
  import { w as win, b as getIonMode, c as getIonTheme } from './ionic-global-CAZb-5i-.js';
@@ -102,8 +102,101 @@ const focusElementInContext = (hostToFocus, fallbackElement) => {
102
102
  let lastOverlayIndex = 0;
103
103
  let lastId = 0;
104
104
  const activeAnimations = new WeakMap();
105
+ const OVERLAY_FOCUS_TRAP_SELECTOR = 'ion-alert,ion-action-sheet,ion-loading,ion-modal,ion-picker-legacy,ion-popover';
106
+ const ION_SELECT_MODAL_SELECTOR = 'ion-select-modal';
107
+ const isSelectModalOptionControl = (el) => el.tagName === 'ION-RADIO' || el.tagName === 'ION-CHECKBOX';
105
108
  /**
106
- * Determines if the overlay's backdrop is always blocking (no background interaction).
109
+ * Returns the currently focused element for keyboard focus checks.
110
+ *
111
+ * Starts from `document.activeElement` (non-shadow / light DOM focus).
112
+ * If focus is inside one or more open shadow roots
113
+ * (e.g. native control inside `ion-radio`), walks through nested
114
+ * `shadowRoot.activeElement` values until the innermost focused node is reached.
115
+ */
116
+ const getActiveElement = (ownerDoc) => {
117
+ var _a;
118
+ let active = ownerDoc.activeElement;
119
+ if (!active) {
120
+ return null;
121
+ }
122
+ while ((_a = active.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) {
123
+ active = active.shadowRoot.activeElement;
124
+ }
125
+ return active;
126
+ };
127
+ /**
128
+ * Walks from a focused node (possibly deep inside shadow roots)
129
+ * up to the nearest `ion-radio` / `ion-checkbox` host.
130
+ */
131
+ const getOptionControlHost = (active) => {
132
+ let n = active;
133
+ while (n) {
134
+ if (isSelectModalOptionControl(n)) {
135
+ return n;
136
+ }
137
+ const root = n.getRootNode();
138
+ if (root instanceof ShadowRoot && root.host instanceof HTMLElement) {
139
+ n = root.host;
140
+ }
141
+ else {
142
+ return null;
143
+ }
144
+ }
145
+ return null;
146
+ };
147
+ /**
148
+ * Sheet modals can have visual order that differs from DOM order.
149
+ * Without sorting, Tab can skip the handle after option traversal.
150
+ *
151
+ * Order: option controls (radio/checkbox) → sheet handle → end-slot
152
+ * header button. Any other focusables are appended after.
153
+ */
154
+ const sortSheetModalFocusables = (overlay, elements) => {
155
+ const optionControls = elements.filter((el) => {
156
+ return overlay.contains(el) && isSelectModalOptionControl(el) && el.tabIndex >= 0;
157
+ });
158
+ const cancelControl = elements.find((el) => overlay.contains(el) &&
159
+ el.tagName === 'ION-BUTTON' &&
160
+ el.closest('ion-header ion-buttons[slot="end"]') !== null);
161
+ const handleControl = elements.find((el) => el.classList.contains('modal-handle'));
162
+ const sortByGeometry = (els) => [...els].sort((a, b) => {
163
+ const ra = a.getBoundingClientRect();
164
+ const rb = b.getBoundingClientRect();
165
+ const topDiff = ra.top - rb.top;
166
+ if (Math.abs(topDiff) > 1) {
167
+ return topDiff;
168
+ }
169
+ return ra.left - rb.left;
170
+ });
171
+ const ordered = [];
172
+ ordered.push(...sortByGeometry(optionControls));
173
+ if (handleControl) {
174
+ ordered.push(handleControl);
175
+ }
176
+ if (cancelControl) {
177
+ ordered.push(cancelControl);
178
+ }
179
+ const used = new Set(ordered);
180
+ for (const el of elements) {
181
+ if (!used.has(el)) {
182
+ ordered.push(el);
183
+ }
184
+ }
185
+ return ordered;
186
+ };
187
+ /**
188
+ * Option controls in groups use a tabindex pattern where only one
189
+ * option is tabbable (`tabIndex="0"`) while the others are `-1`.
190
+ * This returns the index of that current tabbable option.
191
+ */
192
+ const getTabbableOptionControlIndex = (elements, overlay) => {
193
+ return elements.findIndex((el) => {
194
+ return overlay.contains(el) && isSelectModalOptionControl(el) && el.tabIndex >= 0;
195
+ });
196
+ };
197
+ /**
198
+ * Determines if the overlay's backdrop is always blocking
199
+ * (no background interaction).
107
200
  * Returns false if showBackdrop=false or backdropBreakpoint > 0.
108
201
  */
109
202
  const isBackdropAlwaysBlocking = (el) => {
@@ -224,7 +317,7 @@ const focusElementInOverlay = (hostToFocus, overlay) => {
224
317
  * Should NOT include: Toast
225
318
  */
226
319
  const trapKeyboardFocus = (ev, doc) => {
227
- const lastOverlay = getPresentedOverlay(doc, 'ion-alert,ion-action-sheet,ion-loading,ion-modal,ion-picker-legacy,ion-popover');
320
+ const lastOverlay = getPresentedOverlay(doc, OVERLAY_FOCUS_TRAP_SELECTOR);
228
321
  const target = ev.target;
229
322
  /**
230
323
  * If no active overlay, ignore this event.
@@ -407,6 +500,30 @@ const connectListeners = (doc) => {
407
500
  doc.addEventListener('focus', (ev) => {
408
501
  trapKeyboardFocus(ev, doc);
409
502
  }, true);
503
+ /**
504
+ * Remember which option control last received focus
505
+ * (arrows, click, or Tab). This pattern keeps `tabIndex=0` on the
506
+ * checked/first radio, so the Tab trap uses this when wrapping back
507
+ * into the list or focusing the option-group slot.
508
+ */
509
+ doc.addEventListener('focusin', (ev) => {
510
+ const lastOverlay = getPresentedOverlay(doc, OVERLAY_FOCUS_TRAP_SELECTOR);
511
+ if (!lastOverlay || lastOverlay.classList.contains(FOCUS_TRAP_DISABLE_CLASS)) {
512
+ return;
513
+ }
514
+ const isSheetModal = lastOverlay.classList.contains('modal-sheet');
515
+ if (!isSheetModal) {
516
+ return;
517
+ }
518
+ const target = ev.target;
519
+ if (!(target instanceof HTMLElement)) {
520
+ return;
521
+ }
522
+ const optionHost = getOptionControlHost(target);
523
+ if (optionHost && lastOverlay.contains(optionHost)) {
524
+ lastOverlay.trapLastSheetOptionControl = optionHost;
525
+ }
526
+ }, true);
410
527
  // Listen for keydown events to intercept Tab navigation.
411
528
  // This is needed for Safari and Firefox which may skip focusable
412
529
  // elements or allow focus to escape the overlay.
@@ -416,10 +533,10 @@ const connectListeners = (doc) => {
416
533
  var _a, _b, _c;
417
534
  if (ev.key !== 'Tab' && ev.key !== 'Alt+Tab')
418
535
  return;
419
- const lastOverlay = getPresentedOverlay(doc, 'ion-alert,ion-action-sheet,ion-loading,ion-modal,ion-picker-legacy,ion-popover');
536
+ const lastOverlay = getPresentedOverlay(doc, OVERLAY_FOCUS_TRAP_SELECTOR);
420
537
  if (!lastOverlay || lastOverlay.classList.contains(FOCUS_TRAP_DISABLE_CLASS))
421
538
  return;
422
- const activeElement = doc.activeElement;
539
+ const activeElement = getActiveElement(doc);
423
540
  if (activeElement === lastOverlay) {
424
541
  ev.preventDefault();
425
542
  focusFirstDescendant(lastOverlay);
@@ -435,16 +552,32 @@ const connectListeners = (doc) => {
435
552
  if (!isInsideOverlay)
436
553
  return;
437
554
  // Get all focusable elements from both light and shadow DOM
438
- const allFocusable = [
555
+ let allFocusable = [
439
556
  ...lastOverlay.querySelectorAll(focusableQueryString),
440
557
  ...(((_c = lastOverlay.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelectorAll(focusableQueryString)) || []),
441
558
  ];
559
+ const selectModalEl = lastOverlay.querySelector(ION_SELECT_MODAL_SELECTOR);
560
+ const isSheetModal = lastOverlay.classList.contains('modal-sheet');
561
+ /**
562
+ * Some sheet modal content, including ion-select-modal,
563
+ * renders option containers as `ion-item.select-interface-option`.
564
+ * These can match focusable selectors in some builds but
565
+ * are not intended tab stops. Keep only true interactive
566
+ * controls so Tab can move from options to header
567
+ * controls/handle.
568
+ */
569
+ if (selectModalEl) {
570
+ allFocusable = allFocusable.filter((el) => !el.matches('ion-item.select-interface-option'));
571
+ }
572
+ if (isSheetModal) {
573
+ allFocusable = sortSheetModalFocusables(lastOverlay, allFocusable);
574
+ }
442
575
  if (allFocusable.length === 0) {
443
576
  ev.preventDefault();
444
577
  return;
445
578
  }
446
579
  // Find current element's index (accounting for shadow DOM)
447
- const currentIndex = activeElement
580
+ let currentIndex = activeElement
448
581
  ? allFocusable.findIndex((el) => {
449
582
  var _a;
450
583
  if (el === activeElement)
@@ -455,6 +588,31 @@ const connectListeners = (doc) => {
455
588
  return rootNode instanceof ShadowRoot && rootNode.host === el;
456
589
  })
457
590
  : -1;
591
+ /**
592
+ * Radio/checkbox groups can move focus onto an option with
593
+ * `tabIndex=-1`, while another option still has `tabIndex=0`
594
+ * in the list. `findIndex` then yields -1 and Tab incorrectly
595
+ * wraps to `allFocusable[0]`.
596
+ * Treat focus on any option control inside the sheet modal
597
+ * as the same trap slot as the listed tabbable option
598
+ * (`tabIndex >= 0`) so Tab goes handle → Cancel, not back
599
+ * to the first radio.
600
+ */
601
+ if (currentIndex < 0 && isSheetModal && activeElement) {
602
+ const optionHost = getOptionControlHost(activeElement);
603
+ if (optionHost && lastOverlay.contains(optionHost)) {
604
+ const directIndex = allFocusable.indexOf(optionHost);
605
+ if (directIndex >= 0) {
606
+ currentIndex = directIndex;
607
+ }
608
+ else {
609
+ const tabbableOptionIndex = getTabbableOptionControlIndex(allFocusable, lastOverlay);
610
+ if (tabbableOptionIndex >= 0) {
611
+ currentIndex = tabbableOptionIndex;
612
+ }
613
+ }
614
+ }
615
+ }
458
616
  ev.preventDefault();
459
617
  // Helper to focus an element, handling shadow DOM properly
460
618
  const focusElement = (element) => {
@@ -468,24 +626,43 @@ const connectListeners = (doc) => {
468
626
  }
469
627
  focusVisibleElement(element);
470
628
  };
629
+ let nextIndex;
471
630
  if (ev.shiftKey) {
472
631
  // Shift+Tab: previous element, wrap to last if at first
473
632
  if (currentIndex <= 0) {
474
- focusLastDescendant(lastOverlay);
633
+ nextIndex = allFocusable.length - 1;
475
634
  }
476
635
  else {
477
- focusElement(allFocusable[currentIndex - 1]);
636
+ nextIndex = currentIndex - 1;
478
637
  }
479
638
  }
480
639
  else {
481
640
  // Tab: next element, wrap to first if at last
482
641
  if (currentIndex < 0 || currentIndex >= allFocusable.length - 1) {
483
- focusFirstDescendant(lastOverlay);
642
+ nextIndex = 0;
484
643
  }
485
644
  else {
486
- focusElement(allFocusable[currentIndex + 1]);
645
+ nextIndex = currentIndex + 1;
646
+ }
647
+ }
648
+ const nextEl = allFocusable[nextIndex];
649
+ const overlayTrap = lastOverlay;
650
+ const tabbableOptionIndex = isSheetModal ? getTabbableOptionControlIndex(allFocusable, lastOverlay) : -1;
651
+ /**
652
+ * The trap list only includes one tabbable option host
653
+ * (`tabIndex >= 0`), usually the checked/first radio.
654
+ * `focusin` tracks the real last-focused option; use it
655
+ * whenever Tab would focus that slot (wrap from Cancel,
656
+ * Shift+Tab from handle, etc.).
657
+ */
658
+ let focusTarget = nextEl;
659
+ if (isSheetModal && tabbableOptionIndex >= 0 && nextIndex === tabbableOptionIndex) {
660
+ const saved = overlayTrap.trapLastSheetOptionControl;
661
+ if ((saved === null || saved === void 0 ? void 0 : saved.isConnected) && lastOverlay.contains(saved) && saved !== nextEl) {
662
+ focusTarget = saved;
487
663
  }
488
664
  }
665
+ focusElement(focusTarget);
489
666
  }, true);
490
667
  // handle back-button click
491
668
  doc.addEventListener('ionBackButton', (ev) => {
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- export{c as createAnimation}from"./p-Cb-0O4h8.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./p-BsfuYVMP.js";export{iosTransitionAnimation}from"./p-8uDL7fql.js";export{mdTransitionAnimation}from"./p-CIk5QtPm.js";export{g as getTimeGivenProgression}from"./p-hHmYLOfE.js";export{createGesture}from"./p-Cl0B-RWe.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./p-CBV-BGvD.js";export{c as componentOnReady}from"./p-CHE1xWbg.js";export{L as LogLevel}from"./p-Omi_TcwW.js";export{I as IonicSafeString}from"./p-CWJdc8f_.js";export{g as getMode,s as setupConfig}from"./p-BwKpO3Is.js";export{o as openURL}from"./p-DaJxRxSQ.js";export{m as menuController}from"./p-C5zxLmJ_.js";export{b as actionSheetController,a as alertController,l as loadingController,m as modalController,p as pickerController,c as popoverController,t as toastController}from"./p-C4uUM9DM.js";import"./p-BTEOs1at.js";import"./p-vXpMhGrs.js";import"./p-FvDKM4Ax.js";const e=e=>{const{swiper:o,extendParams:s}=e,t={effect:void 0,direction:"horizontal",initialSlide:0,loop:!1,parallax:!1,slidesPerView:1,spaceBetween:0,speed:300,slidesPerColumn:1,slidesPerColumnFill:"column",slidesPerGroup:1,centeredSlides:!1,slidesOffsetBefore:0,slidesOffsetAfter:0,touchEventsTarget:"container",freeMode:!1,freeModeMomentum:!0,freeModeMomentumRatio:1,freeModeMomentumBounce:!0,freeModeMomentumBounceRatio:1,freeModeMomentumVelocityRatio:1,freeModeSticky:!1,freeModeMinimumVelocity:.02,autoHeight:!1,setWrapperSize:!1,zoom:{maxRatio:3,minRatio:1,toggle:!1},touchRatio:1,touchAngle:45,simulateTouch:!0,touchStartPreventDefault:!1,shortSwipes:!0,longSwipes:!0,longSwipesRatio:.5,longSwipesMs:300,followFinger:!0,threshold:0,touchMoveStopPropagation:!0,touchReleaseOnEdges:!1,iOSEdgeSwipeDetection:!1,iOSEdgeSwipeThreshold:20,resistance:!0,resistanceRatio:.85,watchSlidesProgress:!1,watchSlidesVisibility:!1,preventClicks:!0,preventClicksPropagation:!0,slideToClickedSlide:!1,loopAdditionalSlides:0,noSwiping:!0,runCallbacksOnInit:!0,coverflowEffect:{rotate:50,stretch:0,depth:100,modifier:1,slideShadows:!0},flipEffect:{slideShadows:!0,limitRotation:!0},cubeEffect:{slideShadows:!0,shadow:!0,shadowOffset:20,shadowScale:.94},fadeEffect:{crossFade:!1},a11y:{prevSlideMessage:"Previous slide",nextSlideMessage:"Next slide",firstSlideMessage:"This is the first slide",lastSlideMessage:"This is the last slide"}};o.pagination&&(t.pagination={type:"bullets",clickable:!1,hideOnClick:!1}),o.scrollbar&&(t.scrollbar={hide:!0}),s(t)};export{e as IonicSlides}
4
+ export{c as createAnimation}from"./p-Cb-0O4h8.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./p-BsfuYVMP.js";export{iosTransitionAnimation}from"./p-8uDL7fql.js";export{mdTransitionAnimation}from"./p-CIk5QtPm.js";export{g as getTimeGivenProgression}from"./p-hHmYLOfE.js";export{createGesture}from"./p-Cl0B-RWe.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./p-CBV-BGvD.js";export{c as componentOnReady}from"./p-CHE1xWbg.js";export{L as LogLevel}from"./p-Omi_TcwW.js";export{I as IonicSafeString}from"./p-CWJdc8f_.js";export{g as getMode,s as setupConfig}from"./p-BwKpO3Is.js";export{o as openURL}from"./p-DaJxRxSQ.js";export{m as menuController}from"./p-C5zxLmJ_.js";export{b as actionSheetController,a as alertController,l as loadingController,m as modalController,p as pickerController,c as popoverController,t as toastController}from"./p-DdyNaGpi.js";import"./p-BTEOs1at.js";import"./p-vXpMhGrs.js";import"./p-FvDKM4Ax.js";const e=e=>{const{swiper:o,extendParams:s}=e,t={effect:void 0,direction:"horizontal",initialSlide:0,loop:!1,parallax:!1,slidesPerView:1,spaceBetween:0,speed:300,slidesPerColumn:1,slidesPerColumnFill:"column",slidesPerGroup:1,centeredSlides:!1,slidesOffsetBefore:0,slidesOffsetAfter:0,touchEventsTarget:"container",freeMode:!1,freeModeMomentum:!0,freeModeMomentumRatio:1,freeModeMomentumBounce:!0,freeModeMomentumBounceRatio:1,freeModeMomentumVelocityRatio:1,freeModeSticky:!1,freeModeMinimumVelocity:.02,autoHeight:!1,setWrapperSize:!1,zoom:{maxRatio:3,minRatio:1,toggle:!1},touchRatio:1,touchAngle:45,simulateTouch:!0,touchStartPreventDefault:!1,shortSwipes:!0,longSwipes:!0,longSwipesRatio:.5,longSwipesMs:300,followFinger:!0,threshold:0,touchMoveStopPropagation:!0,touchReleaseOnEdges:!1,iOSEdgeSwipeDetection:!1,iOSEdgeSwipeThreshold:20,resistance:!0,resistanceRatio:.85,watchSlidesProgress:!1,watchSlidesVisibility:!1,preventClicks:!0,preventClicksPropagation:!0,slideToClickedSlide:!1,loopAdditionalSlides:0,noSwiping:!0,runCallbacksOnInit:!0,coverflowEffect:{rotate:50,stretch:0,depth:100,modifier:1,slideShadows:!0},flipEffect:{slideShadows:!0,limitRotation:!0},cubeEffect:{slideShadows:!0,shadow:!0,shadowOffset:20,shadowScale:.94},fadeEffect:{crossFade:!1},a11y:{prevSlideMessage:"Previous slide",nextSlideMessage:"Next slide",firstSlideMessage:"This is the first slide",lastSlideMessage:"This is the last slide"}};o.pagination&&(t.pagination={type:"bullets",clickable:!1,hideOnClick:!1}),o.scrollbar&&(t.scrollbar={hide:!0}),s(t)};export{e as IonicSlides}