voyager-ionic-core 8.8.4 → 8.8.6

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 (74) hide show
  1. package/components/ion-action-sheet.js +1 -1
  2. package/components/ion-content.js +1 -1
  3. package/components/ion-datetime.js +1 -1
  4. package/components/ion-modal.js +1 -1
  5. package/components/ion-radio-group.js +1 -1
  6. package/components/ion-segment.js +1 -1
  7. package/components/ion-select-modal.js +1 -1
  8. package/components/ion-select-popover.js +1 -1
  9. package/components/ion-select.js +1 -1
  10. package/components/p-0z8QSI5b.js +4 -0
  11. package/components/{p-ApmKVjaE.js → p-BGHGpkPX.js} +1 -1
  12. package/components/p-BlNv564p.js +4 -0
  13. package/components/p-D-cP12ZN.js +4 -0
  14. package/components/p-DZhbcvo5.js +4 -0
  15. package/components/{p-Bk2zuNWT.js → p-DvOO1fxp.js} +1 -1
  16. package/dist/cjs/ion-action-sheet.cjs.entry.js +4 -4
  17. package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
  18. package/dist/cjs/ion-datetime_3.cjs.entry.js +30 -14
  19. package/dist/cjs/ion-modal.cjs.entry.js +99 -45
  20. package/dist/cjs/ion-radio_2.cjs.entry.js +13 -1
  21. package/dist/cjs/ion-segment_2.cjs.entry.js +6 -2
  22. package/dist/cjs/ion-select-modal.cjs.entry.js +18 -7
  23. package/dist/cjs/ion-select_3.cjs.entry.js +18 -7
  24. package/dist/collection/components/action-sheet/action-sheet.js +4 -4
  25. package/dist/collection/components/content/content.css +1 -1
  26. package/dist/collection/components/datetime/datetime.js +30 -14
  27. package/dist/collection/components/modal/modal.js +73 -44
  28. package/dist/collection/components/modal/safe-area-utils.js +27 -2
  29. package/dist/collection/components/radio-group/radio-group.js +13 -1
  30. package/dist/collection/components/radio-group/test/fixtures.js +2 -2
  31. package/dist/collection/components/segment/segment.js +6 -2
  32. package/dist/collection/components/select-modal/select-modal.js +18 -7
  33. package/dist/collection/components/select-modal/test/fixtures.js +4 -0
  34. package/dist/collection/components/select-popover/select-popover.js +18 -7
  35. package/dist/collection/components/select-popover/test/fixtures.js +4 -0
  36. package/dist/docs.json +1 -1
  37. package/dist/esm/ion-action-sheet.entry.js +4 -4
  38. package/dist/esm/ion-app_8.entry.js +1 -1
  39. package/dist/esm/ion-datetime_3.entry.js +30 -14
  40. package/dist/esm/ion-modal.entry.js +99 -45
  41. package/dist/esm/ion-radio_2.entry.js +13 -1
  42. package/dist/esm/ion-segment_2.entry.js +6 -2
  43. package/dist/esm/ion-select-modal.entry.js +18 -7
  44. package/dist/esm/ion-select_3.entry.js +18 -7
  45. package/dist/ionic/ionic.esm.js +1 -1
  46. package/dist/ionic/p-1ca9c36b.entry.js +4 -0
  47. package/dist/ionic/p-28a9e720.entry.js +4 -0
  48. package/dist/ionic/p-7761ef65.entry.js +4 -0
  49. package/dist/ionic/{p-4dd5e8e0.entry.js → p-8fda6a62.entry.js} +1 -1
  50. package/dist/ionic/p-a893c61c.entry.js +4 -0
  51. package/dist/ionic/{p-9eac4eb1.entry.js → p-aa812c4b.entry.js} +1 -1
  52. package/dist/ionic/p-cb27fe68.entry.js +4 -0
  53. package/dist/ionic/p-ce2edb36.entry.js +4 -0
  54. package/dist/types/components/datetime/datetime.d.ts +7 -0
  55. package/dist/types/components/modal/modal.d.ts +41 -3
  56. package/dist/types/components/modal/safe-area-utils.d.ts +16 -0
  57. package/dist/types/components/radio-group/test/fixtures.d.ts +1 -1
  58. package/dist/types/components/select-modal/select-modal.d.ts +1 -0
  59. package/dist/types/components/select-modal/test/fixtures.d.ts +1 -0
  60. package/dist/types/components/select-popover/select-popover.d.ts +1 -0
  61. package/dist/types/components/select-popover/test/fixtures.d.ts +1 -0
  62. package/hydrate/index.js +189 -81
  63. package/hydrate/index.mjs +189 -81
  64. package/package.json +1 -1
  65. package/components/p-1KVKSLu5.js +0 -4
  66. package/components/p-BI7WNErr.js +0 -4
  67. package/components/p-BTF2nRLo.js +0 -4
  68. package/components/p-EK4xUz-q.js +0 -4
  69. package/dist/ionic/p-4c67ce4c.entry.js +0 -4
  70. package/dist/ionic/p-51c11c47.entry.js +0 -4
  71. package/dist/ionic/p-5681dde4.entry.js +0 -4
  72. package/dist/ionic/p-9cdbabbb.entry.js +0 -4
  73. package/dist/ionic/p-cb78f5a0.entry.js +0 -4
  74. package/dist/ionic/p-e6c5f060.entry.js +0 -4
