voyager-ionic-core 8.7.6 → 8.7.11

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 (266) hide show
  1. package/components/button.js +3 -7
  2. package/components/checkbox.js +64 -13
  3. package/components/header.js +42 -4
  4. package/components/index2.js +74 -3
  5. package/components/ion-accordion.js +93 -14
  6. package/components/ion-datetime.js +35 -2
  7. package/components/ion-input.js +6 -13
  8. package/components/ion-select.js +59 -10
  9. package/components/ion-textarea.js +5 -12
  10. package/components/ion-toggle.js +63 -16
  11. package/components/radio-group.js +60 -7
  12. package/components/validity.js +17 -0
  13. package/dist/cjs/{index-CD5Rjp23.js → index-094mMFB-.js} +76 -5
  14. package/dist/cjs/index.cjs.js +3 -3
  15. package/dist/cjs/ion-accordion_2.cjs.entry.js +91 -13
  16. package/dist/cjs/ion-app_8.cjs.entry.js +43 -5
  17. package/dist/cjs/ion-button_2.cjs.entry.js +3 -7
  18. package/dist/cjs/ion-checkbox.cjs.entry.js +61 -12
  19. package/dist/cjs/ion-datetime_3.cjs.entry.js +35 -2
  20. package/dist/cjs/ion-input.cjs.entry.js +6 -13
  21. package/dist/cjs/ion-modal.cjs.entry.js +1 -1
  22. package/dist/cjs/ion-nav_2.cjs.entry.js +1 -1
  23. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  24. package/dist/cjs/ion-radio_2.cjs.entry.js +57 -6
  25. package/dist/cjs/ion-select_3.cjs.entry.js +56 -9
  26. package/dist/cjs/ion-textarea.cjs.entry.js +5 -12
  27. package/dist/cjs/ion-toggle.cjs.entry.js +59 -14
  28. package/dist/cjs/ionic.cjs.js +1 -1
  29. package/dist/cjs/{ios.transition-j9CclgEW.js → ios.transition-BOt_uW73.js} +1 -1
  30. package/dist/cjs/loader.cjs.js +1 -1
  31. package/dist/cjs/{md.transition-CwFyRSfv.js → md.transition-Dt968VXB.js} +1 -1
  32. package/dist/cjs/validity-BpS37YFM.js +19 -0
  33. package/dist/collection/components/accordion/accordion.js +93 -14
  34. package/dist/collection/components/button/button.js +3 -7
  35. package/dist/collection/components/checkbox/checkbox.js +68 -13
  36. package/dist/collection/components/datetime/datetime.js +35 -2
  37. package/dist/collection/components/header/header.ios.css +27 -1
  38. package/dist/collection/components/header/header.js +5 -4
  39. package/dist/collection/components/header/header.utils.js +37 -0
  40. package/dist/collection/components/input/input.js +6 -14
  41. package/dist/collection/components/radio-group/radio-group.js +64 -7
  42. package/dist/collection/components/select/select.js +60 -12
  43. package/dist/collection/components/textarea/textarea.js +5 -13
  44. package/dist/collection/components/toggle/toggle.js +63 -16
  45. package/dist/collection/utils/forms/index.js +1 -0
  46. package/dist/collection/utils/forms/validity.js +15 -0
  47. package/dist/collection/utils/test/playwright/page/utils/set-content.js +7 -0
  48. package/dist/collection/utils/test/playwright/page/utils/spy-on-event.js +32 -0
  49. package/dist/collection/utils/transition/index.js +74 -3
  50. package/dist/docs.json +1 -1
  51. package/dist/esm/{index-D6G2seR8.js → index-r2D9DEro.js} +76 -5
  52. package/dist/esm/index.js +3 -3
  53. package/dist/esm/ion-accordion_2.entry.js +91 -13
  54. package/dist/esm/ion-app_8.entry.js +43 -5
  55. package/dist/esm/ion-button_2.entry.js +3 -7
  56. package/dist/esm/ion-checkbox.entry.js +61 -12
  57. package/dist/esm/ion-datetime_3.entry.js +35 -2
  58. package/dist/esm/ion-input.entry.js +6 -13
  59. package/dist/esm/ion-modal.entry.js +1 -1
  60. package/dist/esm/ion-nav_2.entry.js +1 -1
  61. package/dist/esm/ion-popover.entry.js +1 -1
  62. package/dist/esm/ion-radio_2.entry.js +57 -6
  63. package/dist/esm/ion-select_3.entry.js +56 -9
  64. package/dist/esm/ion-textarea.entry.js +5 -12
  65. package/dist/esm/ion-toggle.entry.js +59 -14
  66. package/dist/esm/ionic.js +1 -1
  67. package/dist/esm/{ios.transition-Bpq9ixwv.js → ios.transition-BDzw0_Hm.js} +1 -1
  68. package/dist/esm/loader.js +1 -1
  69. package/dist/esm/{md.transition-zOA0oanq.js → md.transition-BzDYi3qq.js} +1 -1
  70. package/dist/esm/validity-DJztqcrH.js +17 -0
  71. package/dist/ionic/index.esm.js +1 -1
  72. package/dist/ionic/ionic.esm.js +1 -1
  73. package/dist/ionic/p-40c261a3.entry.js +4 -0
  74. package/dist/ionic/p-43ed1ef5.entry.js +4 -0
  75. package/dist/ionic/p-4e41ea20.entry.js +4 -0
  76. package/dist/ionic/{p-323421af.entry.js → p-5a39a99a.entry.js} +1 -1
  77. package/dist/ionic/p-5fb517e4.entry.js +4 -0
  78. package/dist/ionic/p-7380261c.entry.js +4 -0
  79. package/dist/ionic/{p-9a36e2e7.entry.js → p-95bddd49.entry.js} +1 -1
  80. package/dist/ionic/{p-DPhQmGJN.js → p-C7hRNDhM.js} +1 -1
  81. package/dist/ionic/p-DJztqcrH.js +4 -0
  82. package/dist/ionic/p-DUt5fQmA.js +4 -0
  83. package/dist/ionic/{p-9R1XyICs.js → p-DZRJwG4S.js} +1 -1
  84. package/dist/ionic/p-c19f63d0.entry.js +4 -0
  85. package/dist/ionic/p-cb93126d.entry.js +4 -0
  86. package/dist/ionic/p-d0a2a1ab.entry.js +4 -0
  87. package/dist/ionic/p-d1f54e28.entry.js +4 -0
  88. package/dist/ionic/p-d3014190.entry.js +4 -0
  89. package/dist/ionic/{p-de7b5fa3.entry.js → p-e16b69e1.entry.js} +1 -1
  90. package/dist/ionic/svg/checkbox-outline.svg +1 -0
  91. package/dist/ionic/svg/checkbox-sharp.svg +1 -0
  92. package/dist/ionic/svg/checkbox.svg +1 -0
  93. package/dist/ionic/svg/checkmark-circle-outline.svg +1 -0
  94. package/dist/ionic/svg/checkmark-circle-sharp.svg +1 -0
  95. package/dist/ionic/svg/checkmark-circle.svg +1 -0
  96. package/dist/ionic/svg/checkmark-done-circle-outline.svg +1 -0
  97. package/dist/ionic/svg/checkmark-done-circle-sharp.svg +1 -0
  98. package/dist/ionic/svg/checkmark-done-circle.svg +1 -0
  99. package/dist/ionic/svg/checkmark-done-outline.svg +1 -0
  100. package/dist/ionic/svg/checkmark-done-sharp.svg +1 -0
  101. package/dist/ionic/svg/checkmark-done.svg +1 -0
  102. package/dist/ionic/svg/checkmark-outline.svg +1 -0
  103. package/dist/ionic/svg/checkmark-sharp.svg +1 -0
  104. package/dist/ionic/svg/checkmark.svg +1 -0
  105. package/dist/ionic/svg/chevron-back-circle-outline.svg +1 -0
  106. package/dist/ionic/svg/chevron-back-circle-sharp.svg +1 -0
  107. package/dist/ionic/svg/chevron-back-circle.svg +1 -0
  108. package/dist/ionic/svg/chevron-back-outline.svg +1 -0
  109. package/dist/ionic/svg/chevron-back-sharp.svg +1 -0
  110. package/dist/ionic/svg/chevron-back.svg +1 -0
  111. package/dist/ionic/svg/chevron-collapse-outline.svg +1 -0
  112. package/dist/ionic/svg/chevron-collapse-sharp.svg +1 -0
  113. package/dist/ionic/svg/chevron-collapse.svg +1 -0
  114. package/dist/ionic/svg/chevron-down-circle-outline.svg +1 -0
  115. package/dist/ionic/svg/chevron-down-circle-sharp.svg +1 -0
  116. package/dist/ionic/svg/chevron-down-circle.svg +1 -0
  117. package/dist/ionic/svg/chevron-down-outline.svg +1 -0
  118. package/dist/ionic/svg/chevron-down-sharp.svg +1 -0
  119. package/dist/ionic/svg/chevron-down.svg +1 -0
  120. package/dist/ionic/svg/chevron-expand-outline.svg +1 -0
  121. package/dist/ionic/svg/chevron-expand-sharp.svg +1 -0
  122. package/dist/ionic/svg/chevron-expand.svg +1 -0
  123. package/dist/ionic/svg/chevron-forward-circle-outline.svg +1 -0
  124. package/dist/ionic/svg/chevron-forward-circle-sharp.svg +1 -0
  125. package/dist/ionic/svg/chevron-forward-circle.svg +1 -0
  126. package/dist/ionic/svg/chevron-forward-outline.svg +1 -0
  127. package/dist/ionic/svg/chevron-forward-sharp.svg +1 -0
  128. package/dist/ionic/svg/chevron-forward.svg +1 -0
  129. package/dist/ionic/svg/chevron-up-circle-outline.svg +1 -0
  130. package/dist/ionic/svg/chevron-up-circle-sharp.svg +1 -0
  131. package/dist/ionic/svg/chevron-up-circle.svg +1 -0
  132. package/dist/ionic/svg/chevron-up-outline.svg +1 -0
  133. package/dist/ionic/svg/chevron-up-sharp.svg +1 -0
  134. package/dist/ionic/svg/chevron-up.svg +1 -0
  135. package/dist/ionic/svg/clipboard-outline.svg +1 -0
  136. package/dist/ionic/svg/clipboard-sharp.svg +1 -0
  137. package/dist/ionic/svg/clipboard.svg +1 -0
  138. package/dist/ionic/svg/close-circle-outline.svg +1 -0
  139. package/dist/ionic/svg/close-circle-sharp.svg +1 -0
  140. package/dist/ionic/svg/close-circle.svg +1 -0
  141. package/dist/ionic/svg/close-outline.svg +1 -0
  142. package/dist/ionic/svg/close-sharp.svg +1 -0
  143. package/dist/ionic/svg/close.svg +1 -0
  144. package/dist/ionic/svg/cloud-circle-outline.svg +1 -0
  145. package/dist/ionic/svg/cloud-circle-sharp.svg +1 -0
  146. package/dist/ionic/svg/cloud-circle.svg +1 -0
  147. package/dist/ionic/svg/cloud-done-outline.svg +1 -0
  148. package/dist/ionic/svg/cloud-done-sharp.svg +1 -0
  149. package/dist/ionic/svg/cloud-done.svg +1 -0
  150. package/dist/ionic/svg/cloud-download-outline.svg +1 -0
  151. package/dist/ionic/svg/cloud-download-sharp.svg +1 -0
  152. package/dist/ionic/svg/cloud-download.svg +1 -0
  153. package/dist/ionic/svg/cloud-offline-outline.svg +1 -0
  154. package/dist/ionic/svg/cloud-offline-sharp.svg +1 -0
  155. package/dist/ionic/svg/cloud-offline.svg +1 -0
  156. package/dist/ionic/svg/cloud-outline.svg +1 -0
  157. package/dist/ionic/svg/cloud-sharp.svg +1 -0
  158. package/dist/ionic/svg/cloud-upload-outline.svg +1 -0
  159. package/dist/ionic/svg/cloud-upload-sharp.svg +1 -0
  160. package/dist/ionic/svg/cloud-upload.svg +1 -0
  161. package/dist/ionic/svg/cloud.svg +1 -0
  162. package/dist/ionic/svg/cloudy-night-outline.svg +1 -0
  163. package/dist/ionic/svg/cloudy-night-sharp.svg +1 -0
  164. package/dist/ionic/svg/cloudy-night.svg +1 -0
  165. package/dist/ionic/svg/cloudy-outline.svg +1 -0
  166. package/dist/ionic/svg/cloudy-sharp.svg +1 -0
  167. package/dist/ionic/svg/cloudy.svg +1 -0
  168. package/dist/ionic/svg/code-download-outline.svg +1 -0
  169. package/dist/ionic/svg/code-download-sharp.svg +1 -0
  170. package/dist/ionic/svg/code-download.svg +1 -0
  171. package/dist/ionic/svg/code-outline.svg +1 -0
  172. package/dist/ionic/svg/code-sharp.svg +1 -0
  173. package/dist/ionic/svg/code-slash-outline.svg +1 -0
  174. package/dist/ionic/svg/code-slash-sharp.svg +1 -0
  175. package/dist/ionic/svg/code-slash.svg +1 -0
  176. package/dist/ionic/svg/code-working-outline.svg +1 -0
  177. package/dist/ionic/svg/code-working-sharp.svg +1 -0
  178. package/dist/ionic/svg/code-working.svg +1 -0
  179. package/dist/ionic/svg/code.svg +1 -0
  180. package/dist/ionic/svg/cog-outline.svg +1 -0
  181. package/dist/ionic/svg/cog-sharp.svg +1 -0
  182. package/dist/ionic/svg/cog.svg +1 -0
  183. package/dist/ionic/svg/color-fill-outline.svg +1 -0
  184. package/dist/ionic/svg/color-fill-sharp.svg +1 -0
  185. package/dist/ionic/svg/color-fill.svg +1 -0
  186. package/dist/ionic/svg/color-filter-outline.svg +1 -0
  187. package/dist/ionic/svg/color-filter-sharp.svg +1 -0
  188. package/dist/ionic/svg/color-filter.svg +1 -0
  189. package/dist/ionic/svg/color-palette-outline.svg +1 -0
  190. package/dist/ionic/svg/color-palette-sharp.svg +1 -0
  191. package/dist/ionic/svg/color-palette.svg +1 -0
  192. package/dist/ionic/svg/color-wand-outline.svg +1 -0
  193. package/dist/ionic/svg/color-wand-sharp.svg +1 -0
  194. package/dist/ionic/svg/color-wand.svg +1 -0
  195. package/dist/ionic/svg/compass-outline.svg +1 -0
  196. package/dist/ionic/svg/compass-sharp.svg +1 -0
  197. package/dist/ionic/svg/compass.svg +1 -0
  198. package/dist/ionic/svg/construct-outline.svg +1 -0
  199. package/dist/ionic/svg/construct-sharp.svg +1 -0
  200. package/dist/ionic/svg/construct.svg +1 -0
  201. package/dist/ionic/svg/contract-outline.svg +1 -0
  202. package/dist/ionic/svg/contract-sharp.svg +1 -0
  203. package/dist/ionic/svg/contract.svg +1 -0
  204. package/dist/ionic/svg/contrast-outline.svg +1 -0
  205. package/dist/ionic/svg/contrast-sharp.svg +1 -0
  206. package/dist/ionic/svg/contrast.svg +1 -0
  207. package/dist/ionic/svg/copy-outline.svg +1 -0
  208. package/dist/ionic/svg/copy-sharp.svg +1 -0
  209. package/dist/ionic/svg/copy.svg +1 -0
  210. package/dist/ionic/svg/create-outline.svg +1 -0
  211. package/dist/ionic/svg/create-sharp.svg +1 -0
  212. package/dist/ionic/svg/create.svg +1 -0
  213. package/dist/ionic/svg/crop-outline.svg +1 -0
  214. package/dist/ionic/svg/crop-sharp.svg +1 -0
  215. package/dist/ionic/svg/crop.svg +1 -0
  216. package/dist/ionic/svg/cube-outline.svg +1 -0
  217. package/dist/ionic/svg/cube-sharp.svg +1 -0
  218. package/dist/ionic/svg/cube.svg +1 -0
  219. package/dist/ionic/svg/cut-outline.svg +1 -0
  220. package/dist/ionic/svg/cut-sharp.svg +1 -0
  221. package/dist/ionic/svg/cut.svg +1 -0
  222. package/dist/ionic/svg/desktop-outline.svg +1 -0
  223. package/dist/ionic/svg/desktop-sharp.svg +1 -0
  224. package/dist/ionic/svg/desktop.svg +1 -0
  225. package/dist/ionic/svg/diamond-outline.svg +1 -0
  226. package/dist/ionic/svg/diamond-sharp.svg +1 -0
  227. package/dist/ionic/svg/diamond.svg +1 -0
  228. package/dist/ionic/svg/dice-outline.svg +1 -0
  229. package/dist/ionic/svg/dice-sharp.svg +1 -0
  230. package/dist/ionic/svg/dice.svg +1 -0
  231. package/dist/ionic/svg/disc-outline.svg +1 -0
  232. package/dist/ionic/svg/disc-sharp.svg +1 -0
  233. package/dist/ionic/svg/disc.svg +1 -0
  234. package/dist/ionic/svg/document-attach-outline.svg +1 -0
  235. package/dist/ionic/svg/document-attach-sharp.svg +1 -0
  236. package/dist/ionic/svg/document-attach.svg +1 -0
  237. package/dist/ionic/svg/document-lock-outline.svg +1 -0
  238. package/dist/ionic/svg/document-lock-sharp.svg +1 -0
  239. package/dist/ionic/svg/document-lock.svg +1 -0
  240. package/dist/ionic/svg/document-outline.svg +1 -0
  241. package/dist/types/components/accordion/accordion.d.ts +18 -1
  242. package/dist/types/components/checkbox/checkbox.d.ts +9 -2
  243. package/dist/types/components/datetime/datetime.d.ts +10 -0
  244. package/dist/types/components/header/header.utils.d.ts +10 -0
  245. package/dist/types/components/input/input.d.ts +0 -4
  246. package/dist/types/components/radio-group/radio-group.d.ts +9 -1
  247. package/dist/types/components/select/select.d.ts +7 -1
  248. package/dist/types/components/textarea/textarea.d.ts +0 -4
  249. package/dist/types/components/toggle/toggle.d.ts +7 -2
  250. package/dist/types/utils/forms/index.d.ts +1 -0
  251. package/dist/types/utils/forms/validity.d.ts +10 -0
  252. package/dist/types/utils/transition/index.d.ts +9 -0
  253. package/hydrate/index.js +687 -413
  254. package/hydrate/index.mjs +687 -413
  255. package/package.json +4 -4
  256. package/dist/ionic/p-1c8a476d.entry.js +0 -4
  257. package/dist/ionic/p-3355a2ff.entry.js +0 -4
  258. package/dist/ionic/p-4efea47a.entry.js +0 -4
  259. package/dist/ionic/p-62e50f80.entry.js +0 -4
  260. package/dist/ionic/p-785026d7.entry.js +0 -4
  261. package/dist/ionic/p-78c74a3e.entry.js +0 -4
  262. package/dist/ionic/p-7bcfc421.entry.js +0 -4
  263. package/dist/ionic/p-83fc84e7.entry.js +0 -4
  264. package/dist/ionic/p-913a7c1e.entry.js +0 -4
  265. package/dist/ionic/p-CMhMiYSX.js +0 -4
  266. package/dist/ionic/p-c17c0a01.entry.js +0 -4
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
5
5
  import { i as inheritAriaAttributes, a as renderHiddenInput } from './helpers-DEn3pfjm.js';
