@ionic/core 8.8.19-dev.11786730389.1a1927bf → 8.8.19-dev.11786735826.1fad7c0a

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 (289) hide show
  1. package/components/ion-action-sheet.js +1 -1
  2. package/components/ion-alert.js +1 -1
  3. package/components/ion-back-button.js +1 -1
  4. package/components/ion-button.js +1 -1
  5. package/components/ion-card.js +1 -1
  6. package/components/ion-chip.js +1 -1
  7. package/components/ion-datetime-button.js +1 -1
  8. package/components/ion-datetime.js +1 -1
  9. package/components/ion-fab-button.js +1 -1
  10. package/components/ion-infinite-scroll-content.js +1 -1
  11. package/components/ion-input-otp.js +1 -1
  12. package/components/ion-input-password-toggle.js +1 -1
  13. package/components/ion-input.js +1 -1
  14. package/components/ion-item-divider.js +1 -1
  15. package/components/ion-item-group.js +1 -1
  16. package/components/ion-item-option.js +1 -1
  17. package/components/ion-item-options.js +1 -1
  18. package/components/ion-item-sliding.js +1 -1
  19. package/components/ion-item.js +1 -1
  20. package/components/ion-label.js +1 -1
  21. package/components/ion-list-header.js +1 -1
  22. package/components/ion-list.js +1 -1
  23. package/components/ion-loading.js +1 -1
  24. package/components/ion-menu-button.js +1 -1
  25. package/components/ion-menu-toggle.js +1 -1
  26. package/components/ion-menu.js +1 -1
  27. package/components/ion-modal.js +1 -1
  28. package/components/ion-nav-link.js +1 -1
  29. package/components/ion-nav.js +1 -1
  30. package/components/ion-note.js +1 -1
  31. package/components/ion-picker-column-option.js +1 -1
  32. package/components/ion-popover.js +1 -1
  33. package/components/ion-progress-bar.js +1 -1
  34. package/components/ion-radio-group.js +1 -1
  35. package/components/ion-radio.js +1 -1
  36. package/components/ion-range.js +1 -1
  37. package/components/ion-refresher-content.js +1 -1
  38. package/components/ion-refresher.js +1 -1
  39. package/components/ion-reorder-group.js +1 -1
  40. package/components/ion-reorder.js +1 -1
  41. package/components/ion-ripple-effect.js +1 -1
  42. package/components/ion-router-link.js +1 -1
  43. package/components/ion-router-outlet.js +1 -1
  44. package/components/ion-row.js +1 -1
  45. package/components/ion-searchbar.js +1 -1
  46. package/components/ion-segment-button.js +1 -1
  47. package/components/ion-segment-content.js +1 -1
  48. package/components/ion-segment-view.js +1 -1
  49. package/components/ion-segment.js +1 -1
  50. package/components/ion-select-modal.js +1 -1
  51. package/components/ion-select-option.js +1 -1
  52. package/components/ion-select-popover.js +1 -1
  53. package/components/ion-select.js +1 -1
  54. package/components/ion-skeleton-text.js +1 -1
  55. package/components/ion-spinner.js +1 -1
  56. package/components/ion-split-pane.js +1 -1
  57. package/components/ion-tab-bar.js +1 -1
  58. package/components/ion-tab-button.js +1 -1
  59. package/components/ion-tab.js +1 -1
  60. package/components/ion-tabs.js +1 -1
  61. package/components/ion-text.js +1 -1
  62. package/components/ion-textarea.js +1 -1
  63. package/components/ion-thumbnail.js +1 -1
  64. package/components/ion-title.js +1 -1
  65. package/components/ion-toast.js +1 -1
  66. package/components/ion-toggle.js +1 -1
  67. package/components/ion-toolbar.js +1 -1
  68. package/components/{p-C1O9GEug.js → p--m5cCjbK.js} +1 -1
  69. package/components/{p-CfnGxjv-.js → p-B4uHHhPK.js} +1 -1
  70. package/components/{p-BxUIQcD9.js → p-BOvLNVKq.js} +1 -1
  71. package/components/{p-D_XD0pVT.js → p-BWBigMsI.js} +1 -1
  72. package/components/{p-xOjr4bwc.js → p-BZ4-QTes.js} +1 -1
  73. package/components/{p-tYCT3JGl.js → p-BjluhQMI.js} +1 -1
  74. package/components/{p-ClFYbgND.js → p-BlsIikdZ.js} +1 -1
  75. package/components/{p-Obm7si-x.js → p-Blug1Vxu.js} +1 -1
  76. package/components/{p-DTY4otpr.js → p-C91npEel.js} +1 -1
  77. package/components/{p-CVnwvOBD.js → p-CVb3x_r3.js} +1 -1
  78. package/components/{p-B6dUPQXq.js → p-CWLrHG8Y.js} +1 -1
  79. package/components/{p-0Pyit076.js → p-CfnNZz_d.js} +1 -1
  80. package/components/{p-DyePCNGr.js → p-DKrhJJV0.js} +1 -1
  81. package/components/p-DSzSbVP2.js +4 -0
  82. package/components/{p-BsVxQGUT.js → p-DgZOZQjJ.js} +1 -1
  83. package/components/{p-BO4sUqUv.js → p-Dmd2xqCe.js} +1 -1
  84. package/components/{p-83oP9gzn.js → p-GG5GhznE.js} +1 -1
  85. package/components/{p-Bc4wROfM.js → p-UQSiXX2Z.js} +1 -1
  86. package/components/{p-Dm7h4Nyv.js → p-qT1szVjw.js} +1 -1
  87. package/components/p-ypuYdSDB.js +4 -0
  88. package/dist/cjs/input.utils-BZdMWVrt.js +35 -0
  89. package/dist/cjs/ion-app_8.cjs.entry.js +5 -5
  90. package/dist/cjs/ion-avatar_3.cjs.entry.js +1 -1
  91. package/dist/cjs/ion-col_3.cjs.entry.js +1 -1
  92. package/dist/cjs/ion-input-otp.cjs.entry.js +3 -3
  93. package/dist/cjs/ion-input-password-toggle.cjs.entry.js +3 -3
  94. package/dist/cjs/ion-input.cjs.entry.js +42 -56
  95. package/dist/cjs/ion-item-option_3.cjs.entry.js +4 -4
  96. package/dist/cjs/ion-item_8.cjs.entry.js +12 -12
  97. package/dist/cjs/ion-loading.cjs.entry.js +2 -2
  98. package/dist/cjs/ion-menu_3.cjs.entry.js +6 -6
  99. package/dist/cjs/ion-modal.cjs.entry.js +4 -4
  100. package/dist/cjs/ion-nav_2.cjs.entry.js +2 -2
  101. package/dist/cjs/ion-picker-column-option.cjs.entry.js +2 -2
  102. package/dist/cjs/ion-popover.cjs.entry.js +2 -2
  103. package/dist/cjs/ion-progress-bar.cjs.entry.js +1 -1
  104. package/dist/cjs/ion-radio_2.cjs.entry.js +4 -4
  105. package/dist/cjs/ion-range.cjs.entry.js +3 -3
  106. package/dist/cjs/ion-refresher_2.cjs.entry.js +2 -2
  107. package/dist/cjs/ion-reorder_2.cjs.entry.js +2 -2
  108. package/dist/cjs/ion-ripple-effect.cjs.entry.js +1 -1
  109. package/dist/cjs/ion-route_4.cjs.entry.js +2 -2
  110. package/dist/cjs/ion-searchbar.cjs.entry.js +4 -4
  111. package/dist/cjs/ion-segment-content.cjs.entry.js +1 -1
  112. package/dist/cjs/ion-segment-view.cjs.entry.js +2 -2
  113. package/dist/cjs/ion-segment_2.cjs.entry.js +4 -4
  114. package/dist/cjs/ion-select-modal.cjs.entry.js +1 -1
  115. package/dist/cjs/ion-select_3.cjs.entry.js +41 -59
  116. package/dist/cjs/ion-spinner.cjs.entry.js +1 -1
  117. package/dist/cjs/ion-split-pane.cjs.entry.js +2 -2
  118. package/dist/cjs/ion-tab-bar_2.cjs.entry.js +4 -4
  119. package/dist/cjs/ion-tab_2.cjs.entry.js +3 -3
  120. package/dist/cjs/ion-text.cjs.entry.js +2 -2
  121. package/dist/cjs/ion-textarea.cjs.entry.js +44 -55
  122. package/dist/cjs/ion-toast.cjs.entry.js +2 -2
  123. package/dist/cjs/ion-toggle.cjs.entry.js +4 -4
  124. package/dist/cjs/slot-mutation-controller-CgTGFqlW.js +397 -0
  125. package/dist/collection/components/input/input.ios.css +56 -24
  126. package/dist/collection/components/input/input.js +36 -51
  127. package/dist/collection/components/input/input.md.css +78 -41
  128. package/dist/collection/components/input-otp/input-otp.js +3 -3
  129. package/dist/collection/components/input-password-toggle/input-password-toggle.js +3 -3
  130. package/dist/collection/components/item-divider/item-divider.js +2 -2
  131. package/dist/collection/components/item-group/item-group.js +1 -1
  132. package/dist/collection/components/item-option/item-option.js +2 -2
  133. package/dist/collection/components/item-options/item-options.js +1 -1
  134. package/dist/collection/components/item-sliding/item-sliding.js +1 -1
  135. package/dist/collection/components/label/label.js +2 -2
  136. package/dist/collection/components/list/list.js +1 -1
  137. package/dist/collection/components/list-header/list-header.js +2 -2
  138. package/dist/collection/components/loading/loading.js +2 -2
  139. package/dist/collection/components/menu/menu.js +2 -2
  140. package/dist/collection/components/menu-button/menu-button.js +2 -2
  141. package/dist/collection/components/menu-toggle/menu-toggle.js +2 -2
  142. package/dist/collection/components/modal/modal.js +4 -4
  143. package/dist/collection/components/nav/nav.js +1 -1
  144. package/dist/collection/components/nav-link/nav-link.js +1 -1
  145. package/dist/collection/components/note/note.js +2 -2
  146. package/dist/collection/components/picker-column-option/picker-column-option.js +2 -2
  147. package/dist/collection/components/popover/popover.js +2 -2
  148. package/dist/collection/components/progress-bar/progress-bar.js +1 -1
  149. package/dist/collection/components/radio/radio.js +3 -3
  150. package/dist/collection/components/radio-group/radio-group.js +1 -1
  151. package/dist/collection/components/range/range.js +3 -3
  152. package/dist/collection/components/refresher/refresher.js +1 -1
  153. package/dist/collection/components/refresher-content/refresher-content.js +1 -1
  154. package/dist/collection/components/reorder/reorder.js +1 -1
  155. package/dist/collection/components/reorder-group/reorder-group.js +1 -1
  156. package/dist/collection/components/ripple-effect/ripple-effect.js +1 -1
  157. package/dist/collection/components/router-link/router-link.js +2 -2
  158. package/dist/collection/components/router-outlet/router-outlet.js +1 -1
  159. package/dist/collection/components/row/row.js +1 -1
  160. package/dist/collection/components/searchbar/searchbar.js +4 -4
  161. package/dist/collection/components/segment/segment.js +2 -2
  162. package/dist/collection/components/segment-button/segment-button.js +2 -2
  163. package/dist/collection/components/segment-content/segment-content.js +1 -1
  164. package/dist/collection/components/segment-view/segment-view.js +2 -2
  165. package/dist/collection/components/select/select.ios.css +63 -69
  166. package/dist/collection/components/select/select.js +40 -55
  167. package/dist/collection/components/select/select.md.css +85 -85
  168. package/dist/collection/components/select-modal/select-modal.js +1 -1
  169. package/dist/collection/components/select-option/select-option.js +1 -1
  170. package/dist/collection/components/select-popover/select-popover.js +1 -1
  171. package/dist/collection/components/skeleton-text/skeleton-text.js +2 -2
  172. package/dist/collection/components/spinner/spinner.js +1 -1
  173. package/dist/collection/components/split-pane/split-pane.js +2 -2
  174. package/dist/collection/components/tab/tab.js +2 -2
  175. package/dist/collection/components/tab-bar/tab-bar.js +2 -2
  176. package/dist/collection/components/tab-button/tab-button.js +2 -2
  177. package/dist/collection/components/tabs/tabs.js +1 -1
  178. package/dist/collection/components/text/text.js +2 -2
  179. package/dist/collection/components/textarea/textarea.ios.css +94 -62
  180. package/dist/collection/components/textarea/textarea.js +39 -51
  181. package/dist/collection/components/textarea/textarea.md.css +103 -77
  182. package/dist/collection/components/thumbnail/thumbnail.js +1 -1
  183. package/dist/collection/components/title/title.js +2 -2
  184. package/dist/collection/components/toast/toast.js +2 -2
  185. package/dist/collection/components/toggle/toggle.js +3 -3
  186. package/dist/collection/components/toolbar/toolbar.js +2 -2
  187. package/dist/collection/utils/forms/index.js +1 -0
  188. package/dist/collection/utils/forms/start-container-controller.js +132 -0
  189. package/dist/docs.json +22 -6
  190. package/dist/esm/input.utils-B1ValN_N.js +33 -0
  191. package/dist/esm/ion-app_8.entry.js +5 -5
  192. package/dist/esm/ion-avatar_3.entry.js +1 -1
  193. package/dist/esm/ion-col_3.entry.js +1 -1
  194. package/dist/esm/ion-input-otp.entry.js +3 -3
  195. package/dist/esm/ion-input-password-toggle.entry.js +3 -3
  196. package/dist/esm/ion-input.entry.js +40 -54
  197. package/dist/esm/ion-item-option_3.entry.js +4 -4
  198. package/dist/esm/ion-item_8.entry.js +12 -12
  199. package/dist/esm/ion-loading.entry.js +2 -2
  200. package/dist/esm/ion-menu_3.entry.js +6 -6
  201. package/dist/esm/ion-modal.entry.js +4 -4
  202. package/dist/esm/ion-nav_2.entry.js +2 -2
  203. package/dist/esm/ion-picker-column-option.entry.js +2 -2
  204. package/dist/esm/ion-popover.entry.js +2 -2
  205. package/dist/esm/ion-progress-bar.entry.js +1 -1
  206. package/dist/esm/ion-radio_2.entry.js +4 -4
  207. package/dist/esm/ion-range.entry.js +3 -3
  208. package/dist/esm/ion-refresher_2.entry.js +2 -2
  209. package/dist/esm/ion-reorder_2.entry.js +2 -2
  210. package/dist/esm/ion-ripple-effect.entry.js +1 -1
  211. package/dist/esm/ion-route_4.entry.js +2 -2
  212. package/dist/esm/ion-searchbar.entry.js +4 -4
  213. package/dist/esm/ion-segment-content.entry.js +1 -1
  214. package/dist/esm/ion-segment-view.entry.js +2 -2
  215. package/dist/esm/ion-segment_2.entry.js +4 -4
  216. package/dist/esm/ion-select-modal.entry.js +1 -1
  217. package/dist/esm/ion-select_3.entry.js +40 -58
  218. package/dist/esm/ion-spinner.entry.js +1 -1
  219. package/dist/esm/ion-split-pane.entry.js +2 -2
  220. package/dist/esm/ion-tab-bar_2.entry.js +4 -4
  221. package/dist/esm/ion-tab_2.entry.js +3 -3
  222. package/dist/esm/ion-text.entry.js +2 -2
  223. package/dist/esm/ion-textarea.entry.js +43 -54
  224. package/dist/esm/ion-toast.entry.js +2 -2
  225. package/dist/esm/ion-toggle.entry.js +4 -4
  226. package/dist/esm/slot-mutation-controller-u741eD3Z.js +393 -0
  227. package/dist/ionic/ionic.esm.js +1 -1
  228. package/dist/ionic/{p-dbfd2d52.entry.js → p-06533d51.entry.js} +1 -1
  229. package/dist/ionic/{p-a641d428.entry.js → p-141f5b47.entry.js} +1 -1
  230. package/dist/ionic/p-14a5e8f5.entry.js +4 -0
  231. package/dist/ionic/{p-12589d59.entry.js → p-19269e04.entry.js} +1 -1
  232. package/dist/ionic/{p-efef6788.entry.js → p-19abbc33.entry.js} +1 -1
  233. package/dist/ionic/{p-5394e1d4.entry.js → p-4204184c.entry.js} +1 -1
  234. package/dist/ionic/p-4bc3ecdb.entry.js +4 -0
  235. package/dist/ionic/{p-21a7fb2a.entry.js → p-4eed9ca1.entry.js} +1 -1
  236. package/dist/ionic/p-5521dc1f.entry.js +4 -0
  237. package/dist/ionic/p-5c3626de.entry.js +4 -0
  238. package/dist/ionic/{p-374b10cb.entry.js → p-5d3b411c.entry.js} +1 -1
  239. package/dist/ionic/p-60576951.entry.js +4 -0
  240. package/dist/ionic/{p-1d2e53e8.entry.js → p-711a8319.entry.js} +1 -1
  241. package/dist/ionic/{p-48b6835f.entry.js → p-71419a55.entry.js} +1 -1
  242. package/dist/ionic/{p-ba8a598f.entry.js → p-79325ffe.entry.js} +1 -1
  243. package/dist/ionic/p-84a38904.entry.js +4 -0
  244. package/dist/ionic/{p-bf7c1f05.entry.js → p-8f204f1e.entry.js} +1 -1
  245. package/dist/ionic/p-9055e897.entry.js +4 -0
  246. package/dist/ionic/{p-6277efba.entry.js → p-9658e083.entry.js} +1 -1
  247. package/dist/ionic/{p-74f30659.entry.js → p-988baee1.entry.js} +1 -1
  248. package/dist/ionic/p-DAztxQR0.js +4 -0
  249. package/dist/ionic/p-DkUB-ROc.js +4 -0
  250. package/dist/ionic/{p-3835a020.entry.js → p-a71232d6.entry.js} +1 -1
  251. package/dist/ionic/{p-08f1ed1c.entry.js → p-a73d8742.entry.js} +1 -1
  252. package/dist/ionic/{p-8a21bdbd.entry.js → p-a8421d18.entry.js} +1 -1
  253. package/dist/ionic/{p-2ce59395.entry.js → p-a8c2c343.entry.js} +1 -1
  254. package/dist/ionic/{p-c3f65965.entry.js → p-bc2bad5f.entry.js} +1 -1
  255. package/dist/ionic/{p-f622796d.entry.js → p-be08d500.entry.js} +1 -1
  256. package/dist/ionic/p-be9a4f51.entry.js +4 -0
  257. package/dist/ionic/{p-33829e94.entry.js → p-ccc5044d.entry.js} +1 -1
  258. package/dist/ionic/{p-3c5fdd27.entry.js → p-ce32912b.entry.js} +1 -1
  259. package/dist/ionic/p-ded69ceb.entry.js +4 -0
  260. package/dist/ionic/{p-cec87704.entry.js → p-e0c6e31e.entry.js} +1 -1
  261. package/dist/ionic/{p-09b47b7d.entry.js → p-e8c3e08a.entry.js} +1 -1
  262. package/dist/ionic/{p-8f3a1995.entry.js → p-ed5d540d.entry.js} +1 -1
  263. package/dist/ionic/{p-6b286c5c.entry.js → p-f2802440.entry.js} +1 -1
  264. package/dist/ionic/{p-62e71297.entry.js → p-fe1bfbcf.entry.js} +1 -1
  265. package/dist/types/components/input/input.d.ts +11 -5
  266. package/dist/types/components/select/select.d.ts +12 -6
  267. package/dist/types/components/textarea/textarea.d.ts +12 -5
  268. package/dist/types/utils/forms/index.d.ts +1 -0
  269. package/dist/types/utils/forms/start-container-controller.d.ts +34 -0
  270. package/hydrate/index.js +350 -267
  271. package/hydrate/index.mjs +350 -267
  272. package/package.json +4 -4
  273. package/components/p-DIgGHN7D.js +0 -4
  274. package/components/p-MCJ7HjWe.js +0 -4
  275. package/dist/cjs/input.utils-CP4fzrqk.js +0 -149
  276. package/dist/cjs/notch-controller-BEUpEgb5.js +0 -155
  277. package/dist/esm/input.utils-J6HWocLA.js +0 -146
  278. package/dist/esm/notch-controller-CrUGyHYz.js +0 -153
  279. package/dist/ionic/p-0266b7e1.entry.js +0 -4
  280. package/dist/ionic/p-214e000a.entry.js +0 -4
  281. package/dist/ionic/p-91c6faf1.entry.js +0 -4
  282. package/dist/ionic/p-9b865fe6.entry.js +0 -4
  283. package/dist/ionic/p-BnMQxWON.js +0 -4
  284. package/dist/ionic/p-ac6ef997.entry.js +0 -4
  285. package/dist/ionic/p-b24dc0a1.entry.js +0 -4
  286. package/dist/ionic/p-c8fbe544.entry.js +0 -4
  287. package/dist/ionic/p-cb12f6c3.entry.js +0 -4
  288. package/dist/ionic/p-ede019ef.entry.js +0 -4
  289. package/dist/ionic/p-kJA95YWg.js +0 -4
