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.mjs CHANGED
@@ -4040,10 +4040,57 @@ const accordionMdCss = ":host{display:block;position:relative;width:100%;backgro
4040
4040
  class Accordion {
4041
4041
  constructor(hostRef) {
4042
4042
  registerInstance(this, hostRef);
4043
- this.updateListener = () => this.updateState(false);
4043
+ this.accordionGroupUpdateHandler = () => {
4044
+ /**
4045
+ * Determine if this update will cause an actual state change.
4046
+ * We only want to mark as "interacted" if the state is changing.
4047
+ */
4048
+ const accordionGroup = this.accordionGroupEl;
4049
+ if (accordionGroup) {
4050
+ const value = accordionGroup.value;
4051
+ const accordionValue = this.value;
4052
+ const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
4053
+ const isExpanded = this.state === 4 /* AccordionState.Expanded */ || this.state === 8 /* AccordionState.Expanding */;
4054
+ const stateWillChange = shouldExpand !== isExpanded;
4055
+ /**
4056
+ * Only mark as interacted if:
4057
+ * 1. This is not the first update we've received with a defined value
4058
+ * 2. The state is actually changing (prevents redundant updates from enabling animations)
4059
+ */
4060
+ if (this.hasReceivedFirstUpdate && stateWillChange) {
4061
+ this.hasInteracted = true;
4062
+ }
4063
+ /**
4064
+ * Only count this as the first update if the group value is defined.
4065
+ * This prevents the initial undefined value from the group's componentDidLoad
4066
+ * from being treated as the first real update.
4067
+ */
4068
+ if (value !== undefined) {
4069
+ this.hasReceivedFirstUpdate = true;
4070
+ }
4071
+ }
4072
+ this.updateState();
4073
+ };
4044
4074
  this.state = 1 /* AccordionState.Collapsed */;
4045
4075
  this.isNext = false;
4046
4076
  this.isPrevious = false;
4077
+ /**
4078
+ * Tracks whether a user-initiated interaction has occurred.
4079
+ * Animations are disabled until the first interaction happens.
4080
+ * This prevents the accordion from animating when it's programmatically
4081
+ * set to an expanded or collapsed state on initial load.
4082
+ */
4083
+ this.hasInteracted = false;
4084
+ /**
4085
+ * Tracks if this accordion has ever been expanded.
4086
+ * Used to prevent the first expansion from animating.
4087
+ */
4088
+ this.hasEverBeenExpanded = false;
4089
+ /**
4090
+ * Tracks if this accordion has received its first update from the group.
4091
+ * Used to distinguish initial programmatic sets from user interactions.
4092
+ */
4093
+ this.hasReceivedFirstUpdate = false;
4047
4094
  /**
4048
4095
  * The value of the accordion. Defaults to an autogenerated
4049
4096
  * value.
@@ -4148,10 +4195,15 @@ class Accordion {
4148
4195
  iconEl.setAttribute('aria-hidden', 'true');
4149
4196
  ionItem.appendChild(iconEl);
4150
4197
  };
4151
- this.expandAccordion = (initialUpdate = false) => {
4198
+ this.expandAccordion = () => {
4152
4199
  const { contentEl, contentElWrapper } = this;
4153
- if (initialUpdate || contentEl === undefined || contentElWrapper === undefined) {
4200
+ /**
4201
+ * If the content elements aren't available yet, just set the state.
4202
+ * This happens on initial render before the DOM is ready.
4203
+ */
4204
+ if (contentEl === undefined || contentElWrapper === undefined) {
4154
4205
  this.state = 4 /* AccordionState.Expanded */;
4206
+ this.hasEverBeenExpanded = true;
4155
4207
  return;
4156
4208
  }
4157
4209
  if (this.state === 4 /* AccordionState.Expanded */) {
@@ -4160,6 +4212,11 @@ class Accordion {
4160
4212
  if (this.currentRaf !== undefined) {
4161
4213
  cancelAnimationFrame(this.currentRaf);
4162
4214
  }
4215
+ /**
4216
+ * Mark that this accordion has been expanded at least once.
4217
+ * This allows subsequent expansions to animate.
4218
+ */
4219
+ this.hasEverBeenExpanded = true;
4163
4220
  if (this.shouldAnimate()) {
4164
4221
  raf(() => {
4165
4222
  this.state = 8 /* AccordionState.Expanding */;
@@ -4177,9 +4234,13 @@ class Accordion {
4177
4234
  this.state = 4 /* AccordionState.Expanded */;
4178
4235
  }
4179
4236
  };
4180
- this.collapseAccordion = (initialUpdate = false) => {
4237
+ this.collapseAccordion = () => {
4181
4238
  const { contentEl } = this;
4182
- if (initialUpdate || contentEl === undefined) {
4239
+ /**
4240
+ * If the content element isn't available yet, just set the state.
4241
+ * This happens on initial render before the DOM is ready.
4242
+ */
4243
+ if (contentEl === undefined) {
4183
4244
  this.state = 1 /* AccordionState.Collapsed */;
4184
4245
  return;
4185
4246
  }
@@ -4214,6 +4275,18 @@ class Accordion {
4214
4275
  * of what is set in the config.
4215
4276
  */
4216
4277
  this.shouldAnimate = () => {
4278
+ /**
4279
+ * Don't animate until after the first user interaction.
4280
+ * This prevents animations on initial load when accordions
4281
+ * start in an expanded or collapsed state programmatically.
4282
+ *
4283
+ * Additionally, don't animate the very first expansion even if
4284
+ * hasInteracted is true. This handles edge cases like React StrictMode
4285
+ * where effects run twice and might incorrectly mark as interacted.
4286
+ */
4287
+ if (!this.hasInteracted || !this.hasEverBeenExpanded) {
4288
+ return false;
4289
+ }
4217
4290
  if (typeof window === 'undefined') {
4218
4291
  return false;
4219
4292
  }
@@ -4230,7 +4303,7 @@ class Accordion {
4230
4303
  }
4231
4304
  return true;
4232
4305
  };
4233
- this.updateState = async (initialUpdate = false) => {
4306
+ this.updateState = async () => {
4234
4307
  const accordionGroup = this.accordionGroupEl;
4235
4308
  const accordionValue = this.value;
4236
4309
  if (!accordionGroup) {
@@ -4239,11 +4312,11 @@ class Accordion {
4239
4312
  const value = accordionGroup.value;
4240
4313
  const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
4241
4314
  if (shouldExpand) {
4242
- this.expandAccordion(initialUpdate);
4315
+ this.expandAccordion();
4243
4316
  this.isNext = this.isPrevious = false;
4244
4317
  }
4245
4318
  else {
4246
- this.collapseAccordion(initialUpdate);
4319
+ this.collapseAccordion();
4247
4320
  /**
4248
4321
  * When using popout or inset,
4249
4322
  * the collapsed accordion items
@@ -4291,14 +4364,14 @@ class Accordion {
4291
4364
  var _a;
4292
4365
  const accordionGroupEl = (this.accordionGroupEl = (_a = this.el) === null || _a === void 0 ? void 0 : _a.closest('ion-accordion-group'));
4293
4366
  if (accordionGroupEl) {
4294
- this.updateState(true);
4295
- addEventListener$1(accordionGroupEl, 'ionValueChange', this.updateListener);
4367
+ this.updateState();
4368
+ addEventListener$1(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
4296
4369
  }
4297
4370
  }
4298
4371
  disconnectedCallback() {
4299
4372
  const accordionGroupEl = this.accordionGroupEl;
4300
4373
  if (accordionGroupEl) {
4301
- removeEventListener(accordionGroupEl, 'ionValueChange', this.updateListener);
4374
+ removeEventListener(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
4302
4375
  }
4303
4376
  }
4304
4377
  componentDidLoad() {
@@ -4322,6 +4395,11 @@ class Accordion {
4322
4395
  const { accordionGroupEl, disabled, readonly, value, state } = this;
4323
4396
  if (disabled || readonly)
4324
4397
  return;
4398
+ /**
4399
+ * Mark that the user has interacted with the accordion.
4400
+ * This enables animations for all future state changes.
4401
+ */
4402
+ this.hasInteracted = true;
4325
4403
  if (accordionGroupEl) {
4326
4404
  /**
4327
4405
  * Because the accordion group may or may
@@ -4342,7 +4420,7 @@ class Accordion {
4342
4420
  const headerPart = expanded ? 'header expanded' : 'header';
4343
4421
  const contentPart = expanded ? 'content expanded' : 'content';
4344
4422
  this.setAria(expanded);
4345
- return (hAsync(Host, { key: '073e1d02c18dcbc20c68648426e87c14750c031d', class: {
4423
+ return (hAsync(Host, { key: '9c90bce01eff7e5774a19f69c872f3761d66cf3c', class: {
4346
4424
  [mode]: true,
4347
4425
  'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
4348
4426
  'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
@@ -4353,7 +4431,7 @@ class Accordion {
4353
4431
  'accordion-disabled': disabled,
4354
4432
  'accordion-readonly': readonly,
4355
4433
  'accordion-animated': this.shouldAnimate(),
4356
- } }, 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" })))));
4434
+ } }, 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" })))));
4357
4435
  }
4358
4436
  static get delegatesFocus() { return true; }
4359
4437
  get el() { return getElement(this); }
@@ -4375,7 +4453,8 @@ class Accordion {
4375
4453
  "toggleIconSlot": [1, "toggle-icon-slot"],
4376
4454
  "state": [32],
4377
4455
  "isNext": [32],
4378
- "isPrevious": [32]
4456
+ "isPrevious": [32],
4457
+ "hasInteracted": [32]
4379
4458
  },
4380
4459
  "$listeners$": undefined,
4381
4460
  "$lazyBundleId$": "-",
@@ -9261,11 +9340,7 @@ class Button {
9261
9340
  target,
9262
9341
  };
9263
9342
  let fill = this.fill;
9264
- /**
9265
- * We check both undefined and null to
9266
- * work around https://github.com/ionic-team/stencil/issues/3586.
9267
- */
9268
- if (fill == null) {
9343
+ if (fill === undefined) {
9269
9344
  fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
9270
9345
  }
9271
9346
  /**
@@ -9278,7 +9353,7 @@ class Button {
9278
9353
  {
9279
9354
  type !== 'button' && this.renderHiddenButton();
9280
9355
  }
9281
- return (hAsync(Host, { key: 'b105ad09215adb3ca2298acdadf0dc9154bbb9b0', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9356
+ return (hAsync(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9282
9357
  [mode]: true,
9283
9358
  [buttonType]: true,
9284
9359
  [`${buttonType}-${expand}`]: expand !== undefined,
@@ -9293,7 +9368,7 @@ class Button {
9293
9368
  'button-disabled': disabled,
9294
9369
  'ion-activatable': true,
9295
9370
  'ion-focusable': true,
9296
- }) }, 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 }))));
9371
+ }) }, 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 }))));
9297
9372
  }
9298
9373
  get el() { return getElement(this); }
9299
9374
  static get watchers() { return {
@@ -9608,6 +9683,202 @@ class CardTitle {
9608
9683
  }; }
9609
9684
  }
9610
9685
 
9686
+ /**
9687
+ * A utility to calculate the size of an outline notch
9688
+ * width relative to the content passed. This is used in
9689
+ * components such as `ion-select` with `fill="outline"`
9690
+ * where we need to pass slotted HTML content. This is not
9691
+ * needed when rendering plaintext content because we can
9692
+ * render the plaintext again hidden with `opacity: 0` inside
9693
+ * of the notch. As a result we can rely on the intrinsic size
9694
+ * of the element to correctly compute the notch width. We
9695
+ * cannot do this with slotted content because we cannot project
9696
+ * it into 2 places at once.
9697
+ *
9698
+ * @internal
9699
+ * @param el: The host element
9700
+ * @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
9701
+ * @param getLabelSlot: A function that returns a reference to the slotted content.
9702
+ */
9703
+ const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
9704
+ let notchVisibilityIO;
9705
+ const needsExplicitNotchWidth = () => {
9706
+ const notchSpacerEl = getNotchSpacerEl();
9707
+ if (
9708
+ /**
9709
+ * If the notch is not being used
9710
+ * then we do not need to set the notch width.
9711
+ */
9712
+ notchSpacerEl === undefined ||
9713
+ /**
9714
+ * If either the label property is being
9715
+ * used or the label slot is not defined,
9716
+ * then we do not need to estimate the notch width.
9717
+ */
9718
+ el.label !== undefined ||
9719
+ getLabelSlot() === null) {
9720
+ return false;
9721
+ }
9722
+ return true;
9723
+ };
9724
+ const calculateNotchWidth = () => {
9725
+ if (needsExplicitNotchWidth()) {
9726
+ /**
9727
+ * Run this the frame after
9728
+ * the browser has re-painted the host element.
9729
+ * Otherwise, the label element may have a width
9730
+ * of 0 and the IntersectionObserver will be used.
9731
+ */
9732
+ raf(() => {
9733
+ setNotchWidth();
9734
+ });
9735
+ }
9736
+ };
9737
+ /**
9738
+ * When using a label prop we can render
9739
+ * the label value inside of the notch and
9740
+ * let the browser calculate the size of the notch.
9741
+ * However, we cannot render the label slot in multiple
9742
+ * places so we need to manually calculate the notch dimension
9743
+ * based on the size of the slotted content.
9744
+ *
9745
+ * This function should only be used to set the notch width
9746
+ * on slotted label content. The notch width for label prop
9747
+ * content is automatically calculated based on the
9748
+ * intrinsic size of the label text.
9749
+ */
9750
+ const setNotchWidth = () => {
9751
+ const notchSpacerEl = getNotchSpacerEl();
9752
+ if (notchSpacerEl === undefined) {
9753
+ return;
9754
+ }
9755
+ if (!needsExplicitNotchWidth()) {
9756
+ notchSpacerEl.style.removeProperty('width');
9757
+ return;
9758
+ }
9759
+ const width = getLabelSlot().scrollWidth;
9760
+ if (
9761
+ /**
9762
+ * If the computed width of the label is 0
9763
+ * and notchSpacerEl's offsetParent is null
9764
+ * then that means the element is hidden.
9765
+ * As a result, we need to wait for the element
9766
+ * to become visible before setting the notch width.
9767
+ *
9768
+ * We do not check el.offsetParent because
9769
+ * that can be null if the host element has
9770
+ * position: fixed applied to it.
9771
+ * notchSpacerEl does not have position: fixed.
9772
+ */
9773
+ width === 0 &&
9774
+ notchSpacerEl.offsetParent === null &&
9775
+ win$1 !== undefined &&
9776
+ 'IntersectionObserver' in win$1) {
9777
+ /**
9778
+ * If there is an IO already attached
9779
+ * then that will update the notch
9780
+ * once the element becomes visible.
9781
+ * As a result, there is no need to create
9782
+ * another one.
9783
+ */
9784
+ if (notchVisibilityIO !== undefined) {
9785
+ return;
9786
+ }
9787
+ const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
9788
+ /**
9789
+ * If the element is visible then we
9790
+ * can try setting the notch width again.
9791
+ */
9792
+ if (ev[0].intersectionRatio === 1) {
9793
+ setNotchWidth();
9794
+ io.disconnect();
9795
+ notchVisibilityIO = undefined;
9796
+ }
9797
+ },
9798
+ /**
9799
+ * Set the root to be the host element
9800
+ * This causes the IO callback
9801
+ * to be fired in WebKit as soon as the element
9802
+ * is visible. If we used the default root value
9803
+ * then WebKit would only fire the IO callback
9804
+ * after any animations (such as a modal transition)
9805
+ * finished, and there would potentially be a flicker.
9806
+ */
9807
+ { threshold: 0.01, root: el }));
9808
+ io.observe(notchSpacerEl);
9809
+ return;
9810
+ }
9811
+ /**
9812
+ * If the element is visible then we can set the notch width.
9813
+ * The notch is only visible when the label is scaled,
9814
+ * which is why we multiply the width by 0.75 as this is
9815
+ * the same amount the label element is scaled by in the host CSS.
9816
+ * (See $form-control-label-stacked-scale in ionic.globals.scss).
9817
+ */
9818
+ notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
9819
+ };
9820
+ const destroy = () => {
9821
+ if (notchVisibilityIO) {
9822
+ notchVisibilityIO.disconnect();
9823
+ notchVisibilityIO = undefined;
9824
+ }
9825
+ };
9826
+ return {
9827
+ calculateNotchWidth,
9828
+ destroy,
9829
+ };
9830
+ };
9831
+
9832
+ /**
9833
+ * Uses the compareWith param to compare two values to determine if they are equal.
9834
+ *
9835
+ * @param currentValue The current value of the control.
9836
+ * @param compareValue The value to compare against.
9837
+ * @param compareWith The function or property name to use to compare values.
9838
+ */
9839
+ const compareOptions = (currentValue, compareValue, compareWith) => {
9840
+ if (typeof compareWith === 'function') {
9841
+ return compareWith(currentValue, compareValue);
9842
+ }
9843
+ else if (typeof compareWith === 'string') {
9844
+ return currentValue[compareWith] === compareValue[compareWith];
9845
+ }
9846
+ else {
9847
+ return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
9848
+ }
9849
+ };
9850
+ /**
9851
+ * Compares a value against the current value(s) to determine if it is selected.
9852
+ *
9853
+ * @param currentValue The current value of the control.
9854
+ * @param compareValue The value to compare against.
9855
+ * @param compareWith The function or property name to use to compare values.
9856
+ */
9857
+ const isOptionSelected = (currentValue, compareValue, compareWith) => {
9858
+ if (currentValue === undefined) {
9859
+ return false;
9860
+ }
9861
+ if (Array.isArray(currentValue)) {
9862
+ return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
9863
+ }
9864
+ else {
9865
+ return compareOptions(currentValue, compareValue, compareWith);
9866
+ }
9867
+ };
9868
+
9869
+ /**
9870
+ * Checks if the form element is in an invalid state based on
9871
+ * Ionic validation classes.
9872
+ *
9873
+ * @param el The form element to check.
9874
+ * @returns `true` if the element is invalid, `false` otherwise.
9875
+ */
9876
+ const checkInvalidState = (el) => {
9877
+ const hasIonTouched = el.classList.contains('ion-touched');
9878
+ const hasIonInvalid = el.classList.contains('ion-invalid');
9879
+ return hasIonTouched && hasIonInvalid;
9880
+ };
9881
+
9611
9882
  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}";
9612
9883
 
9613
9884
  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}";
@@ -9673,6 +9944,10 @@ class Checkbox {
9673
9944
  * submitting if the value is invalid.
9674
9945
  */
9675
9946
  this.required = false;
9947
+ /**
9948
+ * Track validation state for proper aria-live announcements.
9949
+ */
9950
+ this.isInvalid = false;
9676
9951
  /**
9677
9952
  * Sets the checked property and emits
9678
9953
  * the ionChange event. Use this to update the
@@ -9688,7 +9963,6 @@ class Checkbox {
9688
9963
  };
9689
9964
  this.toggleChecked = (ev) => {
9690
9965
  ev.preventDefault();
9691
- this.setFocus();
9692
9966
  this.setChecked(!this.checked);
9693
9967
  this.indeterminate = false;
9694
9968
  };
@@ -9720,18 +9994,29 @@ class Checkbox {
9720
9994
  ev.stopPropagation();
9721
9995
  };
9722
9996
  }
9997
+ connectedCallback() {
9998
+ const { el } = this;
9999
+ // Always set initial state
10000
+ this.isInvalid = checkInvalidState(el);
10001
+ }
9723
10002
  componentWillLoad() {
9724
10003
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
10004
+ this.hintTextId = this.getHintTextId();
10005
+ }
10006
+ disconnectedCallback() {
10007
+ // Clean up validation observer to prevent memory leaks.
10008
+ if (this.validationObserver) {
10009
+ this.validationObserver.disconnect();
10010
+ this.validationObserver = undefined;
10011
+ }
9725
10012
  }
9726
10013
  /** @internal */
9727
10014
  async setFocus() {
9728
- if (this.focusEl) {
9729
- this.focusEl.focus();
9730
- }
10015
+ this.el.focus();
9731
10016
  }
9732
- getHintTextID() {
9733
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
9734
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
10017
+ getHintTextId() {
10018
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
10019
+ if (isInvalid && errorText) {
9735
10020
  return errorTextId;
9736
10021
  }
9737
10022
  if (helperText) {
@@ -9744,7 +10029,7 @@ class Checkbox {
9744
10029
  * This element should only be rendered if hint text is set.
9745
10030
  */
9746
10031
  renderHintText() {
9747
- const { helperText, errorText, helperTextId, errorTextId } = this;
10032
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
9748
10033
  /**
9749
10034
  * undefined and empty string values should
9750
10035
  * be treated as not having helper/error text.
@@ -9753,7 +10038,7 @@ class Checkbox {
9753
10038
  if (!hasHintText) {
9754
10039
  return;
9755
10040
  }
9756
- 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)));
10041
+ 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)));
9757
10042
  }
9758
10043
  render() {
9759
10044
  const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
@@ -9763,7 +10048,7 @@ class Checkbox {
9763
10048
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
9764
10049
  // The host element must have a checkbox role to ensure proper VoiceOver
9765
10050
  // support in Safari for accessibility.
9766
- 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, {
10051
+ 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, {
9767
10052
  [mode]: true,
9768
10053
  'in-item': hostContext('ion-item', el),
9769
10054
  'checkbox-checked': checked,
@@ -9773,10 +10058,10 @@ class Checkbox {
9773
10058
  [`checkbox-justify-${justify}`]: justify !== undefined,
9774
10059
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
9775
10060
  [`checkbox-label-placement-${labelPlacement}`]: true,
9776
- }), 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: {
10061
+ }) }, 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: {
9777
10062
  'label-text-wrapper': true,
9778
10063
  'label-text-wrapper-hidden': !hasLabelContent,
9779
- }, 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)))));
10064
+ }, 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)))));
9780
10065
  }
9781
10066
  getSVGPath(mode, indeterminate) {
9782
10067
  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" }));
@@ -9806,6 +10091,8 @@ class Checkbox {
9806
10091
  "justify": [1],
9807
10092
  "alignment": [1],
9808
10093
  "required": [4],
10094
+ "isInvalid": [32],
10095
+ "hintTextId": [32],
9809
10096
  "setFocus": [64]
9810
10097
  },
9811
10098
  "$listeners$": undefined,
@@ -12950,17 +13237,39 @@ class Datetime {
12950
13237
  destroyKeyboardMO();
12951
13238
  }
12952
13239
  };
12953
- this.processValue = (value) => {
12954
- const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
12955
- const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
12956
- const { minParts, maxParts, workingParts, el } = this;
12957
- this.warnIfIncorrectValueUsage();
12958
- /**
12959
- * Return early if the value wasn't parsed correctly, such as
12960
- * if an improperly formatted date string was provided.
12961
- */
12962
- if (!valueToProcess) {
12963
- return;
13240
+ /**
13241
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
13242
+ * Fallback to ensure the datetime becomes ready even if
13243
+ * IntersectionObserver never reports it as intersecting.
13244
+ *
13245
+ * This is primarily used in environments where the observer
13246
+ * might not fire as expected, such as when running under
13247
+ * synthetic tests that stub IntersectionObserver.
13248
+ */
13249
+ this.ensureReadyIfVisible = () => {
13250
+ if (this.el.classList.contains('datetime-ready')) {
13251
+ return;
13252
+ }
13253
+ const rect = this.el.getBoundingClientRect();
13254
+ if (rect.width === 0 || rect.height === 0) {
13255
+ return;
13256
+ }
13257
+ this.initializeListeners();
13258
+ writeTask(() => {
13259
+ this.el.classList.add('datetime-ready');
13260
+ });
13261
+ };
13262
+ this.processValue = (value) => {
13263
+ const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
13264
+ const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
13265
+ const { minParts, maxParts, workingParts, el } = this;
13266
+ this.warnIfIncorrectValueUsage();
13267
+ /**
13268
+ * Return early if the value wasn't parsed correctly, such as
13269
+ * if an improperly formatted date string was provided.
13270
+ */
13271
+ if (!valueToProcess) {
13272
+ return;
12964
13273
  }
12965
13274
  /**
12966
13275
  * Datetime should only warn of out of bounds values
@@ -13267,6 +13576,17 @@ class Datetime {
13267
13576
  * triggering the `hiddenIO` observer below.
13268
13577
  */
13269
13578
  raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(intersectionTrackerRef));
13579
+ /**
13580
+ * TODO(FW-6931): Remove this fallback upon solving the root cause
13581
+ * Fallback: If IntersectionObserver never reports that the
13582
+ * datetime is visible but the host clearly has layout, ensure
13583
+ * we still initialize listeners and mark the component as ready.
13584
+ *
13585
+ * We schedule this after everything has had a chance to run.
13586
+ */
13587
+ setTimeout(() => {
13588
+ this.ensureReadyIfVisible();
13589
+ }, 100);
13270
13590
  /**
13271
13591
  * We need to clean up listeners when the datetime is hidden
13272
13592
  * in a popover/modal so that we can properly scroll containers
@@ -14022,7 +14342,7 @@ class Datetime {
14022
14342
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
14023
14343
  const hasWheelVariant = hasDatePresentation && preferWheel;
14024
14344
  renderHiddenInput(true, el, name, formatValue(value), disabled);
14025
- return (hAsync(Host, { key: '57492534800ea059a7c2bbd9f0059cc0b75ae8d2', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
14345
+ return (hAsync(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses$1(color, {
14026
14346
  [mode]: true,
14027
14347
  ['datetime-readonly']: readonly,
14028
14348
  ['datetime-disabled']: disabled,
@@ -14032,7 +14352,7 @@ class Datetime {
14032
14352
  [`datetime-size-${size}`]: true,
14033
14353
  [`datetime-prefer-wheel`]: hasWheelVariant,
14034
14354
  [`datetime-grid`]: isGridStyle,
14035
- })) }, hAsync("div", { key: '97dac5e5195635ac0bc5fb472b9d09e5c3c6bbc3', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
14355
+ })) }, hAsync("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
14036
14356
  }
14037
14357
  get el() { return getElement(this); }
14038
14358
  static get watchers() { return {
@@ -15227,6 +15547,8 @@ class Grid {
15227
15547
  }
15228
15548
 
15229
15549
  const TRANSITION = 'all 0.2s ease-in-out';
15550
+ const ROLE_NONE = 'none';
15551
+ const ROLE_BANNER = 'banner';
15230
15552
  const cloneElement = (tagName) => {
15231
15553
  const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
15232
15554
  if (getCachedEl !== null) {
@@ -15353,6 +15675,7 @@ const setHeaderActive = (headerIndex, active = true) => {
15353
15675
  const toolbars = headerIndex.toolbars;
15354
15676
  const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
15355
15677
  if (active) {
15678
+ headerEl.setAttribute('role', ROLE_BANNER);
15356
15679
  headerEl.classList.remove('header-collapse-condense-inactive');
15357
15680
  ionTitles.forEach((ionTitle) => {
15358
15681
  if (ionTitle) {
@@ -15361,6 +15684,16 @@ const setHeaderActive = (headerIndex, active = true) => {
15361
15684
  });
15362
15685
  }
15363
15686
  else {
15687
+ /**
15688
+ * There can only be one banner landmark per page.
15689
+ * By default, all ion-headers have the banner role.
15690
+ * This causes an accessibility issue when using a
15691
+ * condensed header since there are two ion-headers
15692
+ * on the page at once (active and inactive).
15693
+ * To solve this, the role needs to be toggled
15694
+ * based on which header is active.
15695
+ */
15696
+ headerEl.setAttribute('role', ROLE_NONE);
15364
15697
  headerEl.classList.add('header-collapse-condense-inactive');
15365
15698
  /**
15366
15699
  * The small title should only be accessed by screen readers
@@ -15420,8 +15753,32 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
15420
15753
  });
15421
15754
  });
15422
15755
  };
15756
+ /**
15757
+ * Get the role type for the ion-header.
15758
+ *
15759
+ * @param isInsideMenu If ion-header is inside ion-menu.
15760
+ * @param isCondensed If ion-header has collapse="condense".
15761
+ * @param mode The current mode.
15762
+ * @returns 'none' if inside ion-menu or if condensed in md
15763
+ * mode, otherwise 'banner'.
15764
+ */
15765
+ const getRoleType = (isInsideMenu, isCondensed, mode) => {
15766
+ // If the header is inside a menu, it should not have the banner role.
15767
+ if (isInsideMenu) {
15768
+ return ROLE_NONE;
15769
+ }
15770
+ /**
15771
+ * Only apply role="none" to `md` mode condensed headers
15772
+ * since the large header is never shown.
15773
+ */
15774
+ if (isCondensed && mode === 'md') {
15775
+ return ROLE_NONE;
15776
+ }
15777
+ // Default to banner role.
15778
+ return ROLE_BANNER;
15779
+ };
15423
15780
 
15424
- 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}";
15781
+ 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}";
15425
15782
 
15426
15783
  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}";
15427
15784
 
@@ -15564,16 +15921,17 @@ class Header {
15564
15921
  const { translucent, inheritedAttributes } = this;
15565
15922
  const mode = getIonMode$1(this);
15566
15923
  const collapse = this.collapse || 'none';
15924
+ const isCondensed = collapse === 'condense';
15567
15925
  // banner role must be at top level, so remove role if inside a menu
15568
- const roleType = hostContext('ion-menu', this.el) ? 'none' : 'banner';
15569
- return (hAsync(Host, Object.assign({ key: 'b6cc27f0b08afc9fcc889683525da765d80ba672', role: roleType, class: {
15926
+ const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
15927
+ return (hAsync(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
15570
15928
  [mode]: true,
15571
15929
  // Used internally for styling
15572
15930
  [`header-${mode}`]: true,
15573
15931
  [`header-translucent`]: this.translucent,
15574
15932
  [`header-collapse-${collapse}`]: true,
15575
15933
  [`header-translucent-${mode}`]: this.translucent,
15576
- } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '395766d4dcee3398bc91960db21f922095292f14', class: "header-background" }), hAsync("slot", { key: '09a67ece27b258ff1248805d43d92a49b2c6859a' })));
15934
+ } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), hAsync("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
15577
15935
  }
15578
15936
  get el() { return getElement(this); }
15579
15937
  static get style() { return {
@@ -16068,322 +16426,139 @@ class InfiniteScroll {
16068
16426
  if (this.position === 'top') {
16069
16427
  /**
16070
16428
  * New content is being added at the top, but the scrollTop position stays the same,
16071
- * which causes a scroll jump visually. This algorithm makes sure to prevent this.
16072
- * (Frame 1)
16073
- * - complete() is called, but the UI hasn't had time to update yet.
16074
- * - Save the current content dimensions.
16075
- * - Wait for the next frame using _dom.read, so the UI will be updated.
16076
- * (Frame 2)
16077
- * - Read the new content dimensions.
16078
- * - Calculate the height difference and the new scroll position.
16079
- * - Delay the scroll position change until other possible dom reads are done using _dom.write to be performant.
16080
- * (Still frame 2, if I'm correct)
16081
- * - Change the scroll position (= visually maintain the scroll position).
16082
- * - Change the state to re-enable the InfiniteScroll.
16083
- * - This should be after changing the scroll position, or it could
16084
- * cause the InfiniteScroll to be triggered again immediately.
16085
- * (Frame 3)
16086
- * Done.
16087
- */
16088
- this.isBusy = true;
16089
- // ******** DOM READ ****************
16090
- // Save the current content dimensions before the UI updates
16091
- const prev = scrollEl.scrollHeight - scrollEl.scrollTop;
16092
- // ******** DOM READ ****************
16093
- requestAnimationFrame(() => {
16094
- readTask(() => {
16095
- // UI has updated, save the new content dimensions
16096
- const scrollHeight = scrollEl.scrollHeight;
16097
- // New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
16098
- const newScrollTop = scrollHeight - prev;
16099
- // ******** DOM WRITE ****************
16100
- requestAnimationFrame(() => {
16101
- writeTask(() => {
16102
- scrollEl.scrollTop = newScrollTop;
16103
- this.isBusy = false;
16104
- this.didFire = false;
16105
- });
16106
- });
16107
- });
16108
- });
16109
- }
16110
- else {
16111
- this.didFire = false;
16112
- }
16113
- }
16114
- canStart() {
16115
- return !this.disabled && !this.isBusy && !!this.scrollEl && !this.isLoading;
16116
- }
16117
- enableScrollEvents(shouldListen) {
16118
- if (this.scrollEl) {
16119
- if (shouldListen) {
16120
- this.scrollEl.addEventListener('scroll', this.onScroll);
16121
- }
16122
- else {
16123
- this.scrollEl.removeEventListener('scroll', this.onScroll);
16124
- }
16125
- }
16126
- }
16127
- render() {
16128
- const mode = getIonMode$1(this);
16129
- const disabled = this.disabled;
16130
- return (hAsync(Host, { key: 'e844956795f69be33396ce4480aa7a54ad01b28c', class: {
16131
- [mode]: true,
16132
- 'infinite-scroll-loading': this.isLoading,
16133
- 'infinite-scroll-enabled': !disabled,
16134
- } }));
16135
- }
16136
- get el() { return getElement(this); }
16137
- static get watchers() { return {
16138
- "threshold": ["thresholdChanged"],
16139
- "disabled": ["disabledChanged"]
16140
- }; }
16141
- static get style() { return infiniteScrollCss; }
16142
- static get cmpMeta() { return {
16143
- "$flags$": 256,
16144
- "$tagName$": "ion-infinite-scroll",
16145
- "$members$": {
16146
- "threshold": [1],
16147
- "disabled": [4],
16148
- "position": [1],
16149
- "isLoading": [32],
16150
- "complete": [64]
16151
- },
16152
- "$listeners$": undefined,
16153
- "$lazyBundleId$": "-",
16154
- "$attrsToReflect$": []
16155
- }; }
16156
- }
16157
-
16158
- 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))}";
16159
-
16160
- 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))}";
16161
-
16162
- class InfiniteScrollContent {
16163
- constructor(hostRef) {
16164
- registerInstance(this, hostRef);
16165
- this.customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
16166
- }
16167
- componentDidLoad() {
16168
- if (this.loadingSpinner === undefined) {
16169
- const mode = getIonMode$1(this);
16170
- this.loadingSpinner = config.get('infiniteLoadingSpinner', config.get('spinner', mode === 'ios' ? 'lines' : 'crescent'));
16171
- }
16172
- }
16173
- renderLoadingText() {
16174
- const { customHTMLEnabled, loadingText } = this;
16175
- if (customHTMLEnabled) {
16176
- return hAsync("div", { class: "infinite-loading-text", innerHTML: sanitizeDOMString(loadingText) });
16177
- }
16178
- return hAsync("div", { class: "infinite-loading-text" }, this.loadingText);
16179
- }
16180
- render() {
16181
- const mode = getIonMode$1(this);
16182
- return (hAsync(Host, { key: '7c16060dcfe2a0b0fb3e2f8f4c449589a76f1baa', class: {
16183
- [mode]: true,
16184
- // Used internally for styling
16185
- [`infinite-scroll-content-${mode}`]: true,
16186
- } }, 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())));
16187
- }
16188
- static get style() { return {
16189
- ios: infiniteScrollContentIosCss,
16190
- md: infiniteScrollContentMdCss
16191
- }; }
16192
- static get cmpMeta() { return {
16193
- "$flags$": 288,
16194
- "$tagName$": "ion-infinite-scroll-content",
16195
- "$members$": {
16196
- "loadingSpinner": [1025, "loading-spinner"],
16197
- "loadingText": [1, "loading-text"]
16198
- },
16199
- "$listeners$": undefined,
16200
- "$lazyBundleId$": "-",
16201
- "$attrsToReflect$": []
16202
- }; }
16203
- }
16204
-
16205
- /**
16206
- * A utility to calculate the size of an outline notch
16207
- * width relative to the content passed. This is used in
16208
- * components such as `ion-select` with `fill="outline"`
16209
- * where we need to pass slotted HTML content. This is not
16210
- * needed when rendering plaintext content because we can
16211
- * render the plaintext again hidden with `opacity: 0` inside
16212
- * of the notch. As a result we can rely on the intrinsic size
16213
- * of the element to correctly compute the notch width. We
16214
- * cannot do this with slotted content because we cannot project
16215
- * it into 2 places at once.
16216
- *
16217
- * @internal
16218
- * @param el: The host element
16219
- * @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
16220
- * @param getLabelSlot: A function that returns a reference to the slotted content.
16221
- */
16222
- const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
16223
- let notchVisibilityIO;
16224
- const needsExplicitNotchWidth = () => {
16225
- const notchSpacerEl = getNotchSpacerEl();
16226
- if (
16227
- /**
16228
- * If the notch is not being used
16229
- * then we do not need to set the notch width.
16230
- */
16231
- notchSpacerEl === undefined ||
16232
- /**
16233
- * If either the label property is being
16234
- * used or the label slot is not defined,
16235
- * then we do not need to estimate the notch width.
16236
- */
16237
- el.label !== undefined ||
16238
- getLabelSlot() === null) {
16239
- return false;
16240
- }
16241
- return true;
16242
- };
16243
- const calculateNotchWidth = () => {
16244
- if (needsExplicitNotchWidth()) {
16245
- /**
16246
- * Run this the frame after
16247
- * the browser has re-painted the host element.
16248
- * Otherwise, the label element may have a width
16249
- * of 0 and the IntersectionObserver will be used.
16250
- */
16251
- raf(() => {
16252
- setNotchWidth();
16253
- });
16254
- }
16255
- };
16256
- /**
16257
- * When using a label prop we can render
16258
- * the label value inside of the notch and
16259
- * let the browser calculate the size of the notch.
16260
- * However, we cannot render the label slot in multiple
16261
- * places so we need to manually calculate the notch dimension
16262
- * based on the size of the slotted content.
16263
- *
16264
- * This function should only be used to set the notch width
16265
- * on slotted label content. The notch width for label prop
16266
- * content is automatically calculated based on the
16267
- * intrinsic size of the label text.
16268
- */
16269
- const setNotchWidth = () => {
16270
- const notchSpacerEl = getNotchSpacerEl();
16271
- if (notchSpacerEl === undefined) {
16272
- return;
16273
- }
16274
- if (!needsExplicitNotchWidth()) {
16275
- notchSpacerEl.style.removeProperty('width');
16276
- return;
16277
- }
16278
- const width = getLabelSlot().scrollWidth;
16279
- if (
16280
- /**
16281
- * If the computed width of the label is 0
16282
- * and notchSpacerEl's offsetParent is null
16283
- * then that means the element is hidden.
16284
- * As a result, we need to wait for the element
16285
- * to become visible before setting the notch width.
16286
- *
16287
- * We do not check el.offsetParent because
16288
- * that can be null if the host element has
16289
- * position: fixed applied to it.
16290
- * notchSpacerEl does not have position: fixed.
16291
- */
16292
- width === 0 &&
16293
- notchSpacerEl.offsetParent === null &&
16294
- win$1 !== undefined &&
16295
- 'IntersectionObserver' in win$1) {
16296
- /**
16297
- * If there is an IO already attached
16298
- * then that will update the notch
16299
- * once the element becomes visible.
16300
- * As a result, there is no need to create
16301
- * another one.
16302
- */
16303
- if (notchVisibilityIO !== undefined) {
16304
- return;
16305
- }
16306
- const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
16307
- /**
16308
- * If the element is visible then we
16309
- * can try setting the notch width again.
16310
- */
16311
- if (ev[0].intersectionRatio === 1) {
16312
- setNotchWidth();
16313
- io.disconnect();
16314
- notchVisibilityIO = undefined;
16315
- }
16316
- },
16317
- /**
16318
- * Set the root to be the host element
16319
- * This causes the IO callback
16320
- * to be fired in WebKit as soon as the element
16321
- * is visible. If we used the default root value
16322
- * then WebKit would only fire the IO callback
16323
- * after any animations (such as a modal transition)
16324
- * finished, and there would potentially be a flicker.
16429
+ * which causes a scroll jump visually. This algorithm makes sure to prevent this.
16430
+ * (Frame 1)
16431
+ * - complete() is called, but the UI hasn't had time to update yet.
16432
+ * - Save the current content dimensions.
16433
+ * - Wait for the next frame using _dom.read, so the UI will be updated.
16434
+ * (Frame 2)
16435
+ * - Read the new content dimensions.
16436
+ * - Calculate the height difference and the new scroll position.
16437
+ * - Delay the scroll position change until other possible dom reads are done using _dom.write to be performant.
16438
+ * (Still frame 2, if I'm correct)
16439
+ * - Change the scroll position (= visually maintain the scroll position).
16440
+ * - Change the state to re-enable the InfiniteScroll.
16441
+ * - This should be after changing the scroll position, or it could
16442
+ * cause the InfiniteScroll to be triggered again immediately.
16443
+ * (Frame 3)
16444
+ * Done.
16325
16445
  */
16326
- { threshold: 0.01, root: el }));
16327
- io.observe(notchSpacerEl);
16328
- return;
16446
+ this.isBusy = true;
16447
+ // ******** DOM READ ****************
16448
+ // Save the current content dimensions before the UI updates
16449
+ const prev = scrollEl.scrollHeight - scrollEl.scrollTop;
16450
+ // ******** DOM READ ****************
16451
+ requestAnimationFrame(() => {
16452
+ readTask(() => {
16453
+ // UI has updated, save the new content dimensions
16454
+ const scrollHeight = scrollEl.scrollHeight;
16455
+ // New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
16456
+ const newScrollTop = scrollHeight - prev;
16457
+ // ******** DOM WRITE ****************
16458
+ requestAnimationFrame(() => {
16459
+ writeTask(() => {
16460
+ scrollEl.scrollTop = newScrollTop;
16461
+ this.isBusy = false;
16462
+ this.didFire = false;
16463
+ });
16464
+ });
16465
+ });
16466
+ });
16329
16467
  }
16330
- /**
16331
- * If the element is visible then we can set the notch width.
16332
- * The notch is only visible when the label is scaled,
16333
- * which is why we multiply the width by 0.75 as this is
16334
- * the same amount the label element is scaled by in the host CSS.
16335
- * (See $form-control-label-stacked-scale in ionic.globals.scss).
16336
- */
16337
- notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
16338
- };
16339
- const destroy = () => {
16340
- if (notchVisibilityIO) {
16341
- notchVisibilityIO.disconnect();
16342
- notchVisibilityIO = undefined;
16468
+ else {
16469
+ this.didFire = false;
16343
16470
  }
16344
- };
16345
- return {
16346
- calculateNotchWidth,
16347
- destroy,
16348
- };
16349
- };
16350
-
16351
- /**
16352
- * Uses the compareWith param to compare two values to determine if they are equal.
16353
- *
16354
- * @param currentValue The current value of the control.
16355
- * @param compareValue The value to compare against.
16356
- * @param compareWith The function or property name to use to compare values.
16357
- */
16358
- const compareOptions = (currentValue, compareValue, compareWith) => {
16359
- if (typeof compareWith === 'function') {
16360
- return compareWith(currentValue, compareValue);
16361
16471
  }
16362
- else if (typeof compareWith === 'string') {
16363
- return currentValue[compareWith] === compareValue[compareWith];
16472
+ canStart() {
16473
+ return !this.disabled && !this.isBusy && !!this.scrollEl && !this.isLoading;
16364
16474
  }
16365
- else {
16366
- return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
16475
+ enableScrollEvents(shouldListen) {
16476
+ if (this.scrollEl) {
16477
+ if (shouldListen) {
16478
+ this.scrollEl.addEventListener('scroll', this.onScroll);
16479
+ }
16480
+ else {
16481
+ this.scrollEl.removeEventListener('scroll', this.onScroll);
16482
+ }
16483
+ }
16367
16484
  }
16368
- };
16369
- /**
16370
- * Compares a value against the current value(s) to determine if it is selected.
16371
- *
16372
- * @param currentValue The current value of the control.
16373
- * @param compareValue The value to compare against.
16374
- * @param compareWith The function or property name to use to compare values.
16375
- */
16376
- const isOptionSelected = (currentValue, compareValue, compareWith) => {
16377
- if (currentValue === undefined) {
16378
- return false;
16485
+ render() {
16486
+ const mode = getIonMode$1(this);
16487
+ const disabled = this.disabled;
16488
+ return (hAsync(Host, { key: 'e844956795f69be33396ce4480aa7a54ad01b28c', class: {
16489
+ [mode]: true,
16490
+ 'infinite-scroll-loading': this.isLoading,
16491
+ 'infinite-scroll-enabled': !disabled,
16492
+ } }));
16379
16493
  }
16380
- if (Array.isArray(currentValue)) {
16381
- return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
16494
+ get el() { return getElement(this); }
16495
+ static get watchers() { return {
16496
+ "threshold": ["thresholdChanged"],
16497
+ "disabled": ["disabledChanged"]
16498
+ }; }
16499
+ static get style() { return infiniteScrollCss; }
16500
+ static get cmpMeta() { return {
16501
+ "$flags$": 256,
16502
+ "$tagName$": "ion-infinite-scroll",
16503
+ "$members$": {
16504
+ "threshold": [1],
16505
+ "disabled": [4],
16506
+ "position": [1],
16507
+ "isLoading": [32],
16508
+ "complete": [64]
16509
+ },
16510
+ "$listeners$": undefined,
16511
+ "$lazyBundleId$": "-",
16512
+ "$attrsToReflect$": []
16513
+ }; }
16514
+ }
16515
+
16516
+ 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))}";
16517
+
16518
+ 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))}";
16519
+
16520
+ class InfiniteScrollContent {
16521
+ constructor(hostRef) {
16522
+ registerInstance(this, hostRef);
16523
+ this.customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
16382
16524
  }
16383
- else {
16384
- return compareOptions(currentValue, compareValue, compareWith);
16525
+ componentDidLoad() {
16526
+ if (this.loadingSpinner === undefined) {
16527
+ const mode = getIonMode$1(this);
16528
+ this.loadingSpinner = config.get('infiniteLoadingSpinner', config.get('spinner', mode === 'ios' ? 'lines' : 'crescent'));
16529
+ }
16385
16530
  }
16386
- };
16531
+ renderLoadingText() {
16532
+ const { customHTMLEnabled, loadingText } = this;
16533
+ if (customHTMLEnabled) {
16534
+ return hAsync("div", { class: "infinite-loading-text", innerHTML: sanitizeDOMString(loadingText) });
16535
+ }
16536
+ return hAsync("div", { class: "infinite-loading-text" }, this.loadingText);
16537
+ }
16538
+ render() {
16539
+ const mode = getIonMode$1(this);
16540
+ return (hAsync(Host, { key: '7c16060dcfe2a0b0fb3e2f8f4c449589a76f1baa', class: {
16541
+ [mode]: true,
16542
+ // Used internally for styling
16543
+ [`infinite-scroll-content-${mode}`]: true,
16544
+ } }, 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())));
16545
+ }
16546
+ static get style() { return {
16547
+ ios: infiniteScrollContentIosCss,
16548
+ md: infiniteScrollContentMdCss
16549
+ }; }
16550
+ static get cmpMeta() { return {
16551
+ "$flags$": 288,
16552
+ "$tagName$": "ion-infinite-scroll-content",
16553
+ "$members$": {
16554
+ "loadingSpinner": [1025, "loading-spinner"],
16555
+ "loadingText": [1, "loading-text"]
16556
+ },
16557
+ "$listeners$": undefined,
16558
+ "$lazyBundleId$": "-",
16559
+ "$attrsToReflect$": []
16560
+ }; }
16561
+ }
16387
16562
 
16388
16563
  /**
16389
16564
  * Used to update a scoped component that uses emulated slots. This fires when
@@ -16750,20 +16925,12 @@ class Input {
16750
16925
  componentWillLoad() {
16751
16926
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes$1(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
16752
16927
  }
16753
- /**
16754
- * Checks if the input is in an invalid state based on Ionic validation classes
16755
- */
16756
- checkInvalidState() {
16757
- const hasIonTouched = this.el.classList.contains('ion-touched');
16758
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
16759
- return hasIonTouched && hasIonInvalid;
16760
- }
16761
16928
  connectedCallback() {
16762
16929
  const { el } = this;
16763
16930
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
16764
16931
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
16765
16932
  // Always set initial state
16766
- this.isInvalid = this.checkInvalidState();
16933
+ this.isInvalid = checkInvalidState(el);
16767
16934
  this.debounceChanged();
16768
16935
  }
16769
16936
  componentDidLoad() {
@@ -17017,7 +17184,7 @@ class Input {
17017
17184
  * TODO(FW-5592): Remove hasStartEndSlots condition
17018
17185
  */
17019
17186
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
17020
- return (hAsync(Host, { key: '8a51f0300d5bc66392f9ab9a6fa0b5d388072a33', class: createColorClasses$1(this.color, {
17187
+ return (hAsync(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses$1(this.color, {
17021
17188
  [mode]: true,
17022
17189
  'has-value': hasValue,
17023
17190
  'has-focus': hasFocus,
@@ -17028,14 +17195,14 @@ class Input {
17028
17195
  'in-item': inItem,
17029
17196
  'in-item-color': hostContext('ion-item.ion-color', this.el),
17030
17197
  'input-disabled': disabled,
17031
- }) }, 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) => {
17198
+ }) }, 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) => {
17032
17199
  /**
17033
17200
  * This prevents mobile browsers from
17034
17201
  * blurring the input when the clear
17035
17202
  * button is activated.
17036
17203
  */
17037
17204
  ev.preventDefault();
17038
- }, 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()));
17205
+ }, 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()));
17039
17206
  }
17040
17207
  get el() { return getElement(this); }
17041
17208
  static get watchers() { return {
@@ -20676,11 +20843,22 @@ const iosTransitionAnimation$1 = () => Promise.resolve().then(function () { retu
20676
20843
  const mdTransitionAnimation$1 = () => Promise.resolve().then(function () { return md_transition; });
20677
20844
  const focusController = createFocusController();
20678
20845
  // TODO(FW-2832): types
20846
+ /**
20847
+ * Executes the main page transition.
20848
+ * It also manages the lifecycle of header visibility (if any)
20849
+ * to prevent visual flickering in iOS. The flickering only
20850
+ * occurs for a condensed header that is placed above the content.
20851
+ *
20852
+ * @param opts Options for the transition.
20853
+ * @returns A promise that resolves when the transition is complete.
20854
+ */
20679
20855
  const transition = (opts) => {
20680
20856
  return new Promise((resolve, reject) => {
20681
20857
  writeTask(() => {
20682
- beforeTransition(opts);
20683
- runTransition(opts).then((result) => {
20858
+ const transitioningInactiveHeader = getIosIonHeader(opts);
20859
+ beforeTransition(opts, transitioningInactiveHeader);
20860
+ runTransition(opts)
20861
+ .then((result) => {
20684
20862
  if (result.animation) {
20685
20863
  result.animation.destroy();
20686
20864
  }
@@ -20689,15 +20867,21 @@ const transition = (opts) => {
20689
20867
  }, (error) => {
20690
20868
  afterTransition(opts);
20691
20869
  reject(error);
20870
+ })
20871
+ .finally(() => {
20872
+ // Ensure that the header is restored to its original state.
20873
+ setHeaderTransitionClass(transitioningInactiveHeader, false);
20692
20874
  });
20693
20875
  });
20694
20876
  });
20695
20877
  };
20696
- const beforeTransition = (opts) => {
20878
+ const beforeTransition = (opts, transitioningInactiveHeader) => {
20697
20879
  const enteringEl = opts.enteringEl;
20698
20880
  const leavingEl = opts.leavingEl;
20699
20881
  focusController.saveViewFocus(leavingEl);
20700
20882
  setZIndex(enteringEl, leavingEl, opts.direction);
20883
+ // Prevent flickering of the header by adding a class.
20884
+ setHeaderTransitionClass(transitioningInactiveHeader, true);
20701
20885
  if (opts.showGoBack) {
20702
20886
  enteringEl.classList.add('can-go-back');
20703
20887
  }
@@ -20886,6 +21070,39 @@ const setZIndex = (enteringEl, leavingEl, direction) => {
20886
21070
  leavingEl.style.zIndex = '100';
20887
21071
  }
20888
21072
  };
21073
+ /**
21074
+ * Add a class to ensure that the header (if any)
21075
+ * does not flicker during the transition. By adding the
21076
+ * transitioning class, we ensure that the header has
21077
+ * the necessary styles to prevent the following flickers:
21078
+ * 1. When entering a page with a condensed header, the
21079
+ * header should never be visible. However,
21080
+ * it briefly renders the background color while
21081
+ * the transition is occurring.
21082
+ * 2. When leaving a page with a condensed header, the
21083
+ * header has an opacity of 0 and the pages
21084
+ * have a z-index which causes the entering page to
21085
+ * briefly show it's content underneath the leaving page.
21086
+ * 3. When entering a page or leaving a page with a fade
21087
+ * header, the header should not have a background color.
21088
+ * However, it briefly shows the background color while
21089
+ * the transition is occurring.
21090
+ *
21091
+ * @param header The header element to modify.
21092
+ * @param isTransitioning Whether the transition is occurring.
21093
+ */
21094
+ const setHeaderTransitionClass = (header, isTransitioning) => {
21095
+ if (!header) {
21096
+ return;
21097
+ }
21098
+ const transitionClass = 'header-transitioning';
21099
+ if (isTransitioning) {
21100
+ header.classList.add(transitionClass);
21101
+ }
21102
+ else {
21103
+ header.classList.remove(transitionClass);
21104
+ }
21105
+ };
20889
21106
  const getIonPageElement = (element) => {
20890
21107
  if (element.classList.contains('ion-page')) {
20891
21108
  return element;
@@ -20897,6 +21114,27 @@ const getIonPageElement = (element) => {
20897
21114
  // idk, return the original element so at least something animates and we don't have a null pointer
20898
21115
  return element;
20899
21116
  };
21117
+ /**
21118
+ * Retrieves the ion-header element from a page based on the
21119
+ * direction of the transition.
21120
+ *
21121
+ * @param opts Options for the transition.
21122
+ * @returns The ion-header element or null if not found or not in 'ios' mode.
21123
+ */
21124
+ const getIosIonHeader = (opts) => {
21125
+ const enteringEl = opts.enteringEl;
21126
+ const leavingEl = opts.leavingEl;
21127
+ const direction = opts.direction;
21128
+ const mode = opts.mode;
21129
+ if (mode !== 'ios') {
21130
+ return null;
21131
+ }
21132
+ const element = direction === 'back' ? leavingEl : enteringEl;
21133
+ if (!element) {
21134
+ return null;
21135
+ }
21136
+ return element.querySelector('ion-header');
21137
+ };
20900
21138
 
20901
21139
  const KEYBOARD_DID_OPEN = 'ionKeyboardDidShow';
20902
21140
 
@@ -27865,6 +28103,10 @@ class RadioGroup {
27865
28103
  this.helperTextId = `${this.inputId}-helper-text`;
27866
28104
  this.errorTextId = `${this.inputId}-error-text`;
27867
28105
  this.labelId = `${this.inputId}-lbl`;
28106
+ /**
28107
+ * Track validation state for proper aria-live announcements.
28108
+ */
28109
+ this.isInvalid = false;
27868
28110
  /**
27869
28111
  * If `true`, the radios can be deselected.
27870
28112
  */
@@ -27946,6 +28188,18 @@ class RadioGroup {
27946
28188
  this.labelId = label.id = this.name + '-lbl';
27947
28189
  }
27948
28190
  }
28191
+ // Always set initial state
28192
+ this.isInvalid = checkInvalidState(this.el);
28193
+ }
28194
+ componentWillLoad() {
28195
+ this.hintTextId = this.getHintTextId();
28196
+ }
28197
+ disconnectedCallback() {
28198
+ // Clean up validation observer to prevent memory leaks.
28199
+ if (this.validationObserver) {
28200
+ this.validationObserver.disconnect();
28201
+ this.validationObserver = undefined;
28202
+ }
27949
28203
  }
27950
28204
  getRadios() {
27951
28205
  return Array.from(this.el.querySelectorAll('ion-radio'));
@@ -28021,16 +28275,16 @@ class RadioGroup {
28021
28275
  * Renders the helper text or error text values
28022
28276
  */
28023
28277
  renderHintText() {
28024
- const { helperText, errorText, helperTextId, errorTextId } = this;
28278
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
28025
28279
  const hasHintText = !!helperText || !!errorText;
28026
28280
  if (!hasHintText) {
28027
28281
  return;
28028
28282
  }
28029
- return (hAsync("div", { class: "radio-group-top" }, hAsync("div", { id: helperTextId, class: "helper-text" }, helperText), hAsync("div", { id: errorTextId, class: "error-text" }, errorText)));
28283
+ 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)));
28030
28284
  }
28031
- getHintTextID() {
28032
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
28033
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
28285
+ getHintTextId() {
28286
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
28287
+ if (isInvalid && errorText) {
28034
28288
  return errorTextId;
28035
28289
  }
28036
28290
  if (helperText) {
@@ -28042,7 +28296,7 @@ class RadioGroup {
28042
28296
  const { label, labelId, el, name, value } = this;
28043
28297
  const mode = getIonMode$1(this);
28044
28298
  renderHiddenInput(true, el, name, value, false);
28045
- 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' }))));
28299
+ 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' }))));
28046
28300
  }
28047
28301
  get el() { return getElement(this); }
28048
28302
  static get watchers() { return {
@@ -28062,6 +28316,8 @@ class RadioGroup {
28062
28316
  "value": [1032],
28063
28317
  "helperText": [1, "helper-text"],
28064
28318
  "errorText": [1, "error-text"],
28319
+ "isInvalid": [32],
28320
+ "hintTextId": [32],
28065
28321
  "setFocus": [64]
28066
28322
  },
28067
28323
  "$listeners$": [[4, "keydown", "onKeydown"]],
@@ -33059,6 +33315,10 @@ class Select {
33059
33315
  * is applied in both cases.
33060
33316
  */
33061
33317
  this.hasFocus = false;
33318
+ /**
33319
+ * Track validation state for proper aria-live announcements.
33320
+ */
33321
+ this.isInvalid = false;
33062
33322
  /**
33063
33323
  * The text to display on the cancel button.
33064
33324
  */
@@ -33181,9 +33441,12 @@ class Select {
33181
33441
  this.mutationO = watchForOptions(this.el, 'ion-select-option', async () => {
33182
33442
  this.updateOverlayOptions();
33183
33443
  });
33444
+ // Always set initial state
33445
+ this.isInvalid = checkInvalidState(this.el);
33184
33446
  }
33185
33447
  componentWillLoad() {
33186
33448
  this.inheritedAttributes = inheritAttributes$1(this.el, ['aria-label']);
33449
+ this.hintTextId = this.getHintTextId();
33187
33450
  }
33188
33451
  componentDidLoad() {
33189
33452
  /**
@@ -33207,6 +33470,11 @@ class Select {
33207
33470
  this.notchController.destroy();
33208
33471
  this.notchController = undefined;
33209
33472
  }
33473
+ // Clean up validation observer to prevent memory leaks.
33474
+ if (this.validationObserver) {
33475
+ this.validationObserver.disconnect();
33476
+ this.validationObserver = undefined;
33477
+ }
33210
33478
  }
33211
33479
  /**
33212
33480
  * Open the select overlay. The overlay is either an alert, action sheet, or popover,
@@ -33677,11 +33945,11 @@ class Select {
33677
33945
  }
33678
33946
  renderListbox() {
33679
33947
  const { disabled, inputId, isExpanded, required } = this;
33680
- 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) }));
33948
+ 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) }));
33681
33949
  }
33682
- getHintTextID() {
33683
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
33684
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
33950
+ getHintTextId() {
33951
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33952
+ if (isInvalid && errorText) {
33685
33953
  return errorTextId;
33686
33954
  }
33687
33955
  if (helperText) {
@@ -33693,10 +33961,10 @@ class Select {
33693
33961
  * Renders the helper text or error text values
33694
33962
  */
33695
33963
  renderHintText() {
33696
- const { helperText, errorText, helperTextId, errorTextId } = this;
33964
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33697
33965
  return [
33698
- hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
33699
- hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
33966
+ hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
33967
+ hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
33700
33968
  ];
33701
33969
  }
33702
33970
  /**
@@ -33744,7 +34012,7 @@ class Select {
33744
34012
  * TODO(FW-5592): Remove hasStartEndSlots condition
33745
34013
  */
33746
34014
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
33747
- return (hAsync(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses$1(this.color, {
34015
+ return (hAsync(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses$1(this.color, {
33748
34016
  [mode]: true,
33749
34017
  'in-item': inItem,
33750
34018
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -33762,7 +34030,7 @@ class Select {
33762
34030
  [`select-justify-${justify}`]: justifyEnabled,
33763
34031
  [`select-shape-${shape}`]: shape !== undefined,
33764
34032
  [`select-label-placement-${labelPlacement}`]: true,
33765
- }) }, 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()));
34033
+ }) }, 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()));
33766
34034
  }
33767
34035
  get el() { return getElement(this); }
33768
34036
  static get watchers() { return {
@@ -33803,6 +34071,8 @@ class Select {
33803
34071
  "required": [4],
33804
34072
  "isExpanded": [32],
33805
34073
  "hasFocus": [32],
34074
+ "isInvalid": [32],
34075
+ "hintTextId": [32],
33806
34076
  "open": [64]
33807
34077
  },
33808
34078
  "$listeners$": undefined,
@@ -34990,20 +35260,12 @@ class Textarea {
34990
35260
  this.el.click();
34991
35261
  }
34992
35262
  }
34993
- /**
34994
- * Checks if the textarea is in an invalid state based on Ionic validation classes
34995
- */
34996
- checkValidationState() {
34997
- const hasIonTouched = this.el.classList.contains('ion-touched');
34998
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
34999
- return hasIonTouched && hasIonInvalid;
35000
- }
35001
35263
  connectedCallback() {
35002
35264
  const { el } = this;
35003
35265
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
35004
35266
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
35005
35267
  // Always set initial state
35006
- this.isInvalid = this.checkValidationState();
35268
+ this.isInvalid = checkInvalidState(this.el);
35007
35269
  this.debounceChanged();
35008
35270
  }
35009
35271
  disconnectedCallback() {
@@ -35257,7 +35519,7 @@ class Textarea {
35257
35519
  * TODO(FW-5592): Remove hasStartEndSlots condition
35258
35520
  */
35259
35521
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
35260
- return (hAsync(Host, { key: '26b46666a92b3f652775bb1c46661f9a30392104', class: createColorClasses$1(this.color, {
35522
+ return (hAsync(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses$1(this.color, {
35261
35523
  [mode]: true,
35262
35524
  'has-value': hasValue,
35263
35525
  'has-focus': hasFocus,
@@ -35266,7 +35528,7 @@ class Textarea {
35266
35528
  [`textarea-shape-${shape}`]: shape !== undefined,
35267
35529
  [`textarea-label-placement-${labelPlacement}`]: true,
35268
35530
  'textarea-disabled': disabled,
35269
- }) }, 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()));
35531
+ }) }, 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()));
35270
35532
  }
35271
35533
  get el() { return getElement(this); }
35272
35534
  static get watchers() { return {
@@ -36281,6 +36543,10 @@ class Toggle {
36281
36543
  this.inheritedAttributes = {};
36282
36544
  this.didLoad = false;
36283
36545
  this.activated = false;
36546
+ /**
36547
+ * Track validation state for proper aria-live announcements.
36548
+ */
36549
+ this.isInvalid = false;
36284
36550
  /**
36285
36551
  * The name of the control, which is submitted with the form data.
36286
36552
  */
@@ -36388,22 +36654,24 @@ class Toggle {
36388
36654
  const { checked, value } = this;
36389
36655
  const isNowChecked = !checked;
36390
36656
  this.checked = isNowChecked;
36391
- this.setFocus();
36392
36657
  this.ionChange.emit({
36393
36658
  checked: isNowChecked,
36394
36659
  value,
36395
36660
  });
36396
36661
  }
36397
36662
  async connectedCallback() {
36663
+ const { didLoad, el } = this;
36398
36664
  /**
36399
36665
  * If we have not yet rendered
36400
36666
  * ion-toggle, then toggleTrack is not defined.
36401
36667
  * But if we are moving ion-toggle via appendChild,
36402
36668
  * then toggleTrack will be defined.
36403
36669
  */
36404
- if (this.didLoad) {
36670
+ if (didLoad) {
36405
36671
  this.setupGesture();
36406
36672
  }
36673
+ // Always set initial state
36674
+ this.isInvalid = checkInvalidState(el);
36407
36675
  }
36408
36676
  componentDidLoad() {
36409
36677
  this.setupGesture();
@@ -36414,9 +36682,15 @@ class Toggle {
36414
36682
  this.gesture.destroy();
36415
36683
  this.gesture = undefined;
36416
36684
  }
36685
+ // Clean up validation observer to prevent memory leaks.
36686
+ if (this.validationObserver) {
36687
+ this.validationObserver.disconnect();
36688
+ this.validationObserver = undefined;
36689
+ }
36417
36690
  }
36418
36691
  componentWillLoad() {
36419
36692
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
36693
+ this.hintTextId = this.getHintTextId();
36420
36694
  }
36421
36695
  onStart() {
36422
36696
  this.activated = true;
@@ -36439,9 +36713,7 @@ class Toggle {
36439
36713
  return this.value || '';
36440
36714
  }
36441
36715
  setFocus() {
36442
- if (this.focusEl) {
36443
- this.focusEl.focus();
36444
- }
36716
+ this.el.focus();
36445
36717
  }
36446
36718
  renderOnOffSwitchLabels(mode, checked) {
36447
36719
  const icon = this.getSwitchLabelIcon(mode, checked);
@@ -36459,9 +36731,9 @@ class Toggle {
36459
36731
  get hasLabel() {
36460
36732
  return this.el.textContent !== '';
36461
36733
  }
36462
- getHintTextID() {
36463
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
36464
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
36734
+ getHintTextId() {
36735
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
36736
+ if (isInvalid && errorText) {
36465
36737
  return errorTextId;
36466
36738
  }
36467
36739
  if (helperText) {
@@ -36474,7 +36746,7 @@ class Toggle {
36474
36746
  * This element should only be rendered if hint text is set.
36475
36747
  */
36476
36748
  renderHintText() {
36477
- const { helperText, errorText, helperTextId, errorTextId } = this;
36749
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
36478
36750
  /**
36479
36751
  * undefined and empty string values should
36480
36752
  * be treated as not having helper/error text.
@@ -36483,15 +36755,15 @@ class Toggle {
36483
36755
  if (!hasHintText) {
36484
36756
  return;
36485
36757
  }
36486
- 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)));
36758
+ 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)));
36487
36759
  }
36488
36760
  render() {
36489
- const { activated, alignment, checked, color, disabled, el, errorTextId, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
36761
+ const { activated, alignment, checked, color, disabled, el, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
36490
36762
  const mode = getIonMode$1(this);
36491
36763
  const value = this.getValue();
36492
36764
  const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
36493
36765
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
36494
- 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, {
36766
+ 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, {
36495
36767
  [mode]: true,
36496
36768
  'in-item': hostContext('ion-item', el),
36497
36769
  'toggle-activated': activated,
@@ -36501,10 +36773,10 @@ class Toggle {
36501
36773
  [`toggle-alignment-${alignment}`]: alignment !== undefined,
36502
36774
  [`toggle-label-placement-${labelPlacement}`]: true,
36503
36775
  [`toggle-${rtl}`]: true,
36504
- }) }, 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: {
36776
+ }) }, 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: {
36505
36777
  'label-text-wrapper': true,
36506
36778
  'label-text-wrapper-hidden': !hasLabel,
36507
- }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: 'd88e1e3dcdd8293f6b61f237cd7a0511dcbce300' }), this.renderHintText()), hAsync("div", { key: '0e924225f5f0caf3c88738acb6c557bd8c1b68f6', class: "native-wrapper" }, this.renderToggleControl()))));
36779
+ }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, hAsync("slot", { key: '7b162b7dd27199cca2a4c995276a18b9f8e44aaf' }), this.renderHintText()), hAsync("div", { key: 'd13c34bd42fca01cc73ddb4ea7e471b33a282a3e', class: "native-wrapper" }, this.renderToggleControl()))));
36508
36780
  }
36509
36781
  get el() { return getElement(this); }
36510
36782
  static get watchers() { return {
@@ -36530,7 +36802,9 @@ class Toggle {
36530
36802
  "justify": [1],
36531
36803
  "alignment": [1],
36532
36804
  "required": [4],
36533
- "activated": [32]
36805
+ "activated": [32],
36806
+ "isInvalid": [32],
36807
+ "hintTextId": [32]
36534
36808
  },
36535
36809
  "$listeners$": undefined,
36536
36810
  "$lazyBundleId$": "-",