6
+ import { c as checkInvalidState } from './validity-DJztqcrH.js';
6
7
  import { c as createColorClasses, h as hostContext } from './theme-DiVJyqlX.js';
7
8
  import { b as getIonMode } from './ionic-global-CDrldh-5.js';
8
9
 
@@ -59,6 +60,10 @@ const Checkbox = class {
59
60
  * submitting if the value is invalid.
60
61
  */
61
62
  this.required = false;
63
+ /**
64
+ * Track validation state for proper aria-live announcements.
65
+ */
66
+ this.isInvalid = false;
62
67
  /**
63
68
  * Sets the checked property and emits
64
69
  * the ionChange event. Use this to update the
@@ -74,7 +79,6 @@ const Checkbox = class {
74
79
  };
75
80
  this.toggleChecked = (ev) => {
76
81
  ev.preventDefault();
77
- this.setFocus();
78
82
  this.setChecked(!this.checked);
79
83
  this.indeterminate = false;
80
84
  };
@@ -106,18 +110,63 @@ const Checkbox = class {
106
110
  ev.stopPropagation();
107
111
  };
108
112
  }
113
+ connectedCallback() {
114
+ const { el } = this;
115
+ // Watch for class changes to update validation state.
116
+ if (typeof MutationObserver !== 'undefined') {
117
+ this.validationObserver = new MutationObserver(() => {
118
+ const newIsInvalid = checkInvalidState(el);
119
+ if (this.isInvalid !== newIsInvalid) {
120
+ this.isInvalid = newIsInvalid;
121
+ /**
122
+ * Screen readers tend to announce changes
123
+ * to `aria-describedby` when the attribute
124
+ * is changed during a blur event for a
125
+ * native form control.
126
+ * However, the announcement can be spotty
127
+ * when using a non-native form control
128
+ * and `forceUpdate()`.
129
+ * This is due to `forceUpdate()` internally
130
+ * rescheduling the DOM update to a lower
131
+ * priority queue regardless if it's called
132
+ * inside a Promise or not, thus causing
133
+ * the screen reader to potentially miss the
134
+ * change.
135
+ * By using a State variable inside a Promise,
136
+ * it guarantees a re-render immediately at
137
+ * a higher priority.
138
+ */
139
+ Promise.resolve().then(() => {
140
+ this.hintTextId = this.getHintTextId();
141
+ });
142
+ }
143
+ });
144
+ this.validationObserver.observe(el, {
145
+ attributes: true,
146
+ attributeFilter: ['class'],
147
+ });
148
+ }
149
+ // Always set initial state
150
+ this.isInvalid = checkInvalidState(el);
151
+ }
109
152
  componentWillLoad() {
110
153
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
154
+ this.hintTextId = this.getHintTextId();
155
+ }
156
+ disconnectedCallback() {
157
+ // Clean up validation observer to prevent memory leaks.
158
+ if (this.validationObserver) {
159
+ this.validationObserver.disconnect();
160
+ this.validationObserver = undefined;
161
+ }
111
162
  }