package/hydrate/index.js CHANGED
@@ -9798,7 +9798,7 @@ class ActionSheet {
9798
9798
  if (isRadio) {
9799
9799
  htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
9800
9800
  }
9801
- return (hAsync("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass$3(b)), { 'action-sheet-selected': isActiveRadio }), onClick: () => {
9801
+ return (hAsync("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass$3(b)), (isRadio && { 'action-sheet-selected': isActiveRadio })), onClick: () => {
9802
9802
  if (isRadio) {
9803
9803
  this.selectRadioButton(b);
9804
9804
  }
@@ -9813,12 +9813,12 @@ class ActionSheet {
9813
9813
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
9814
9814
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
9815
9815
  const headerID = `action-sheet-${overlayIndex}-header`;
9816
- return (hAsync(Host, Object.assign({ key: '173fcff5b1da7c33c267de4667591c946b8c8d03', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
9816
+ return (hAsync(Host, Object.assign({ key: '48b63b870f2816b4cad3c606f3d9956854cee79a', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
9817
9817
  zIndex: `${20000 + this.overlayIndex}`,
9818
- }, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), hAsync("ion-backdrop", { key: '521ede659f747864f6c974e09016436eceb7158c', tappable: this.backdropDismiss }), hAsync("div", { key: '7a7946fc434bc444f16a70638f5e948c69d33fcd', tabindex: "0", "aria-hidden": "true" }), hAsync("div", { key: 'bcff39a580489dbafa255842e57aa8602c6d0f18', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, hAsync("div", { key: '84bba13ce14261f0f0daa3f9c77648c9e7f36e0e', class: "action-sheet-container" }, hAsync("div", { key: 'd9c8ac404fd6719a7adf8cb36549f67616f9a0c4', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (hAsync("div", { key: '180433a8ad03ef5c54728a1a8f34715b6921d658', id: headerID, class: {
9818
+ }, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), hAsync("ion-backdrop", { key: '41dd5781f139d26b3feea33ab387451aeafacd51', tappable: this.backdropDismiss }), hAsync("div", { key: 'f797c2657782e4e83adf90d2d796108e857a1fc0', tabindex: "0", "aria-hidden": "true" }), hAsync("div", { key: '8e74209321fc5e8712e3e293c91a6fa036ea45ab', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, hAsync("div", { key: 'c811860be2eed0b6c73fc2cc5a59cb94db2e8912', class: "action-sheet-container" }, hAsync("div", { key: 'd36ee14b70d73eb5cd69e0c57bc5cc31daf86ab3', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (hAsync("div", { key: '922695de191edc86451eed2552acc4f54993ea52', id: headerID, class: {
9819
9819
  'action-sheet-title': true,
9820
9820
  'action-sheet-has-sub-title': this.subHeader !== undefined,
9821
- } }, header, this.subHeader && hAsync("div", { key: '7138e79e61b1a8f42bc5a9175c57fa2f15d7ec5a', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (hAsync("div", { key: 'b617c722f5b8028d73ed34b69310f312c65f34a7', class: "action-sheet-group action-sheet-group-cancel" }, hAsync("button", Object.assign({ key: 'd0dd876fc48815df3710413c201c0b445a8e16c0' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass$3(cancelButton), onClick: () => this.buttonClick(cancelButton) }), hAsync("span", { key: 'e7b960157cc6fc5fe92a12090b2be55e8ae072e4', class: "action-sheet-button-inner" }, cancelButton.icon && (hAsync("ion-icon", { key: '05498ffc60cab911dbff0ecbc6168dea59ada9a5', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && hAsync("ion-ripple-effect", { key: '3d401346cea301be4ca03671f7370f6f4b0b6bde' })))))), hAsync("div", { key: '971f3c5fcc07f36c28eb469a47ec0290c692e139', tabindex: "0", "aria-hidden": "true" })));
9821
+ } }, header, this.subHeader && hAsync("div", { key: 'a119d4a93668e829f7106f11cbbdd437310f3e80', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (hAsync("div", { key: '1d27f36d09bedd2e4c61fea9dd9d05f8f9271aef', class: "action-sheet-group action-sheet-group-cancel" }, hAsync("button", Object.assign({ key: 'ed13658176c54cc45808a54a0331c297430d9bc6' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass$3(cancelButton), onClick: () => this.buttonClick(cancelButton) }), hAsync("span", { key: '5208e3e3535b775a443cef6fb1decd790db69b0c', class: "action-sheet-button-inner" }, cancelButton.icon && (hAsync("ion-icon", { key: 'b7ba489e1ee50524a5b4af670eef787844a16286', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && hAsync("ion-ripple-effect", { key: 'b3f88898114855853259f0001def26b4bd6e4a98' })))))), hAsync("div", { key: '0dfd0fcdc633bf565990eaa06679608e749cf8f9', tabindex: "0", "aria-hidden": "true" })));
9822
9822
  }
9823
9823
  get el() { return getElement(this); }
9824
9824
  static get watchers() { return {
@@ -12367,7 +12367,7 @@ const isRTL$1 = (hostEl) => {
12367
12367
  return (document === null || document === void 0 ? void 0 : document.dir.toLowerCase()) === 'rtl';
12368
12368
  };
12369
12369
 
12370
- const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
12370
+ const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom) + var(--ion-content-safe-area-padding-bottom, 0px));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
12371
12371
 
12372
12372
  /**
12373
12373
  * @slot - Content is placed in the scrollable area if provided without a slot.
@@ -14781,6 +14781,12 @@ class Datetime {
14781
14781
  this.ionStyle = createEvent(this, "ionStyle", 7);
14782
14782
  this.ionRender = createEvent(this, "ionRender", 7);
14783
14783
  this.inputId = `ion-dt-${datetimeIds++}`;
14784
+ /**
14785
+ * Set true only by `visibleCallback`. Lets `hiddenCallback` ignore the
14786
+ * synthetic "not intersecting" entry IntersectionObserver fires on
14787
+ * `observe()` when the host mounts offscreen.
14788
+ */
14789
+ this.hasBeenIntersecting = false;
14784
14790
  this.prevPresentation = null;
14785
14791
  this.showMonthAndYear = false;
14786
14792
  this.activeParts = [];
@@ -15343,7 +15349,21 @@ class Datetime {
15343
15349
  if (rect.width === 0 || rect.height === 0) {
15344
15350
  return;
15345
15351
  }
15352
+ this.markReady();
15353
+ };
15354
+ this.markReady = () => {
15355
+ if (this.el.classList.contains('datetime-ready')) {
15356
+ return;
15357
+ }
15346
15358
  this.initializeListeners();
15359
+ /**
15360
+ * TODO FW-2793: Datetime needs a frame to ensure that it
15361
+ * can properly scroll contents into view. As a result
15362
+ * we hide the scrollable content until after that frame
15363
+ * so users do not see the content quickly shifting. The downside
15364
+ * is that the content will pop into view a frame after. Maybe there
15365
+ * is a better way to handle this?
15366
+ */
15347
15367
  writeTask(() => {
15348
15368
  this.el.classList.add('datetime-ready');
15349
15369
  });
@@ -15636,6 +15656,7 @@ class Datetime {
15636
15656
  this.clearFocusVisible = undefined;
15637
15657
  }
15638
15658
  this.loadTimeoutCleanup();
15659
+ this.hasBeenIntersecting = false;
15639
15660
  }
15640
15661
  initializeListeners() {
15641
15662
  this.initializeCalendarListener();
@@ -15655,18 +15676,8 @@ class Datetime {
15655
15676
  if (!ev.isIntersecting) {
15656
15677
  return;
15657
15678
  }
15658
- this.initializeListeners();
15659
- /**
15660
- * TODO FW-2793: Datetime needs a frame to ensure that it
15661
- * can properly scroll contents into view. As a result
15662
- * we hide the scrollable content until after that frame
15663
- * so users do not see the content quickly shifting. The downside
15664
- * is that the content will pop into view a frame after. Maybe there
15665
- * is a better way to handle this?
15666
- */
15667
- writeTask(() => {
15668
- this.el.classList.add('datetime-ready');
15669
- });
15679
+ this.hasBeenIntersecting = true;
15680
+ this.markReady();
15670
15681
  };
15671
15682
  const visibleIO = new IntersectionObserver(visibleCallback, { threshold: 0.01, root: el });
15672
15683
  /**
@@ -15702,6 +15713,11 @@ class Datetime {
15702
15713
  if (ev.isIntersecting) {
15703
15714
  return;
15704
15715
  }
15716
+ // Ignore the initial "not intersecting" entry IntersectionObserver fires on observe().
15717
+ if (!this.hasBeenIntersecting) {
15718
+ return;
15719
+ }
15720
+ this.hasBeenIntersecting = false;
15705
15721
  this.destroyInteractionListeners();
15706
15722
  /**
15707
15723
  * When datetime is hidden, we need to make sure that
@@ -16445,7 +16461,7 @@ class Datetime {
16445
16461
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
16446
16462
  const hasWheelVariant = hasDatePresentation && preferWheel;
16447
16463
  renderHiddenInput(true, el, name, formatValue(value), disabled);
16448
- return (hAsync(Host, { key: '59e0811aa273e88dfb8e4b703e6824088a457380', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
16464
+ return (hAsync(Host, { key: '323c8c2327088f00934b8c93c3306538cb9b5677', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
16449
16465
  [mode]: true,
16450
16466
  ['datetime-readonly']: readonly,
16451
16467
  ['datetime-disabled']: disabled,
@@ -16455,7 +16471,7 @@ class Datetime {
16455
16471
  [`datetime-size-${size}`]: true,
16456
16472
  [`datetime-prefer-wheel`]: hasWheelVariant,
16457
16473
  [`datetime-grid`]: isGridStyle,
16458
- })) }, hAsync("div", { key: '3753ff3dde3085070916c3de83687a219a49e553', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
16474
+ })) }, hAsync("div", { key: '1e0855c8909bc3f1e48a21ad68159fa782060691', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
16459
16475
  }
16460
16476
  get el() { return getElement(this); }
16461
16477
  static get watchers() { return {
@@ -25008,6 +25024,12 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
25008
25024
  const MODAL_INSET_MIN_WIDTH = 768;
25009
25025
  const MODAL_INSET_MIN_HEIGHT = 600;
25010
25026
  const EDGE_THRESHOLD = 5;
25027
+ /**
25028
+ * CSS values for `--width` / `--height` that are treated as fullscreen
25029
+ * (modal touches the corresponding screen edges). Empty string means the
25030
+ * property was not overridden. See `hasCustomModalDimensions()`.
25031
+ */
25032
+ const FULLSCREEN_SIZE_VALUES = new Set(['', '100%', '100vw', '100vh', '100dvw', '100dvh', '100svw', '100svh']);
25011
25033
  /**
25012
25034
  * Cache for resolved root safe-area-top value, invalidated once per frame.
25013
25035
  */
@@ -25056,6 +25078,22 @@ const getRootSafeAreaTop = () => {
25056
25078
  }
25057
25079
  return value;
25058
25080
  };
25081
+ /**
25082
+ * True when the modal host declares BOTH a non-fullscreen `--width` AND a
25083
+ * non-fullscreen `--height` (i.e. a centered-dialog-like modal that doesn't
25084
+ * touch any screen edge).
25085
+ *
25086
+ * The conservative "both axes" check avoids mis-zeroing safe-area for
25087
+ * partial-custom modals where the modal still touches top/bottom edges
25088
+ * (e.g. only `--width` overridden). Partial cases fall through to the
25089
+ * existing position-based post-animation correction.
25090
+ */
25091
+ const hasCustomModalDimensions = (hostEl) => {
25092
+ const styles = getComputedStyle(hostEl);
25093
+ const width = styles.getPropertyValue('--width').trim();
25094
+ const height = styles.getPropertyValue('--height').trim();
25095
+ return !FULLSCREEN_SIZE_VALUES.has(width) && !FULLSCREEN_SIZE_VALUES.has(height);
25096
+ };
25059
25097
  /**
25060
25098
  * Returns the initial safe-area configuration based on modal type.
25061
25099
  * This is called before animation starts and uses configuration-based prediction.
@@ -25090,8 +25128,11 @@ const getInitialSafeAreaConfig = (context) => {
25090
25128
  }
25091
25129
  // On viewports that meet the centered dialog media query breakpoints,
25092
25130
  // regular modals render as centered dialogs (not fullscreen), so they
25093
- // don't touch any screen edges and don't need safe-area insets.
25094
- if (isCenteredDialogViewport()) {
25131
+ // don't touch any screen edges and don't need safe-area insets. Also
25132
+ // applies to phone viewports when the modal declares custom --width and
25133
+ // --height; these don't touch screen edges either, so the initial
25134
+ // prediction must be zero to avoid a post-animation correction flash.
25135
+ if (isCenteredDialogViewport() || context.hasCustomDimensions) {
25095
25136
  return {
25096
25137
  top: '0px',
25097
25138
  bottom: '0px',
@@ -25403,12 +25444,10 @@ class Modal {
25403
25444
  // since the viewport may have crossed the centered-dialog breakpoint.
25404
25445
  if (!context.isSheetModal && !context.isCardModal) {
25405
25446
  this.updateSafeAreaOverrides();
25406
- // Re-evaluate fullscreen safe-area padding: clear first, then re-apply
25407
- if (this.wrapperEl) {
25408
- this.wrapperEl.style.removeProperty('height');
25409
- this.wrapperEl.style.removeProperty('padding-bottom');
25410
- }
25411
- this.applyFullscreenSafeArea();
25447
+ // Re-evaluate fullscreen safe-area padding: clear first, then re-apply.
25448
+ const { contentEl, hasFooter } = this.findContentAndFooter();
25449
+ this.clearContentSafeAreaPadding(contentEl);
25450
+ this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
25412
25451
  }
25413
25452
  }, 50); // Debounce to avoid excessive calls during active resizing
25414
25453
  }
@@ -26155,6 +26194,11 @@ class Modal {
26155
26194
  }
26156
26195
  /**
26157
26196
  * Creates the context object for safe-area utilities.
26197
+ *
26198
+ * `hasCustomDimensions` is only set by `setInitialSafeAreaOverrides()`
26199
+ * because it is only read by `getInitialSafeAreaConfig()`. Other callers
26200
+ * (resize handler, post-animation update, fullscreen-padding apply) would
26201
+ * pay a `getComputedStyle()` cost for a value they never consult.
26158
26202
  */
26159
26203
  getSafeAreaContext() {
26160
26204
  return {
@@ -26176,7 +26220,7 @@ class Modal {
26176
26220
  * sheets to prevent header content from getting double-offset padding).
26177
26221
  */
26178
26222
  setInitialSafeAreaOverrides() {
26179
- const context = this.getSafeAreaContext();
26223
+ const context = Object.assign(Object.assign({}, this.getSafeAreaContext()), { hasCustomDimensions: hasCustomModalDimensions(this.el) });
26180
26224
  const safeAreaConfig = getInitialSafeAreaConfig(context);
26181
26225
  applySafeAreaOverrides(this.el, safeAreaConfig);
26182
26226
  // Set the internal offset property with the resolved root safe-area-top value
@@ -26216,59 +26260,85 @@ class Modal {
26216
26260
  applySafeAreaOverrides(el, safeAreaConfig);
26217
26261
  }
26218
26262
  /**
26219
- * Applies padding-bottom to fullscreen modal wrapper to prevent
26220
- * content from overlapping system navigation bar.
26263
+ * Applies safe-area-bottom scroll padding to ion-content inside
26264
+ * fullscreen modals that have no ion-footer. This prevents content
26265
+ * from being hidden behind the system navigation bar while keeping
26266
+ * the modal background edge-to-edge (no visible gap).
26221
26267
  */
26222
26268
  applyFullscreenSafeArea() {
26223
- const { wrapperEl, el } = this;
26224
- if (!wrapperEl)
26225
- return;
26226
26269
  const context = this.getSafeAreaContext();
26227
26270
  if (context.isSheetModal || context.isCardModal)
26228
26271
  return;
26229
- // Check for standard Ionic layout children (ion-content, ion-footer),
26230
- // searching one level deep for wrapped components (e.g.,
26231
- // <app-footer><ion-footer>...</ion-footer></app-footer>).
26232
- // Note: uses a manual loop instead of querySelector(':scope > ...') because
26233
- // Stencil's mock-doc (used in spec tests) does not support :scope.
26234
- let hasContent = false;
26272
+ const { contentEl, hasFooter } = this.findContentAndFooter();
26273
+ this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
26274
+ }
26275
+ /**
26276
+ * Sets --ion-content-safe-area-padding-bottom on the given ion-content
26277
+ * when no footer is present, so ion-content's .inner-scroll includes
26278
+ * safe-area-bottom in its scroll padding. This keeps the modal background
26279
+ * edge-to-edge while ensuring content scrolls clear of the system nav bar.
26280
+ *
26281
+ * --ion-content-safe-area-padding-bottom is an internal CSS property used
26282
+ * only by this code path. It is not part of ion-content's public API and
26283
+ * should not be set by consumers. The default of 0px makes it a no-op
26284
+ * when unset, which is the expected state for ion-content used outside of
26285
+ * a fullscreen modal without a footer.
26286
+ */
26287
+ applyFullscreenSafeAreaTo(contentEl, hasFooter) {
26288
+ // Only apply for standard Ionic layouts (has ion-content but no
26289
+ // ion-footer). When a footer is present it handles its own safe-area
26290
+ // padding. Custom modals with raw HTML are developer-controlled.
26291
+ if (!contentEl || hasFooter)
26292
+ return;
26293
+ contentEl.style.setProperty('--ion-content-safe-area-padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
26294
+ }
26295
+ /**
26296
+ * Removes the internal --ion-content-safe-area-padding-bottom property
26297
+ * from an already-located ion-content. Callers do their own
26298
+ * findContentAndFooter() so they can also read hasFooter if needed.
26299
+ */
26300
+ clearContentSafeAreaPadding(contentEl) {
26301
+ if (!contentEl)
26302
+ return;
26303
+ contentEl.style.removeProperty('--ion-content-safe-area-padding-bottom');
26304
+ }
26305
+ /**
26306
+ * Finds ion-content and ion-footer among direct children and one level of
26307
+ * grandchildren (for wrapped components like <app-footer><ion-footer>).
26308
+ *
26309
+ * Intentionally does NOT use findIonContent() or querySelector() because
26310
+ * those search the full subtree and would match ion-content inside nested
26311
+ * routes/pages. We only want direct slot children (+ one wrapper level).
26312
+ *
26313
+ * Uses a manual loop instead of querySelector(':scope > ...') because
26314
+ * Stencil's mock-doc (used in spec tests) does not support :scope.
26315
+ */
26316
+ findContentAndFooter() {
26317
+ let contentEl = null;
26235
26318
  let hasFooter = false;
26236
- for (const child of Array.from(el.children)) {
26319
+ for (const child of Array.from(this.el.children)) {
26237
26320
  if (child.tagName === 'ION-CONTENT')
26238
- hasContent = true;
26321
+ contentEl = child;
26239
26322
  if (child.tagName === 'ION-FOOTER')
26240
26323
  hasFooter = true;
26241
26324
  for (const grandchild of Array.from(child.children)) {
26242
- if (grandchild.tagName === 'ION-CONTENT')
26243
- hasContent = true;
26325
+ if (grandchild.tagName === 'ION-CONTENT' && !contentEl)
26326
+ contentEl = grandchild;
26244
26327
  if (grandchild.tagName === 'ION-FOOTER')
26245
26328
  hasFooter = true;
26246
26329
  }
26247
26330
  }
26248
- // Only apply wrapper padding for standard Ionic layouts (has ion-content
26249
- // but no ion-footer). Custom modals with raw HTML are fully
26250
- // developer-controlled and should not be modified.
26251
- if (!hasContent || hasFooter)
26252
- return;
26253
- // Reduce wrapper height by safe-area and add equivalent padding so the
26254
- // total visual size stays the same but the flex content area shrinks.
26255
- // Using height + padding instead of box-sizing: border-box avoids
26256
- // breaking custom modals that set --border-width (border-box would
26257
- // include the border inside the height, changing the layout).
26258
- wrapperEl.style.setProperty('height', 'calc(var(--height) - var(--ion-safe-area-bottom, 0px))');
26259
- wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
26331
+ return { contentEl, hasFooter };
26260
26332
  }
26261
26333
  /**
26262
- * Clears all safe-area overrides and padding from wrapper.
26334
+ * Clears all safe-area overrides and padding.
26263
26335
  */
26264
26336
  cleanupSafeAreaOverrides() {
26265
26337
  clearSafeAreaOverrides(this.el);
26266
26338
  // Remove internal sheet offset property
26267
26339
  this.el.style.removeProperty('--ion-modal-offset-top');
26268
- if (this.wrapperEl) {
26269
- this.wrapperEl.style.removeProperty('height');
26270
- this.wrapperEl.style.removeProperty('padding-bottom');
26271
- }
26340
+ const { contentEl } = this.findContentAndFooter();
26341
+ this.clearContentSafeAreaPadding(contentEl);
26272
26342
  }
26273
26343
  render() {
26274
26344
  const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
@@ -26277,20 +26347,20 @@ class Modal {
26277
26347
  const isCardModal = presentingElement !== undefined && mode === 'ios';
26278
26348
  const isHandleCycle = handleBehavior === 'cycle';
26279
26349
  const isSheetModalWithHandle = isSheetModal && showHandle;
26280
- return (hAsync(Host, Object.assign({ key: '1a53e8f87532abccc169ca4b24973a39c5f9ba16', "no-router": true,
26350
+ return (hAsync(Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
26281
26351
  // Allow the modal to be navigable when the handle is focusable
26282
26352
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
26283
26353
  zIndex: `${20000 + this.overlayIndex}`,
26284
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: 'fa8e0a436c0d458331402e1850f87af3dc97b582', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: 'f00de6027d3c8b5bc93db3b0f7a50a87628d40bb', class: "modal-shadow" }), hAsync("div", Object.assign({ key: 'ae5e33bd6c58e541edb2edbca92420ea02dd5175',
26354
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), hAsync("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
26285
26355
  /*
26286
26356
  role and aria-modal must be used on the
26287
26357
  same element. They must also be set inside the
26288
26358
  shadow DOM otherwise ion-button will not be highlighted
26289
26359
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
26290
26360
  */
26291
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '141cdd8f8522331f4b764e2a4d79ec6596b1eb3a', class: "modal-handle",
26361
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
26292
26362
  // Prevents the handle from receiving keyboard focus when it does not cycle
26293
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '7de20298b61abee67a16d275c9ebd9a25ce7dd26', onSlotchange: this.onSlotChange }))));
26363
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
26294
26364
  }
26295
26365
  get el() { return getElement(this); }
26296
26366
  static get watchers() { return {
@@ -31303,6 +31373,18 @@ class RadioGroup {
31303
31373
  // to the bottom of the screen
31304
31374
  ev.preventDefault();
31305
31375
  }
31376
+ // Inside a select interface, Enter commits the focused radio
31377
+ // value (matching native <select>). The !ev.repeat guard stops
31378
+ // a held Enter on the triggering ion-select from re-committing
31379
+ // once focus lands in the opened popover/modal.
31380
+ if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
31381
+ const previousValue = this.value;
31382
+ this.value = current.value;
31383
+ if (previousValue !== this.value) {
31384
+ this.emitValueChange(ev);
31385
+ }
31386
+ ev.preventDefault();
31387
+ }
31306
31388
  }
31307
31389
  }
31308
31390
  /** @internal */
@@ -31335,7 +31417,7 @@ class RadioGroup {
31335
31417
  const { label, labelId, el, name, value } = this;
31336
31418
  const mode = getIonMode$1(this);
31337
31419
  renderHiddenInput(true, el, name, value, false);
31338
- return (hAsync(Host, { key: 'db593b3ed511e9395e3c7bfd91b787328692cd6d', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), hAsync("slot", { key: 'd683b01c1ba34fe843c4b320bce4661a117472a5' })));
31420
+ return (hAsync(Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), hAsync("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
31339
31421
  }
31340
31422
  get el() { return getElement(this); }
31341
31423
  static get watchers() { return {
@@ -35975,6 +36057,7 @@ class Segment {
35975
36057
  return segmentContent === null || segmentContent === void 0 ? void 0 : segmentContent.closest('ion-segment-view');
35976
36058
  }
35977
36059
  handleSegmentViewScroll(ev) {
36060
+ var _a;
35978
36061
  const { scrollRatio, isManualScroll } = ev.detail;
35979
36062
  if (!isManualScroll) {
35980
36063
  return;
@@ -35991,6 +36074,9 @@ class Segment {
35991
36074
  const index = buttons.findIndex((button) => button.value === this.value);
35992
36075
  const current = buttons[index];
35993
36076
  const nextIndex = Math.round(scrollRatio * (buttons.length - 1));
36077
+ if ((_a = buttons[nextIndex]) === null || _a === void 0 ? void 0 : _a.disabled) {
36078
+ return;
36079
+ }
35994
36080
  if (this.lastNextIndex === undefined || this.lastNextIndex !== nextIndex) {
35995
36081
  this.lastNextIndex = nextIndex;
35996
36082
  this.triggerScrollOnValueChange = false;
@@ -36201,14 +36287,14 @@ class Segment {
36201
36287
  }
36202
36288
  render() {
36203
36289
  const mode = getIonMode$1(this);
36204
- return (hAsync(Host, { key: '725cc37b25c539fa5e3ae8d90530ae33ededc3de', role: "tablist", onClick: this.onClick, class: createColorClasses$1(this.color, {
36290
+ return (hAsync(Host, { key: 'eda6b7b88b7967b55cf9098c59b655b348a42224', role: "tablist", onClick: this.onClick, class: createColorClasses$1(this.color, {
36205
36291
  [mode]: true,
36206
36292
  'in-toolbar': hostContext('ion-toolbar', this.el),
36207
36293
  'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
36208
36294
  'segment-activated': this.activated,
36209
36295
  'segment-disabled': this.disabled,
36210
36296
  'segment-scrollable': this.scrollable,
36211
- }) }, hAsync("slot", { key: 'c51cf7ea50325866a9367d214e12bc3754870335', onSlotchange: this.onSlottedItemsChange })));
36297
+ }) }, hAsync("slot", { key: 'fdb451f235ce59c5bb50c61a13c69160ece2d5df', onSlotchange: this.onSlottedItemsChange })));
36212
36298
  }
36213
36299
  get el() { return getElement(this); }
36214
36300
  static get watchers() { return {
@@ -37425,6 +37511,10 @@ const selectModalMdCss = () => `.sc-ion-select-modal-md-h{height:100%}ion-list.s
37425
37511
  class SelectModal {
37426
37512
  constructor(hostRef) {
37427
37513
  registerInstance(this, hostRef);
37514
+ // Tracks the option that received Enter-keydown so keyup only
37515
+ // dismisses when the press started on the same option. Prevents
37516
+ // Enter on the triggering ion-select from auto-dismissing.
37517
+ this.pendingEnterTarget = null;
37428
37518
  /**
37429
37519
  * The text to display on the cancel button.
37430
37520
  */
@@ -37474,15 +37564,22 @@ class SelectModal {
37474
37564
  return (hAsync("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (hAsync("ion-item", { lines: "none", class: Object.assign({
37475
37565
  // TODO FW-4784
37476
37566
  'item-radio-checked': option.value === checked
37477
- }, getClassMap(option.cssClass)) }, hAsync("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyUp: (ev) => {
37567
+ }, getClassMap(option.cssClass)) }, hAsync("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
37568
+ if (ev.key === 'Enter' && !ev.repeat) {
37569
+ this.pendingEnterTarget = ev.currentTarget;
37570
+ }
37571
+ }, onKeyUp: (ev) => {
37478
37572
  if (ev.key === ' ') {
37479
- /**
37480
- * Selecting a radio option with keyboard navigation,
37481
- * either through the Enter or Space keys, should
37482
- * dismiss the modal.
37483
- */
37573
+ // Space selects and dismisses in one press.
37484
37574
  this.closeModal();
37485
37575
  }
37576
+ else if (ev.key === 'Enter') {
37577
+ const shouldClose = this.pendingEnterTarget === ev.currentTarget;
37578
+ this.pendingEnterTarget = null;
37579
+ if (shouldClose) {
37580
+ this.closeModal();
37581
+ }
37582
+ }
37486
37583
  } }, option.text))))));
37487
37584
  }
37488
37585
  renderCheckboxOptions() {
@@ -37495,7 +37592,7 @@ class SelectModal {
37495
37592
  } }, option.text))));
37496
37593
  }
37497
37594
  render() {
37498
- return (hAsync(Host, { key: 'f8a4cd6ff23ff01eaa1bdaf3c046814e7b30b23b', class: getIonMode$1(this) }, hAsync("ion-header", { key: '9e29a7e57ad5cf332641111882f16852187ec8ba' }, hAsync("ion-toolbar", { key: 'e6af5d6eabbf4b10799fc8a0b8f91d29b12d41f5' }, this.header !== undefined && hAsync("ion-title", { key: '6056e52d15dbf307571d25e0305d67228a79237d' }, this.header), hAsync("ion-buttons", { key: 'c9aa4fb2e21a93f3a95c5a8f0ba8b7d5553c5a72', slot: "end" }, hAsync("ion-button", { key: '5ffbf512719bcb053b652fc96b1b6154d0593095', onClick: () => this.closeModal() }, this.cancelText)))), hAsync("ion-content", { key: '0ec9098798a4e6de7a83a0a7e9d10bdcd7c98a78' }, hAsync("ion-list", { key: 'd60b1700d3c2f8655951632de810900707a101f0' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
37595
+ return (hAsync(Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: getIonMode$1(this) }, hAsync("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, hAsync("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && hAsync("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), hAsync("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, hAsync("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), hAsync("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, hAsync("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
37499
37596
  }
37500
37597
  get el() { return getElement(this); }
37501
37598
  static get style() { return {
@@ -37558,6 +37655,10 @@ const selectPopoverMdCss = () => `.sc-ion-select-popover-md-h ion-list.sc-ion-se
37558
37655
  class SelectPopover {
37559
37656
  constructor(hostRef) {
37560
37657
  registerInstance(this, hostRef);
37658
+ // Tracks the option that received Enter-keydown so keyup only
37659
+ // dismisses when the press started on the same option. Prevents
37660
+ // Enter on the triggering ion-select from auto-dismissing.
37661
+ this.pendingEnterTarget = null;
37561
37662
  /**
37562
37663
  * An array of options for the popover
37563
37664
  */
@@ -37633,21 +37734,28 @@ class SelectPopover {
37633
37734
  return (hAsync("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (hAsync("ion-item", { class: Object.assign({
37634
37735
  // TODO FW-4784
37635
37736
  'item-radio-checked': option.value === checked
37636
- }, getClassMap(option.cssClass)) }, hAsync("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyUp: (ev) => {
37737
+ }, getClassMap(option.cssClass)) }, hAsync("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
37738
+ if (ev.key === 'Enter' && !ev.repeat) {
37739
+ this.pendingEnterTarget = ev.currentTarget;
37740
+ }
37741
+ }, onKeyUp: (ev) => {
37637
37742
  if (ev.key === ' ') {
37638
- /**
37639
- * Selecting a radio option with keyboard navigation,
37640
- * either through the Enter or Space keys, should
37641
- * dismiss the popover.
37642
- */
37743
+ // Space selects and dismisses in one press.
37643
37744
  this.dismissParentPopover();
37644
37745
  }
37746
+ else if (ev.key === 'Enter') {
37747
+ const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
37748
+ this.pendingEnterTarget = null;
37749
+ if (shouldDismiss) {
37750
+ this.dismissParentPopover();
37751
+ }
37752
+ }
37645
37753
  } }, option.text))))));
37646
37754
  }
37647
37755
  render() {
37648
37756
  const { header, message, options, subHeader } = this;
37649
37757
  const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
37650
- return (hAsync(Host, { key: '0c9845a40d3fc392b0a7d64e2a6ed27d94bb7634', class: getIonMode$1(this) }, hAsync("ion-list", { key: '84a30f6661b0f8c00e6fa199658ed2adbcf27358' }, header !== undefined && hAsync("ion-list-header", { key: '13f5f56bbfbc06751fa516291a2da72629b60ece' }, header), hasSubHeaderOrMessage && (hAsync("ion-item", { key: '3d39d18e720e798bbde334e79e6832091c7dfb81' }, hAsync("ion-label", { key: 'd3051b0d140120b44bf5e79572f6f287e7cfb03a', class: "ion-text-wrap" }, subHeader !== undefined && hAsync("h3", { key: 'b16805956f3316f8ec703c123b76f717488e8637' }, subHeader), message !== undefined && hAsync("p", { key: '2215ac4ab4146a14e75a79192e319a8016286b5f' }, message)))), this.renderOptions(options))));
37758
+ return (hAsync(Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: getIonMode$1(this) }, hAsync("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && hAsync("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (hAsync("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, hAsync("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && hAsync("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && hAsync("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
37651
37759
  }
37652
37760
  get el() { return getElement(this); }
37653
37761
  static get style() { return {