@@ -112,20 +112,6 @@
112
112
  box-sizing: border-box;
113
113
  }
114
114
 
115
- /**
116
- * Since the label sits on top of the element,
117
- * the component needs to be taller otherwise the
118
- * label will appear too close to the textarea text.
119
- * Also, floating and stacked labels should not
120
- * push the label down since it it
121
- * sits on top of the textarea.
122
- */
123
- :host(.textarea-label-placement-floating),
124
- :host(.textarea-label-placement-stacked) {
125
- --padding-top: 0px;
126
- min-height: 56px;
127
- }
128
-
129
115
  /**
130
116
  * When the cols property is set we should
131
117
  * respect that width instead of defaulting
@@ -248,8 +234,8 @@
248
234
  padding-inline-start: var(--padding-start);
249
235
  -webkit-padding-end: var(--padding-end);
250
236
  padding-inline-end: var(--padding-end);
251
- padding-top: 0px;
252
- padding-bottom: 0px;
237
+ padding-top: 0;
238
+ padding-bottom: 0;
253
239
  border-radius: var(--border-radius);
254
240
  display: flex;
255
241
  position: relative;
@@ -262,23 +248,46 @@
262
248
  line-height: normal;
263
249
  }
264
250
 
251
+ .textarea-control {
252
+ padding-left: 0;
253
+ padding-right: 0;
254
+ padding-top: var(--padding-top);
255
+ padding-bottom: var(--padding-bottom);
256
+ display: flex;
257
+ flex-grow: 1;
258
+ align-items: start;
259
+ align-self: stretch;
260
+ width: 100%;
261
+ /**
262
+ * Allows the control to shrink below the label and textarea's intrinsic
263
+ * width, preventing the start and end slots from being pushed outside
264
+ * the wrapper. The label and textarea truncate independently, so it can
265
+ * use whatever space remains after accounting for the slots.
266
+ */
267
+ min-width: 0;
268
+ }
269
+
265
270
  .native-wrapper {
266
271
  position: relative;
267
272
  width: 100%;
268
273
  height: 100%;
269
274
  }