112
163
  /** @internal */
113
164
  async setFocus() {
114
- if (this.focusEl) {
115
- this.focusEl.focus();
116
- }
165
+ this.el.focus();
117
166
  }
118
- getHintTextID() {
119
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
120
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
167
+ getHintTextId() {
168
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
169
+ if (isInvalid && errorText) {
121
170
  return errorTextId;
122
171
  }
123
172
  if (helperText) {
@@ -130,7 +179,7 @@ const Checkbox = class {
130
179
  * This element should only be rendered if hint text is set.
131
180
  */
132
181
  renderHintText() {
133
- const { helperText, errorText, helperTextId, errorTextId } = this;
182
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
134
183
  /**
135
184
  * undefined and empty string values should
136
185
  * be treated as not having helper/error text.
@@ -139,7 +188,7 @@ const Checkbox = class {
139
188
  if (!hasHintText) {
140
189
  return;
141
190
  }
142
- return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
191
+ return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
143
192
  }
144
193
  render() {
145
194
  const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
@@ -149,7 +198,7 @@ const Checkbox = class {
149
198
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
150
199
  // The host element must have a checkbox role to ensure proper VoiceOver
151
200
  // support in Safari for accessibility.
152
- return (h(Host, { key: '26cbe7220e555107200e9b5deeae754aa534a80b', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, class: createColorClasses(color, {
201
+ return (h(Host, { key: 'ae0fbd4b21accbac132e6b85c513512ad9179394', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": 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: createColorClasses(color, {
153
202
  [mode]: true,
154
203
  'in-item': hostContext('ion-item', el),
155
204
  'checkbox-checked': checked,
@@ -159,10 +208,10 @@ const Checkbox = class {
159
208
  [`checkbox-justify-${justify}`]: justify !== undefined,
160
209
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
161
210
  [`checkbox-label-placement-${labelPlacement}`]: true,
162
- }), onClick: this.onClick }, h("label", { key: 'f025cec5ff08e8be4487b9cc0324616ca5dfae2a', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: 'dc53f7e4e240dc2e18556e6350df2b5c3169f553', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, onFocus: () => this.onFocus(), onBlur: () => this.onBlur(), ref: (focusEl) => (this.focusEl = focusEl), required: required }, inheritedAttributes)), h("div", { key: 'a625e9b50c3b617de8bbbfd624d772454fecaf2d', class: {
211
+ }) }, h("label", { key: '7a3d7f3c27dde514f2dbf2e34f4629fad33ec3bf', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4130d77ddf034271fecccda14e101a5a809921b6', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '5daa74f4e62b0947e37764762524001ee42609d9', class: {
163
212
  'label-text-wrapper': true,
164
213
  'label-text-wrapper-hidden': !hasLabelContent,
165
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '87d1a90691327945f4343406706e4ab27f453844' }), this.renderHintText()), h("div", { key: 'b57fed8cdecee4df1ef0d57f157267ee77fac653', class: "native-wrapper" }, h("svg", { key: '13a8aac044d46dc99e3b60a1a643785511f216ac', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
214
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '23ff66138f8c3a2f56f39113fc842d54b2f7952a' }), this.renderHintText()), h("div", { key: 'ab914d9623c19fc46821d5e62db92f1192ebbe7e', class: "native-wrapper" }, h("svg", { key: '66e3f4f5dcaa9756fb0e9452299954f9ed3dcb7b', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
166
215
  }
167
216
  getSVGPath(mode, indeterminate) {
168
217
  let path = indeterminate ? (h("path", { d: "M6 12L18 12", part: "mark" })) : (h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -784,6 +784,28 @@ const Datetime = class {
784
784
  destroyKeyboardMO();
785
785
  }
786
786
  };
787
+ /**
788
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
789
+ * Fallback to ensure the datetime becomes ready even if
790
+ * IntersectionObserver never reports it as intersecting.
791
+ *
792
+ * This is primarily used in environments where the observer
793
+ * might not fire as expected, such as when running under
794
+ * synthetic tests that stub IntersectionObserver.
795
+ */
796
+ this.ensureReadyIfVisible = () => {
797
+ if (this.el.classList.contains('datetime-ready')) {
798
+ return;
799
+ }
800
+ const rect = this.el.getBoundingClientRect();
801
+ if (rect.width === 0 || rect.height === 0) {
802
+ return;
803
+ }
804
+ this.initializeListeners();
805
+ writeTask(() => {
806
+ this.el.classList.add('datetime-ready');
807
+ });
808
+ };
787
809
  this.processValue = (value) => {
788
810
  const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
789
811
  const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
@@ -1101,6 +1123,17 @@ const Datetime = class {
1101
1123
  * triggering the `hiddenIO` observer below.
1102
1124
  */
1103
1125
  raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(intersectionTrackerRef));
1126
+ /**
1127
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
1128
+ * Fallback: If IntersectionObserver never reports that the
1129
+ * datetime is visible but the host clearly has layout, ensure
1130
+ * we still initialize listeners and mark the component as ready.
1131
+ *
1132
+ * We schedule this after everything has had a chance to run.
1133
+ */
1134
+ setTimeout(() => {
1135
+ this.ensureReadyIfVisible();
1136
+ }, 100);
1104
1137
  /**
1105
1138
  * We need to clean up listeners when the datetime is hidden
1106
1139
  * in a popover/modal so that we can properly scroll containers
@@ -1856,7 +1889,7 @@ const Datetime = class {
1856
1889
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1857
1890
  const hasWheelVariant = hasDatePresentation && preferWheel;
1858
1891
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1859
- return (h(Host, { key: '57492534800ea059a7c2bbd9f0059cc0b75ae8d2', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1892
+ return (h(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1860
1893
  [mode]: true,
1861
1894
  ['datetime-readonly']: readonly,
1862
1895
  ['datetime-disabled']: disabled,
@@ -1866,7 +1899,7 @@ const Datetime = class {
1866
1899
  [`datetime-size-${size}`]: true,
1867
1900
  [`datetime-prefer-wheel`]: hasWheelVariant,
1868
1901
  [`datetime-grid`]: isGridStyle,
1869
- })) }, h("div", { key: '97dac5e5195635ac0bc5fb472b9d09e5c3c6bbc3', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1902
+ })) }, h("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1870
1903
  }
1871
1904
  get el() { return getElement(this); }
1872
1905
  static get watchers() { return {
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, i as forceUpdate, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
5
5
  import { c as createNotchController } from './notch-controller-BwelN_JM.js';
6
+ import { c as checkInvalidState } from './validity-DJztqcrH.js';
6
7
  import { d as debounceEvent, i as inheritAriaAttributes, b as inheritAttributes, c as componentOnReady } from './helpers-DEn3pfjm.js';
7
8
  import { c as createSlotMutationController, g as getCounterText } from './input.utils-DrvTa8gz.js';
8
9
  import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
@@ -230,14 +231,6 @@ const Input = class {
230
231
  componentWillLoad() {
231
232
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
232
233
  }
233
- /**
234
- * Checks if the input is in an invalid state based on Ionic validation classes
235
- */
236
- checkInvalidState() {
237
- const hasIonTouched = this.el.classList.contains('ion-touched');
238
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
239
- return hasIonTouched && hasIonInvalid;
240
- }
241
234
  connectedCallback() {
242
235
  const { el } = this;
243
236
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
@@ -245,7 +238,7 @@ const Input = class {
245
238
  // Watch for class changes to update validation state
246
239
  if (typeof MutationObserver !== 'undefined') {
247
240
  this.validationObserver = new MutationObserver(() => {
248
- const newIsInvalid = this.checkInvalidState();
241
+ const newIsInvalid = checkInvalidState(el);
249
242
  if (this.isInvalid !== newIsInvalid) {
250
243
  this.isInvalid = newIsInvalid;
251
244
  // Force a re-render to update aria-describedby immediately
@@ -258,7 +251,7 @@ const Input = class {
258
251
  });
259
252
  }
260
253
  // Always set initial state
261
- this.isInvalid = this.checkInvalidState();
254
+ this.isInvalid = checkInvalidState(el);
262
255
  this.debounceChanged();
263
256
  {
264
257
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -522,7 +515,7 @@ const Input = class {
522
515
  * TODO(FW-5592): Remove hasStartEndSlots condition
523
516
  */
524
517
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
525
- return (h(Host, { key: '8a51f0300d5bc66392f9ab9a6fa0b5d388072a33', class: createColorClasses(this.color, {
518
+ return (h(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses(this.color, {
526
519
  [mode]: true,
527
520
  'has-value': hasValue,
528
521
  'has-focus': hasFocus,
@@ -533,14 +526,14 @@ const Input = class {
533
526
  'in-item': inItem,
534
527
  'in-item-color': hostContext('ion-item.ion-color', this.el),
535
528
  'input-disabled': disabled,
536
- }) }, h("label", { key: '9f8cf88d7d0e27931b51bd9c67f048c7fc6f5703', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '7ad30bf9777774062a6ccf9a3ba804f251eef1bb', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '8af0b0325d101df8eed7d24f2767d6ca4d307319', name: "start" }), h("input", Object.assign({ key: '1c53f7f9fa2567f3df19681cf4e7c21be382eae6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: 'b081d0e1ec1444b4c9cca145fc9cd2ad4a68b3da', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
529
+ }) }, h("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), h("input", Object.assign({ key: '1a1d75b0e414a95c89d5a760757c33548d234aca', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
537
530
  /**
538
531
  * This prevents mobile browsers from
539
532
  * blurring the input when the clear
540
533
  * button is activated.
541
534
  */
542
535
  ev.preventDefault();
543
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '01535299241c3635460c05646420acf62a1ff567', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '480f3eb58b08ae792866a5b9b4c068748c5567cc', name: "end" })), shouldRenderHighlight && h("div", { key: 'a8609cacee88e4a09f1cca65b6a47cb79a56f35e', class: "input-highlight" })), this.renderBottomContent()));
536
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && h("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
544
537
  }
545
538
  get el() { return getElement(this); }
546
539
  static get watchers() { return {
@@ -9,7 +9,7 @@ import { c as createLockController } from './lock-controller-B-hirT0v.js';
9
9
  import { g as getCapacitor } from './capacitor-CFERIeaU.js';
10
10
  import { G as GESTURE, O as OVERLAY_GESTURE_PRIORITY, F as FOCUS_TRAP_DISABLE_CLASS, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-BymNv-BL.js';
11
11
  import { g as getClassMap } from './theme-DiVJyqlX.js';
12
- import { e as deepReady, w as waitForMount } from './index-D6G2seR8.js';
12
+ import { e as deepReady, w as waitForMount } from './index-r2D9DEro.js';
13
13
  import { b as getIonMode } from './ionic-global-CDrldh-5.js';
14
14
  import { KEYBOARD_DID_OPEN } from './keyboard-ywgs5efA.js';
15
15
  import { c as createAnimation } from './animation-Dt8bGnA-.js';
@@ -4,7 +4,7 @@
4
4
  import { r as registerInstance, c as createEvent, e as config, f as printIonWarning, h, g as getElement, d as Host } from './index-C8IsBmNU.js';
5
5
  import { g as getTimeGivenProgression } from './cubic-bezier-hHmYLOfE.js';
6
6
  import { s as shallowEqualStringMap, l as assert } from './helpers-DEn3pfjm.js';
7
- import { l as lifecycle, t as transition, s as setPageHidden, d as LIFECYCLE_WILL_UNLOAD, b as LIFECYCLE_WILL_LEAVE, c as LIFECYCLE_DID_LEAVE } from './index-D6G2seR8.js';
7
+ import { l as lifecycle, t as transition, s as setPageHidden, d as LIFECYCLE_WILL_UNLOAD, b as LIFECYCLE_WILL_LEAVE, c as LIFECYCLE_DID_LEAVE } from './index-r2D9DEro.js';
8
8
  import { b as getIonMode } from './ionic-global-CDrldh-5.js';
9
9
  import { a as attachComponent } from './framework-delegate-BYawdMXj.js';
10
10
 
@@ -8,7 +8,7 @@ import { g as getElementRoot, r as raf, f as addEventListener, h as hasLazyBuild
8
8
  import { c as createLockController } from './lock-controller-B-hirT0v.js';
9
9
  import { b as getIonMode, a as isPlatform } from './ionic-global-CDrldh-5.js';
10
10
  import { g as getClassMap } from './theme-DiVJyqlX.js';
11
- import { e as deepReady, w as waitForMount } from './index-D6G2seR8.js';
11
+ import { e as deepReady, w as waitForMount } from './index-r2D9DEro.js';
12
12
  import { c as createAnimation } from './animation-Dt8bGnA-.js';
13
13
  import './index-ZjP4CjeZ.js';
14
14
  import './hardware-back-button-CPLxO-Ev.js';
@@ -6,6 +6,7 @@ import { f as addEventListener, m as removeEventListener, a as renderHiddenInput
6
6
  import { i as isOptionSelected } from './compare-with-utils-sObYyvOy.js';
7
7
  import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
8
8
  import { b as getIonMode } from './ionic-global-CDrldh-5.js';
9
+ import { c as checkInvalidState } from './validity-DJztqcrH.js';
9
10
 
10
11
  const radioIosCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;max-width:100%;min-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2;-webkit-box-sizing:border-box;box-sizing:border-box}:host(.radio-disabled){pointer-events:none}.radio-icon{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%;contain:layout size style}.radio-icon,.radio-inner{-webkit-box-sizing:border-box;box-sizing:border-box}input{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;margin:0;padding:0;border:0;outline:0;clip:rect(0 0 0 0);opacity:0;overflow:hidden;-webkit-appearance:none;-moz-appearance:none}:host(:focus){outline:none}:host(.in-item){-ms-flex:1 1 0px;flex:1 1 0;width:100%;height:100%}:host([slot=start]),:host([slot=end]){-ms-flex:initial;flex:initial;width:auto}.radio-wrapper{display:-ms-flexbox;display:flex;position:relative;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;min-height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.radio-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.radio-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between) .radio-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.radio-justify-start) .radio-wrapper{-ms-flex-pack:start;justify-content:start}:host(.radio-justify-end) .radio-wrapper{-ms-flex-pack:end;justify-content:end}:host(.radio-alignment-start) .radio-wrapper{-ms-flex-align:start;align-items:start}:host(.radio-alignment-center) .radio-wrapper{-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between),:host(.radio-justify-start),:host(.radio-justify-end),:host(.radio-alignment-start),:host(.radio-alignment-center){display:block}:host(.radio-label-placement-start) .radio-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.radio-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-end) .radio-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse}:host(.radio-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.radio-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px}:host(.radio-label-placement-stacked) .radio-wrapper{-ms-flex-direction:column;flex-direction:column}:host(.radio-label-placement-stacked) .label-text-wrapper{-webkit-transform:scale(0.75);transform:scale(0.75);margin-left:0;margin-right:0;margin-bottom:16px;max-width:calc(100% / 0.75)}:host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host{--color-checked:var(--ion-color-primary, #0054e9)}:host(.ion-color.radio-checked) .radio-inner{border-color:var(--ion-color-base)}.item-radio.item-ios ion-label{-webkit-margin-start:0;margin-inline-start:0}.radio-inner{width:33%;height:50%}:host(.radio-checked) .radio-inner{-webkit-transform:rotate(45deg);transform:rotate(45deg);border-width:0.125rem;border-top-width:0;border-left-width:0;border-style:solid;border-color:var(--color-checked)}:host(.radio-disabled){opacity:0.3}:host(.ion-focused) .radio-icon::after{border-radius:var(--inner-border-radius);top:-8px;display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #1a65eb);content:\"\";opacity:0.2}:host(.ion-focused) .radio-icon::after{inset-inline-start:-9px}.native-wrapper .radio-icon{width:0.9375rem;height:1.5rem}";
11
12
 
@@ -175,6 +176,10 @@ const RadioGroup = class {
175
176
  this.helperTextId = `${this.inputId}-helper-text`;
176
177
  this.errorTextId = `${this.inputId}-error-text`;
177
178
  this.labelId = `${this.inputId}-lbl`;
179
+ /**
180
+ * Track validation state for proper aria-live announcements.
181
+ */
182
+ this.isInvalid = false;
178
183
  /**
179
184
  * If `true`, the radios can be deselected.
180
185
  */
@@ -256,6 +261,52 @@ const RadioGroup = class {
256
261
  this.labelId = label.id = this.name + '-lbl';
257
262
  }
258
263
  }
264
+ // Watch for class changes to update validation state.
265
+ if (typeof MutationObserver !== 'undefined') {
266
+ this.validationObserver = new MutationObserver(() => {
267
+ const newIsInvalid = checkInvalidState(this.el);
268
+ if (this.isInvalid !== newIsInvalid) {
269
+ this.isInvalid = newIsInvalid;
270
+ /**
271
+ * Screen readers tend to announce changes
272
+ * to `aria-describedby` when the attribute
273
+ * is changed during a blur event for a
274
+ * native form control.
275
+ * However, the announcement can be spotty
276
+ * when using a non-native form control
277
+ * and `forceUpdate()`.
278
+ * This is due to `forceUpdate()` internally
279
+ * rescheduling the DOM update to a lower
280
+ * priority queue regardless if it's called
281
+ * inside a Promise or not, thus causing
282
+ * the screen reader to potentially miss the
283
+ * change.
284
+ * By using a State variable inside a Promise,
285
+ * it guarantees a re-render immediately at
286
+ * a higher priority.
287
+ */
288
+ Promise.resolve().then(() => {
289
+ this.hintTextId = this.getHintTextId();
290
+ });
291
+ }
292
+ });
293
+ this.validationObserver.observe(this.el, {
294
+ attributes: true,
295
+ attributeFilter: ['class'],
296
+ });
297
+ }
298
+ // Always set initial state
299
+ this.isInvalid = checkInvalidState(this.el);
300
+ }
301
+ componentWillLoad() {
302
+ this.hintTextId = this.getHintTextId();
303
+ }
304
+ disconnectedCallback() {
305
+ // Clean up validation observer to prevent memory leaks.
306
+ if (this.validationObserver) {
307
+ this.validationObserver.disconnect();
308
+ this.validationObserver = undefined;
309
+ }
259
310
  }
260
311
  getRadios() {
261
312
  return Array.from(this.el.querySelectorAll('ion-radio'));
@@ -331,16 +382,16 @@ const RadioGroup = class {
331
382
  * Renders the helper text or error text values
332
383
  */
333
384
  renderHintText() {
334
- const { helperText, errorText, helperTextId, errorTextId } = this;
385
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
335
386
  const hasHintText = !!helperText || !!errorText;
336
387
  if (!hasHintText) {
337
388
  return;
338
389
  }
339
- return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text" }, errorText)));
390
+ return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", role: "alert" }, isInvalid ? errorText : null)));
340
391
  }
341
- getHintTextID() {
342
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
343
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
392
+ getHintTextId() {
393
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
394
+ if (isInvalid && errorText) {
344
395
  return errorTextId;
345
396
  }
346
397
  if (helperText) {
@@ -352,7 +403,7 @@ const RadioGroup = class {
352
403
  const { label, labelId, el, name, value } = this;
353
404
  const mode = getIonMode(this);
354
405
  renderHiddenInput(true, el, name, value, false);
355
- return (h(Host, { key: '81b8ebc96b2f383c36717f290d2959cc921ad6e8', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, onClick: this.onClick, class: mode }, this.renderHintText(), h("div", { key: '45b09efc10776b889a8f372cba80d25a3fc849da', class: "radio-group-wrapper" }, h("slot", { key: '58714934542c2fdd7396de160364f3f06b32e8f8' }))));
406
+ return (h(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(), h("div", { key: '85045b45a0100a45f3b9a35d1c5a25ec63d525c4', class: "radio-group-wrapper" }, h("slot", { key: '53dacb87ce62398e78771fb2efaf839ab922d946' }))));
356
407
  }
357
408
  get el() { return getElement(this); }
358
409
  static get watchers() { return {
@@ -4,6 +4,7 @@
4
4
  import { r as registerInstance, c as createEvent, f as printIonWarning, h, d as Host, g as getElement, i as forceUpdate } from './index-C8IsBmNU.js';
5
5
  import { c as createNotchController } from './notch-controller-BwelN_JM.js';
6
6
  import { i as isOptionSelected, c as compareOptions } from './compare-with-utils-sObYyvOy.js';
7
+ import { c as checkInvalidState } from './validity-DJztqcrH.js';
7
8
  import { b as inheritAttributes, a as renderHiddenInput, n as focusVisibleElement } from './helpers-DEn3pfjm.js';
8
9
  import { c as popoverController, b as actionSheetController, a as alertController, m as modalController, s as safeCall } from './overlays-BymNv-BL.js';
9
10
  import { i as isRTL } from './dir-C53feagD.js';
@@ -43,6 +44,10 @@ const Select = class {
43
44
  * is applied in both cases.
44
45
  */
45
46
  this.hasFocus = false;
47
+ /**
48
+ * Track validation state for proper aria-live announcements.
49
+ */
50
+ this.isInvalid = false;
46
51
  /**
47
52
  * The text to display on the cancel button.
48
53
  */
@@ -172,9 +177,46 @@ const Select = class {
172
177
  */
173
178
  forceUpdate(this);
174
179
  });
180
+ // Watch for class changes to update validation state.
181
+ if (typeof MutationObserver !== 'undefined') {
182
+ this.validationObserver = new MutationObserver(() => {
183
+ const newIsInvalid = checkInvalidState(this.el);
184
+ if (this.isInvalid !== newIsInvalid) {
185
+ this.isInvalid = newIsInvalid;
186
+ /**
187
+ * Screen readers tend to announce changes
188
+ * to `aria-describedby` when the attribute
189
+ * is changed during a blur event for a
190
+ * native form control.
191
+ * However, the announcement can be spotty
192
+ * when using a non-native form control
193
+ * and `forceUpdate()`.
194
+ * This is due to `forceUpdate()` internally
195
+ * rescheduling the DOM update to a lower
196
+ * priority queue regardless if it's called
197
+ * inside a Promise or not, thus causing
198
+ * the screen reader to potentially miss the
199
+ * change.
200
+ * By using a State variable inside a Promise,
201
+ * it guarantees a re-render immediately at
202
+ * a higher priority.
203
+ */
204
+ Promise.resolve().then(() => {
205
+ this.hintTextId = this.getHintTextId();
206
+ });
207
+ }
208
+ });
209
+ this.validationObserver.observe(el, {
210
+ attributes: true,
211
+ attributeFilter: ['class'],
212
+ });
213
+ }
214
+ // Always set initial state
215
+ this.isInvalid = checkInvalidState(this.el);
175
216
  }
176
217
  componentWillLoad() {
177
218
  this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
219
+ this.hintTextId = this.getHintTextId();
178
220
  }
179
221
  componentDidLoad() {
180
222
  /**
@@ -198,6 +240,11 @@ const Select = class {
198
240
  this.notchController.destroy();
199
241
  this.notchController = undefined;
200
242
  }
243
+ // Clean up validation observer to prevent memory leaks.
244
+ if (this.validationObserver) {
245
+ this.validationObserver.disconnect();
246
+ this.validationObserver = undefined;
247
+ }
201
248
  }
202
249
  /**
203
250
  * Open the select overlay. The overlay is either an alert, action sheet, or popover,
@@ -668,11 +715,11 @@ const Select = class {
668
715
  }
669
716
  renderListbox() {
670
717
  const { disabled, inputId, isExpanded, required } = this;
671
- return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
718
+ return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
672
719
  }
673
- getHintTextID() {
674
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
675
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
720
+ getHintTextId() {
721
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
722
+ if (isInvalid && errorText) {
676
723
  return errorTextId;
677
724
  }
678
725
  if (helperText) {
@@ -684,10 +731,10 @@ const Select = class {
684
731
  * Renders the helper text or error text values
685
732
  */
686
733
  renderHintText() {
687
- const { helperText, errorText, helperTextId, errorTextId } = this;
734
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
688
735
  return [
689
- h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
690
- h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
736
+ h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
737
+ h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
691
738
  ];
692
739
  }
693
740
  /**
@@ -735,7 +782,7 @@ const Select = class {
735
782
  * TODO(FW-5592): Remove hasStartEndSlots condition
736
783
  */
737
784
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
738
- return (h(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses(this.color, {
785
+ return (h(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses(this.color, {
739
786
  [mode]: true,
740
787
  'in-item': inItem,
741
788
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -753,7 +800,7 @@ const Select = class {
753
800
  [`select-justify-${justify}`]: justifyEnabled,
754
801
  [`select-shape-${shape}`]: shape !== undefined,
755
802
  [`select-label-placement-${labelPlacement}`]: true,
756
- }) }, h("label", { key: '0d0c8ec55269adcac625f2899a547f4e7f3e3741', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'f6dfc93c0e23cbe75a2947abde67d842db2dad78', class: "select-wrapper-inner" }, h("slot", { key: '957bfadf9f101f519091419a362d3abdc2be66f6', name: "start" }), h("div", { key: 'ca349202a484e7f2e884533fd330f0b136754f7d', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'f0e62a6533ff1c8f62bd2d27f60b23385c4fa9ed', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'fb840d46bafafb09898ebeebbe8c181906a3d8a2', class: "select-highlight" })), this.renderBottomContent()));
803
+ }) }, h("label", { key: '6005b34a0c50bc4d7653a4276bc232ecd02e083c', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'c7e07aa81ae856c057f16275dd058f37c5670a47', class: "select-wrapper-inner" }, h("slot", { key: '7fc2deefe0424404caacdbbd9e08ed43ba55d28a', name: "start" }), h("div", { key: '157d74ee717b1bc30b5f1c233a09b0c8456aa68e', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'ea66db304528b82bf9317730b6dce3db2612f235', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: '786eb1530b7476f0615d4e7c0bf4e7e4dc66509c', class: "select-highlight" })), this.renderBottomContent()));
757
804
  }
758
805
  get el() { return getElement(this); }
759
806
  static get watchers() { return {
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, i as forceUpdate, w as writeTask, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
5
5
  import { c as createNotchController } from './notch-controller-BwelN_JM.js';
6
+ import { c as checkInvalidState } from './validity-DJztqcrH.js';
6
7
  import { d as debounceEvent, i as inheritAriaAttributes, b as inheritAttributes, c as componentOnReady } from './helpers-DEn3pfjm.js';
7
8
  import { c as createSlotMutationController, g as getCounterText } from './input.utils-DrvTa8gz.js';
8
9
  import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
@@ -190,14 +191,6 @@ const Textarea = class {
190
191
  this.el.click();
191
192
  }
192
193
  }
193
- /**
194
- * Checks if the textarea is in an invalid state based on Ionic validation classes
195
- */
196
- checkValidationState() {
197
- const hasIonTouched = this.el.classList.contains('ion-touched');
198
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
199
- return hasIonTouched && hasIonInvalid;
200
- }
201
194
  connectedCallback() {
202
195
  const { el } = this;
203
196
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
@@ -205,7 +198,7 @@ const Textarea = class {
205
198
  // Watch for class changes to update validation state
206
199
  if (typeof MutationObserver !== 'undefined') {
207
200
  this.validationObserver = new MutationObserver(() => {
208
- const newIsInvalid = this.checkValidationState();
201
+ const newIsInvalid = checkInvalidState(this.el);
209
202
  if (this.isInvalid !== newIsInvalid) {
210
203
  this.isInvalid = newIsInvalid;
211
204
  // Force a re-render to update aria-describedby immediately
@@ -218,7 +211,7 @@ const Textarea = class {
218
211
  });
219
212
  }
220
213
  // Always set initial state
221
- this.isInvalid = this.checkValidationState();
214
+ this.isInvalid = checkInvalidState(this.el);
222
215
  this.debounceChanged();
223
216
  {
224
217
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -482,7 +475,7 @@ const Textarea = class {
482
475
  * TODO(FW-5592): Remove hasStartEndSlots condition
483
476
  */
484
477
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
485
- return (h(Host, { key: '26b46666a92b3f652775bb1c46661f9a30392104', class: createColorClasses(this.color, {
478
+ return (h(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses(this.color, {
486
479
  [mode]: true,
487
480
  'has-value': hasValue,
488
481
  'has-focus': hasFocus,
@@ -491,7 +484,7 @@ const Textarea = class {
491
484
  [`textarea-shape-${shape}`]: shape !== undefined,
492
485
  [`textarea-label-placement-${labelPlacement}`]: true,
493
486
  'textarea-disabled': disabled,
494
- }) }, h("label", { key: '2649da816216959ebe1f34cafd9dedbac20ec3c2', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'dca98593efece1b044dbcda045fa70882d715cb2', class: "textarea-wrapper-inner" }, h("div", { key: '2019daf87fddca5ec0b2e336f0376fd9642bae1b', class: "start-slot-wrapper" }, h("slot", { key: '36c423c394a71d08261705b9d6729e756bf65924', name: "start" })), h("div", { key: '0c3ea34105c7eddfa4094371c5d288c50ed10db3', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: 'ce173b83b16aff43d293fa1edef9b66c6676227b', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), h("div", { key: '756e343cfd208bb5ad9ecf08d77cbb0a9606dc7b', class: "end-slot-wrapper" }, h("slot", { key: '0eb596814a037fa4634ff8c5bac0045540edfe21', name: "end" }))), shouldRenderHighlight && h("div", { key: 'df62f896eb6e0e2d1217aa487c198eb82a52bcb8', class: "textarea-highlight" })), this.renderBottomContent()));
487
+ }) }, h("label", { key: '8a2dd59a60f7469df84018eb0ede3a9ec3862703', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '1bfc368236e3da7a225a45118c27fbfc1fe5fa46', class: "textarea-wrapper-inner" }, h("div", { key: '215cbb2635ff52e31a8973376989b85e7245d40f', class: "start-slot-wrapper" }, h("slot", { key: '9f6b461cdee9d629deb695d2bea054ece2f32305', name: "start" })), h("div", { key: 'c1af35a2d5bc452bebe0b22a26d15ff52b4e9fc8', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '69a69b3cf0932baafbe37e6e846f1a571608d3f2', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), h("div", { key: 'c053ea8b865d0e29763aed2e4939cc9c9e374c15', class: "end-slot-wrapper" }, h("slot", { key: '930aa641833b0df54b9ea10368fc2f46d5f491f6', name: "end" }))), shouldRenderHighlight && h("div", { key: '8d12597d15f5f429d80e8272ea99e64ed924e482', class: "textarea-highlight" })), this.renderBottomContent()));
495
488
  }
496
489
  get el() { return getElement(this); }
497
490
  static get watchers() { return {