voyager-ionic-core 8.8.2 → 8.8.5

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 (103) hide show
  1. package/components/ion-action-sheet.js +1 -1
  2. package/components/ion-checkbox.js +1 -1
  3. package/components/ion-content.js +1 -1
  4. package/components/ion-datetime.js +1 -1
  5. package/components/ion-input-otp.js +1 -1
  6. package/components/ion-modal.js +1 -1
  7. package/components/ion-picker-column.js +1 -1
  8. package/components/ion-radio-group.js +1 -1
  9. package/components/ion-select-modal.js +1 -1
  10. package/components/ion-select-popover.js +1 -1
  11. package/components/ion-select.js +1 -1
  12. package/components/p-0z8QSI5b.js +4 -0
  13. package/components/{p-ApmKVjaE.js → p-BGHGpkPX.js} +1 -1
  14. package/components/p-BlNv564p.js +4 -0
  15. package/components/p-D-cP12ZN.js +4 -0
  16. package/components/p-D3Ti70Hx.js +4 -0
  17. package/components/{p-Bk2zuNWT.js → p-DvOO1fxp.js} +1 -1
  18. package/components/p-FBcnjE5W.js +4 -0
  19. package/components/p-SBseW5KJ.js +4 -0
  20. package/css/palettes/dark.always.css +1 -1
  21. package/css/palettes/dark.always.css.map +1 -1
  22. package/css/palettes/dark.class.css +1 -1
  23. package/css/palettes/dark.class.css.map +1 -1
  24. package/css/palettes/dark.system.css +1 -1
  25. package/css/palettes/dark.system.css.map +1 -1
  26. package/css/palettes/high-contrast-dark.always.css +1 -1
  27. package/css/palettes/high-contrast-dark.always.css.map +1 -1
  28. package/css/palettes/high-contrast-dark.class.css +1 -1
  29. package/css/palettes/high-contrast-dark.class.css.map +1 -1
  30. package/css/palettes/high-contrast-dark.system.css +1 -1
  31. package/css/palettes/high-contrast-dark.system.css.map +1 -1
  32. package/dist/cjs/ion-action-sheet.cjs.entry.js +4 -4
  33. package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
  34. package/dist/cjs/ion-checkbox.cjs.entry.js +39 -32
  35. package/dist/cjs/ion-datetime_3.cjs.entry.js +17 -6
  36. package/dist/cjs/ion-input-otp.cjs.entry.js +21 -6
  37. package/dist/cjs/ion-modal.cjs.entry.js +99 -45
  38. package/dist/cjs/ion-picker-column.cjs.entry.js +4 -4
  39. package/dist/cjs/ion-radio_2.cjs.entry.js +13 -1
  40. package/dist/cjs/ion-select-modal.cjs.entry.js +18 -7
  41. package/dist/cjs/ion-select_3.cjs.entry.js +18 -7
  42. package/dist/collection/components/action-sheet/action-sheet.js +4 -4
  43. package/dist/collection/components/checkbox/checkbox.js +39 -32
  44. package/dist/collection/components/content/content.css +1 -1
  45. package/dist/collection/components/datetime/datetime.js +17 -6
  46. package/dist/collection/components/input-otp/input-otp.js +21 -6
  47. package/dist/collection/components/modal/modal.js +73 -44
  48. package/dist/collection/components/modal/safe-area-utils.js +27 -2
  49. package/dist/collection/components/picker-column/picker-column.js +5 -5
  50. package/dist/collection/components/radio-group/radio-group.js +13 -1
  51. package/dist/collection/components/radio-group/test/fixtures.js +2 -2
  52. package/dist/collection/components/select-modal/select-modal.js +18 -7
  53. package/dist/collection/components/select-modal/test/fixtures.js +4 -0
  54. package/dist/collection/components/select-popover/select-popover.js +18 -7
  55. package/dist/collection/components/select-popover/test/fixtures.js +4 -0
  56. package/dist/docs.json +1 -1
  57. package/dist/esm/ion-action-sheet.entry.js +4 -4
  58. package/dist/esm/ion-app_8.entry.js +1 -1
  59. package/dist/esm/ion-checkbox.entry.js +39 -32
  60. package/dist/esm/ion-datetime_3.entry.js +17 -6
  61. package/dist/esm/ion-input-otp.entry.js +21 -6
  62. package/dist/esm/ion-modal.entry.js +99 -45
  63. package/dist/esm/ion-picker-column.entry.js +5 -5
  64. package/dist/esm/ion-radio_2.entry.js +13 -1
  65. package/dist/esm/ion-select-modal.entry.js +18 -7
  66. package/dist/esm/ion-select_3.entry.js +18 -7
  67. package/dist/ionic/ionic.esm.js +1 -1
  68. package/dist/ionic/p-078037da.entry.js +4 -0
  69. package/dist/ionic/p-23ec35e4.entry.js +4 -0
  70. package/dist/ionic/p-268a3397.entry.js +4 -0
  71. package/dist/ionic/p-28a9e720.entry.js +4 -0
  72. package/dist/ionic/p-4c67ce4c.entry.js +4 -0
  73. package/dist/ionic/p-87125490.entry.js +4 -0
  74. package/dist/ionic/{p-4dd5e8e0.entry.js → p-8fda6a62.entry.js} +1 -1
  75. package/dist/ionic/{p-9eac4eb1.entry.js → p-aa812c4b.entry.js} +1 -1
  76. package/dist/ionic/p-cb27fe68.entry.js +4 -0
  77. package/dist/ionic/p-ce2edb36.entry.js +4 -0
  78. package/dist/types/components/checkbox/checkbox.d.ts +0 -1
  79. package/dist/types/components/datetime/datetime.d.ts +1 -0
  80. package/dist/types/components/modal/modal.d.ts +41 -3
  81. package/dist/types/components/modal/safe-area-utils.d.ts +16 -0
  82. package/dist/types/components/radio-group/test/fixtures.d.ts +1 -1
  83. package/dist/types/components/select-modal/select-modal.d.ts +1 -0
  84. package/dist/types/components/select-modal/test/fixtures.d.ts +1 -0
  85. package/dist/types/components/select-popover/select-popover.d.ts +1 -0
  86. package/dist/types/components/select-popover/test/fixtures.d.ts +1 -0
  87. package/hydrate/index.js +199 -87
  88. package/hydrate/index.mjs +199 -87
  89. package/package.json +4 -4
  90. package/components/p-1KVKSLu5.js +0 -4
  91. package/components/p-BI7WNErr.js +0 -4
  92. package/components/p-C7AoMl7c.js +0 -4
  93. package/components/p-D6pdfDIA.js +0 -4
  94. package/components/p-cvHphHJA.js +0 -4
  95. package/components/p-e-EDj2CO.js +0 -4
  96. package/dist/ionic/p-2d4eb1b4.entry.js +0 -4
  97. package/dist/ionic/p-3e143d1d.entry.js +0 -4
  98. package/dist/ionic/p-51c11c47.entry.js +0 -4
  99. package/dist/ionic/p-5681dde4.entry.js +0 -4
  100. package/dist/ionic/p-9fae83d8.entry.js +0 -4
  101. package/dist/ionic/p-c744307d.entry.js +0 -4
  102. package/dist/ionic/p-cb78f5a0.entry.js +0 -4
  103. package/dist/ionic/p-e6c5f060.entry.js +0 -4
