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
package/hydrate/index.js CHANGED
@@ -4042,10 +4042,57 @@ const accordionMdCss = ":host{display:block;position:relative;width:100%;backgro
4042
4042
  class Accordion {
4043
4043
  constructor(hostRef) {
4044
4044
  registerInstance(this, hostRef);
4045
- this.updateListener = () => this.updateState(false);
4045
+ this.accordionGroupUpdateHandler = () => {
4046
+ /**
4047
+ * Determine if this update will cause an actual state change.
4048
+ * We only want to mark as "interacted" if the state is changing.
4049
+ */
4050
+ const accordionGroup = this.accordionGroupEl;
4051
+ if (accordionGroup) {
4052
+ const value = accordionGroup.value;
4053
+ const accordionValue = this.value;
4054
+ const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
4055
+ const isExpanded = this.state === 4 /* AccordionState.Expanded */ || this.state === 8 /* AccordionState.Expanding */;
4056
+ const stateWillChange = shouldExpand !== isExpanded;
4057
+ /**
4058
+ * Only mark as interacted if:
4059
+ * 1. This is not the first update we've received with a defined value
4060
+ * 2. The state is actually changing (prevents redundant updates from enabling animations)
4061
+ */
4062
+ if (this.hasReceivedFirstUpdate && stateWillChange) {
4063
+ this.hasInteracted = true;
4064
+ }
4065
+ /**
4066
+ * Only count this as the first update if the group value is defined.
4067
+ * This prevents the initial undefined value from the group's componentDidLoad
4068
+ * from being treated as the first real update.
4069
+ */
4070
+ if (value !== undefined) {
4071
+ this.hasReceivedFirstUpdate = true;
4072
+ }
4073
+ }
4074
+ this.updateState();
4075
+ };
4046
4076
  this.state = 1 /* AccordionState.Collapsed */;
4047
4077
  this.isNext = false;
4048
4078
  this.isPrevious = false;
4079
+ /**
4080
+ * Tracks whether a user-initiated interaction has occurred.
4081
+ * Animations are disabled until the first interaction happens.
4082
+ * This prevents the accordion from animating when it's programmatically
4083
+ * set to an expanded or collapsed state on initial load.
4084
+ */
4085
+ this.hasInteracted = false;
4086
+ /**
4087
+ * Tracks if this accordion has ever been expanded.
4088
+ * Used to prevent the first expansion from animating.
4089
+ */
4090
+ this.hasEverBeenExpanded = false;
4091
+ /**
4092
+ * Tracks if this accordion has received its first update from the group.
4093
+ * Used to distinguish initial programmatic sets from user interactions.
4094
+ */
4095
+ this.hasReceivedFirstUpdate = false;
4049
4096
  /**
4050
4097
  * The value of the accordion. Defaults to an autogenerated
4051
4098
  * value.
@@ -4150,10 +4197,15 @@ class Accordion {
4150
4197
  iconEl.setAttribute('aria-hidden', 'true');
4151
4198
  ionItem.appendChild(iconEl);
4152
4199
  };
4153
- this.expandAccordion = (initialUpdate = false) => {
4200
+ this.expandAccordion = () => {
4154
4201
  const { contentEl, contentElWrapper } = this;
4155
- if (initialUpdate || contentEl === undefined || contentElWrapper === undefined) {
4202
+ /**
4203
+ * If the content elements aren't available yet, just set the state.
4204
+ * This happens on initial render before the DOM is ready.
4205
+ */
4206
+ if (contentEl === undefined || contentElWrapper === undefined) {
4156
4207
  this.state = 4 /* AccordionState.Expanded */;
4208
+ this.hasEverBeenExpanded = true;
4157
4209
  return;
4158
4210
  }
4159
4211
  if (this.state === 4 /* AccordionState.Expanded */) {
@@ -4162,6 +4214,11 @@ class Accordion {
4162
4214
  if (this.currentRaf !== undefined) {
4163
4215
  cancelAnimationFrame(this.currentRaf);
4164
4216
  }
4217
+ /**
4218
+ * Mark that this accordion has been expanded at least once.
4219
+ * This allows subsequent expansions to animate.
4220
+ */
4221
+ this.hasEverBeenExpanded = true;
4165
4222
  if (this.shouldAnimate()) {
4166
4223
  raf(() => {
4167
4224
  this.state = 8 /* AccordionState.Expanding */;
@@ -4179,9 +4236,13 @@ class Accordion {
4179
4236
  this.state = 4 /* AccordionState.Expanded */;
4180
4237
  }
4181
4238
  };
4182
- this.collapseAccordion = (initialUpdate = false) => {
4239
+ this.collapseAccordion = () => {
4183
4240
  const { contentEl } = this;
4184
- if (initialUpdate || contentEl === undefined) {
4241
+ /**
4242
+ * If the content element isn't available yet, just set the state.
4243
+ * This happens on initial render before the DOM is ready.
4244
+ */
4245
+ if (contentEl === undefined) {
4185
4246
  this.state = 1 /* AccordionState.Collapsed */;
4186
4247
  return;
4187
4248
  }
@@ -4216,6 +4277,18 @@ class Accordion {
4216
4277
  * of what is set in the config.
4217
4278
  */
4218
4279
  this.shouldAnimate = () => {
4280
+ /**
4281
+ * Don't animate until after the first user interaction.
4282
+ * This prevents animations on initial load when accordions
4283
+ * start in an expanded or collapsed state programmatically.
4284
+ *
4285
+ * Additionally, don't animate the very first expansion even if
4286
+ * hasInteracted is true. This handles edge cases like React StrictMode
4287
+ * where effects run twice and might incorrectly mark as interacted.
4288
+ */
4289
+ if (!this.hasInteracted || !this.hasEverBeenExpanded) {
4290
+ return false;
4291
+ }
4219
4292
  if (typeof window === 'undefined') {
4220
4293
  return false;
4221
4294
  }
@@ -4232,7 +4305,7 @@ class Accordion {
4232
4305
  }
4233
4306
  return true;
4234
4307
  };
4235
- this.updateState = async (initialUpdate = false) => {
4308
+ this.updateState = async () => {
4236
4309
  const accordionGroup = this.accordionGroupEl;
4237
4310
  const accordionValue = this.value;
4238
4311
  if (!accordionGroup) {
@@ -4241,11 +4314,11 @@ class Accordion {
4241
4314
  const value = accordionGroup.value;
4242
4315
  const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
4243
4316
  if (shouldExpand) {
4244
- this.expandAccordion(initialUpdate);
4317
+ this.expandAccordion();
4245
4318
  this.isNext = this.isPrevious = false;
4246
4319
  }
4247
4320
  else {
4248
- this.collapseAccordion(initialUpdate);
4321
+ this.collapseAccordion();
4249
4322
  /**
4250
4323
  * When using popout or inset,
4251
4324
  * the collapsed accordion items
@@ -4293,14 +4366,14 @@ class Accordion {
4293
4366
  var _a;
4294
4367
  const accordionGroupEl = (this.accordionGroupEl = (_a = this.el) === null || _a === void 0 ? void 0 : _a.closest('ion-accordion-group'));
4295
4368
  if (accordionGroupEl) {
4296
- this.updateState(true);
4297
- addEventListener$1(accordionGroupEl, 'ionValueChange', this.updateListener);
4369
+ this.updateState();
4370
+ addEventListener$1(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
4298
4371
  }
4299
4372
  }
4300
4373
  disconnectedCallback() {
4301
4374
  const accordionGroupEl = this.accordionGroupEl;
4302
4375
  if (accordionGroupEl) {
4303
- removeEventListener(accordionGroupEl, 'ionValueChange', this.updateListener);
4376
+ removeEventListener(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
4304
4377
  }
4305
4378
  }
4306
4379
  componentDidLoad() {
@@ -4324,6 +4397,11 @@ class Accordion {
4324
4397
  const { accordionGroupEl, disabled, readonly, value, state } = this;
4325
4398
  if (disabled || readonly)
4326
4399
  return;
4400
+ /**
4401
+ * Mark that the user has interacted with the accordion.
4402
+ * This enables animations for all future state changes.
4403
+ */
4404
+ this.hasInteracted = true;
4327
4405
  if (accordionGroupEl) {
4328
4406
  /**
4329
4407
  * Because the accordion group may or may
@@ -4344,7 +4422,7 @@ class Accordion {
4344
4422
  const headerPart = expanded ? 'header expanded' : 'header';
4345
4423
  const contentPart = expanded ? 'content expanded' : 'content';
4346
4424
  this.setAria(expanded);
4347
- return (hAsync(Host, { key: '073e1d02c18dcbc20c68648426e87c14750c031d', class: {
4425
+ return (hAsync(Host, { key: '9c90bce01eff7e5774a19f69c872f3761d66cf3c', class: {
4348
4426
  [mode]: true,
4349
4427
  'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
4350
4428
  'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
@@ -4355,7 +4433,7 @@ class Accordion {
4355
4433
  'accordion-disabled': disabled,
4356
4434
  'accordion-readonly': readonly,
4357
4435
  'accordion-animated': this.shouldAnimate(),
4358
- } }, hAsync("div", { key: '9b4cf326de8bb6b4033992903c0c1bfd7eea9bcc', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, hAsync("slot", { key: '464c32a37f64655eacf4218284214f5f30b14a1e', name: "header" })), hAsync("div", { key: '8bb52e6a62d7de0106b253201a89a32e79d9a594', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, hAsync("div", { key: '1d9dfd952ad493754aaeea7a8f625b33c2dd90a0', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, hAsync("slot", { key: '970dfbc55a612d739d0ca3b7b1a08e5c96d0c479', name: "content" })))));
4436
+ } }, hAsync("div", { key: 'cab40d5bcf3c93fd78e70b6d3906a541e725837d', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, hAsync("slot", { key: '672bc7fb3f9e18076b41e20fc9eaeab7cafcf3a2', name: "header" })), hAsync("div", { key: 'fd777ca5b4ab04aa4f44c339d58c8cd987c52bcb', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, hAsync("div", { key: '0aad70a71e2cd2c16b2e98fa0bdd40421d95fe16', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, hAsync("slot", { key: 'd630e10ac7c56b4dbf943b523f26759b83aead55', name: "content" })))));
4359
4437
  }
4360
4438
  static get delegatesFocus() { return true; }
4361
4439
  get el() { return getElement(this); }
@@ -4377,7 +4455,8 @@ class Accordion {
4377
4455
  "toggleIconSlot": [1, "toggle-icon-slot"],
4378
4456
  "state": [32],
4379
4457
  "isNext": [32],
4380
- "isPrevious": [32]
4458
+ "isPrevious": [32],
4459
+ "hasInteracted": [32]
4381
4460
  },
4382
4461
  "$listeners$": undefined,
4383
4462
  "$lazyBundleId$": "-",
@@ -9263,11 +9342,7 @@ class Button {
9263
9342
  target,
9264
9343
  };
9265
9344
  let fill = this.fill;
9266
- /**
9267
- * We check both undefined and null to
9268
- * work around https://github.com/ionic-team/stencil/issues/3586.
9269
- */
9270
- if (fill == null) {
9345
+ if (fill === undefined) {
9271
9346
  fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
9272
9347
  }
9273
9348
  /**
@@ -9280,7 +9355,7 @@ class Button {
9280
9355
  {
9281
9356
  type !== 'button' && this.renderHiddenButton();
9282
9357
  }
9283
- return (hAsync(Host, { key: 'b105ad09215adb3ca2298acdadf0dc9154bbb9b0', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9358
+ return (hAsync(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9284
9359
  [mode]: true,
9285
9360
  [buttonType]: true,
9286
9361
  [`${buttonType}-${expand}`]: expand !== undefined,
@@ -9295,7 +9370,7 @@ class Button {
9295
9370
  'button-disabled': disabled,
9296
9371
  'ion-activatable': true,
9297
9372
  'ion-focusable': true,
9298
- }) }, hAsync(TagType, Object.assign({ key: '66b4e7112bcb9e41d5a723fbbadb0a3104f9ee1d' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), hAsync("span", { key: '1439fc3da280221028dcf7ce8ec9dab273c4d4bb', class: "button-inner" }, hAsync("slot", { key: 'd5269ae1afc87ec7b99746032f59cbae93720a9f', name: "icon-only", onSlotchange: this.slotChanged }), hAsync("slot", { key: '461c83e97aa246aa86d83e14f1e15a288d35041e', name: "start" }), hAsync("slot", { key: '807170d47101f9f6a333dd4ff489c89284f306fe' }), hAsync("slot", { key: 'e67f116dd0349a0d27893e4f3ff0ccef1d402f80', name: "end" })), mode === 'md' && hAsync("ion-ripple-effect", { key: '273f0bd9645a36c1bfd18a5c2ab4f81e22b7b989', type: this.rippleType }))));
9373
+ }) }, hAsync(TagType, Object.assign({ key: 'fadec13053469dd0405bbbc61b70ced568aa4826' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), hAsync("span", { key: '6bf0e5144fb1148002e88038522402b789689d2c', class: "button-inner" }, hAsync("slot", { key: '25da0ca155cfa9e2754842c34f4fd09f576ac2d2', name: "icon-only", onSlotchange: this.slotChanged }), hAsync("slot", { key: '51414065bb11953ec9d818f8d9353589bc9072c5', name: "start" }), hAsync("slot", { key: 'c9b5f8842aeabd20628df2f4600f1257ea913d8d' }), hAsync("slot", { key: '478dd3671c7be1909fc84e672f0fa8dfe6082263', name: "end" })), mode === 'md' && hAsync("ion-ripple-effect", { key: 'e1d55f85a55144d743f58a5914cd116cb065fa8c', type: this.rippleType }))));
9299
9374
  }
9300
9375
  get el() { return getElement(this); }
9301
9376
  static get watchers() { return {
@@ -9610,6 +9685,202 @@ class CardTitle {
9610
9685
  }; }
9611
9686
  }
9612
9687
 
9688
+ /**
9689
+ * A utility to calculate the size of an outline notch
9690
+ * width relative to the content passed. This is used in
9691
+ * components such as `ion-select` with `fill="outline"`
9692
+ * where we need to pass slotted HTML content. This is not
9693
+ * needed when rendering plaintext content because we can
9694
+ * render the plaintext again hidden with `opacity: 0` inside
9695
+ * of the notch. As a result we can rely on the intrinsic size
9696
+ * of the element to correctly compute the notch width. We
9697
+ * cannot do this with slotted content because we cannot project
9698
+ * it into 2 places at once.
9699
+ *
9700
+ * @internal
9701
+ * @param el: The host element
9702
+ * @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
9703
+ * @param getLabelSlot: A function that returns a reference to the slotted content.
9704
+ */
9705
+ const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
9706
+ let notchVisibilityIO;
9707
+ const needsExplicitNotchWidth = () => {
9708
+ const notchSpacerEl = getNotchSpacerEl();
9709
+ if (
9710
+ /**
9711
+ * If the notch is not being used
9712
+ * then we do not need to set the notch width.
9713
+ */
9714
+ notchSpacerEl === undefined ||
9715
+ /**
9716
+ * If either the label property is being
9717
+ * used or the label slot is not defined,
9718
+ * then we do not need to estimate the notch width.
9719
+ */
9720
+ el.label !== undefined ||
9721
+ getLabelSlot() === null) {
9722
+ return false;
9723
+ }
9724
+ return true;
9725
+ };
9726
+ const calculateNotchWidth = () => {
9727
+ if (needsExplicitNotchWidth()) {
9728
+ /**
9729
+ * Run this the frame after
9730
+ * the browser has re-painted the host element.
9731
+ * Otherwise, the label element may have a width
9732
+ * of 0 and the IntersectionObserver will be used.
9733
+ */
9734
+ raf(() => {
9735
+ setNotchWidth();
9736
+ });
9737
+ }
9738
+ };
9739
+ /**
9740
+ * When using a label prop we can render
9741
+ * the label value inside of the notch and
9742
+ * let the browser calculate the size of the notch.
9743
+ * However, we cannot render the label slot in multiple
9744
+ * places so we need to manually calculate the notch dimension
9745
+ * based on the size of the slotted content.
9746
+ *
9747
+ * This function should only be used to set the notch width
9748
+ * on slotted label content. The notch width for label prop
9749
+ * content is automatically calculated based on the
9750
+ * intrinsic size of the label text.
9751
+ */
9752
+ const setNotchWidth = () => {
9753
+ const notchSpacerEl = getNotchSpacerEl();
9754
+ if (notchSpacerEl === undefined) {
9755
+ return;
9756
+ }
9757
+ if (!needsExplicitNotchWidth()) {
9758
+ notchSpacerEl.style.removeProperty('width');
9759
+ return;
9760
+ }
9761
+ const width = getLabelSlot().scrollWidth;
9762
+ if (
9763
+ /**
9764
+ * If the computed width of the label is 0
9765
+ * and notchSpacerEl's offsetParent is null
9766
+ * then that means the element is hidden.
9767
+ * As a result, we need to wait for the element
9768
+ * to become visible before setting the notch width.
9769
+ *
9770
+ * We do not check el.offsetParent because
9771
+ * that can be null if the host element has
9772
+ * position: fixed applied to it.
9773
+ * notchSpacerEl does not have position: fixed.
9774
+ */
9775
+ width === 0 &&
9776
+ notchSpacerEl.offsetParent === null &&
9777
+ win$1 !== undefined &&
9778
+ 'IntersectionObserver' in win$1) {
9779
+ /**
9780
+ * If there is an IO already attached
9781
+ * then that will update the notch
9782
+ * once the element becomes visible.
9783
+ * As a result, there is no need to create
9784
+ * another one.
9785
+ */
9786
+ if (notchVisibilityIO !== undefined) {
9787
+ return;
9788
+ }
9789
+ const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
9790
+ /**
9791
+ * If the element is visible then we
9792
+ * can try setting the notch width again.
9793
+ */
9794
+ if (ev[0].intersectionRatio === 1) {
9795
+ setNotchWidth();
9796
+ io.disconnect();
9797
+ notchVisibilityIO = undefined;
9798
+ }
9799
+ },
9800
+ /**
9801
+ * Set the root to be the host element
9802
+ * This causes the IO callback
9803
+ * to be fired in WebKit as soon as the element
9804
+ * is visible. If we used the default root value
9805
+ * then WebKit would only fire the IO callback
9806
+ * after any animations (such as a modal transition)
9807
+ * finished, and there would potentially be a flicker.
9808
+ */
9809
+ { threshold: 0.01, root: el }));
9810
+ io.observe(notchSpacerEl);
9811
+ return;
9812
+ }
9813
+ /**
9814
+ * If the element is visible then we can set the notch width.
9815
+ * The notch is only visible when the label is scaled,
9816
+ * which is why we multiply the width by 0.75 as this is
9817
+ * the same amount the label element is scaled by in the host CSS.
9818
+ * (See $form-control-label-stacked-scale in ionic.globals.scss).
9819
+ */
9820
+ notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
9821
+ };
9822
+ const destroy = () => {
9823
+ if (notchVisibilityIO) {
9824
+ notchVisibilityIO.disconnect();
9825
+ notchVisibilityIO = undefined;
9826
+ }
9827
+ };
9828
+ return {
9829
+ calculateNotchWidth,
9830
+ destroy,
9831
+ };
9832
+ };
9833
+
9834
+ /**
9835
+ * Uses the compareWith param to compare two values to determine if they are equal.
9836
+ *
9837
+ * @param currentValue The current value of the control.
9838
+ * @param compareValue The value to compare against.
9839
+ * @param compareWith The function or property name to use to compare values.
9840
+ */
9841
+ const compareOptions = (currentValue, compareValue, compareWith) => {
9842
+ if (typeof compareWith === 'function') {
9843
+ return compareWith(currentValue, compareValue);
9844
+ }
9845
+ else if (typeof compareWith === 'string') {
9846
+ return currentValue[compareWith] === compareValue[compareWith];
9847
+ }
9848
+ else {
9849
+ return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
9850
+ }
9851
+ };
9852
+ /**
9853
+ * Compares a value against the current value(s) to determine if it is selected.
9854
+ *
9855
+ * @param currentValue The current value of the control.
9856
+ * @param compareValue The value to compare against.
9857
+ * @param compareWith The function or property name to use to compare values.
9858
+ */
9859
+ const isOptionSelected = (currentValue, compareValue, compareWith) => {
9860
+ if (currentValue === undefined) {
9861
+ return false;
9862
+ }
9863
+ if (Array.isArray(currentValue)) {
9864
+ return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
9865
+ }
9866
+ else {
9867
+ return compareOptions(currentValue, compareValue, compareWith);
9868
+ }
9869
+ };
9870
+
9871
+ /**
9872
+ * Checks if the form element is in an invalid state based on
9873
+ * Ionic validation classes.
9874
+ *
9875
+ * @param el The form element to check.
9876
+ * @returns `true` if the element is invalid, `false` otherwise.
9877
+ */
9878
+ const checkInvalidState = (el) => {
9879
+ const hasIonTouched = el.classList.contains('ion-touched');
9880
+ const hasIonInvalid = el.classList.contains('ion-invalid');
9881
+ return hasIonTouched && hasIonInvalid;
9882
+ };
9883
+
9613
9884
  const checkboxIosCss = ":host{--checkbox-background-checked:var(--ion-color-primary, #0054e9);--border-color-checked:var(--ion-color-primary, #0054e9);--checkmark-color:var(--ion-color-primary-contrast, #fff);--transition:none;display:inline-block;position:relative;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2}: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}:host(.ion-color){--checkbox-background-checked:var(--ion-color-base);--border-color-checked:var(--ion-color-base);--checkmark-color:var(--ion-color-contrast)}.checkbox-wrapper{display:-ms-flexbox;display:flex;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper,:host(.in-item:not(.checkbox-label-placement-stacked):not([slot])) .native-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.checkbox-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.checkbox-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}input{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.checkbox-icon{border-radius:var(--border-radius);position:relative;width:var(--size);height:var(--size);-webkit-transition:var(--transition);transition:var(--transition);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--checkbox-background);-webkit-box-sizing:border-box;box-sizing:border-box}.checkbox-icon path{fill:none;stroke:var(--checkmark-color);stroke-width:var(--checkmark-width);opacity:0}.checkbox-bottom{padding-top:4px;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;font-size:0.75rem;white-space:normal}:host(.checkbox-label-placement-stacked) .checkbox-bottom{font-size:1rem}.checkbox-bottom .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.checkbox-bottom .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}:host(.ion-touched.ion-invalid) .checkbox-bottom .error-text{display:block}:host(.ion-touched.ion-invalid) .checkbox-bottom .helper-text{display:none}:host(.checkbox-label-placement-start) .checkbox-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.checkbox-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.checkbox-label-placement-end) .checkbox-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse;-ms-flex-pack:start;justify-content:start}:host(.checkbox-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.checkbox-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.checkbox-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px;max-width:200px}:host(.checkbox-label-placement-stacked) .checkbox-wrapper{-ms-flex-direction:column;flex-direction:column;text-align:center}:host(.checkbox-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(.checkbox-label-placement-stacked.checkbox-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.checkbox-label-placement-stacked.checkbox-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).checkbox-label-placement-stacked.checkbox-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.checkbox-label-placement-stacked.checkbox-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.checkbox-label-placement-stacked.checkbox-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.checkbox-label-placement-stacked.checkbox-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).checkbox-label-placement-stacked.checkbox-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.checkbox-label-placement-stacked.checkbox-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host(.checkbox-justify-space-between) .checkbox-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.checkbox-justify-start) .checkbox-wrapper{-ms-flex-pack:start;justify-content:start}:host(.checkbox-justify-end) .checkbox-wrapper{-ms-flex-pack:end;justify-content:end}:host(.checkbox-alignment-start) .checkbox-wrapper{-ms-flex-align:start;align-items:start}:host(.checkbox-alignment-center) .checkbox-wrapper{-ms-flex-align:center;align-items:center}:host(.checkbox-justify-space-between),:host(.checkbox-justify-start),:host(.checkbox-justify-end),:host(.checkbox-alignment-start),:host(.checkbox-alignment-center){display:block}:host(.checkbox-checked) .checkbox-icon,:host(.checkbox-indeterminate) .checkbox-icon{border-color:var(--border-color-checked);background:var(--checkbox-background-checked)}:host(.checkbox-checked) .checkbox-icon path,:host(.checkbox-indeterminate) .checkbox-icon path{opacity:1}:host(.checkbox-disabled){pointer-events:none}:host{--border-radius:50%;--border-width:0.125rem;--border-style:solid;--border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.23);--checkbox-background:var(--ion-item-background, var(--ion-background-color, #fff));--size:min(1.375rem, 55.836px);--checkmark-width:1.5px}:host(.checkbox-disabled){opacity:0.3}";
9614
9885
 
9615
9886
  const checkboxMdCss = ":host{--checkbox-background-checked:var(--ion-color-primary, #0054e9);--border-color-checked:var(--ion-color-primary, #0054e9);--checkmark-color:var(--ion-color-primary-contrast, #fff);--transition:none;display:inline-block;position:relative;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2}: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}:host(.ion-color){--checkbox-background-checked:var(--ion-color-base);--border-color-checked:var(--ion-color-base);--checkmark-color:var(--ion-color-contrast)}.checkbox-wrapper{display:-ms-flexbox;display:flex;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper,:host(.in-item:not(.checkbox-label-placement-stacked):not([slot])) .native-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.checkbox-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.checkbox-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}input{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.checkbox-icon{border-radius:var(--border-radius);position:relative;width:var(--size);height:var(--size);-webkit-transition:var(--transition);transition:var(--transition);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--checkbox-background);-webkit-box-sizing:border-box;box-sizing:border-box}.checkbox-icon path{fill:none;stroke:var(--checkmark-color);stroke-width:var(--checkmark-width);opacity:0}.checkbox-bottom{padding-top:4px;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;font-size:0.75rem;white-space:normal}:host(.checkbox-label-placement-stacked) .checkbox-bottom{font-size:1rem}.checkbox-bottom .error-text{display:none;color:var(--ion-color-danger, #c5000f)}.checkbox-bottom .helper-text{display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d))}:host(.ion-touched.ion-invalid) .checkbox-bottom .error-text{display:block}:host(.ion-touched.ion-invalid) .checkbox-bottom .helper-text{display:none}:host(.checkbox-label-placement-start) .checkbox-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.checkbox-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.checkbox-label-placement-end) .checkbox-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse;-ms-flex-pack:start;justify-content:start}:host(.checkbox-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.checkbox-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.checkbox-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px;max-width:200px}:host(.checkbox-label-placement-stacked) .checkbox-wrapper{-ms-flex-direction:column;flex-direction:column;text-align:center}:host(.checkbox-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(.checkbox-label-placement-stacked.checkbox-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.checkbox-label-placement-stacked.checkbox-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).checkbox-label-placement-stacked.checkbox-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.checkbox-label-placement-stacked.checkbox-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.checkbox-label-placement-stacked.checkbox-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.checkbox-label-placement-stacked.checkbox-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).checkbox-label-placement-stacked.checkbox-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.checkbox-label-placement-stacked.checkbox-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host(.checkbox-justify-space-between) .checkbox-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.checkbox-justify-start) .checkbox-wrapper{-ms-flex-pack:start;justify-content:start}:host(.checkbox-justify-end) .checkbox-wrapper{-ms-flex-pack:end;justify-content:end}:host(.checkbox-alignment-start) .checkbox-wrapper{-ms-flex-align:start;align-items:start}:host(.checkbox-alignment-center) .checkbox-wrapper{-ms-flex-align:center;align-items:center}:host(.checkbox-justify-space-between),:host(.checkbox-justify-start),:host(.checkbox-justify-end),:host(.checkbox-alignment-start),:host(.checkbox-alignment-center){display:block}:host(.checkbox-checked) .checkbox-icon,:host(.checkbox-indeterminate) .checkbox-icon{border-color:var(--border-color-checked);background:var(--checkbox-background-checked)}:host(.checkbox-checked) .checkbox-icon path,:host(.checkbox-indeterminate) .checkbox-icon path{opacity:1}:host(.checkbox-disabled){pointer-events:none}:host{--border-radius:calc(var(--size) * .125);--border-width:2px;--border-style:solid;--border-color:rgb(var(--ion-text-color-rgb, 0, 0, 0), 0.6);--checkmark-width:3;--checkbox-background:var(--ion-item-background, var(--ion-background-color, #fff));--transition:background 180ms cubic-bezier(0.4, 0, 0.2, 1);--size:18px}.checkbox-icon path{stroke-dasharray:30;stroke-dashoffset:30}:host(.checkbox-checked) .checkbox-icon path,:host(.checkbox-indeterminate) .checkbox-icon path{stroke-dashoffset:0;-webkit-transition:stroke-dashoffset 90ms linear 90ms;transition:stroke-dashoffset 90ms linear 90ms}:host(.checkbox-disabled) .label-text-wrapper{opacity:0.38}:host(.checkbox-disabled) .native-wrapper{opacity:0.63}";
@@ -9675,6 +9946,10 @@ class Checkbox {
9675
9946
  * submitting if the value is invalid.
9676
9947
  */
9677
9948
  this.required = false;
9949
+ /**
9950
+ * Track validation state for proper aria-live announcements.
9951
+ */
9952
+ this.isInvalid = false;
9678
9953
  /**
9679
9954
  * Sets the checked property and emits
9680
9955
  * the ionChange event. Use this to update the
@@ -9690,7 +9965,6 @@ class Checkbox {
9690
9965
  };
9691
9966
  this.toggleChecked = (ev) => {
9692
9967
  ev.preventDefault();
9693
- this.setFocus();
9694
9968
  this.setChecked(!this.checked);
9695
9969
  this.indeterminate = false;
9696
9970
  };
@@ -9722,18 +9996,29 @@ class Checkbox {
9722
9996
  ev.stopPropagation();
9723
9997
  };
9724
9998
  }
9999
+ connectedCallback() {
10000
+ const { el } = this;
10001
+ // Always set initial state
10002
+ this.isInvalid = checkInvalidState(el);
10003
+ }
9725
10004
  componentWillLoad() {
9726
10005
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
10006
+ this.hintTextId = this.getHintTextId();
10007
+ }
10008
+ disconnectedCallback() {
10009
+ // Clean up validation observer to prevent memory leaks.
10010
+ if (this.validationObserver) {
10011
+ this.validationObserver.disconnect();
10012
+ this.validationObserver = undefined;
10013
+ }
9727
10014
  }
9728
10015
  /** @internal */
9729
10016
  async setFocus() {
9730
- if (this.focusEl) {
9731
- this.focusEl.focus();
9732
- }
10017
+ this.el.focus();
9733
10018
  }
9734
- getHintTextID() {
9735
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
9736
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
10019
+ getHintTextId() {
10020
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
10021
+ if (isInvalid && errorText) {
9737
10022
  return errorTextId;
9738
10023
  }
9739
10024
  if (helperText) {
@@ -9746,7 +10031,7 @@ class Checkbox {
9746
10031
  * This element should only be rendered if hint text is set.
9747
10032
  */
9748
10033
  renderHintText() {
9749
- const { helperText, errorText, helperTextId, errorTextId } = this;
10034
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
9750
10035
  /**
9751
10036
  * undefined and empty string values should
9752
10037
  * be treated as not having helper/error text.
@@ -9755,7 +10040,7 @@ class Checkbox {
9755
10040
  if (!hasHintText) {
9756
10041
  return;
9757
10042
  }
9758
- return (hAsync("div", { class: "checkbox-bottom" }, hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
10043
+ return (hAsync("div", { class: "checkbox-bottom" }, hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
9759
10044
  }
9760
10045
  render() {
9761
10046
  const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
@@ -9765,7 +10050,7 @@ class Checkbox {
9765
10050
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
9766
10051
  // The host element must have a checkbox role to ensure proper VoiceOver
9767
10052
  // support in Safari for accessibility.
9768
- return (hAsync(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$1(color, {
10053
+ return (hAsync(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$1(color, {
9769
10054
  [mode]: true,
9770
10055
  'in-item': hostContext('ion-item', el),
9771
10056
  'checkbox-checked': checked,
@@ -9775,10 +10060,10 @@ class Checkbox {
9775
10060
  [`checkbox-justify-${justify}`]: justify !== undefined,
9776
10061
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
9777
10062
  [`checkbox-label-placement-${labelPlacement}`]: true,
9778
- }), onClick: this.onClick }, hAsync("label", { key: 'f025cec5ff08e8be4487b9cc0324616ca5dfae2a', class: "checkbox-wrapper", htmlFor: inputId }, hAsync("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)), hAsync("div", { key: 'a625e9b50c3b617de8bbbfd624d772454fecaf2d', class: {
10063
+ }) }, hAsync("label", { key: '7a3d7f3c27dde514f2dbf2e34f4629fad33ec3bf', class: "checkbox-wrapper", htmlFor: inputId }, hAsync("input", Object.assign({ key: '4130d77ddf034271fecccda14e101a5a809921b6', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), hAsync("div", { key: '5daa74f4e62b0947e37764762524001ee42609d9', class: {
9779
10064
  'label-text-wrapper': true,
9780
10065
  'label-text-wrapper-hidden': !hasLabelContent,
9781
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: '87d1a90691327945f4343406706e4ab27f453844' }), this.renderHintText()), hAsync("div", { key: 'b57fed8cdecee4df1ef0d57f157267ee77fac653', class: "native-wrapper" }, hAsync("svg", { key: '13a8aac044d46dc99e3b60a1a643785511f216ac', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
10066
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: '23ff66138f8c3a2f56f39113fc842d54b2f7952a' }), this.renderHintText()), hAsync("div", { key: 'ab914d9623c19fc46821d5e62db92f1192ebbe7e', class: "native-wrapper" }, hAsync("svg", { key: '66e3f4f5dcaa9756fb0e9452299954f9ed3dcb7b', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
9782
10067
  }
9783
10068
  getSVGPath(mode, indeterminate) {
9784
10069
  let path = indeterminate ? (hAsync("path", { d: "M6 12L18 12", part: "mark" })) : (hAsync("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -9808,6 +10093,8 @@ class Checkbox {
9808
10093
  "justify": [1],
9809
10094
  "alignment": [1],
9810
10095
  "required": [4],
10096
+ "isInvalid": [32],
10097
+ "hintTextId": [32],
9811
10098
  "setFocus": [64]
9812
10099
  },
9813
10100
  "$listeners$": undefined,
@@ -12952,17 +13239,39 @@ class Datetime {
12952
13239
  destroyKeyboardMO();
12953
13240
  }
12954
13241
  };
12955
- this.processValue = (value) => {
12956
- const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
12957
- const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
12958
- const { minParts, maxParts, workingParts, el } = this;
12959
- this.warnIfIncorrectValueUsage();
12960
- /**
12961
- * Return early if the value wasn't parsed correctly, such as
12962
- * if an improperly formatted date string was provided.
12963
- */
12964
- if (!valueToProcess) {
12965
- return;
13242
+ /**
13243
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
13244
+ * Fallback to ensure the datetime becomes ready even if
13245
+ * IntersectionObserver never reports it as intersecting.
13246
+ *
13247
+ * This is primarily used in environments where the observer
13248
+ * might not fire as expected, such as when running under
13249
+ * synthetic tests that stub IntersectionObserver.
13250
+ */
13251
+ this.ensureReadyIfVisible = () => {
13252
+ if (this.el.classList.contains('datetime-ready')) {
13253
+ return;
13254
+ }
13255
+ const rect = this.el.getBoundingClientRect();
13256
+ if (rect.width === 0 || rect.height === 0) {
13257
+ return;
13258
+ }
13259
+ this.initializeListeners();
13260
+ writeTask(() => {
13261
+ this.el.classList.add('datetime-ready');
13262
+ });
13263
+ };
13264
+ this.processValue = (value) => {
13265
+ const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
13266
+ const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
13267
+ const { minParts, maxParts, workingParts, el } = this;
13268
+ this.warnIfIncorrectValueUsage();
13269
+ /**
13270
+ * Return early if the value wasn't parsed correctly, such as
13271
+ * if an improperly formatted date string was provided.
13272
+ */
13273
+ if (!valueToProcess) {
13274
+ return;
12966
13275
  }
12967
13276
  /**
12968
13277
  * Datetime should only warn of out of bounds values
@@ -13269,6 +13578,17 @@ class Datetime {
13269
13578
  * triggering the `hiddenIO` observer below.
13270
13579
  */
13271
13580
  raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(intersectionTrackerRef));
13581
+ /**
13582
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
13583
+ * Fallback: If IntersectionObserver never reports that the
13584
+ * datetime is visible but the host clearly has layout, ensure
13585
+ * we still initialize listeners and mark the component as ready.
13586
+ *
13587
+ * We schedule this after everything has had a chance to run.
13588
+ */
13589
+ setTimeout(() => {
13590
+ this.ensureReadyIfVisible();
13591
+ }, 100);
13272
13592
  /**
13273
13593
  * We need to clean up listeners when the datetime is hidden
13274
13594
  * in a popover/modal so that we can properly scroll containers
@@ -14024,7 +14344,7 @@ class Datetime {
14024
14344
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
14025
14345
  const hasWheelVariant = hasDatePresentation && preferWheel;
14026
14346
  renderHiddenInput(true, el, name, formatValue(value), disabled);
14027
- return (hAsync(Host, { key: '57492534800ea059a7c2bbd9f0059cc0b75ae8d2', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
14347
+ return (hAsync(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
14028
14348
  [mode]: true,
14029
14349
  ['datetime-readonly']: readonly,
14030
14350
  ['datetime-disabled']: disabled,
@@ -14034,7 +14354,7 @@ class Datetime {
14034
14354
  [`datetime-size-${size}`]: true,
14035
14355
  [`datetime-prefer-wheel`]: hasWheelVariant,
14036
14356
  [`datetime-grid`]: isGridStyle,
14037
- })) }, hAsync("div", { key: '97dac5e5195635ac0bc5fb472b9d09e5c3c6bbc3', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
14357
+ })) }, hAsync("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
14038
14358
  }
14039
14359
  get el() { return getElement(this); }
14040
14360
  static get watchers() { return {
@@ -15229,6 +15549,8 @@ class Grid {
15229
15549
  }
15230
15550
 
15231
15551
  const TRANSITION = 'all 0.2s ease-in-out';
15552
+ const ROLE_NONE = 'none';
15553
+ const ROLE_BANNER = 'banner';
15232
15554
  const cloneElement = (tagName) => {
15233
15555
  const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
15234
15556
  if (getCachedEl !== null) {
@@ -15355,6 +15677,7 @@ const setHeaderActive = (headerIndex, active = true) => {
15355
15677
  const toolbars = headerIndex.toolbars;
15356
15678
  const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
15357
15679
  if (active) {
15680
+ headerEl.setAttribute('role', ROLE_BANNER);
15358
15681
  headerEl.classList.remove('header-collapse-condense-inactive');
15359
15682
  ionTitles.forEach((ionTitle) => {
15360
15683
  if (ionTitle) {
@@ -15363,6 +15686,16 @@ const setHeaderActive = (headerIndex, active = true) => {
15363
15686
  });
15364
15687
  }
15365
15688
  else {
15689
+ /**
15690
+ * There can only be one banner landmark per page.
15691
+ * By default, all ion-headers have the banner role.
15692
+ * This causes an accessibility issue when using a
15693
+ * condensed header since there are two ion-headers
15694
+ * on the page at once (active and inactive).
15695
+ * To solve this, the role needs to be toggled
15696
+ * based on which header is active.
15697
+ */
15698
+ headerEl.setAttribute('role', ROLE_NONE);
15366
15699
  headerEl.classList.add('header-collapse-condense-inactive');
15367
15700
  /**
15368
15701
  * The small title should only be accessed by screen readers
@@ -15422,8 +15755,32 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
15422
15755
  });
15423
15756
  });
15424
15757
  };
15758
+ /**
15759
+ * Get the role type for the ion-header.
15760
+ *
15761
+ * @param isInsideMenu If ion-header is inside ion-menu.
15762
+ * @param isCondensed If ion-header has collapse="condense".
15763
+ * @param mode The current mode.
15764
+ * @returns 'none' if inside ion-menu or if condensed in md
15765
+ * mode, otherwise 'banner'.
15766
+ */
15767
+ const getRoleType = (isInsideMenu, isCondensed, mode) => {
15768
+ // If the header is inside a menu, it should not have the banner role.
15769
+ if (isInsideMenu) {
15770
+ return ROLE_NONE;
15771
+ }
15772
+ /**
15773
+ * Only apply role="none" to `md` mode condensed headers
15774
+ * since the large header is never shown.
15775
+ */
15776
+ if (isCondensed && mode === 'md') {
15777
+ return ROLE_NONE;
15778
+ }
15779
+ // Default to banner role.
15780
+ return ROLE_BANNER;
15781
+ };
15425
15782
 
15426
- const headerIosCss = "ion-header{display:block;position:relative;-ms-flex-order:-1;order:-1;width:100%;z-index:10}ion-header ion-toolbar:first-of-type{padding-top:var(--ion-safe-area-top, 0)}.header-ios ion-toolbar:last-of-type{--border-width:0 0 0.55px}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.header-background{left:0;right:0;top:0;bottom:0;position:absolute;-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}.header-translucent-ios ion-toolbar{--opacity:.8}.header-collapse-condense-inactive .header-background{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}}.header-ios.ion-no-border ion-toolbar:last-of-type{--border-width:0}.header-collapse-fade ion-toolbar{--opacity-scale:inherit}.header-collapse-condense{z-index:9}.header-collapse-condense ion-toolbar{position:-webkit-sticky;position:sticky;top:0}.header-collapse-condense ion-toolbar:first-of-type{padding-top:0px;z-index:1}.header-collapse-condense ion-toolbar{--background:var(--ion-background-color, #fff);z-index:0}.header-collapse-condense ion-toolbar:last-of-type{--border-width:0px}.header-collapse-condense ion-toolbar ion-searchbar{padding-top:0px;padding-bottom:13px}.header-collapse-main{--opacity-scale:1}.header-collapse-main ion-toolbar{--opacity-scale:inherit}.header-collapse-main ion-toolbar.in-toolbar ion-title,.header-collapse-main ion-toolbar.in-toolbar ion-buttons{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-buttons.buttons-collapse{opacity:0;pointer-events:none}.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-buttons.buttons-collapse{visibility:hidden}ion-header.header-ios:not(.header-collapse-main):has(~ion-content ion-header.header-ios[collapse=condense],~ion-content ion-header.header-ios.header-collapse-condense){opacity:0}";
15783
+ const headerIosCss = "ion-header{display:block;position:relative;-ms-flex-order:-1;order:-1;width:100%;z-index:10}ion-header ion-toolbar:first-of-type{padding-top:var(--ion-safe-area-top, 0)}.header-ios ion-toolbar:last-of-type{--border-width:0 0 0.55px}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.header-background{left:0;right:0;top:0;bottom:0;position:absolute;-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}.header-translucent-ios ion-toolbar{--opacity:.8}.header-collapse-condense-inactive .header-background{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}}.header-ios.ion-no-border ion-toolbar:last-of-type{--border-width:0}.header-collapse-fade ion-toolbar{--opacity-scale:inherit}.header-collapse-fade.header-transitioning ion-toolbar{--background:transparent;--border-style:none}.header-collapse-condense{z-index:9}.header-collapse-condense ion-toolbar{position:-webkit-sticky;position:sticky;top:0}.header-collapse-condense ion-toolbar:first-of-type{padding-top:0px;z-index:1}.header-collapse-condense ion-toolbar{z-index:0}.header-collapse-condense ion-toolbar:last-of-type{--border-width:0px}.header-collapse-condense ion-toolbar ion-searchbar{padding-top:0px;padding-bottom:13px}.header-collapse-main{--opacity-scale:1}.header-collapse-main ion-toolbar{--opacity-scale:inherit}.header-collapse-main ion-toolbar.in-toolbar ion-title,.header-collapse-main ion-toolbar.in-toolbar ion-buttons{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.header-collapse-condense ion-toolbar,.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--background:var(--ion-background-color, #fff)}.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--border-style:none;--opacity-scale:1}.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-buttons.buttons-collapse{opacity:0;pointer-events:none}.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-buttons.buttons-collapse{visibility:hidden}ion-header.header-ios:not(.header-collapse-main):has(~ion-content ion-header.header-ios[collapse=condense],~ion-content ion-header.header-ios.header-collapse-condense){opacity:0}";
15427
15784
 
15428
15785
  const headerMdCss = "ion-header{display:block;position:relative;-ms-flex-order:-1;order:-1;width:100%;z-index:10}ion-header ion-toolbar:first-of-type{padding-top:var(--ion-safe-area-top, 0)}.header-md{-webkit-box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)}.header-collapse-condense{display:none}.header-md.ion-no-border{-webkit-box-shadow:none;box-shadow:none}";
15429
15786
 
@@ -15566,16 +15923,17 @@ class Header {
15566
15923
  const { translucent, inheritedAttributes } = this;
15567
15924
  const mode = getIonMode$1(this);
15568
15925
  const collapse = this.collapse || 'none';
15926
+ const isCondensed = collapse === 'condense';
15569
15927
  // banner role must be at top level, so remove role if inside a menu
15570
- const roleType = hostContext('ion-menu', this.el) ? 'none' : 'banner';
15571
- return (hAsync(Host, Object.assign({ key: 'b6cc27f0b08afc9fcc889683525da765d80ba672', role: roleType, class: {
15928
+ const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
15929
+ return (hAsync(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
15572
15930
  [mode]: true,
15573
15931
  // Used internally for styling
15574
15932
  [`header-${mode}`]: true,
15575
15933
  [`header-translucent`]: this.translucent,
15576
15934
  [`header-collapse-${collapse}`]: true,
15577
15935
  [`header-translucent-${mode}`]: this.translucent,
15578
- } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '395766d4dcee3398bc91960db21f922095292f14', class: "header-background" }), hAsync("slot", { key: '09a67ece27b258ff1248805d43d92a49b2c6859a' })));
15936
+ } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), hAsync("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
15579
15937
  }
15580
15938
  get el() { return getElement(this); }
15581
15939
  static get style() { return {
@@ -16070,322 +16428,139 @@ class InfiniteScroll {
16070
16428
  if (this.position === 'top') {
16071
16429
  /**
16072
16430
  * New content is being added at the top, but the scrollTop position stays the same,
16073
- * which causes a scroll jump visually. This algorithm makes sure to prevent this.
16074
- * (Frame 1)
16075
- * - complete() is called, but the UI hasn't had time to update yet.
16076
- * - Save the current content dimensions.
16077
- * - Wait for the next frame using _dom.read, so the UI will be updated.
16078
- * (Frame 2)
16079
- * - Read the new content dimensions.
16080
- * - Calculate the height difference and the new scroll position.
16081
- * - Delay the scroll position change until other possible dom reads are done using _dom.write to be performant.
16082
- * (Still frame 2, if I'm correct)
16083
- * - Change the scroll position (= visually maintain the scroll position).
16084
- * - Change the state to re-enable the InfiniteScroll.
16085
- * - This should be after changing the scroll position, or it could
16086
- * cause the InfiniteScroll to be triggered again immediately.
16087
- * (Frame 3)
16088
- * Done.
16089
- */
16090
- this.isBusy = true;
16091
- // ******** DOM READ ****************
16092
- // Save the current content dimensions before the UI updates
16093
- const prev = scrollEl.scrollHeight - scrollEl.scrollTop;
16094
- // ******** DOM READ ****************
16095
- requestAnimationFrame(() => {
16096
- readTask(() => {
16097
- // UI has updated, save the new content dimensions
16098
- const scrollHeight = scrollEl.scrollHeight;
16099
- // New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
16100
- const newScrollTop = scrollHeight - prev;
16101
- // ******** DOM WRITE ****************
16102
- requestAnimationFrame(() => {
16103
- writeTask(() => {
16104
- scrollEl.scrollTop = newScrollTop;
16105
- this.isBusy = false;
16106
- this.didFire = false;
16107
- });
16108
- });
16109
- });
16110
- });
16111
- }
16112
- else {
16113
- this.didFire = false;
16114
- }
16115
- }
16116
- canStart() {
16117
- return !this.disabled && !this.isBusy && !!this.scrollEl && !this.isLoading;
16118
- }
16119
- enableScrollEvents(shouldListen) {
16120
- if (this.scrollEl) {
16121
- if (shouldListen) {
16122
- this.scrollEl.addEventListener('scroll', this.onScroll);
16123
- }
16124
- else {
16125
- this.scrollEl.removeEventListener('scroll', this.onScroll);
16126
- }
16127
- }
16128
- }
16129
- render() {
16130
- const mode = getIonMode$1(this);
16131
- const disabled = this.disabled;
16132
- return (hAsync(Host, { key: 'e844956795f69be33396ce4480aa7a54ad01b28c', class: {
16133
- [mode]: true,
16134
- 'infinite-scroll-loading': this.isLoading,
16135
- 'infinite-scroll-enabled': !disabled,
16136
- } }));
16137
- }
16138
- get el() { return getElement(this); }
16139
- static get watchers() { return {
16140
- "threshold": ["thresholdChanged"],
16141
- "disabled": ["disabledChanged"]
16142
- }; }
16143
- static get style() { return infiniteScrollCss; }
16144
- static get cmpMeta() { return {
16145
- "$flags$": 256,
16146
- "$tagName$": "ion-infinite-scroll",
16147
- "$members$": {
16148
- "threshold": [1],
16149
- "disabled": [4],
16150
- "position": [1],
16151
- "isLoading": [32],
16152
- "complete": [64]
16153
- },
16154
- "$listeners$": undefined,
16155
- "$lazyBundleId$": "-",
16156
- "$attrsToReflect$": []
16157
- }; }
16158
- }
16159
-
16160
- const infiniteScrollContentIosCss = "ion-infinite-scroll-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;min-height:84px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.infinite-loading{margin-left:0;margin-right:0;margin-top:0;margin-bottom:32px;display:none;width:100%}.infinite-loading-text{-webkit-margin-start:32px;margin-inline-start:32px;-webkit-margin-end:32px;margin-inline-end:32px;margin-top:4px;margin-bottom:0}.infinite-scroll-loading ion-infinite-scroll-content>.infinite-loading{display:block}.infinite-scroll-content-ios .infinite-loading-text{color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-ios line,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-small-ios line,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-crescent circle{stroke:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-bubbles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-circles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-dots circle{fill:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}";
16161
-
16162
- const infiniteScrollContentMdCss = "ion-infinite-scroll-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;min-height:84px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.infinite-loading{margin-left:0;margin-right:0;margin-top:0;margin-bottom:32px;display:none;width:100%}.infinite-loading-text{-webkit-margin-start:32px;margin-inline-start:32px;-webkit-margin-end:32px;margin-inline-end:32px;margin-top:4px;margin-bottom:0}.infinite-scroll-loading ion-infinite-scroll-content>.infinite-loading{display:block}.infinite-scroll-content-md .infinite-loading-text{color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-md .infinite-loading-spinner .spinner-lines-md line,.infinite-scroll-content-md .infinite-loading-spinner .spinner-lines-small-md line,.infinite-scroll-content-md .infinite-loading-spinner .spinner-crescent circle{stroke:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-md .infinite-loading-spinner .spinner-bubbles circle,.infinite-scroll-content-md .infinite-loading-spinner .spinner-circles circle,.infinite-scroll-content-md .infinite-loading-spinner .spinner-dots circle{fill:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}";
16163
-
16164
- class InfiniteScrollContent {
16165
- constructor(hostRef) {
16166
- registerInstance(this, hostRef);
16167
- this.customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
16168
- }
16169
- componentDidLoad() {
16170
- if (this.loadingSpinner === undefined) {
16171
- const mode = getIonMode$1(this);
16172
- this.loadingSpinner = config.get('infiniteLoadingSpinner', config.get('spinner', mode === 'ios' ? 'lines' : 'crescent'));
16173
- }
16174
- }
16175
- renderLoadingText() {
16176
- const { customHTMLEnabled, loadingText } = this;
16177
- if (customHTMLEnabled) {
16178
- return hAsync("div", { class: "infinite-loading-text", innerHTML: sanitizeDOMString(loadingText) });
16179
- }
16180
- return hAsync("div", { class: "infinite-loading-text" }, this.loadingText);
16181
- }
16182
- render() {
16183
- const mode = getIonMode$1(this);
16184
- return (hAsync(Host, { key: '7c16060dcfe2a0b0fb3e2f8f4c449589a76f1baa', class: {
16185
- [mode]: true,
16186
- // Used internally for styling
16187
- [`infinite-scroll-content-${mode}`]: true,
16188
- } }, hAsync("div", { key: 'a94f4d8746e053dc718f97520bd7e48cb316443a', class: "infinite-loading" }, this.loadingSpinner && (hAsync("div", { key: '10143d5d2a50a2a2bc5de1cee8e7ab51263bcf23', class: "infinite-loading-spinner" }, hAsync("ion-spinner", { key: '8846e88191690d9c61a0b462889ed56fbfed8b0d', name: this.loadingSpinner }))), this.loadingText !== undefined && this.renderLoadingText())));
16189
- }
16190
- static get style() { return {
16191
- ios: infiniteScrollContentIosCss,
16192
- md: infiniteScrollContentMdCss
16193
- }; }
16194
- static get cmpMeta() { return {
16195
- "$flags$": 288,
16196
- "$tagName$": "ion-infinite-scroll-content",
16197
- "$members$": {
16198
- "loadingSpinner": [1025, "loading-spinner"],
16199
- "loadingText": [1, "loading-text"]
16200
- },
16201
- "$listeners$": undefined,
16202
- "$lazyBundleId$": "-",
16203
- "$attrsToReflect$": []
16204
- }; }
16205
- }
16206
-
16207
- /**
16208
- * A utility to calculate the size of an outline notch
16209
- * width relative to the content passed. This is used in
16210
- * components such as `ion-select` with `fill="outline"`
16211
- * where we need to pass slotted HTML content. This is not
16212
- * needed when rendering plaintext content because we can
16213
- * render the plaintext again hidden with `opacity: 0` inside
16214
- * of the notch. As a result we can rely on the intrinsic size
16215
- * of the element to correctly compute the notch width. We
16216
- * cannot do this with slotted content because we cannot project
16217
- * it into 2 places at once.
16218
- *
16219
- * @internal
16220
- * @param el: The host element
16221
- * @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
16222
- * @param getLabelSlot: A function that returns a reference to the slotted content.
16223
- */
16224
- const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
16225
- let notchVisibilityIO;
16226
- const needsExplicitNotchWidth = () => {
16227
- const notchSpacerEl = getNotchSpacerEl();
16228
- if (
16229
- /**
16230
- * If the notch is not being used
16231
- * then we do not need to set the notch width.
16232
- */
16233
- notchSpacerEl === undefined ||
16234
- /**
16235
- * If either the label property is being
16236
- * used or the label slot is not defined,
16237
- * then we do not need to estimate the notch width.
16238
- */
16239
- el.label !== undefined ||
16240
- getLabelSlot() === null) {
16241
- return false;
16242
- }
16243
- return true;
16244
- };
16245
- const calculateNotchWidth = () => {
16246
- if (needsExplicitNotchWidth()) {
16247
- /**
16248
- * Run this the frame after
16249
- * the browser has re-painted the host element.
16250
- * Otherwise, the label element may have a width
16251
- * of 0 and the IntersectionObserver will be used.
16252
- */
16253
- raf(() => {
16254
- setNotchWidth();
16255
- });
16256
- }
16257
- };
16258
- /**
16259
- * When using a label prop we can render
16260
- * the label value inside of the notch and
16261
- * let the browser calculate the size of the notch.
16262
- * However, we cannot render the label slot in multiple
16263
- * places so we need to manually calculate the notch dimension
16264
- * based on the size of the slotted content.
16265
- *
16266
- * This function should only be used to set the notch width
16267
- * on slotted label content. The notch width for label prop
16268
- * content is automatically calculated based on the
16269
- * intrinsic size of the label text.
16270
- */
16271
- const setNotchWidth = () => {
16272
- const notchSpacerEl = getNotchSpacerEl();
16273
- if (notchSpacerEl === undefined) {
16274
- return;
16275
- }
16276
- if (!needsExplicitNotchWidth()) {
16277
- notchSpacerEl.style.removeProperty('width');
16278
- return;
16279
- }
16280
- const width = getLabelSlot().scrollWidth;
16281
- if (
16282
- /**
16283
- * If the computed width of the label is 0
16284
- * and notchSpacerEl's offsetParent is null
16285
- * then that means the element is hidden.
16286
- * As a result, we need to wait for the element
16287
- * to become visible before setting the notch width.
16288
- *
16289
- * We do not check el.offsetParent because
16290
- * that can be null if the host element has
16291
- * position: fixed applied to it.
16292
- * notchSpacerEl does not have position: fixed.
16293
- */
16294
- width === 0 &&
16295
- notchSpacerEl.offsetParent === null &&
16296
- win$1 !== undefined &&
16297
- 'IntersectionObserver' in win$1) {
16298
- /**
16299
- * If there is an IO already attached
16300
- * then that will update the notch
16301
- * once the element becomes visible.
16302
- * As a result, there is no need to create
16303
- * another one.
16304
- */
16305
- if (notchVisibilityIO !== undefined) {
16306
- return;
16307
- }
16308
- const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
16309
- /**
16310
- * If the element is visible then we
16311
- * can try setting the notch width again.
16312
- */
16313
- if (ev[0].intersectionRatio === 1) {
16314
- setNotchWidth();
16315
- io.disconnect();
16316
- notchVisibilityIO = undefined;
16317
- }
16318
- },
16319
- /**
16320
- * Set the root to be the host element
16321
- * This causes the IO callback
16322
- * to be fired in WebKit as soon as the element
16323
- * is visible. If we used the default root value
16324
- * then WebKit would only fire the IO callback
16325
- * after any animations (such as a modal transition)
16326
- * finished, and there would potentially be a flicker.
16431
+ * which causes a scroll jump visually. This algorithm makes sure to prevent this.
16432
+ * (Frame 1)
16433
+ * - complete() is called, but the UI hasn't had time to update yet.
16434
+ * - Save the current content dimensions.
16435
+ * - Wait for the next frame using _dom.read, so the UI will be updated.
16436
+ * (Frame 2)
16437
+ * - Read the new content dimensions.
16438
+ * - Calculate the height difference and the new scroll position.
16439
+ * - Delay the scroll position change until other possible dom reads are done using _dom.write to be performant.
16440
+ * (Still frame 2, if I'm correct)
16441
+ * - Change the scroll position (= visually maintain the scroll position).
16442
+ * - Change the state to re-enable the InfiniteScroll.
16443
+ * - This should be after changing the scroll position, or it could
16444
+ * cause the InfiniteScroll to be triggered again immediately.
16445
+ * (Frame 3)
16446
+ * Done.
16327
16447
  */
16328
- { threshold: 0.01, root: el }));
16329
- io.observe(notchSpacerEl);
16330
- return;
16448
+ this.isBusy = true;
16449
+ // ******** DOM READ ****************
16450
+ // Save the current content dimensions before the UI updates
16451
+ const prev = scrollEl.scrollHeight - scrollEl.scrollTop;
16452
+ // ******** DOM READ ****************
16453
+ requestAnimationFrame(() => {
16454
+ readTask(() => {
16455
+ // UI has updated, save the new content dimensions
16456
+ const scrollHeight = scrollEl.scrollHeight;
16457
+ // New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
16458
+ const newScrollTop = scrollHeight - prev;
16459
+ // ******** DOM WRITE ****************
16460
+ requestAnimationFrame(() => {
16461
+ writeTask(() => {
16462
+ scrollEl.scrollTop = newScrollTop;
16463
+ this.isBusy = false;
16464
+ this.didFire = false;
16465
+ });
16466
+ });
16467
+ });
16468
+ });
16331
16469
  }
16332
- /**
16333
- * If the element is visible then we can set the notch width.
16334
- * The notch is only visible when the label is scaled,
16335
- * which is why we multiply the width by 0.75 as this is
16336
- * the same amount the label element is scaled by in the host CSS.
16337
- * (See $form-control-label-stacked-scale in ionic.globals.scss).
16338
- */
16339
- notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
16340
- };
16341
- const destroy = () => {
16342
- if (notchVisibilityIO) {
16343
- notchVisibilityIO.disconnect();
16344
- notchVisibilityIO = undefined;
16470
+ else {
16471
+ this.didFire = false;
16345
16472
  }
16346
- };
16347
- return {
16348
- calculateNotchWidth,
16349
- destroy,
16350
- };
16351
- };
16352
-
16353
- /**
16354
- * Uses the compareWith param to compare two values to determine if they are equal.
16355
- *
16356
- * @param currentValue The current value of the control.
16357
- * @param compareValue The value to compare against.
16358
- * @param compareWith The function or property name to use to compare values.
16359
- */
16360
- const compareOptions = (currentValue, compareValue, compareWith) => {
16361
- if (typeof compareWith === 'function') {
16362
- return compareWith(currentValue, compareValue);
16363
16473
  }
16364
- else if (typeof compareWith === 'string') {
16365
- return currentValue[compareWith] === compareValue[compareWith];
16474
+ canStart() {
16475
+ return !this.disabled && !this.isBusy && !!this.scrollEl && !this.isLoading;
16366
16476
  }
16367
- else {
16368
- return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
16477
+ enableScrollEvents(shouldListen) {
16478
+ if (this.scrollEl) {
16479
+ if (shouldListen) {
16480
+ this.scrollEl.addEventListener('scroll', this.onScroll);
16481
+ }
16482
+ else {
16483
+ this.scrollEl.removeEventListener('scroll', this.onScroll);
16484
+ }
16485
+ }
16369
16486
  }
16370
- };
16371
- /**
16372
- * Compares a value against the current value(s) to determine if it is selected.
16373
- *
16374
- * @param currentValue The current value of the control.
16375
- * @param compareValue The value to compare against.
16376
- * @param compareWith The function or property name to use to compare values.
16377
- */
16378
- const isOptionSelected = (currentValue, compareValue, compareWith) => {
16379
- if (currentValue === undefined) {
16380
- return false;
16487
+ render() {
16488
+ const mode = getIonMode$1(this);
16489
+ const disabled = this.disabled;
16490
+ return (hAsync(Host, { key: 'e844956795f69be33396ce4480aa7a54ad01b28c', class: {
16491
+ [mode]: true,
16492
+ 'infinite-scroll-loading': this.isLoading,
16493
+ 'infinite-scroll-enabled': !disabled,
16494
+ } }));
16381
16495
  }
16382
- if (Array.isArray(currentValue)) {
16383
- return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
16496
+ get el() { return getElement(this); }
16497
+ static get watchers() { return {
16498
+ "threshold": ["thresholdChanged"],
16499
+ "disabled": ["disabledChanged"]
16500
+ }; }
16501
+ static get style() { return infiniteScrollCss; }
16502
+ static get cmpMeta() { return {
16503
+ "$flags$": 256,
16504
+ "$tagName$": "ion-infinite-scroll",
16505
+ "$members$": {
16506
+ "threshold": [1],
16507
+ "disabled": [4],
16508
+ "position": [1],
16509
+ "isLoading": [32],
16510
+ "complete": [64]
16511
+ },
16512
+ "$listeners$": undefined,
16513
+ "$lazyBundleId$": "-",
16514
+ "$attrsToReflect$": []
16515
+ }; }
16516
+ }
16517
+
16518
+ const infiniteScrollContentIosCss = "ion-infinite-scroll-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;min-height:84px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.infinite-loading{margin-left:0;margin-right:0;margin-top:0;margin-bottom:32px;display:none;width:100%}.infinite-loading-text{-webkit-margin-start:32px;margin-inline-start:32px;-webkit-margin-end:32px;margin-inline-end:32px;margin-top:4px;margin-bottom:0}.infinite-scroll-loading ion-infinite-scroll-content>.infinite-loading{display:block}.infinite-scroll-content-ios .infinite-loading-text{color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-ios line,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-lines-small-ios line,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-crescent circle{stroke:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-ios .infinite-loading-spinner .spinner-bubbles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-circles circle,.infinite-scroll-content-ios .infinite-loading-spinner .spinner-dots circle{fill:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}";
16519
+
16520
+ const infiniteScrollContentMdCss = "ion-infinite-scroll-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;min-height:84px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.infinite-loading{margin-left:0;margin-right:0;margin-top:0;margin-bottom:32px;display:none;width:100%}.infinite-loading-text{-webkit-margin-start:32px;margin-inline-start:32px;-webkit-margin-end:32px;margin-inline-end:32px;margin-top:4px;margin-bottom:0}.infinite-scroll-loading ion-infinite-scroll-content>.infinite-loading{display:block}.infinite-scroll-content-md .infinite-loading-text{color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-md .infinite-loading-spinner .spinner-lines-md line,.infinite-scroll-content-md .infinite-loading-spinner .spinner-lines-small-md line,.infinite-scroll-content-md .infinite-loading-spinner .spinner-crescent circle{stroke:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}.infinite-scroll-content-md .infinite-loading-spinner .spinner-bubbles circle,.infinite-scroll-content-md .infinite-loading-spinner .spinner-circles circle,.infinite-scroll-content-md .infinite-loading-spinner .spinner-dots circle{fill:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}";
16521
+
16522
+ class InfiniteScrollContent {
16523
+ constructor(hostRef) {
16524
+ registerInstance(this, hostRef);
16525
+ this.customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
16384
16526
  }
16385
- else {
16386
- return compareOptions(currentValue, compareValue, compareWith);
16527
+ componentDidLoad() {
16528
+ if (this.loadingSpinner === undefined) {
16529
+ const mode = getIonMode$1(this);
16530
+ this.loadingSpinner = config.get('infiniteLoadingSpinner', config.get('spinner', mode === 'ios' ? 'lines' : 'crescent'));
16531
+ }
16387
16532
  }
16388
- };
16533
+ renderLoadingText() {
16534
+ const { customHTMLEnabled, loadingText } = this;
16535
+ if (customHTMLEnabled) {
16536
+ return hAsync("div", { class: "infinite-loading-text", innerHTML: sanitizeDOMString(loadingText) });
16537
+ }
16538
+ return hAsync("div", { class: "infinite-loading-text" }, this.loadingText);
16539
+ }
16540
+ render() {
16541
+ const mode = getIonMode$1(this);
16542
+ return (hAsync(Host, { key: '7c16060dcfe2a0b0fb3e2f8f4c449589a76f1baa', class: {
16543
+ [mode]: true,
16544
+ // Used internally for styling
16545
+ [`infinite-scroll-content-${mode}`]: true,
16546
+ } }, hAsync("div", { key: 'a94f4d8746e053dc718f97520bd7e48cb316443a', class: "infinite-loading" }, this.loadingSpinner && (hAsync("div", { key: '10143d5d2a50a2a2bc5de1cee8e7ab51263bcf23', class: "infinite-loading-spinner" }, hAsync("ion-spinner", { key: '8846e88191690d9c61a0b462889ed56fbfed8b0d', name: this.loadingSpinner }))), this.loadingText !== undefined && this.renderLoadingText())));
16547
+ }
16548
+ static get style() { return {
16549
+ ios: infiniteScrollContentIosCss,
16550
+ md: infiniteScrollContentMdCss
16551
+ }; }
16552
+ static get cmpMeta() { return {
16553
+ "$flags$": 288,
16554
+ "$tagName$": "ion-infinite-scroll-content",
16555
+ "$members$": {
16556
+ "loadingSpinner": [1025, "loading-spinner"],
16557
+ "loadingText": [1, "loading-text"]
16558
+ },
16559
+ "$listeners$": undefined,
16560
+ "$lazyBundleId$": "-",
16561
+ "$attrsToReflect$": []
16562
+ }; }
16563
+ }
16389
16564
 
16390
16565
  /**
16391
16566
  * Used to update a scoped component that uses emulated slots. This fires when
@@ -16752,20 +16927,12 @@ class Input {
16752
16927
  componentWillLoad() {
16753
16928
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes$1(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
16754
16929
  }
16755
- /**
16756
- * Checks if the input is in an invalid state based on Ionic validation classes
16757
- */
16758
- checkInvalidState() {
16759
- const hasIonTouched = this.el.classList.contains('ion-touched');
16760
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
16761
- return hasIonTouched && hasIonInvalid;
16762
- }
16763
16930
  connectedCallback() {
16764
16931
  const { el } = this;
16765
16932
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
16766
16933
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
16767
16934
  // Always set initial state
16768
- this.isInvalid = this.checkInvalidState();
16935
+ this.isInvalid = checkInvalidState(el);
16769
16936
  this.debounceChanged();
16770
16937
  }
16771
16938
  componentDidLoad() {
@@ -17019,7 +17186,7 @@ class Input {
17019
17186
  * TODO(FW-5592): Remove hasStartEndSlots condition
17020
17187
  */
17021
17188
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
17022
- return (hAsync(Host, { key: '8a51f0300d5bc66392f9ab9a6fa0b5d388072a33', class: createColorClasses$1(this.color, {
17189
+ return (hAsync(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses$1(this.color, {
17023
17190
  [mode]: true,
17024
17191
  'has-value': hasValue,
17025
17192
  'has-focus': hasFocus,
@@ -17030,14 +17197,14 @@ class Input {
17030
17197
  'in-item': inItem,
17031
17198
  'in-item-color': hostContext('ion-item.ion-color', this.el),
17032
17199
  'input-disabled': disabled,
17033
- }) }, hAsync("label", { key: '9f8cf88d7d0e27931b51bd9c67f048c7fc6f5703', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '7ad30bf9777774062a6ccf9a3ba804f251eef1bb', class: "native-wrapper", onClick: this.onLabelClick }, hAsync("slot", { key: '8af0b0325d101df8eed7d24f2767d6ca4d307319', name: "start" }), hAsync("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 && (hAsync("button", { key: 'b081d0e1ec1444b4c9cca145fc9cd2ad4a68b3da', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
17200
+ }) }, hAsync("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, hAsync("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), hAsync("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 && (hAsync("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
17034
17201
  /**
17035
17202
  * This prevents mobile browsers from
17036
17203
  * blurring the input when the clear
17037
17204
  * button is activated.
17038
17205
  */
17039
17206
  ev.preventDefault();
17040
- }, onClick: this.clearTextInput }, hAsync("ion-icon", { key: '01535299241c3635460c05646420acf62a1ff567', "aria-hidden": "true", icon: clearIconData }))), hAsync("slot", { key: '480f3eb58b08ae792866a5b9b4c068748c5567cc', name: "end" })), shouldRenderHighlight && hAsync("div", { key: 'a8609cacee88e4a09f1cca65b6a47cb79a56f35e', class: "input-highlight" })), this.renderBottomContent()));
17207
+ }, onClick: this.clearTextInput }, hAsync("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), hAsync("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && hAsync("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
17041
17208
  }
17042
17209
  get el() { return getElement(this); }
17043
17210
  static get watchers() { return {
@@ -20678,11 +20845,22 @@ const iosTransitionAnimation$1 = () => Promise.resolve().then(function () { retu
20678
20845
  const mdTransitionAnimation$1 = () => Promise.resolve().then(function () { return md_transition; });
20679
20846
  const focusController = createFocusController();
20680
20847
  // TODO(FW-2832): types
20848
+ /**
20849
+ * Executes the main page transition.
20850
+ * It also manages the lifecycle of header visibility (if any)
20851
+ * to prevent visual flickering in iOS. The flickering only
20852
+ * occurs for a condensed header that is placed above the content.
20853
+ *
20854
+ * @param opts Options for the transition.
20855
+ * @returns A promise that resolves when the transition is complete.
20856
+ */
20681
20857
  const transition = (opts) => {
20682
20858
  return new Promise((resolve, reject) => {
20683
20859
  writeTask(() => {
20684
- beforeTransition(opts);
20685
- runTransition(opts).then((result) => {
20860
+ const transitioningInactiveHeader = getIosIonHeader(opts);
20861
+ beforeTransition(opts, transitioningInactiveHeader);
20862
+ runTransition(opts)
20863
+ .then((result) => {
20686
20864
  if (result.animation) {
20687
20865
  result.animation.destroy();
20688
20866
  }
@@ -20691,15 +20869,21 @@ const transition = (opts) => {
20691
20869
  }, (error) => {
20692
20870
  afterTransition(opts);
20693
20871
  reject(error);
20872
+ })
20873
+ .finally(() => {
20874
+ // Ensure that the header is restored to its original state.
20875
+ setHeaderTransitionClass(transitioningInactiveHeader, false);
20694
20876
  });
20695
20877
  });
20696
20878
  });
20697
20879
  };
20698
- const beforeTransition = (opts) => {
20880
+ const beforeTransition = (opts, transitioningInactiveHeader) => {
20699
20881
  const enteringEl = opts.enteringEl;
20700
20882
  const leavingEl = opts.leavingEl;
20701
20883
  focusController.saveViewFocus(leavingEl);
20702
20884
  setZIndex(enteringEl, leavingEl, opts.direction);
20885
+ // Prevent flickering of the header by adding a class.
20886
+ setHeaderTransitionClass(transitioningInactiveHeader, true);
20703
20887
  if (opts.showGoBack) {
20704
20888
  enteringEl.classList.add('can-go-back');
20705
20889
  }
@@ -20888,6 +21072,39 @@ const setZIndex = (enteringEl, leavingEl, direction) => {
20888
21072
  leavingEl.style.zIndex = '100';
20889
21073
  }
20890
21074
  };
21075
+ /**
21076
+ * Add a class to ensure that the header (if any)
21077
+ * does not flicker during the transition. By adding the
21078
+ * transitioning class, we ensure that the header has
21079
+ * the necessary styles to prevent the following flickers:
21080
+ * 1. When entering a page with a condensed header, the
21081
+ * header should never be visible. However,
21082
+ * it briefly renders the background color while
21083
+ * the transition is occurring.
21084
+ * 2. When leaving a page with a condensed header, the
21085
+ * header has an opacity of 0 and the pages
21086
+ * have a z-index which causes the entering page to
21087
+ * briefly show it's content underneath the leaving page.
21088
+ * 3. When entering a page or leaving a page with a fade
21089
+ * header, the header should not have a background color.
21090
+ * However, it briefly shows the background color while
21091
+ * the transition is occurring.
21092
+ *
21093
+ * @param header The header element to modify.
21094
+ * @param isTransitioning Whether the transition is occurring.
21095
+ */
21096
+ const setHeaderTransitionClass = (header, isTransitioning) => {
21097
+ if (!header) {
21098
+ return;
21099
+ }
21100
+ const transitionClass = 'header-transitioning';
21101
+ if (isTransitioning) {
21102
+ header.classList.add(transitionClass);
21103
+ }
21104
+ else {
21105
+ header.classList.remove(transitionClass);
21106
+ }
21107
+ };
20891
21108
  const getIonPageElement = (element) => {
20892
21109
  if (element.classList.contains('ion-page')) {
20893
21110
  return element;
@@ -20899,6 +21116,27 @@ const getIonPageElement = (element) => {
20899
21116
  // idk, return the original element so at least something animates and we don't have a null pointer
20900
21117
  return element;
20901
21118
  };
21119
+ /**
21120
+ * Retrieves the ion-header element from a page based on the
21121
+ * direction of the transition.
21122
+ *
21123
+ * @param opts Options for the transition.
21124
+ * @returns The ion-header element or null if not found or not in 'ios' mode.
21125
+ */
21126
+ const getIosIonHeader = (opts) => {
21127
+ const enteringEl = opts.enteringEl;
21128
+ const leavingEl = opts.leavingEl;
21129
+ const direction = opts.direction;
21130
+ const mode = opts.mode;
21131
+ if (mode !== 'ios') {
21132
+ return null;
21133
+ }
21134
+ const element = direction === 'back' ? leavingEl : enteringEl;
21135
+ if (!element) {
21136
+ return null;
21137
+ }
21138
+ return element.querySelector('ion-header');
21139
+ };
20902
21140
 
20903
21141
  const KEYBOARD_DID_OPEN = 'ionKeyboardDidShow';
20904
21142
 
@@ -27867,6 +28105,10 @@ class RadioGroup {
27867
28105
  this.helperTextId = `${this.inputId}-helper-text`;
27868
28106
  this.errorTextId = `${this.inputId}-error-text`;
27869
28107
  this.labelId = `${this.inputId}-lbl`;
28108
+ /**
28109
+ * Track validation state for proper aria-live announcements.
28110
+ */
28111
+ this.isInvalid = false;
27870
28112
  /**
27871
28113
  * If `true`, the radios can be deselected.
27872
28114
  */
@@ -27948,6 +28190,18 @@ class RadioGroup {
27948
28190
  this.labelId = label.id = this.name + '-lbl';
27949
28191
  }
27950
28192
  }
28193
+ // Always set initial state
28194
+ this.isInvalid = checkInvalidState(this.el);
28195
+ }
28196
+ componentWillLoad() {
28197
+ this.hintTextId = this.getHintTextId();
28198
+ }
28199
+ disconnectedCallback() {
28200
+ // Clean up validation observer to prevent memory leaks.
28201
+ if (this.validationObserver) {
28202
+ this.validationObserver.disconnect();
28203
+ this.validationObserver = undefined;
28204
+ }
27951
28205
  }
27952
28206
  getRadios() {
27953
28207
  return Array.from(this.el.querySelectorAll('ion-radio'));
@@ -28023,16 +28277,16 @@ class RadioGroup {
28023
28277
  * Renders the helper text or error text values
28024
28278
  */
28025
28279
  renderHintText() {
28026
- const { helperText, errorText, helperTextId, errorTextId } = this;
28280
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
28027
28281
  const hasHintText = !!helperText || !!errorText;
28028
28282
  if (!hasHintText) {
28029
28283
  return;
28030
28284
  }
28031
- return (hAsync("div", { class: "radio-group-top" }, hAsync("div", { id: helperTextId, class: "helper-text" }, helperText), hAsync("div", { id: errorTextId, class: "error-text" }, errorText)));
28285
+ return (hAsync("div", { class: "radio-group-top" }, hAsync("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), hAsync("div", { id: errorTextId, class: "error-text", role: "alert" }, isInvalid ? errorText : null)));
28032
28286
  }
28033
- getHintTextID() {
28034
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
28035
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
28287
+ getHintTextId() {
28288
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
28289
+ if (isInvalid && errorText) {
28036
28290
  return errorTextId;
28037
28291
  }
28038
28292
  if (helperText) {
@@ -28044,7 +28298,7 @@ class RadioGroup {
28044
28298
  const { label, labelId, el, name, value } = this;
28045
28299
  const mode = getIonMode$1(this);
28046
28300
  renderHiddenInput(true, el, name, value, false);
28047
- return (hAsync(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(), hAsync("div", { key: '45b09efc10776b889a8f372cba80d25a3fc849da', class: "radio-group-wrapper" }, hAsync("slot", { key: '58714934542c2fdd7396de160364f3f06b32e8f8' }))));
28301
+ return (hAsync(Host, { key: 'db593b3ed511e9395e3c7bfd91b787328692cd6d', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), hAsync("div", { key: '85045b45a0100a45f3b9a35d1c5a25ec63d525c4', class: "radio-group-wrapper" }, hAsync("slot", { key: '53dacb87ce62398e78771fb2efaf839ab922d946' }))));
28048
28302
  }
28049
28303
  get el() { return getElement(this); }
28050
28304
  static get watchers() { return {
@@ -28064,6 +28318,8 @@ class RadioGroup {
28064
28318
  "value": [1032],
28065
28319
  "helperText": [1, "helper-text"],
28066
28320
  "errorText": [1, "error-text"],
28321
+ "isInvalid": [32],
28322
+ "hintTextId": [32],
28067
28323
  "setFocus": [64]
28068
28324
  },
28069
28325
  "$listeners$": [[4, "keydown", "onKeydown"]],
@@ -33061,6 +33317,10 @@ class Select {
33061
33317
  * is applied in both cases.
33062
33318
  */
33063
33319
  this.hasFocus = false;
33320
+ /**
33321
+ * Track validation state for proper aria-live announcements.
33322
+ */
33323
+ this.isInvalid = false;
33064
33324
  /**
33065
33325
  * The text to display on the cancel button.
33066
33326
  */
@@ -33183,9 +33443,12 @@ class Select {
33183
33443
  this.mutationO = watchForOptions(this.el, 'ion-select-option', async () => {
33184
33444
  this.updateOverlayOptions();
33185
33445
  });
33446
+ // Always set initial state
33447
+ this.isInvalid = checkInvalidState(this.el);
33186
33448
  }
33187
33449
  componentWillLoad() {
33188
33450
  this.inheritedAttributes = inheritAttributes$1(this.el, ['aria-label']);
33451
+ this.hintTextId = this.getHintTextId();
33189
33452
  }
33190
33453
  componentDidLoad() {
33191
33454
  /**
@@ -33209,6 +33472,11 @@ class Select {
33209
33472
  this.notchController.destroy();
33210
33473
  this.notchController = undefined;
33211
33474
  }
33475
+ // Clean up validation observer to prevent memory leaks.
33476
+ if (this.validationObserver) {
33477
+ this.validationObserver.disconnect();
33478
+ this.validationObserver = undefined;
33479
+ }
33212
33480
  }
33213
33481
  /**
33214
33482
  * Open the select overlay. The overlay is either an alert, action sheet, or popover,
@@ -33679,11 +33947,11 @@ class Select {
33679
33947
  }
33680
33948
  renderListbox() {
33681
33949
  const { disabled, inputId, isExpanded, required } = this;
33682
- return (hAsync("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) }));
33950
+ return (hAsync("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) }));
33683
33951
  }
33684
- getHintTextID() {
33685
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
33686
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
33952
+ getHintTextId() {
33953
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33954
+ if (isInvalid && errorText) {
33687
33955
  return errorTextId;
33688
33956
  }
33689
33957
  if (helperText) {
@@ -33695,10 +33963,10 @@ class Select {
33695
33963
  * Renders the helper text or error text values
33696
33964
  */
33697
33965
  renderHintText() {
33698
- const { helperText, errorText, helperTextId, errorTextId } = this;
33966
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33699
33967
  return [
33700
- hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
33701
- hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
33968
+ hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
33969
+ hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
33702
33970
  ];
33703
33971
  }
33704
33972
  /**
@@ -33746,7 +34014,7 @@ class Select {
33746
34014
  * TODO(FW-5592): Remove hasStartEndSlots condition
33747
34015
  */
33748
34016
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
33749
- return (hAsync(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses$1(this.color, {
34017
+ return (hAsync(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses$1(this.color, {
33750
34018
  [mode]: true,
33751
34019
  'in-item': inItem,
33752
34020
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -33764,7 +34032,7 @@ class Select {
33764
34032
  [`select-justify-${justify}`]: justifyEnabled,
33765
34033
  [`select-shape-${shape}`]: shape !== undefined,
33766
34034
  [`select-label-placement-${labelPlacement}`]: true,
33767
- }) }, hAsync("label", { key: '0d0c8ec55269adcac625f2899a547f4e7f3e3741', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'f6dfc93c0e23cbe75a2947abde67d842db2dad78', class: "select-wrapper-inner" }, hAsync("slot", { key: '957bfadf9f101f519091419a362d3abdc2be66f6', name: "start" }), hAsync("div", { key: 'ca349202a484e7f2e884533fd330f0b136754f7d', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), hAsync("slot", { key: 'f0e62a6533ff1c8f62bd2d27f60b23385c4fa9ed', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && hAsync("div", { key: 'fb840d46bafafb09898ebeebbe8c181906a3d8a2', class: "select-highlight" })), this.renderBottomContent()));
34035
+ }) }, hAsync("label", { key: '6005b34a0c50bc4d7653a4276bc232ecd02e083c', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'c7e07aa81ae856c057f16275dd058f37c5670a47', class: "select-wrapper-inner" }, hAsync("slot", { key: '7fc2deefe0424404caacdbbd9e08ed43ba55d28a', name: "start" }), hAsync("div", { key: '157d74ee717b1bc30b5f1c233a09b0c8456aa68e', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), hAsync("slot", { key: 'ea66db304528b82bf9317730b6dce3db2612f235', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && hAsync("div", { key: '786eb1530b7476f0615d4e7c0bf4e7e4dc66509c', class: "select-highlight" })), this.renderBottomContent()));
33768
34036
  }
33769
34037
  get el() { return getElement(this); }
33770
34038
  static get watchers() { return {
@@ -33805,6 +34073,8 @@ class Select {
33805
34073
  "required": [4],
33806
34074
  "isExpanded": [32],
33807
34075
  "hasFocus": [32],
34076
+ "isInvalid": [32],
34077
+ "hintTextId": [32],
33808
34078
  "open": [64]
33809
34079
  },
33810
34080
  "$listeners$": undefined,
@@ -34992,20 +35262,12 @@ class Textarea {
34992
35262
  this.el.click();
34993
35263
  }
34994
35264
  }
34995
- /**
34996
- * Checks if the textarea is in an invalid state based on Ionic validation classes
34997
- */
34998
- checkValidationState() {
34999
- const hasIonTouched = this.el.classList.contains('ion-touched');
35000
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
35001
- return hasIonTouched && hasIonInvalid;
35002
- }
35003
35265
  connectedCallback() {
35004
35266
  const { el } = this;
35005
35267
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
35006
35268
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
35007
35269
  // Always set initial state
35008
- this.isInvalid = this.checkValidationState();
35270
+ this.isInvalid = checkInvalidState(this.el);
35009
35271
  this.debounceChanged();
35010
35272
  }
35011
35273
  disconnectedCallback() {
@@ -35259,7 +35521,7 @@ class Textarea {
35259
35521
  * TODO(FW-5592): Remove hasStartEndSlots condition
35260
35522
  */
35261
35523
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
35262
- return (hAsync(Host, { key: '26b46666a92b3f652775bb1c46661f9a30392104', class: createColorClasses$1(this.color, {
35524
+ return (hAsync(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses$1(this.color, {
35263
35525
  [mode]: true,
35264
35526
  'has-value': hasValue,
35265
35527
  'has-focus': hasFocus,
@@ -35268,7 +35530,7 @@ class Textarea {
35268
35530
  [`textarea-shape-${shape}`]: shape !== undefined,
35269
35531
  [`textarea-label-placement-${labelPlacement}`]: true,
35270
35532
  'textarea-disabled': disabled,
35271
- }) }, hAsync("label", { key: '2649da816216959ebe1f34cafd9dedbac20ec3c2', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'dca98593efece1b044dbcda045fa70882d715cb2', class: "textarea-wrapper-inner" }, hAsync("div", { key: '2019daf87fddca5ec0b2e336f0376fd9642bae1b', class: "start-slot-wrapper" }, hAsync("slot", { key: '36c423c394a71d08261705b9d6729e756bf65924', name: "start" })), hAsync("div", { key: '0c3ea34105c7eddfa4094371c5d288c50ed10db3', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, hAsync("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)), hAsync("div", { key: '756e343cfd208bb5ad9ecf08d77cbb0a9606dc7b', class: "end-slot-wrapper" }, hAsync("slot", { key: '0eb596814a037fa4634ff8c5bac0045540edfe21', name: "end" }))), shouldRenderHighlight && hAsync("div", { key: 'df62f896eb6e0e2d1217aa487c198eb82a52bcb8', class: "textarea-highlight" })), this.renderBottomContent()));
35533
+ }) }, hAsync("label", { key: '8a2dd59a60f7469df84018eb0ede3a9ec3862703', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '1bfc368236e3da7a225a45118c27fbfc1fe5fa46', class: "textarea-wrapper-inner" }, hAsync("div", { key: '215cbb2635ff52e31a8973376989b85e7245d40f', class: "start-slot-wrapper" }, hAsync("slot", { key: '9f6b461cdee9d629deb695d2bea054ece2f32305', name: "start" })), hAsync("div", { key: 'c1af35a2d5bc452bebe0b22a26d15ff52b4e9fc8', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, hAsync("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)), hAsync("div", { key: 'c053ea8b865d0e29763aed2e4939cc9c9e374c15', class: "end-slot-wrapper" }, hAsync("slot", { key: '930aa641833b0df54b9ea10368fc2f46d5f491f6', name: "end" }))), shouldRenderHighlight && hAsync("div", { key: '8d12597d15f5f429d80e8272ea99e64ed924e482', class: "textarea-highlight" })), this.renderBottomContent()));
35272
35534
  }
35273
35535
  get el() { return getElement(this); }
35274
35536
  static get watchers() { return {
@@ -36283,6 +36545,10 @@ class Toggle {
36283
36545
  this.inheritedAttributes = {};
36284
36546
  this.didLoad = false;
36285
36547
  this.activated = false;
36548
+ /**
36549
+ * Track validation state for proper aria-live announcements.
36550
+ */
36551
+ this.isInvalid = false;
36286
36552
  /**
36287
36553
  * The name of the control, which is submitted with the form data.
36288
36554
  */
@@ -36390,22 +36656,24 @@ class Toggle {
36390
36656
  const { checked, value } = this;
36391
36657
  const isNowChecked = !checked;
36392
36658
  this.checked = isNowChecked;
36393
- this.setFocus();
36394
36659
  this.ionChange.emit({
36395
36660
  checked: isNowChecked,
36396
36661
  value,
36397
36662
  });
36398
36663
  }
36399
36664
  async connectedCallback() {
36665
+ const { didLoad, el } = this;
36400
36666
  /**
36401
36667
  * If we have not yet rendered
36402
36668
  * ion-toggle, then toggleTrack is not defined.
36403
36669
  * But if we are moving ion-toggle via appendChild,
36404
36670
  * then toggleTrack will be defined.
36405
36671
  */
36406
- if (this.didLoad) {
36672
+ if (didLoad) {
36407
36673
  this.setupGesture();
36408
36674
  }
36675
+ // Always set initial state
36676
+ this.isInvalid = checkInvalidState(el);
36409
36677
  }
36410
36678
  componentDidLoad() {
36411
36679
  this.setupGesture();
@@ -36416,9 +36684,15 @@ class Toggle {
36416
36684
  this.gesture.destroy();
36417
36685
  this.gesture = undefined;
36418
36686
  }
36687
+ // Clean up validation observer to prevent memory leaks.
36688
+ if (this.validationObserver) {
36689
+ this.validationObserver.disconnect();
36690
+ this.validationObserver = undefined;
36691
+ }
36419
36692
  }
36420
36693
  componentWillLoad() {
36421
36694
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
36695
+ this.hintTextId = this.getHintTextId();
36422
36696
  }
36423
36697
  onStart() {
36424
36698
  this.activated = true;
@@ -36441,9 +36715,7 @@ class Toggle {
36441
36715
  return this.value || '';
36442
36716
  }
36443
36717
  setFocus() {
36444
- if (this.focusEl) {
36445
- this.focusEl.focus();
36446
- }
36718
+ this.el.focus();
36447
36719
  }
36448
36720
  renderOnOffSwitchLabels(mode, checked) {
36449
36721
  const icon = this.getSwitchLabelIcon(mode, checked);
@@ -36461,9 +36733,9 @@ class Toggle {
36461
36733
  get hasLabel() {
36462
36734
  return this.el.textContent !== '';
36463
36735
  }
36464
- getHintTextID() {
36465
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
36466
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
36736
+ getHintTextId() {
36737
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
36738
+ if (isInvalid && errorText) {
36467
36739
  return errorTextId;
36468
36740
  }
36469
36741
  if (helperText) {
@@ -36476,7 +36748,7 @@ class Toggle {
36476
36748
  * This element should only be rendered if hint text is set.
36477
36749
  */
36478
36750
  renderHintText() {
36479
- const { helperText, errorText, helperTextId, errorTextId } = this;
36751
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
36480
36752
  /**
36481
36753
  * undefined and empty string values should
36482
36754
  * be treated as not having helper/error text.
@@ -36485,15 +36757,15 @@ class Toggle {
36485
36757
  if (!hasHintText) {
36486
36758
  return;
36487
36759
  }
36488
- return (hAsync("div", { class: "toggle-bottom" }, hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
36760
+ return (hAsync("div", { class: "toggle-bottom" }, hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
36489
36761
  }
36490
36762
  render() {
36491
- const { activated, alignment, checked, color, disabled, el, errorTextId, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
36763
+ const { activated, alignment, checked, color, disabled, el, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
36492
36764
  const mode = getIonMode$1(this);
36493
36765
  const value = this.getValue();
36494
36766
  const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
36495
36767
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
36496
- return (hAsync(Host, { key: '21037ea2e8326f58c84becadde475f007f931924', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === errorTextId, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, class: createColorClasses$1(color, {
36768
+ return (hAsync(Host, { key: 'f569148edd89ee041a4719ffc4733c16b05229bd', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, "aria-labelledby": hasLabel ? 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, class: createColorClasses$1(color, {
36497
36769
  [mode]: true,
36498
36770
  'in-item': hostContext('ion-item', el),
36499
36771
  'toggle-activated': activated,
@@ -36503,10 +36775,10 @@ class Toggle {
36503
36775
  [`toggle-alignment-${alignment}`]: alignment !== undefined,
36504
36776
  [`toggle-label-placement-${labelPlacement}`]: true,
36505
36777
  [`toggle-${rtl}`]: true,
36506
- }) }, hAsync("label", { key: '4d153679d118d01286f6633d1c19558a97745ff6', class: "toggle-wrapper", htmlFor: inputId }, hAsync("input", Object.assign({ key: '0dfcd4df15b8d41bec5ff5f8912503afbb7bec53', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, onFocus: () => this.onFocus(), onBlur: () => this.onBlur(), ref: (focusEl) => (this.focusEl = focusEl), required: required }, inheritedAttributes)), hAsync("div", { key: 'ffed3a07ba2ab70e5b232e6041bc3b6b34be8331', class: {
36778
+ }) }, hAsync("label", { key: '3027f2ac4be6de422a14486d847fbee77f615db1', class: "toggle-wrapper", htmlFor: inputId }, hAsync("input", Object.assign({ key: '4b0304c9e879e432b80184b4e5de37d55c11b436', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), hAsync("div", { key: '8ef265ec942e7f01ff31cbb202ed146c6bf94e02', class: {
36507
36779
  'label-text-wrapper': true,
36508
36780
  'label-text-wrapper-hidden': !hasLabel,
36509
- }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: 'd88e1e3dcdd8293f6b61f237cd7a0511dcbce300' }), this.renderHintText()), hAsync("div", { key: '0e924225f5f0caf3c88738acb6c557bd8c1b68f6', class: "native-wrapper" }, this.renderToggleControl()))));
36781
+ }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: '7b162b7dd27199cca2a4c995276a18b9f8e44aaf' }), this.renderHintText()), hAsync("div", { key: 'd13c34bd42fca01cc73ddb4ea7e471b33a282a3e', class: "native-wrapper" }, this.renderToggleControl()))));
36510
36782
  }
36511
36783
  get el() { return getElement(this); }
36512
36784
  static get watchers() { return {
@@ -36532,7 +36804,9 @@ class Toggle {
36532
36804
  "justify": [1],
36533
36805
  "alignment": [1],
36534
36806
  "required": [4],
36535
- "activated": [32]
36807
+ "activated": [32],
36808
+ "isInvalid": [32],
36809
+ "hintTextId": [32]
36536
36810
  },
36537
36811
  "$listeners$": undefined,
36538
36812
  "$lazyBundleId$": "-",