270
275
 
271
- :host(.has-focus) textarea {
272
- caret-color: var(--highlight-color);
276
+ /**
277
+ * When the label sits beside the textarea, the label should size to its content
278
+ * and the textarea should take the remaining space. A zero flex basis prevents
279
+ * the textarea from requesting its full width and forcing the label to shrink.
280
+ * Stacked and floating placements use a column layout, so their flex basis
281
+ * controls height instead and does not need to be overridden.
282
+ */
283
+ :host(.textarea-label-placement-start) .native-wrapper,
284
+ :host(.textarea-label-placement-end) .native-wrapper,
285
+ :host(.textarea-label-placement-fixed) .native-wrapper {
286
+ flex: 1 1 0;
273
287
  }
274
288
 
275
- .native-wrapper textarea {
276
- -webkit-padding-start: 0px;
277
- padding-inline-start: 0px;
278
- -webkit-padding-end: 0px;
279
- padding-inline-end: 0px;
280
- padding-top: var(--padding-top);
281
- padding-bottom: var(--padding-bottom);
289
+ :host(.has-focus) textarea {
290
+ caret-color: var(--highlight-color);
282
291
  }
283
292
 
284
293
  .native-wrapper {
@@ -305,8 +314,8 @@
305
314
  .native-wrapper::after {
306
315
  padding-left: 0;
307
316
  padding-right: 0;
308
- padding-top: var(--padding-top);
309
- padding-bottom: var(--padding-bottom);
317
+ padding-top: 0;
318
+ padding-bottom: 0;
310
319
  margin-left: 0;
311
320
  margin-right: 0;
312
321
  margin-top: 0;
@@ -332,12 +341,6 @@
332
341
  word-break: break-word;
333
342
  }
334
343
 
335
- .textarea-wrapper-inner {
336
- display: flex;
337
- width: 100%;
338
- min-height: inherit;
339
- }
340
-
341
344
  :host(.ion-touched.ion-invalid) {
342
345
  --highlight-color: var(--highlight-color-invalid);
343
346
  }
@@ -421,12 +424,12 @@
421
424
  }
422
425
 
423
426
  .label-text-wrapper {
424
- -webkit-padding-start: 0px;
425
- padding-inline-start: 0px;
426
- -webkit-padding-end: 0px;
427
- padding-inline-end: 0px;
428
- padding-top: var(--padding-top);
429
- padding-bottom: var(--padding-bottom);
427
+ /**
428
+ * Allows the label to shrink below its intrinsic content width.
429
+ * Without this the label keeps its full width and can push the
430
+ * textarea and end slot outside the wrapper.
431
+ */
432
+ min-width: 0;
430
433
  /**
431
434
  * Label text should not extend
432
435
  * beyond the bounds of the textarea.
@@ -445,6 +448,18 @@
445
448
  pointer-events: none;
446
449
  }
447
450
 
451
+ /**
452
+ * When start/end slots are added or removed, we temporarily
453
+ * disable the label transition to prevent the floating label
454
+ * from animating as its position is adjusted.
455
+ *
456
+ * This class is applied by the start container controller, so
457
+ * renaming it here requires renaming it there too.
458
+ */
459
+ :host(.skip-label-transition) .label-text-wrapper {
460
+ transition: none;
461
+ }
462
+
448
463
  /**
449
464
  * We need to use two elements instead of
450
465
  * one. The .label-text-wrapper is responsible
@@ -488,7 +503,7 @@
488
503
  * Label is on the left of the textarea in LTR and
489
504
  * on the right in RTL.
490
505
  */
491
- :host(.textarea-label-placement-start) .textarea-wrapper {
506
+ :host(.textarea-label-placement-start) .textarea-control {
492
507
  flex-direction: row;
493
508
  }
494
509
 
@@ -510,7 +525,7 @@
510
525
  * Label is on the right of the textarea in LTR and
511
526
  * on the left in RTL.
512
527
  */
513
- :host(.textarea-label-placement-end) .textarea-wrapper {
528
+ :host(.textarea-label-placement-end) .textarea-control {
514
529
  flex-direction: row-reverse;
515
530
  }
516
531
 
@@ -560,8 +575,15 @@
560
575
  * down when the textarea is focused or has a value.
561
576
  *
562
577
  */
563
- :host(.textarea-label-placement-stacked) .textarea-wrapper,
564
- :host(.textarea-label-placement-floating) .textarea-wrapper {
578
+ :host(.textarea-label-placement-stacked),
579
+ :host(.textarea-label-placement-floating) {
580
+ --padding-top: 20px;
581
+ --padding-bottom: 6px;
582
+ }
583
+
584
+ :host(.textarea-label-placement-stacked) .textarea-control,
585
+ :host(.textarea-label-placement-floating) .textarea-control {
586
+ padding-top: 0;
565
587
  flex-direction: column;
566
588
  align-items: start;
567
589
  }
@@ -574,12 +596,10 @@
574
596
  :host(.textarea-label-placement-stacked) .label-text-wrapper,
575
597
  :host(.textarea-label-placement-floating) .label-text-wrapper {
576
598
  transform-origin: left top;
577
- -webkit-padding-start: 0px;
578
- padding-inline-start: 0px;
579
- -webkit-padding-end: 0px;
580
- padding-inline-end: 0px;
581
- padding-top: 0px;
582
- padding-bottom: 0px;
599
+ padding-left: 0;
600
+ padding-right: 0;
601
+ padding-top: 0;
602
+ padding-bottom: 0;
583
603
  max-width: 100%;
584
604
  /**
585
605
  * The 2 ensures the label
@@ -614,13 +634,6 @@
614
634
  margin-bottom: 0px;
615
635
  }
616
636
 
617
- :host(.textarea-label-placement-stacked) ::slotted([slot=start]),
618
- :host(.textarea-label-placement-stacked) ::slotted([slot=end]),
619
- :host(.textarea-label-placement-floating) ::slotted([slot=start]),
620
- :host(.textarea-label-placement-floating) ::slotted([slot=end]) {
621
- margin-top: 8px;
622
- }
623
-
624
637
  /**
625
638
  * This makes the label sit over the textarea
626
639
  * when the textarea is blurred and has no value.
@@ -655,19 +668,17 @@
655
668
  max-width: calc(100% / 0.75);
656
669
  }
657
670
 
658
- .start-slot-wrapper, .end-slot-wrapper {
659
- padding-left: 0;
660
- padding-right: 0;
661
- padding-top: var(--padding-top);
662
- padding-bottom: var(--padding-bottom);
671
+ .textarea-start,
672
+ .textarea-end {
663
673
  display: flex;
674
+ position: relative;
664
675
  flex-shrink: 0;
665
676
  align-self: start;
666
677
  }
667
678
 
668
679
  ::slotted([slot=start]),
669
680
  ::slotted([slot=end]) {
670
- margin-top: 0;
681
+ margin-top: var(--padding-top);
671
682
  }
672
683
 
673
684
  ::slotted([slot=start]:last-of-type) {
@@ -856,10 +867,31 @@
856
867
  font-size: inherit;
857
868
  }
858
869
 
870
+ /**
871
+ * Give stacked and floating textareas a taller minimum height to
872
+ * accommodate the label and textarea content.
873
+ */
874
+ :host(.textarea-label-placement-floating),
875
+ :host(.textarea-label-placement-stacked) {
876
+ min-height: 56px;
877
+ }
878
+
859
879
  :host(.textarea-disabled) {
860
880
  opacity: 0.3;
861
881
  }
862
882
 
883
+ ::slotted(ion-button[slot=start]),
884
+ ::slotted(ion-button[slot=end]) {
885
+ margin-top: 2px;
886
+ }
887
+
888
+ :host(.textarea-label-placement-floating) ::slotted(ion-button[slot=start]),
889
+ :host(.textarea-label-placement-floating) ::slotted(ion-button[slot=end]),
890
+ :host(.textarea-label-placement-stacked) ::slotted(ion-button[slot=start]),
891
+ :host(.textarea-label-placement-stacked) ::slotted(ion-button[slot=end]) {
892
+ margin-top: 10px;
893
+ }
894
+
863
895
  /**
864
896
  * Slotted buttons have a lot of default padding that can
865
897
  * cause them to look misaligned from other pieces such
@@ -2,7 +2,7 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { Build, Host, forceUpdate, h, writeTask, } from "@stencil/core";
5
- import { createNotchController, checkInvalidState } from "../../utils/forms/index";
5
+ import { createNotchController, createStartContainerController, checkInvalidState } from "../../utils/forms/index";
6
6
  import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from "../../utils/helpers";
7
7
  import { createSlotMutationController } from "../../utils/slot-mutation-controller";
8
8
  import { createColorClasses, hostContext } from "../../utils/theme";
@@ -30,9 +30,11 @@ export class Textarea {
30
30
  textareaWrapper;
31
31
  inheritedAttributes = {};
32
32
  originalIonInput;
33
- notchSpacerEl;
34
33
  slotMutationController;
35
34
  notchController;
35
+ notchSpacerEl;
36
+ startContainerController;
37
+ startContainerEl;
36
38
  /**
37
39
  * The value of the textarea when the textarea is focused.
38
40
  */
@@ -260,8 +262,15 @@ export class Textarea {
260
262
  }
261
263
  connectedCallback() {
262
264
  const { el } = this;
263
- this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
265
+ this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => {
266
+ this.startContainerController?.calculateStartContainerWidth();
267
+ forceUpdate(this);
268
+ });
264
269
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
270
+ this.startContainerController = createStartContainerController(el, () => this.startContainerEl, () => {
271
+ return Build.isBrowser && getIonMode(this) === 'md' && this.fill === 'outline';
272
+ });
273
+ this.startContainerController.calculateStartContainerWidth();
265
274
  // Watch for class changes to update validation state
266
275
  if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
267
276
  this.validationObserver = new MutationObserver(() => {
@@ -300,6 +309,10 @@ export class Textarea {
300
309
  this.notchController.destroy();
301
310
  this.notchController = undefined;
302
311
  }
312
+ if (this.startContainerController) {
313
+ this.startContainerController.destroy();
314
+ this.startContainerController = undefined;
315
+ }
303
316
  // Clean up validation observer to prevent memory leaks
304
317
  if (this.validationObserver) {
305
318
  this.validationObserver.disconnect();
@@ -318,6 +331,7 @@ export class Textarea {
318
331
  }
319
332
  componentDidRender() {
320
333
  this.notchController?.calculateNotchWidth();
334
+ this.startContainerController?.calculateStartContainerWidth();
321
335
  }
322
336
  /**
323
337
  * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global
@@ -480,43 +494,29 @@ export class Textarea {
480
494
  return this.label !== undefined || this.labelSlot !== null;
481
495
  }
482
496
  /**
483
- * Stops propagation when the label is clicked,
484
- * otherwise, two clicks will be triggered.
497
+ * Stops propagation for clicks on the textarea's own content (label text,
498
+ * textarea field) to prevent double-click events. Allows clicks on slotted
499
+ * content to propagate so event delegation works for parent handlers.
500
+ *
501
+ * Only slots belonging directly to this textarea should be considered.
502
+ * The textarea itself may have a slot attribute when placed in an item
503
+ * or toolbar, which does not make its content slotted content.
485
504
  */
486
505
  onLabelClick = (ev) => {
487
- // Only stop propagation if the click was directly on the label
488
- // and not on the input or other child elements
489
- if (ev.target === ev.currentTarget) {
506
+ const target = ev.target;
507
+ const slotted = target.closest('[slot="start"], [slot="end"]');
508
+ if (slotted === null || slotted === this.el || !this.el.contains(slotted)) {
490
509
  ev.stopPropagation();
491
510
  }
492
511
  };
493
512
  /**
494
- * Renders the border container when fill="outline".
513
+ * Renders the outline border with a notch for the label.
495
514
  */
496
- renderLabelContainer() {
497
- const mode = getIonMode(this);
498
- const hasOutlineFill = mode === 'md' && this.fill === 'outline';
499
- if (hasOutlineFill) {
500
- /**
501
- * The outline fill has a special outline
502
- * that appears around the textarea and the label.
503
- * Certain stacked and floating label placements cause the
504
- * label to translate up and create a "cut out"
505
- * inside of that border by using the notch-spacer element.
506
- */
507
- return [
508
- h("div", { class: "textarea-outline-container" }, h("div", { class: "textarea-outline-start" }), h("div", { class: {
509
- 'textarea-outline-notch': true,
510
- 'textarea-outline-notch-hidden': !this.hasLabel,
511
- } }, h("div", { class: "notch-spacer", "aria-hidden": "true", ref: (el) => (this.notchSpacerEl = el) }, this.label)), h("div", { class: "textarea-outline-end" })),
512
- this.renderLabel(),
513
- ];
514
- }
515
- /**
516
- * If not using the outline style,
517
- * we can render just the label.
518
- */
519
- return this.renderLabel();
515
+ renderOutlineContainer() {
516
+ return (h("div", { class: "textarea-outline-container" }, h("div", { class: "textarea-outline-start" }), h("div", { class: {
517
+ 'textarea-outline-notch': true,
518
+ 'textarea-outline-notch-hidden': !this.hasLabel,
519
+ } }, h("div", { class: "notch-spacer", "aria-hidden": "true", ref: (el) => (this.notchSpacerEl = el) }, this.label)), h("div", { class: "textarea-outline-end" })));
520
520
  }
521
521
  /**
522
522
  * Renders the helper text or error text values
@@ -564,32 +564,20 @@ export class Textarea {
564
564
  return (h("div", { class: "textarea-bottom" }, this.renderHintText(), this.renderCounter()));
565
565
  }
566
566
  render() {
567
- const { inputId, disabled, fill, shape, labelPlacement, el, hasFocus } = this;
567
+ const { inputId, disabled, fill, shape, labelPlacement, hasFocus } = this;
568
568
  const mode = getIonMode(this);
569
569
  const value = this.getValue();
570
570
  const inItem = hostContext('ion-item', this.el);
571
571
  const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
572
572
  const hasValue = this.hasValue();
573
- const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
573
+ const hasOutlineFill = mode === 'md' && fill === 'outline';
574
574
  /**
575
575
  * If the label is stacked, it should always sit above the textarea.
576
576
  * For floating labels, the label should move above the textarea if
577
- * the textarea has a value, is focused, or has anything in either
578
- * the start or end slot.
579
- *
580
- * If there is content in the start slot, the label would overlap
581
- * it if not forced to float. This is also applied to the end slot
582
- * because with the default or solid fills, the textarea is not
583
- * vertically centered in the container, but the label is. This
584
- * causes the slots and label to appear vertically offset from each
585
- * other when the label isn't floating above the input. This doesn't
586
- * apply to the outline fill, but this was not accounted for to keep
587
- * things consistent.
588
- *
589
- * TODO(FW-5592): Remove hasStartEndSlots condition
577
+ * the textarea has a value or is focused.
590
578
  */
591
- const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
592
- return (h(Host, { key: 'eca1dfed53189408f614661ef3902cdb73d5f5de', class: createColorClasses(this.color, {
579
+ const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus));
580
+ return (h(Host, { key: '78da9c96e6a29f19b237f268dd8bfd044afe27b6', class: createColorClasses(this.color, {
593
581
  [mode]: true,
594
582
  'has-value': hasValue,
595
583
  'has-focus': hasFocus,
@@ -598,7 +586,7 @@ export class Textarea {
598
586
  [`textarea-shape-${shape}`]: shape !== undefined,
599
587
  [`textarea-label-placement-${labelPlacement}`]: true,
600
588
  'textarea-disabled': disabled,
601
- }) }, h("label", { key: 'd4d835ffedbf8b6de192096e21167ec4f7e01d30', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '39b03e26c23473de31a0237543eaf1e0875e6ed9', class: "textarea-wrapper-inner" }, h("div", { key: 'e9e0997e7dc6d25f09e8439d81e138590c33a151', class: "start-slot-wrapper" }, h("slot", { key: '2d88aace11d74196758e40af27a777eebb270019', name: "start" })), h("div", { key: '8434424404f9fe707f9209ac6495a33bae060538', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", { key: '5bb8eaeba76c39bccadb7c041a10fb077c422dcc', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined, ...this.inheritedAttributes }, value)), h("div", { key: '107707676552f8faca3e708285cc3266061a0394', class: "end-slot-wrapper" }, h("slot", { key: 'dc890ffdce898f3d39a315e7bc432840559be466', name: "end" }))), shouldRenderHighlight && h("div", { key: '4554ad09ac0908cea2e90bfa2301b4cd80ac7242', class: "textarea-highlight" })), this.renderBottomContent()));
589
+ }) }, h("label", { key: 'c5ea3f26e01e66b857d3a1c88dbafbf2dbb81fdb', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, hasOutlineFill && this.renderOutlineContainer(), h("div", { key: '5a9d0efc8c13d9bd4ec4ceadf1bdb84205c47b3d', class: "textarea-start", ref: (el) => (this.startContainerEl = el) }, h("slot", { key: 'da08262e33f3a5b454847297338a74f07c5b60c9', name: "start" })), h("div", { key: '6461dc7381929eb83679ec07563b7bdf8bfc3f9f', class: "textarea-control" }, this.renderLabel(), h("div", { key: '8f7902a9e6040384d25d0e2061c04f6b49acf187', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", { key: '2149d8b2c23a164c536327c98d7a632e1e85428a', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined, ...this.inheritedAttributes }, value))), h("div", { key: '84f5f42add89875c3f0cce734b512a9e7be81e03', class: "textarea-end" }, h("slot", { key: 'ea654d78013a00b8af33deb89d42c2b7f6c9cb99', name: "end" })), shouldRenderHighlight && h("div", { key: 'b72195d2606816b3b71e65214d78cef1740f4804', class: "textarea-highlight" })), this.renderBottomContent()));
602
590
  }
603
591
  static get is() { return "ion-textarea"; }
604
592
  static get encapsulation() { return "scoped"; }