@@ -473,7 +473,7 @@ const ActionSheet = class {
473
473
  if (isRadio) {
474
474
  htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
475
475
  }
476
- return (index.h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), { 'action-sheet-selected': isActiveRadio }), onClick: () => {
476
+ return (index.h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), (isRadio && { 'action-sheet-selected': isActiveRadio })), onClick: () => {
477
477
  if (isRadio) {
478
478
  this.selectRadioButton(b);
479
479
  }
@@ -488,12 +488,12 @@ const ActionSheet = class {
488
488
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
489
489
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
490
490
  const headerID = `action-sheet-${overlayIndex}-header`;
491
- return (index.h(index.Host, Object.assign({ key: '173fcff5b1da7c33c267de4667591c946b8c8d03', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
491
+ return (index.h(index.Host, Object.assign({ key: 'a56ee2ab59c763036140dbd10306a708c26e3c17', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
492
492
  zIndex: `${20000 + this.overlayIndex}`,
493
- }, class: Object.assign(Object.assign({ [mode]: true }, theme.getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), index.h("ion-backdrop", { key: '521ede659f747864f6c974e09016436eceb7158c', tappable: this.backdropDismiss }), index.h("div", { key: '7a7946fc434bc444f16a70638f5e948c69d33fcd', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: 'bcff39a580489dbafa255842e57aa8602c6d0f18', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, index.h("div", { key: '84bba13ce14261f0f0daa3f9c77648c9e7f36e0e', class: "action-sheet-container" }, index.h("div", { key: 'd9c8ac404fd6719a7adf8cb36549f67616f9a0c4', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (index.h("div", { key: '180433a8ad03ef5c54728a1a8f34715b6921d658', id: headerID, class: {
493
+ }, class: Object.assign(Object.assign({ [mode]: true }, theme.getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), index.h("ion-backdrop", { key: 'c32eb4281fd6348c7d3989a3f509c211263048e6', tappable: this.backdropDismiss }), index.h("div", { key: '7f0123114a876fc7cfff3cfb564aded4a7017797', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: '645b1d5fde39a8907f21983d66e6ecb7a99aa05d', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, index.h("div", { key: 'a78fb02848462d1a4f9356ac4fa1c43a2e5d90e4', class: "action-sheet-container" }, index.h("div", { key: '5e846f53e067b211b985d6e1512b72b9d7c1a3aa', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (index.h("div", { key: 'a90a0e096e1b2fa78b9adb9253c0a517f16e62cb', id: headerID, class: {
494
494
  'action-sheet-title': true,
495
495
  'action-sheet-has-sub-title': this.subHeader !== undefined,
496
- } }, header, this.subHeader && index.h("div", { key: '7138e79e61b1a8f42bc5a9175c57fa2f15d7ec5a', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (index.h("div", { key: 'b617c722f5b8028d73ed34b69310f312c65f34a7', class: "action-sheet-group action-sheet-group-cancel" }, index.h("button", Object.assign({ key: 'd0dd876fc48815df3710413c201c0b445a8e16c0' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), index.h("span", { key: 'e7b960157cc6fc5fe92a12090b2be55e8ae072e4', class: "action-sheet-button-inner" }, cancelButton.icon && (index.h("ion-icon", { key: '05498ffc60cab911dbff0ecbc6168dea59ada9a5', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && index.h("ion-ripple-effect", { key: '3d401346cea301be4ca03671f7370f6f4b0b6bde' })))))), index.h("div", { key: '971f3c5fcc07f36c28eb469a47ec0290c692e139', tabindex: "0", "aria-hidden": "true" })));
496
+ } }, header, this.subHeader && index.h("div", { key: '40f00b12341625c548546de1885b9c9d93bc169c', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (index.h("div", { key: 'ef6974cb63089623df08087274b82745443cee8c', class: "action-sheet-group action-sheet-group-cancel" }, index.h("button", Object.assign({ key: 'b02911a6491d60f9dcb5da7d942392a9e96552c1' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), index.h("span", { key: '1187433e676eda55e52b5ae328a8e68bba22deb6', class: "action-sheet-button-inner" }, cancelButton.icon && (index.h("ion-icon", { key: '079ab2a6bd40b996950053617f1c1c8207ecb1f1', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && index.h("ion-ripple-effect", { key: '3bc473add8ac299f202f8c359d26708872c02f52' })))))), index.h("div", { key: '9b1ae7b4e3649e9b85632f0d65627ca81499e68d', tabindex: "0", "aria-hidden": "true" })));
497
497
  }
498
498
  get el() { return index.getElement(this); }
499
499
  static get watchers() { return {
@@ -159,7 +159,7 @@ Buttons.style = {
159
159
  md: buttonsMdCss()
160
160
  };
161
161
 
162
- 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)}`;
162
+ 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)}`;
163
163
 
164
164
  const Content = class {
165
165
  constructor(hostRef) {
@@ -112,48 +112,55 @@ const Checkbox = class {
112
112
  this.onDivLabelClick = (ev) => {
113
113
  ev.stopPropagation();
114
114
  };
115
- this.onSlotChange = () => {
116
- this.hasLabelContent = this.el.textContent !== '';
117
- };
118
115
  }
119
116
  connectedCallback() {
120
117
  const { el } = this;
121
- // Watch for class changes to update validation state.
122
118
  if (typeof MutationObserver !== 'undefined') {
123
- this.validationObserver = new MutationObserver(() => {
124
- const newIsInvalid = validity.checkInvalidState(el);
125
- if (this.isInvalid !== newIsInvalid) {
126
- this.isInvalid = newIsInvalid;
127
- /**
128
- * Screen readers tend to announce changes
129
- * to `aria-describedby` when the attribute
130
- * is changed during a blur event for a
131
- * native form control.
132
- * However, the announcement can be spotty
133
- * when using a non-native form control
134
- * and `forceUpdate()`.
135
- * This is due to `forceUpdate()` internally
136
- * rescheduling the DOM update to a lower
137
- * priority queue regardless if it's called
138
- * inside a Promise or not, thus causing
139
- * the screen reader to potentially miss the
140
- * change.
141
- * By using a State variable inside a Promise,
142
- * it guarantees a re-render immediately at
143
- * a higher priority.
144
- */
145
- Promise.resolve().then(() => {
146
- this.hintTextId = this.getHintTextId();
147
- });
119
+ this.validationObserver = new MutationObserver((mutations) => {
120
+ // Watch for label content changes
121
+ if (mutations.some((mutation) => mutation.type === 'characterData' || mutation.type === 'childList')) {
122
+ this.hasLabelContent = this.el.textContent !== '';
123
+ }
124
+ // Watch for class changes to update validation state.
125
+ if (mutations.some((mutation) => mutation.type === 'attributes' && mutation.target === el)) {
126
+ const newIsInvalid = validity.checkInvalidState(el);
127
+ if (this.isInvalid !== newIsInvalid) {
128
+ this.isInvalid = newIsInvalid;
129
+ /**
130
+ * Screen readers tend to announce changes
131
+ * to `aria-describedby` when the attribute
132
+ * is changed during a blur event for a
133
+ * native form control.
134
+ * However, the announcement can be spotty
135
+ * when using a non-native form control
136
+ * and `forceUpdate()`.
137
+ * This is due to `forceUpdate()` internally
138
+ * rescheduling the DOM update to a lower
139
+ * priority queue regardless if it's called
140
+ * inside a Promise or not, thus causing
141
+ * the screen reader to potentially miss the
142
+ * change.
143
+ * By using a State variable inside a Promise,
144
+ * it guarantees a re-render immediately at
145
+ * a higher priority.
146
+ */
147
+ Promise.resolve().then(() => {
148
+ this.hintTextId = this.getHintTextId();
149
+ });
150
+ }
148
151
  }
149
152
  });
150
153
  this.validationObserver.observe(el, {
151
154
  attributes: true,
152
155
  attributeFilter: ['class'],
156
+ characterData: true,
157
+ childList: true,
158
+ subtree: true,
153
159
  });
154
160
  }
155
161
  // Always set initial state
156
162
  this.isInvalid = validity.checkInvalidState(el);
163
+ this.hasLabelContent = this.el.textContent !== '';
157
164
  }
158
165
  componentWillLoad() {
159
166
  this.inheritedAttributes = Object.assign({}, helpers.inheritAriaAttributes(this.el));
@@ -203,7 +210,7 @@ const Checkbox = class {
203
210
  helpers.renderHiddenInput(true, el, name, checked ? value : '', disabled);
204
211
  // The host element must have a checkbox role to ensure proper VoiceOver
205
212
  // support in Safari for accessibility.
206
- return (index.h(index.Host, { key: '78eb720868106bcbe8357e50ebae2ab2fce8bdd6', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: theme.createColorClasses(color, {
213
+ return (index.h(index.Host, { key: '0da370f94c5cdf3b08bc9008395558334a300f35', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: theme.createColorClasses(color, {
207
214
  [mode]: true,
208
215
  'in-item': theme.hostContext('ion-item', el),
209
216
  'checkbox-checked': checked,
@@ -213,10 +220,10 @@ const Checkbox = class {
213
220
  [`checkbox-justify-${justify}`]: justify !== undefined,
214
221
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
215
222
  [`checkbox-label-placement-${labelPlacement}`]: true,
216
- }) }, index.h("label", { key: '3bd09a96e126bc72a0589a9c9ff8459cb60e1084', class: "checkbox-wrapper", htmlFor: inputId }, index.h("input", Object.assign({ key: 'f55c566f36dc6b0f069c75dccdebd2c5b3e7385e', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), index.h("div", { key: '11db1139eabfe0b83688c574b81d1a6e8b7ae8c6', class: {
223
+ }) }, index.h("label", { key: '991f1763356671230af119a5fbdc22d0a39974e7', class: "checkbox-wrapper", htmlFor: inputId }, index.h("input", Object.assign({ key: '982f8a7f84d013b272b17607936355d2b6c251f4', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), index.h("div", { key: 'c8f9e8baa20ac68e69fd3c6fcf0e7a26a1084d83', class: {
217
224
  'label-text-wrapper': true,
218
225
  'label-text-wrapper-hidden': !this.hasLabelContent,
219
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, index.h("slot", { key: '6b3dd09e86063e2bc48014a1715cd788038ca01d', onSlotchange: this.onSlotChange }), this.renderHintText()), index.h("div", { key: 'bb2c75c8a893fd81e213c6b2ba807d5cba5a4966', class: "native-wrapper" }, index.h("svg", { key: '028a4c7d211c3697a91743d6242f202209e14c1a', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
226
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, index.h("slot", { key: '6018205e0a73dec826c7881d687f1c2ca8dcb0ab' }), this.renderHintText()), index.h("div", { key: '57530b9d6ff59ee7ab98f960cd65d66ee87cfd1d', class: "native-wrapper" }, index.h("svg", { key: '63d719154ff44459e9ca448e3f5d7de94d9ab248', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
220
227
  }
221
228
  getSVGPath(mode, indeterminate) {
222
229
  let path = indeterminate ? (index.h("path", { d: "M6 12L18 12", part: "mark" })) : (index.h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -813,6 +813,12 @@ const Datetime = class {
813
813
  this.el.classList.add('datetime-ready');
814
814
  });
815
815
  };
816
+ this.loadTimeoutCleanup = () => {
817
+ if (this.loadTimeout) {
818
+ clearTimeout(this.loadTimeout);
819
+ this.loadTimeout = undefined;
820
+ }
821
+ };
816
822
  this.processValue = (value) => {
817
823
  const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
818
824
  const valueToProcess = hasValue ? data.parseDate(value) : this.defaultParts;
@@ -960,9 +966,10 @@ const Datetime = class {
960
966
  if (!prevMonth) {
961
967
  return;
962
968
  }
969
+ const left = prevMonth.offsetWidth * 2;
963
970
  calendarBodyRef.scrollTo({
964
971
  top: 0,
965
- left: 0,
972
+ left: left * (dir.isRTL(this.el) ? 1 : -1),
966
973
  behavior: 'smooth',
967
974
  });
968
975
  };
@@ -1084,15 +1091,16 @@ const Datetime = class {
1084
1091
  }
1085
1092
  connectedCallback() {
1086
1093
  this.clearFocusVisible = focusVisible.startFocusVisible(this.el).destroy;
1094
+ this.loadTimeout = setTimeout(() => {
1095
+ this.ensureReadyIfVisible();
1096
+ }, 100);
1087
1097
  }
1088
1098
  disconnectedCallback() {
1089
1099
  if (this.clearFocusVisible) {
1090
1100
  this.clearFocusVisible();
1091
1101
  this.clearFocusVisible = undefined;
1092
1102
  }
1093
- if (this.loadTimeout) {
1094
- clearTimeout(this.loadTimeout);
1095
- }
1103
+ this.loadTimeoutCleanup();
1096
1104
  }
1097
1105
  initializeListeners() {
1098
1106
  this.initializeCalendarListener();
@@ -1140,7 +1148,10 @@ const Datetime = class {
1140
1148
  * we still initialize listeners and mark the component as ready.
1141
1149
  *
1142
1150
  * We schedule this after everything has had a chance to run.
1151
+ *
1152
+ * We also clean up the load timeout to ensure that we don't have multiple timeouts running.
1143
1153
  */
1154
+ this.loadTimeoutCleanup();
1144
1155
  this.loadTimeout = setTimeout(() => {
1145
1156
  this.ensureReadyIfVisible();
1146
1157
  }, 100);
@@ -1899,7 +1910,7 @@ const Datetime = class {
1899
1910
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1900
1911
  const hasWheelVariant = hasDatePresentation && preferWheel;
1901
1912
  helpers.renderHiddenInput(true, el, name, data.formatValue(value), disabled);
1902
- return (index.h(index.Host, { key: '191a6d7ce7bc2d57bfaaebd8aee31e3c36f2b56a', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1913
+ return (index.h(index.Host, { key: '59e0811aa273e88dfb8e4b703e6824088a457380', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1903
1914
  [mode]: true,
1904
1915
  ['datetime-readonly']: readonly,
1905
1916
  ['datetime-disabled']: disabled,
@@ -1909,7 +1920,7 @@ const Datetime = class {
1909
1920
  [`datetime-size-${size}`]: true,
1910
1921
  [`datetime-prefer-wheel`]: hasWheelVariant,
1911
1922
  [`datetime-grid`]: isGridStyle,
1912
- })) }, index.h("div", { key: '5c75290394cf7dc37c7dcba6372af003a50a9c04', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1923
+ })) }, index.h("div", { key: '3753ff3dde3085070916c3de83687a219a49e553', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1913
1924
  }
1914
1925
  get el() { return index.getElement(this); }
1915
1926
  static get watchers() { return {
@@ -142,9 +142,18 @@ const InputOTP = class {
142
142
  * - Tab: Allows normal tab navigation between components
143
143
  */
144
144
  this.onKeyDown = (index) => (event) => {
145
- const { length } = this;
145
+ const { disabled, length, readonly } = this;
146
146
  const rtl = dir.isRTL(this.el);
147
147
  const input = event.target;
148
+ if (disabled) {
149
+ return;
150
+ }
151
+ if (readonly) {
152
+ if (event.key === 'Backspace' || event.key === 'Delete') {
153
+ event.preventDefault();
154
+ return;
155
+ }
156
+ }
148
157
  // Meta shortcuts are used to copy, paste, and select text
149
158
  // We don't want to handle these keys here
150
159
  const metaShortcuts = ['a', 'c', 'v', 'x', 'r', 'z', 'y'];
@@ -207,10 +216,13 @@ const InputOTP = class {
207
216
  */
208
217
  this.onInput = (index) => (event) => {
209
218
  var _a, _b;
210
- const { length, validKeyPattern } = this;
219
+ const { disabled, length, readonly, validKeyPattern } = this;
211
220
  const input = event.target;
212
221
  const value = input.value;
213
222
  const previousValue = this.previousInputValues[index] || '';
223
+ if (disabled || readonly) {
224
+ return;
225
+ }
214
226
  // 1. Autofill handling
215
227
  // If the length of the value increases by more than 1 from the previous
216
228
  // value, treat this as autofill. This is to prevent the case where the
@@ -329,8 +341,11 @@ const InputOTP = class {
329
341
  */
330
342
  this.onPaste = (event) => {
331
343
  var _a, _b;
332
- const { inputRefs, length, validKeyPattern } = this;
344
+ const { disabled, inputRefs, length, readonly, validKeyPattern } = this;
333
345
  event.preventDefault();
346
+ if (disabled || readonly) {
347
+ return;
348
+ }
334
349
  const pastedText = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
335
350
  // If there is no pasted text, still emit the input change event
336
351
  // because this is how the native input element behaves
@@ -617,7 +632,7 @@ const InputOTP = class {
617
632
  const tabbableIndex = this.getTabbableIndex();
618
633
  const pattern = this.getPattern();
619
634
  const hasDescription = ((_b = (_a = el.querySelector('.input-otp-description')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== '';
620
- return (index.h(index.Host, { key: 'f15a29fb17b681ef55885ca36d3d5f899cbaca83', class: theme.createColorClasses(color, {
635
+ return (index.h(index.Host, { key: '5c1386ae6b8038ec33ca94fd818c9353b1b37f75', class: theme.createColorClasses(color, {
621
636
  [mode]: true,
622
637
  'has-focus': hasFocus,
623
638
  [`input-otp-size-${size}`]: true,
@@ -625,10 +640,10 @@ const InputOTP = class {
625
640
  [`input-otp-fill-${fill}`]: true,
626
641
  'input-otp-disabled': disabled,
627
642
  'input-otp-readonly': readonly,
628
- }) }, index.h("div", Object.assign({ key: 'd7e1d4edd8aafcf2ed4313301287282e90fc7e82', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index$1) => (index.h(index.Fragment, null, index.h("div", { class: "native-wrapper" }, index.h("input", { class: "native-input", id: `${inputId}-${index$1}`, "aria-label": `Input ${index$1 + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index$1 === tabbableIndex ? 0 : -1, value: inputValues[index$1] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index$1] = el), onInput: this.onInput(index$1), onBlur: this.onBlur, onFocus: this.onFocus(index$1), onKeyDown: this.onKeyDown(index$1), onPaste: this.onPaste })), this.showSeparator(index$1) && index.h("div", { class: "input-otp-separator" }))))), index.h("div", { key: '3724a3159d02860971879a906092f9965f5a7c47', class: {
643
+ }) }, index.h("div", Object.assign({ key: '9a19129688e55095f8386826c73ef3f9744becff', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index$1) => (index.h(index.Fragment, null, index.h("div", { class: "native-wrapper" }, index.h("input", { class: "native-input", id: `${inputId}-${index$1}`, "aria-label": `Input ${index$1 + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index$1 === tabbableIndex ? 0 : -1, value: inputValues[index$1] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index$1] = el), onInput: this.onInput(index$1), onBlur: this.onBlur, onFocus: this.onFocus(index$1), onKeyDown: this.onKeyDown(index$1), onPaste: this.onPaste })), this.showSeparator(index$1) && index.h("div", { class: "input-otp-separator" }))))), index.h("div", { key: '7853819c3610c4691191f1836b947bf4ec17939d', class: {
629
644
  'input-otp-description': true,
630
645
  'input-otp-description-hidden': !hasDescription,
631
- } }, index.h("slot", { key: '11baa2624926a08274508afe0833d9237a8dc35c' }))));
646
+ } }, index.h("slot", { key: 'f4674d47d3d3991f21a0a79321ebc323968071dc' }))));
632
647
  }
633
648
  get el() { return index.getElement(this); }
634
649
  static get watchers() { return {
@@ -1648,6 +1648,12 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1648
1648
  const MODAL_INSET_MIN_WIDTH = 768;
1649
1649
  const MODAL_INSET_MIN_HEIGHT = 600;
1650
1650
  const EDGE_THRESHOLD = 5;
1651
+ /**
1652
+ * CSS values for `--width` / `--height` that are treated as fullscreen
1653
+ * (modal touches the corresponding screen edges). Empty string means the
1654
+ * property was not overridden. See `hasCustomModalDimensions()`.
1655
+ */
1656
+ const FULLSCREEN_SIZE_VALUES = new Set(['', '100%', '100vw', '100vh', '100dvw', '100dvh', '100svw', '100svh']);
1651
1657
  /**
1652
1658
  * Cache for resolved root safe-area-top value, invalidated once per frame.
1653
1659
  */
@@ -1696,6 +1702,22 @@ const getRootSafeAreaTop = () => {
1696
1702
  }
1697
1703
  return value;
1698
1704
  };
1705
+ /**
1706
+ * True when the modal host declares BOTH a non-fullscreen `--width` AND a
1707
+ * non-fullscreen `--height` (i.e. a centered-dialog-like modal that doesn't
1708
+ * touch any screen edge).
1709
+ *
1710
+ * The conservative "both axes" check avoids mis-zeroing safe-area for
1711
+ * partial-custom modals where the modal still touches top/bottom edges
1712
+ * (e.g. only `--width` overridden). Partial cases fall through to the
1713
+ * existing position-based post-animation correction.
1714
+ */
1715
+ const hasCustomModalDimensions = (hostEl) => {
1716
+ const styles = getComputedStyle(hostEl);
1717
+ const width = styles.getPropertyValue('--width').trim();
1718
+ const height = styles.getPropertyValue('--height').trim();
1719
+ return !FULLSCREEN_SIZE_VALUES.has(width) && !FULLSCREEN_SIZE_VALUES.has(height);
1720
+ };
1699
1721
  /**
1700
1722
  * Returns the initial safe-area configuration based on modal type.
1701
1723
  * This is called before animation starts and uses configuration-based prediction.
@@ -1730,8 +1752,11 @@ const getInitialSafeAreaConfig = (context) => {
1730
1752
  }
1731
1753
  // On viewports that meet the centered dialog media query breakpoints,
1732
1754
  // regular modals render as centered dialogs (not fullscreen), so they
1733
- // don't touch any screen edges and don't need safe-area insets.
1734
- if (isCenteredDialogViewport()) {
1755
+ // don't touch any screen edges and don't need safe-area insets. Also
1756
+ // applies to phone viewports when the modal declares custom --width and
1757
+ // --height; these don't touch screen edges either, so the initial
1758
+ // prediction must be zero to avoid a post-animation correction flash.
1759
+ if (isCenteredDialogViewport() || context.hasCustomDimensions) {
1735
1760
  return {
1736
1761
  top: '0px',
1737
1762
  bottom: '0px',
@@ -2033,12 +2058,10 @@ const Modal = class {
2033
2058
  // since the viewport may have crossed the centered-dialog breakpoint.
2034
2059
  if (!context.isSheetModal && !context.isCardModal) {
2035
2060
  this.updateSafeAreaOverrides();
2036
- // Re-evaluate fullscreen safe-area padding: clear first, then re-apply
2037
- if (this.wrapperEl) {
2038
- this.wrapperEl.style.removeProperty('height');
2039
- this.wrapperEl.style.removeProperty('padding-bottom');
2040
- }
2041
- this.applyFullscreenSafeArea();
2061
+ // Re-evaluate fullscreen safe-area padding: clear first, then re-apply.
2062
+ const { contentEl, hasFooter } = this.findContentAndFooter();
2063
+ this.clearContentSafeAreaPadding(contentEl);
2064
+ this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
2042
2065
  }
2043
2066
  }, 50); // Debounce to avoid excessive calls during active resizing
2044
2067
  }
@@ -2785,6 +2808,11 @@ const Modal = class {
2785
2808
  }
2786
2809
  /**
2787
2810
  * Creates the context object for safe-area utilities.
2811
+ *
2812
+ * `hasCustomDimensions` is only set by `setInitialSafeAreaOverrides()`
2813
+ * because it is only read by `getInitialSafeAreaConfig()`. Other callers
2814
+ * (resize handler, post-animation update, fullscreen-padding apply) would
2815
+ * pay a `getComputedStyle()` cost for a value they never consult.
2788
2816
  */
2789
2817
  getSafeAreaContext() {
2790
2818
  return {
@@ -2806,7 +2834,7 @@ const Modal = class {
2806
2834
  * sheets to prevent header content from getting double-offset padding).
2807
2835
  */
2808
2836
  setInitialSafeAreaOverrides() {
2809
- const context = this.getSafeAreaContext();
2837
+ const context = Object.assign(Object.assign({}, this.getSafeAreaContext()), { hasCustomDimensions: hasCustomModalDimensions(this.el) });
2810
2838
  const safeAreaConfig = getInitialSafeAreaConfig(context);
2811
2839
  applySafeAreaOverrides(this.el, safeAreaConfig);
2812
2840
  // Set the internal offset property with the resolved root safe-area-top value
@@ -2846,59 +2874,85 @@ const Modal = class {
2846
2874
  applySafeAreaOverrides(el, safeAreaConfig);
2847
2875
  }
2848
2876
  /**
2849
- * Applies padding-bottom to fullscreen modal wrapper to prevent
2850
- * content from overlapping system navigation bar.
2877
+ * Applies safe-area-bottom scroll padding to ion-content inside
2878
+ * fullscreen modals that have no ion-footer. This prevents content
2879
+ * from being hidden behind the system navigation bar while keeping
2880
+ * the modal background edge-to-edge (no visible gap).
2851
2881
  */
2852
2882
  applyFullscreenSafeArea() {
2853
- const { wrapperEl, el } = this;
2854
- if (!wrapperEl)
2855
- return;
2856
2883
  const context = this.getSafeAreaContext();
2857
2884
  if (context.isSheetModal || context.isCardModal)
2858
2885
  return;
2859
- // Check for standard Ionic layout children (ion-content, ion-footer),
2860
- // searching one level deep for wrapped components (e.g.,
2861
- // <app-footer><ion-footer>...</ion-footer></app-footer>).
2862
- // Note: uses a manual loop instead of querySelector(':scope > ...') because
2863
- // Stencil's mock-doc (used in spec tests) does not support :scope.
2864
- let hasContent = false;
2886
+ const { contentEl, hasFooter } = this.findContentAndFooter();
2887
+ this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
2888
+ }
2889
+ /**
2890
+ * Sets --ion-content-safe-area-padding-bottom on the given ion-content
2891
+ * when no footer is present, so ion-content's .inner-scroll includes
2892
+ * safe-area-bottom in its scroll padding. This keeps the modal background
2893
+ * edge-to-edge while ensuring content scrolls clear of the system nav bar.
2894
+ *
2895
+ * --ion-content-safe-area-padding-bottom is an internal CSS property used
2896
+ * only by this code path. It is not part of ion-content's public API and
2897
+ * should not be set by consumers. The default of 0px makes it a no-op
2898
+ * when unset, which is the expected state for ion-content used outside of
2899
+ * a fullscreen modal without a footer.
2900
+ */
2901
+ applyFullscreenSafeAreaTo(contentEl, hasFooter) {
2902
+ // Only apply for standard Ionic layouts (has ion-content but no
2903
+ // ion-footer). When a footer is present it handles its own safe-area
2904
+ // padding. Custom modals with raw HTML are developer-controlled.
2905
+ if (!contentEl || hasFooter)
2906
+ return;
2907
+ contentEl.style.setProperty('--ion-content-safe-area-padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
2908
+ }
2909
+ /**
2910
+ * Removes the internal --ion-content-safe-area-padding-bottom property
2911
+ * from an already-located ion-content. Callers do their own
2912
+ * findContentAndFooter() so they can also read hasFooter if needed.
2913
+ */
2914
+ clearContentSafeAreaPadding(contentEl) {
2915
+ if (!contentEl)
2916
+ return;
2917
+ contentEl.style.removeProperty('--ion-content-safe-area-padding-bottom');
2918
+ }
2919
+ /**
2920
+ * Finds ion-content and ion-footer among direct children and one level of
2921
+ * grandchildren (for wrapped components like <app-footer><ion-footer>).
2922
+ *
2923
+ * Intentionally does NOT use findIonContent() or querySelector() because
2924
+ * those search the full subtree and would match ion-content inside nested
2925
+ * routes/pages. We only want direct slot children (+ one wrapper level).
2926
+ *
2927
+ * Uses a manual loop instead of querySelector(':scope > ...') because
2928
+ * Stencil's mock-doc (used in spec tests) does not support :scope.
2929
+ */
2930
+ findContentAndFooter() {
2931
+ let contentEl = null;
2865
2932
  let hasFooter = false;
2866
- for (const child of Array.from(el.children)) {
2933
+ for (const child of Array.from(this.el.children)) {
2867
2934
  if (child.tagName === 'ION-CONTENT')
2868
- hasContent = true;
2935
+ contentEl = child;
2869
2936
  if (child.tagName === 'ION-FOOTER')
2870
2937
  hasFooter = true;
2871
2938
  for (const grandchild of Array.from(child.children)) {
2872
- if (grandchild.tagName === 'ION-CONTENT')
2873
- hasContent = true;
2939
+ if (grandchild.tagName === 'ION-CONTENT' && !contentEl)
2940
+ contentEl = grandchild;
2874
2941
  if (grandchild.tagName === 'ION-FOOTER')
2875
2942
  hasFooter = true;
2876
2943
  }
2877
2944
  }
2878
- // Only apply wrapper padding for standard Ionic layouts (has ion-content
2879
- // but no ion-footer). Custom modals with raw HTML are fully
2880
- // developer-controlled and should not be modified.
2881
- if (!hasContent || hasFooter)
2882
- return;
2883
- // Reduce wrapper height by safe-area and add equivalent padding so the
2884
- // total visual size stays the same but the flex content area shrinks.
2885
- // Using height + padding instead of box-sizing: border-box avoids
2886
- // breaking custom modals that set --border-width (border-box would
2887
- // include the border inside the height, changing the layout).
2888
- wrapperEl.style.setProperty('height', 'calc(var(--height) - var(--ion-safe-area-bottom, 0px))');
2889
- wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
2945
+ return { contentEl, hasFooter };
2890
2946
  }
2891
2947
  /**
2892
- * Clears all safe-area overrides and padding from wrapper.
2948
+ * Clears all safe-area overrides and padding.
2893
2949
  */
2894
2950
  cleanupSafeAreaOverrides() {
2895
2951
  clearSafeAreaOverrides(this.el);
2896
2952
  // Remove internal sheet offset property
2897
2953
  this.el.style.removeProperty('--ion-modal-offset-top');
2898
- if (this.wrapperEl) {
2899
- this.wrapperEl.style.removeProperty('height');
2900
- this.wrapperEl.style.removeProperty('padding-bottom');
2901
- }
2954
+ const { contentEl } = this.findContentAndFooter();
2955
+ this.clearContentSafeAreaPadding(contentEl);
2902
2956
  }
2903
2957
  render() {
2904
2958
  const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
@@ -2907,20 +2961,20 @@ const Modal = class {
2907
2961
  const isCardModal = presentingElement !== undefined && mode === 'ios';
2908
2962
  const isHandleCycle = handleBehavior === 'cycle';
2909
2963
  const isSheetModalWithHandle = isSheetModal && showHandle;
2910
- return (index$3.h(index$3.Host, Object.assign({ key: '1a53e8f87532abccc169ca4b24973a39c5f9ba16', "no-router": true,
2964
+ return (index$3.h(index$3.Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
2911
2965
  // Allow the modal to be navigable when the handle is focusable
2912
2966
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
2913
2967
  zIndex: `${20000 + this.overlayIndex}`,
2914
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: 'fa8e0a436c0d458331402e1850f87af3dc97b582', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: 'f00de6027d3c8b5bc93db3b0f7a50a87628d40bb', class: "modal-shadow" }), index$3.h("div", Object.assign({ key: 'ae5e33bd6c58e541edb2edbca92420ea02dd5175',
2968
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), index$3.h("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
2915
2969
  /*
2916
2970
  role and aria-modal must be used on the
2917
2971
  same element. They must also be set inside the
2918
2972
  shadow DOM otherwise ion-button will not be highlighted
2919
2973
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
2920
2974
  */
2921
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: '141cdd8f8522331f4b764e2a4d79ec6596b1eb3a', class: "modal-handle",
2975
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
2922
2976
  // Prevents the handle from receiving keyboard focus when it does not cycle
2923
- 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) })), index$3.h("slot", { key: '7de20298b61abee67a16d275c9ebd9a25ce7dd26', onSlotchange: this.onSlotChange }))));
2977
+ 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) })), index$3.h("slot", { key: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
2924
2978
  }
2925
2979
  get el() { return index$3.getElement(this); }
2926
2980
  static get watchers() { return {
@@ -450,7 +450,7 @@ const PickerColumn = class {
450
450
  * Because this initial call to scrollActiveItemIntoView has to fire before
451
451
  * the scroll listener is set up, we need to manage the active class manually.
452
452
  */
453
- const oldActive = helpers.getElementRoot(el).querySelector(`.${PICKER_ITEM_ACTIVE_CLASS}`);
453
+ const oldActive = el.querySelector(`.${PICKER_ITEM_ACTIVE_CLASS}`);
454
454
  if (oldActive) {
455
455
  this.setPickerItemActiveState(oldActive, false);
456
456
  }
@@ -550,14 +550,14 @@ const PickerColumn = class {
550
550
  render() {
551
551
  const { color, disabled, isActive, numericInput } = this;
552
552
  const mode = ionicGlobal.getIonMode(this);
553
- return (index.h(index.Host, { key: 'db903fd415f8a2d91994dececca481c1af8ba6a9', class: theme.createColorClasses(color, {
553
+ return (index.h(index.Host, { key: '234c96a501d7ac413b9b0ea56b33017681e25b40', class: theme.createColorClasses(color, {
554
554
  [mode]: true,
555
555
  ['picker-column-active']: isActive,
556
556
  ['picker-column-numeric-input']: numericInput,
557
557
  ['picker-column-disabled']: disabled,
558
- }) }, index.h("slot", { key: '02ce9e1dd7df91afcd50b06416552bcffb5dec98', name: "prefix" }), index.h("div", { key: '6dfd7d2429bec19244a6b1afb4448121963a031b', class: "picker-opts", ref: (el) => {
558
+ }) }, index.h("slot", { key: '9dc15ea0601ddd2cb2e0a745e91e036a8bd96f8b', name: "prefix" }), index.h("div", { key: 'de4fe28ee4bc46b7c0420d6ab0df0e7809443da9', class: "picker-opts", ref: (el) => {
559
559
  this.scrollEl = el;
560
- }, role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": this.getOptionValueText(this.activeItem), "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }, index.h("div", { key: 'e30ce0b9cefbfe4d4441fa33acf595da31855c3f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '8be2bd293c12c6ba720d9b31d0d561a96f42e97d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '8afdcddddabbf646fbb55cb0ba4448309a2c1dd9', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("slot", { key: '6aa0dacc34d6848575ad5b122b9046982308ca43' }), index.h("div", { key: '92ec8a357414c1b779b11d1dd18fb87a7ee63982', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: 'b89457cb74b5907c25594ff6720ac54ca537e933', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '5bbc92e6bc24de08e39873bf08c5b668373ac0f8', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), index.h("slot", { key: 'd7bf2b519214f0f3576a4ca79844ad97827dd97f', name: "suffix" })));
560
+ }, role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": this.getOptionValueText(this.activeItem), "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }, index.h("div", { key: '5297617462cc30e9444039ae032d8bdf718349af', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '55ea39ef867bcb1a11a912d52ecd20cb886c5fb3', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '3496730ce6182ebfd33e0ee4bafc130feb575a31', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("slot", { key: '44c3628aa957d60f799dc7019f72fe8b676c7843' }), index.h("div", { key: '5a1809f6c949678a67e0d4b5bfe93ea335c0161d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '98fd57f1c66dbaebc2db2dd5da142671b3159fd1', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '85590708abddfa885994e549deac64866fec938f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), index.h("slot", { key: 'bb7e674f543696a80fcbfb1f68f2e975826898a6', name: "suffix" })));
561
561
  }
562
562
  get el() { return index.getElement(this); }
563
563
  static get watchers() { return {
@@ -375,6 +375,18 @@ const RadioGroup = class {
375
375
  // to the bottom of the screen
376
376
  ev.preventDefault();
377
377
  }
378
+ // Inside a select interface, Enter commits the focused radio
379
+ // value (matching native <select>). The !ev.repeat guard stops
380
+ // a held Enter on the triggering ion-select from re-committing
381
+ // once focus lands in the opened popover/modal.
382
+ if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
383
+ const previousValue = this.value;
384
+ this.value = current.value;
385
+ if (previousValue !== this.value) {
386
+ this.emitValueChange(ev);
387
+ }
388
+ ev.preventDefault();
389
+ }
378
390
  }
379
391
  }
380
392
  /** @internal */
@@ -407,7 +419,7 @@ const RadioGroup = class {
407
419
  const { label, labelId, el, name, value } = this;
408
420
  const mode = ionicGlobal.getIonMode(this);
409
421
  helpers.renderHiddenInput(true, el, name, value, false);
410
- return (index.h(index.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(), index.h("slot", { key: 'd683b01c1ba34fe843c4b320bce4661a117472a5' })));
422
+ return (index.h(index.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(), index.h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
411
423
  }
412
424
  get el() { return index.getElement(this); }
413
425
  static get watchers() { return {