@ionic/core 7.5.8 → 7.6.0

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 (381) hide show
  1. package/components/animation.js +12 -11
  2. package/components/checkbox.js +5 -3
  3. package/components/index4.js +10 -12
  4. package/components/index5.js +0 -22
  5. package/components/input-shims.js +12 -7
  6. package/components/input.utils.js +3 -2
  7. package/components/ion-datetime.js +2 -2
  8. package/components/ion-infinite-scroll.js +11 -3
  9. package/components/ion-input.js +31 -8
  10. package/components/ion-modal.js +2 -2
  11. package/components/ion-range.js +5 -3
  12. package/components/ion-select.js +44 -29
  13. package/components/ion-skeleton-text.js +14 -1
  14. package/components/ion-textarea.js +30 -7
  15. package/components/ion-toast.js +419 -74
  16. package/components/ion-toggle.js +5 -3
  17. package/components/item.js +2 -2
  18. package/components/label.js +2 -2
  19. package/components/overlays.js +2 -1
  20. package/components/radio-group.js +2 -0
  21. package/components/radio.js +45 -5
  22. package/dist/cjs/{animation-cc40e39c.js → animation-9b401d39.js} +13 -12
  23. package/dist/cjs/{button-active-417ccdcb.js → button-active-a5185907.js} +1 -1
  24. package/dist/cjs/{capacitor-46980811.js → capacitor-c04564bf.js} +1 -1
  25. package/dist/cjs/compare-with-utils-df1001d7.js +44 -0
  26. package/dist/cjs/{haptic-216df36c.js → haptic-b882e0bb.js} +1 -1
  27. package/dist/cjs/{index-8c1e77d6.js → index-b4f986cd.js} +1 -1
  28. package/dist/cjs/index-c8d52405.js +10 -0
  29. package/dist/cjs/{index-5cbb20f9.js → index-ce101dc9.js} +11 -13
  30. package/dist/cjs/{index-d4c2e02c.js → index-f8f13389.js} +2 -2
  31. package/dist/cjs/index.cjs.js +8 -8
  32. package/dist/cjs/{input-shims-87102b6a.js → input-shims-a23bb0f4.js} +15 -10
  33. package/dist/cjs/{input.utils-2a11c1fe.js → input.utils-3d0166a2.js} +4 -3
  34. package/dist/cjs/ion-action-sheet.cjs.entry.js +6 -6
  35. package/dist/cjs/ion-alert.cjs.entry.js +6 -6
  36. package/dist/cjs/ion-app_8.cjs.entry.js +8 -8
  37. package/dist/cjs/ion-checkbox.cjs.entry.js +5 -3
  38. package/dist/cjs/ion-datetime_3.cjs.entry.js +7 -7
  39. package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +11 -3
  40. package/dist/cjs/ion-input.cjs.entry.js +34 -11
  41. package/dist/cjs/ion-item_8.cjs.entry.js +17 -4
  42. package/dist/cjs/ion-loading.cjs.entry.js +3 -3
  43. package/dist/cjs/ion-menu_3.cjs.entry.js +5 -5
  44. package/dist/cjs/ion-modal.cjs.entry.js +8 -8
  45. package/dist/cjs/ion-nav_2.cjs.entry.js +1 -1
  46. package/dist/cjs/ion-picker-column-internal.cjs.entry.js +3 -3
  47. package/dist/cjs/ion-popover.cjs.entry.js +4 -4
  48. package/dist/cjs/ion-radio_2.cjs.entry.js +9 -4
  49. package/dist/cjs/ion-range.cjs.entry.js +5 -3
  50. package/dist/cjs/ion-refresher_2.cjs.entry.js +4 -4
  51. package/dist/cjs/ion-reorder_2.cjs.entry.js +3 -3
  52. package/dist/cjs/ion-select_3.cjs.entry.js +51 -35
  53. package/dist/cjs/ion-tab-bar_2.cjs.entry.js +4 -4
  54. package/dist/cjs/ion-textarea.cjs.entry.js +33 -10
  55. package/dist/cjs/ion-toast.cjs.entry.js +434 -92
  56. package/dist/cjs/ion-toggle.cjs.entry.js +8 -6
  57. package/dist/cjs/ionic.cjs.js +1 -1
  58. package/dist/cjs/{ios.transition-a4ffb5a2.js → ios.transition-6787b799.js} +3 -3
  59. package/dist/cjs/{keyboard-9390928c.js → keyboard-0272231f.js} +1 -1
  60. package/dist/cjs/{keyboard-7d621ee9.js → keyboard-af1bb365.js} +3 -3
  61. package/dist/cjs/{keyboard-controller-2158b478.js → keyboard-controller-c05e747a.js} +2 -2
  62. package/dist/cjs/loader.cjs.js +1 -1
  63. package/dist/cjs/{md.transition-292abe1c.js → md.transition-e033d20a.js} +3 -3
  64. package/dist/cjs/{notch-controller-1dce508a.js → notch-controller-9735e7ac.js} +1 -1
  65. package/dist/cjs/{overlays-5c379703.js → overlays-27a6fb16.js} +3 -1
  66. package/dist/collection/components/checkbox/checkbox.ios.css +0 -6
  67. package/dist/collection/components/checkbox/checkbox.js +4 -1
  68. package/dist/collection/components/checkbox/checkbox.md.css +0 -6
  69. package/dist/collection/components/checkbox/test/checkbox.spec.js +14 -0
  70. package/dist/collection/components/datetime/datetime.ios.css +1 -0
  71. package/dist/collection/components/datetime/datetime.md.css +1 -0
  72. package/dist/collection/components/infinite-scroll/infinite-scroll.js +11 -3
  73. package/dist/collection/components/infinite-scroll/test/small-dom-update/infinite-scroll.e2e.js +31 -0
  74. package/dist/collection/components/input/input.ios.css +10 -3
  75. package/dist/collection/components/input/input.js +31 -6
  76. package/dist/collection/components/input/input.md.css +13 -12
  77. package/dist/collection/components/input/test/slot/input.e2e.js +51 -0
  78. package/dist/collection/components/item/item.ios.css +41 -3
  79. package/dist/collection/components/item/item.md.css +41 -3
  80. package/dist/collection/components/item/test/basic/item.e2e.js +1 -1
  81. package/dist/collection/components/item/test/disabled/item.e2e.js +0 -1
  82. package/dist/collection/components/label/label.ios.css +14 -3
  83. package/dist/collection/components/label/label.md.css +14 -3
  84. package/dist/collection/components/modal/gestures/swipe-to-close.js +2 -1
  85. package/dist/collection/components/note/test/a11y/note.e2e.js +43 -0
  86. package/dist/collection/components/radio/radio.ios.css +0 -6
  87. package/dist/collection/components/radio/radio.js +7 -3
  88. package/dist/collection/components/radio/radio.md.css +0 -6
  89. package/dist/collection/components/radio/test/radio.spec.js +14 -0
  90. package/dist/collection/components/radio-group/radio-group.js +24 -0
  91. package/dist/collection/components/radio-group/test/radio-group.spec.js +69 -0
  92. package/dist/collection/components/range/range.ios.css +0 -6
  93. package/dist/collection/components/range/range.js +4 -1
  94. package/dist/collection/components/range/range.md.css +0 -6
  95. package/dist/collection/components/range/test/range.spec.js +16 -0
  96. package/dist/collection/components/select/select.ios.css +60 -7
  97. package/dist/collection/components/select/select.js +45 -28
  98. package/dist/collection/components/select/select.md.css +63 -19
  99. package/dist/collection/components/select/test/item/select.e2e.js +15 -0
  100. package/dist/collection/components/select/test/label/select.e2e.js +1 -1
  101. package/dist/collection/components/select/test/legacy/wrapping/select.e2e.js +1 -1
  102. package/dist/collection/components/select/test/slot/select.e2e.js +67 -0
  103. package/dist/collection/components/skeleton-text/skeleton-text.js +39 -0
  104. package/dist/collection/components/textarea/test/slot/textarea.e2e.js +51 -0
  105. package/dist/collection/components/textarea/textarea.ios.css +37 -5
  106. package/dist/collection/components/textarea/textarea.js +30 -5
  107. package/dist/collection/components/textarea/textarea.md.css +47 -14
  108. package/dist/collection/components/toast/animations/ios.enter.js +2 -1
  109. package/dist/collection/components/toast/animations/md.enter.js +4 -3
  110. package/dist/collection/components/toast/animations/utils.js +20 -14
  111. package/dist/collection/components/toast/gestures/swipe-to-dismiss.js +267 -0
  112. package/dist/collection/components/toast/test/swipe-gesture/toast.e2e.js +84 -0
  113. package/dist/collection/components/toast/test/toast-config.spec.js +25 -0
  114. package/dist/collection/components/toast/test/toast.spec.js +80 -0
  115. package/dist/collection/components/toast/toast.ios.css +10 -10
  116. package/dist/collection/components/toast/toast.js +104 -1
  117. package/dist/collection/components/toast/toast.md.css +10 -0
  118. package/dist/collection/components/toggle/test/toggle.spec.js +12 -0
  119. package/dist/collection/components/toggle/toggle.ios.css +0 -6
  120. package/dist/collection/components/toggle/toggle.js +4 -1
  121. package/dist/collection/components/toggle/toggle.md.css +0 -6
  122. package/dist/collection/utils/animation/animation.js +12 -11
  123. package/dist/collection/utils/animation/test/animation.spec.js +16 -0
  124. package/dist/collection/utils/browser/index.js +0 -22
  125. package/dist/collection/utils/forms/compare-with-utils.js +39 -0
  126. package/dist/collection/utils/forms/index.js +1 -0
  127. package/dist/collection/utils/input-shims/input-shims.js +12 -6
  128. package/dist/collection/utils/menu-controller/index.js +10 -12
  129. package/dist/collection/utils/overlays.js +1 -0
  130. package/dist/collection/utils/slot-mutation-controller.js +3 -2
  131. package/dist/docs.json +159 -2
  132. package/dist/esm/{animation-fb0e0f6f.js → animation-dde8cc0d.js} +13 -12
  133. package/dist/esm/{button-active-3fdf3e4f.js → button-active-c6ee4f50.js} +1 -1
  134. package/dist/esm/{capacitor-b0e5fe83.js → capacitor-59395cbd.js} +1 -1
  135. package/dist/esm/compare-with-utils-a96ff2ea.js +41 -0
  136. package/dist/esm/{haptic-152c4172.js → haptic-554688a5.js} +1 -1
  137. package/dist/esm/{index-80492ed3.js → index-0c12c22b.js} +11 -13
  138. package/dist/esm/{index-d8817fb0.js → index-1932b201.js} +2 -2
  139. package/dist/esm/{index-ba9976bb.js → index-82eeb47f.js} +1 -1
  140. package/dist/esm/index-a5d50daf.js +7 -0
  141. package/dist/esm/index.js +8 -8
  142. package/dist/esm/{input-shims-0a2e541d.js → input-shims-6539ce13.js} +14 -9
  143. package/dist/esm/{input.utils-2846d430.js → input.utils-a5a2d164.js} +4 -3
  144. package/dist/esm/ion-action-sheet.entry.js +6 -6
  145. package/dist/esm/ion-alert.entry.js +6 -6
  146. package/dist/esm/ion-app_8.entry.js +8 -8
  147. package/dist/esm/ion-checkbox.entry.js +5 -3
  148. package/dist/esm/ion-datetime_3.entry.js +7 -7
  149. package/dist/esm/ion-infinite-scroll_2.entry.js +11 -3
  150. package/dist/esm/ion-input.entry.js +34 -11
  151. package/dist/esm/ion-item_8.entry.js +17 -4
  152. package/dist/esm/ion-loading.entry.js +3 -3
  153. package/dist/esm/ion-menu_3.entry.js +5 -5
  154. package/dist/esm/ion-modal.entry.js +8 -8
  155. package/dist/esm/ion-nav_2.entry.js +1 -1
  156. package/dist/esm/ion-picker-column-internal.entry.js +3 -3
  157. package/dist/esm/ion-popover.entry.js +4 -4
  158. package/dist/esm/ion-radio_2.entry.js +9 -4
  159. package/dist/esm/ion-range.entry.js +5 -3
  160. package/dist/esm/ion-refresher_2.entry.js +4 -4
  161. package/dist/esm/ion-reorder_2.entry.js +3 -3
  162. package/dist/esm/ion-select_3.entry.js +47 -31
  163. package/dist/esm/ion-tab-bar_2.entry.js +4 -4
  164. package/dist/esm/ion-textarea.entry.js +33 -10
  165. package/dist/esm/ion-toast.entry.js +417 -75
  166. package/dist/esm/ion-toggle.entry.js +8 -6
  167. package/dist/esm/ionic.js +1 -1
  168. package/dist/esm/{ios.transition-fd16bec1.js → ios.transition-802a84a6.js} +3 -3
  169. package/dist/esm/{keyboard-c402e885.js → keyboard-52278bd7.js} +3 -3
  170. package/dist/esm/{keyboard-dbb05fb9.js → keyboard-73175e24.js} +1 -1
  171. package/dist/esm/{keyboard-controller-5d90929a.js → keyboard-controller-ec5c2bfa.js} +2 -2
  172. package/dist/esm/loader.js +1 -1
  173. package/dist/esm/{md.transition-94ac93e1.js → md.transition-43c2874d.js} +3 -3
  174. package/dist/esm/{notch-controller-d09b6aca.js → notch-controller-fea7f9c5.js} +1 -1
  175. package/dist/esm/{overlays-abf58489.js → overlays-32ef481d.js} +3 -2
  176. package/dist/esm-es5/{animation-fb0e0f6f.js → animation-dde8cc0d.js} +1 -1
  177. package/dist/esm-es5/{button-active-3fdf3e4f.js → button-active-c6ee4f50.js} +1 -1
  178. package/dist/esm-es5/{capacitor-b0e5fe83.js → capacitor-59395cbd.js} +1 -1
  179. package/dist/esm-es5/compare-with-utils-a96ff2ea.js +4 -0
  180. package/dist/esm-es5/{haptic-152c4172.js → haptic-554688a5.js} +1 -1
  181. package/dist/esm-es5/index-0c12c22b.js +4 -0
  182. package/dist/esm-es5/{index-d8817fb0.js → index-1932b201.js} +1 -1
  183. package/dist/esm-es5/{index-ba9976bb.js → index-82eeb47f.js} +1 -1
  184. package/dist/esm-es5/index.js +1 -1
  185. package/dist/esm-es5/input-shims-6539ce13.js +4 -0
  186. package/dist/esm-es5/input.utils-a5a2d164.js +4 -0
  187. package/dist/esm-es5/ion-action-sheet.entry.js +1 -1
  188. package/dist/esm-es5/ion-alert.entry.js +1 -1
  189. package/dist/esm-es5/ion-app_8.entry.js +1 -1
  190. package/dist/esm-es5/ion-checkbox.entry.js +1 -1
  191. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  192. package/dist/esm-es5/ion-infinite-scroll_2.entry.js +1 -1
  193. package/dist/esm-es5/ion-input.entry.js +1 -1
  194. package/dist/esm-es5/ion-item_8.entry.js +1 -1
  195. package/dist/esm-es5/ion-loading.entry.js +1 -1
  196. package/dist/esm-es5/ion-menu_3.entry.js +1 -1
  197. package/dist/esm-es5/ion-modal.entry.js +1 -1
  198. package/dist/esm-es5/ion-nav_2.entry.js +1 -1
  199. package/dist/esm-es5/ion-picker-column-internal.entry.js +1 -1
  200. package/dist/esm-es5/ion-popover.entry.js +1 -1
  201. package/dist/esm-es5/ion-radio_2.entry.js +1 -1
  202. package/dist/esm-es5/ion-range.entry.js +1 -1
  203. package/dist/esm-es5/ion-refresher_2.entry.js +1 -1
  204. package/dist/esm-es5/ion-reorder_2.entry.js +1 -1
  205. package/dist/esm-es5/ion-select_3.entry.js +1 -1
  206. package/dist/esm-es5/ion-tab-bar_2.entry.js +1 -1
  207. package/dist/esm-es5/ion-textarea.entry.js +1 -1
  208. package/dist/esm-es5/ion-toast.entry.js +1 -1
  209. package/dist/esm-es5/ion-toggle.entry.js +1 -1
  210. package/dist/esm-es5/ionic.js +1 -1
  211. package/dist/esm-es5/{ios.transition-fd16bec1.js → ios.transition-802a84a6.js} +1 -1
  212. package/dist/esm-es5/{keyboard-c402e885.js → keyboard-52278bd7.js} +1 -1
  213. package/dist/esm-es5/{keyboard-dbb05fb9.js → keyboard-73175e24.js} +1 -1
  214. package/dist/esm-es5/{keyboard-controller-5d90929a.js → keyboard-controller-ec5c2bfa.js} +1 -1
  215. package/dist/esm-es5/loader.js +1 -1
  216. package/dist/esm-es5/md.transition-43c2874d.js +4 -0
  217. package/dist/esm-es5/{notch-controller-d09b6aca.js → notch-controller-fea7f9c5.js} +1 -1
  218. package/dist/esm-es5/{overlays-abf58489.js → overlays-32ef481d.js} +1 -1
  219. package/dist/html.html-data.json +14 -1
  220. package/dist/ionic/index.esm.js +1 -1
  221. package/dist/ionic/ionic.esm.js +1 -1
  222. package/dist/ionic/p-01d14eba.system.entry.js +4 -0
  223. package/dist/ionic/{p-3de71759.entry.js → p-042e6f4b.entry.js} +1 -1
  224. package/dist/ionic/p-05d83227.system.entry.js +4 -0
  225. package/dist/ionic/{p-32e19bee.system.js → p-0bd51c34.system.js} +1 -1
  226. package/dist/ionic/{p-73637557.system.entry.js → p-16756e94.system.entry.js} +1 -1
  227. package/dist/ionic/p-1b19c04b.entry.js +4 -0
  228. package/dist/ionic/{p-95c26010.system.js → p-1cca10d6.system.js} +1 -1
  229. package/dist/ionic/p-1e4371bd.js +4 -0
  230. package/dist/ionic/{p-0519cd21.entry.js → p-251666e2.entry.js} +1 -1
  231. package/dist/ionic/{p-4e8e09e1.js → p-27281edd.js} +1 -1
  232. package/dist/ionic/p-28249b10.system.entry.js +4 -0
  233. package/dist/ionic/p-28a2c2a2.entry.js +4 -0
  234. package/dist/ionic/p-29d03b3a.js +4 -0
  235. package/dist/ionic/p-2b7827c7.js +4 -0
  236. package/dist/ionic/p-2d3b9fa3.system.entry.js +4 -0
  237. package/dist/ionic/p-2d539df1.entry.js +4 -0
  238. package/dist/ionic/{p-a0126dd5.system.entry.js → p-3137c025.system.entry.js} +1 -1
  239. package/dist/ionic/{p-30029cc2.system.js → p-31fcb233.system.js} +1 -1
  240. package/dist/ionic/{p-b0dc1cf9.system.js → p-33a37fad.system.js} +1 -1
  241. package/dist/ionic/p-36d187af.js +4 -0
  242. package/dist/ionic/p-376a6063.js +4 -0
  243. package/dist/ionic/p-38f2c6bb.system.js +4 -0
  244. package/dist/ionic/{p-2ba0657d.system.entry.js → p-39c31982.system.entry.js} +1 -1
  245. package/dist/ionic/{p-75445583.js → p-3a75d7fd.js} +1 -1
  246. package/dist/ionic/{p-d40a974a.entry.js → p-3dd98a73.entry.js} +1 -1
  247. package/dist/ionic/p-3e6b47e8.system.entry.js +4 -0
  248. package/dist/ionic/p-3f5179b5.entry.js +4 -0
  249. package/dist/ionic/p-40f68333.system.js +4 -0
  250. package/dist/ionic/{p-643a8fae.js → p-42f189f4.js} +1 -1
  251. package/dist/ionic/{p-9c3b6849.entry.js → p-438e1b8a.entry.js} +1 -1
  252. package/dist/ionic/{p-b752714f.system.entry.js → p-46d400b4.system.entry.js} +1 -1
  253. package/dist/ionic/{p-61d7d7fb.js → p-47b6ba5b.js} +1 -1
  254. package/dist/ionic/{p-b04e4d62.entry.js → p-4d67d27a.entry.js} +1 -1
  255. package/dist/ionic/{p-0b1bc636.entry.js → p-4f3ae45a.entry.js} +1 -1
  256. package/dist/ionic/{p-18b6bd23.system.entry.js → p-517a9885.system.entry.js} +1 -1
  257. package/dist/ionic/p-53ec4f1c.system.js +4 -0
  258. package/dist/ionic/p-5e4eec2e.entry.js +4 -0
  259. package/dist/ionic/{p-57477107.system.js → p-5e745192.system.js} +1 -1
  260. package/dist/ionic/{p-ad019ef0.entry.js → p-5eda1d7d.entry.js} +1 -1
  261. package/dist/ionic/{p-4890093d.js → p-6de472cb.js} +1 -1
  262. package/dist/ionic/{p-08f196ce.system.entry.js → p-6de62649.system.entry.js} +1 -1
  263. package/dist/ionic/p-72f6ff89.js +4 -0
  264. package/dist/ionic/p-765f4dde.entry.js +4 -0
  265. package/dist/ionic/{p-3f6270b3.entry.js → p-78aadecb.entry.js} +1 -1
  266. package/dist/ionic/p-7d92bf15.system.entry.js +4 -0
  267. package/dist/ionic/p-7ee0f3f2.entry.js +4 -0
  268. package/dist/ionic/p-8314281e.system.js +4 -0
  269. package/dist/ionic/p-88d5fbd3.js +4 -0
  270. package/dist/ionic/{p-0e4f2e17.system.js → p-8985cdb6.system.js} +1 -1
  271. package/dist/ionic/{p-d7be6b7e.system.entry.js → p-8acae6b0.system.entry.js} +1 -1
  272. package/dist/ionic/p-8d69a624.entry.js +4 -0
  273. package/dist/ionic/p-8de17614.system.entry.js +4 -0
  274. package/dist/ionic/p-91d33c4a.js +4 -0
  275. package/dist/ionic/p-979d4f5c.system.js +4 -0
  276. package/dist/ionic/p-9b7f0b0f.entry.js +4 -0
  277. package/dist/ionic/p-9e447eb1.system.js +4 -0
  278. package/dist/ionic/p-9f6ee2c0.system.entry.js +4 -0
  279. package/dist/ionic/{p-94389114.system.entry.js → p-a0bb7b92.system.entry.js} +1 -1
  280. package/dist/ionic/{p-f2c21dae.system.js → p-a0d7bb5e.system.js} +1 -1
  281. package/dist/ionic/{p-82045bc1.entry.js → p-a3277a67.entry.js} +1 -1
  282. package/dist/ionic/{p-50880712.entry.js → p-a9978d08.entry.js} +1 -1
  283. package/dist/ionic/{p-3b686a65.system.entry.js → p-ada96dd0.system.entry.js} +1 -1
  284. package/dist/ionic/p-afd6ca9d.entry.js +4 -0
  285. package/dist/ionic/p-b56d0866.system.entry.js +4 -0
  286. package/dist/ionic/{p-67da080d.entry.js → p-b6f9d32a.entry.js} +1 -1
  287. package/dist/ionic/p-c61cc894.js +4 -0
  288. package/dist/ionic/{p-304383f9.system.js → p-cabad15b.system.js} +1 -1
  289. package/dist/ionic/{p-31158892.js → p-cdbeaadc.js} +1 -1
  290. package/dist/ionic/p-d032f825.system.entry.js +4 -0
  291. package/dist/ionic/p-d51674c5.system.js +4 -0
  292. package/dist/ionic/{p-98d82c73.system.js → p-ddbb2d5b.system.js} +1 -1
  293. package/dist/ionic/p-de6f8b28.system.js +4 -0
  294. package/dist/ionic/p-de756e5c.js +4 -0
  295. package/dist/ionic/{p-73ea8f82.system.entry.js → p-dee7c1d0.system.entry.js} +1 -1
  296. package/dist/ionic/p-dfd9432a.entry.js +4 -0
  297. package/dist/ionic/{p-af5cfad2.system.js → p-e24ce1e5.system.js} +1 -1
  298. package/dist/ionic/p-e3584231.system.js +1 -1
  299. package/dist/ionic/p-f0445d82.system.entry.js +4 -0
  300. package/dist/ionic/p-f656e032.system.entry.js +4 -0
  301. package/dist/ionic/{p-17f893be.js → p-f70970cf.js} +1 -1
  302. package/dist/ionic/{p-e553006f.system.entry.js → p-fba0a6e7.system.entry.js} +1 -1
  303. package/dist/ionic/p-feae5133.entry.js +4 -0
  304. package/dist/types/components/checkbox/checkbox.d.ts +1 -0
  305. package/dist/types/components/infinite-scroll/infinite-scroll.d.ts +7 -0
  306. package/dist/types/components/input/input.d.ts +2 -0
  307. package/dist/types/components/radio/radio.d.ts +1 -0
  308. package/dist/types/components/radio-group/radio-group-interface.d.ts +1 -0
  309. package/dist/types/components/radio-group/radio-group.d.ts +8 -1
  310. package/dist/types/components/range/range.d.ts +1 -0
  311. package/dist/types/components/router/router.d.ts +2 -1
  312. package/dist/types/components/select/select.d.ts +6 -1
  313. package/dist/types/components/skeleton-text/skeleton-text.d.ts +9 -1
  314. package/dist/types/components/textarea/textarea.d.ts +2 -0
  315. package/dist/types/components/toast/animations/utils.d.ts +15 -4
  316. package/dist/types/components/toast/gestures/swipe-to-dismiss.d.ts +9 -0
  317. package/dist/types/components/toast/toast-interface.d.ts +2 -0
  318. package/dist/types/components/toast/toast.d.ts +28 -1
  319. package/dist/types/components/toggle/toggle.d.ts +1 -0
  320. package/dist/types/components.d.ts +37 -6
  321. package/dist/types/interface.d.ts +1 -5
  322. package/dist/types/jest.d.ts +5 -0
  323. package/dist/types/utils/animation/animation-interface.d.ts +4 -4
  324. package/dist/types/utils/browser/index.d.ts +11 -2
  325. package/dist/types/utils/forms/compare-with-utils.d.ts +18 -0
  326. package/dist/types/utils/forms/index.d.ts +1 -0
  327. package/dist/types/utils/hardware-back-button.d.ts +4 -0
  328. package/dist/types/utils/overlays.d.ts +1 -0
  329. package/dist/types/utils/slot-mutation-controller.d.ts +2 -2
  330. package/hydrate/index.js +643 -184
  331. package/package.json +1 -1
  332. package/dist/cjs/index-306a7476.js +0 -32
  333. package/dist/collection/components/item/test/text/item.e2e.js +0 -14
  334. package/dist/esm/index-7a14ecec.js +0 -29
  335. package/dist/esm-es5/index-80492ed3.js +0 -4
  336. package/dist/esm-es5/input-shims-0a2e541d.js +0 -4
  337. package/dist/esm-es5/input.utils-2846d430.js +0 -4
  338. package/dist/esm-es5/md.transition-94ac93e1.js +0 -4
  339. package/dist/ionic/p-1d17219f.entry.js +0 -4
  340. package/dist/ionic/p-232d1f22.entry.js +0 -4
  341. package/dist/ionic/p-25b32c3d.entry.js +0 -4
  342. package/dist/ionic/p-2ab4e925.system.entry.js +0 -4
  343. package/dist/ionic/p-2d5aeccc.js +0 -4
  344. package/dist/ionic/p-2d600c25.system.js +0 -4
  345. package/dist/ionic/p-308e1341.js +0 -4
  346. package/dist/ionic/p-32303650.system.entry.js +0 -4
  347. package/dist/ionic/p-34c39c48.entry.js +0 -4
  348. package/dist/ionic/p-3c58a71b.js +0 -4
  349. package/dist/ionic/p-3e960213.system.entry.js +0 -4
  350. package/dist/ionic/p-3fd26bd6.system.entry.js +0 -4
  351. package/dist/ionic/p-3fe96156.system.js +0 -4
  352. package/dist/ionic/p-42ba3902.entry.js +0 -4
  353. package/dist/ionic/p-4a31c99f.entry.js +0 -4
  354. package/dist/ionic/p-5a68960f.entry.js +0 -4
  355. package/dist/ionic/p-6470b136.entry.js +0 -4
  356. package/dist/ionic/p-6c37f051.js +0 -4
  357. package/dist/ionic/p-7b8d0af2.system.entry.js +0 -4
  358. package/dist/ionic/p-7faba551.system.entry.js +0 -4
  359. package/dist/ionic/p-83af3685.system.js +0 -4
  360. package/dist/ionic/p-856c430d.js +0 -4
  361. package/dist/ionic/p-93000a81.system.entry.js +0 -4
  362. package/dist/ionic/p-98b14ece.entry.js +0 -4
  363. package/dist/ionic/p-990ffd0e.js +0 -4
  364. package/dist/ionic/p-a6f9dd7b.entry.js +0 -4
  365. package/dist/ionic/p-a98dc0c1.entry.js +0 -4
  366. package/dist/ionic/p-a9e3b5be.system.entry.js +0 -4
  367. package/dist/ionic/p-ab9456e9.system.entry.js +0 -4
  368. package/dist/ionic/p-b4bad293.system.entry.js +0 -4
  369. package/dist/ionic/p-c3289ef2.entry.js +0 -4
  370. package/dist/ionic/p-c3d09cf2.system.entry.js +0 -4
  371. package/dist/ionic/p-d1cb29a8.system.entry.js +0 -4
  372. package/dist/ionic/p-d43f1f3d.system.js +0 -4
  373. package/dist/ionic/p-d662d908.js +0 -4
  374. package/dist/ionic/p-d9695ad9.system.js +0 -4
  375. package/dist/ionic/p-e4d90573.js +0 -4
  376. package/dist/ionic/p-f54c72b0.system.js +0 -4
  377. package/dist/ionic/p-f7a36cd0.system.js +0 -4
  378. package/dist/ionic/p-f967631d.js +0 -4
  379. /package/dist/esm-es5/{index-7a14ecec.js → index-a5d50daf.js} +0 -0
  380. /package/dist/ionic/{p-0e94957a.system.js → p-792919fd.system.js} +0 -0
  381. /package/dist/ionic/{p-b347cfd1.js → p-7b30edcc.js} +0 -0
@@ -2,7 +2,7 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { Host, h, forceUpdate } from "@stencil/core";
5
- import { createLegacyFormController, createNotchController } from "../../utils/forms/index";
5
+ import { compareOptions, createLegacyFormController, createNotchController, isOptionSelected } from "../../utils/forms/index";
6
6
  import { findItemLabel, focusElement, getAriaLabel, renderHiddenInput, inheritAttributes } from "../../utils/helpers";
7
7
  import { printIonWarning } from "../../utils/logging/index";
8
8
  import { actionSheetController, alertController, popoverController } from "../../utils/overlays";
@@ -16,6 +16,8 @@ import { getIonMode } from "../../global/ionic-global";
16
16
  * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
17
17
  *
18
18
  * @slot label - The label text to associate with the select. Use the `labelPlacement` property to control where the label is placed relative to the select. Use this if you need to render a label with custom HTML.
19
+ * @slot start - Content to display at the leading edge of the select.
20
+ * @slot end - Content to display at the trailing edge of the select.
19
21
  *
20
22
  * @part placeholder - The text displayed in the select when there is no value.
21
23
  * @part text - The displayed value of the select.
@@ -30,8 +32,22 @@ export class Select {
30
32
  // This flag ensures we log the deprecation warning at most once.
31
33
  this.hasLoggedDeprecationWarning = false;
32
34
  this.onClick = (ev) => {
33
- this.setFocus();
34
- this.open(ev);
35
+ const target = ev.target;
36
+ const closestSlot = target.closest('[slot="start"], [slot="end"]');
37
+ if (target === this.el || closestSlot === null) {
38
+ this.setFocus();
39
+ this.open(ev);
40
+ }
41
+ else {
42
+ /**
43
+ * Prevent clicks to the start/end slots from opening the select.
44
+ * We ensure the target isn't this element in case the select is slotted
45
+ * in, for example, an item. This would prevent the select from ever
46
+ * being opened since the element itself has slot="start"/"end".
47
+ */
48
+ ev.stopPropagation();
49
+ ev.preventDefault();
50
+ }
35
51
  };
36
52
  this.onFocus = () => {
37
53
  this.ionFocus.emit();
@@ -465,6 +481,8 @@ export class Select {
465
481
  style['has-placeholder'] = this.placeholder !== undefined;
466
482
  style['has-value'] = this.hasValue();
467
483
  style['has-focus'] = this.isExpanded;
484
+ // TODO(FW-3194): remove this
485
+ style['legacy'] = !!this.legacy;
468
486
  }
469
487
  this.ionStyle.emit(style);
470
488
  }
@@ -532,7 +550,27 @@ export class Select {
532
550
  const rtl = isRTL(el) ? 'rtl' : 'ltr';
533
551
  const inItem = hostContext('ion-item', this.el);
534
552
  const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
553
+ const hasValue = this.hasValue();
554
+ const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
535
555
  renderHiddenInput(true, el, name, parseValue(value), disabled);
556
+ /**
557
+ * If the label is stacked, it should always sit above the select.
558
+ * For floating labels, the label should move above the select if
559
+ * the select has a value, is open, or has anything in either
560
+ * the start or end slot.
561
+ *
562
+ * If there is content in the start slot, the label would overlap
563
+ * it if not forced to float. This is also applied to the end slot
564
+ * because with the default or solid fills, the select is not
565
+ * vertically centered in the container, but the label is. This
566
+ * causes the slots and label to appear vertically offset from each
567
+ * other when the label isn't floating above the input. This doesn't
568
+ * apply to the outline fill, but this was not accounted for to keep
569
+ * things consistent.
570
+ *
571
+ * TODO(FW-5592): Remove hasStartEndSlots condition
572
+ */
573
+ const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
536
574
  return (h(Host, { onClick: this.onClick, class: createColorClasses(this.color, {
537
575
  [mode]: true,
538
576
  'in-item': inItem,
@@ -540,7 +578,8 @@ export class Select {
540
578
  'select-disabled': disabled,
541
579
  'select-expanded': isExpanded,
542
580
  'has-expanded-icon': expandedIcon !== undefined,
543
- 'has-value': this.hasValue(),
581
+ 'has-value': hasValue,
582
+ 'label-floating': labelShouldFloat,
544
583
  'has-placeholder': placeholder !== undefined,
545
584
  'ion-focusable': true,
546
585
  [`select-${rtl}`]: true,
@@ -548,7 +587,7 @@ export class Select {
548
587
  [`select-justify-${justify}`]: justifyEnabled,
549
588
  [`select-shape-${shape}`]: shape !== undefined,
550
589
  [`select-label-placement-${labelPlacement}`]: true,
551
- }) }, h("label", { class: "select-wrapper", id: "select-label" }, this.renderLabelContainer(), h("div", { class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), !hasFloatingOrStackedLabel && this.renderSelectIcon(), this.renderListbox()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { class: "select-highlight" }))));
590
+ }) }, h("label", { class: "select-wrapper", id: "select-label" }, this.renderLabelContainer(), h("div", { class: "select-wrapper-inner" }, h("slot", { name: "start" }), h("div", { class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { class: "select-highlight" }))));
552
591
  }
553
592
  // TODO FW-3194 - Remove this
554
593
  renderLegacySelect() {
@@ -737,7 +776,7 @@ Developers can use the "legacy" property to continue using the legacy form marku
737
776
  "optional": true,
738
777
  "docs": {
739
778
  "tags": [],
740
- "text": "A property name or function used to compare object values"
779
+ "text": "This property allows developers to specify a custom function or property\nname for comparing objects when determining the selected option in the\nion-select. When not specified, the default behavior will use strict\nequality (===) for comparison."
741
780
  },
742
781
  "attribute": "compare-with",
743
782
  "reflect": false
@@ -1215,17 +1254,6 @@ Developers can use the "legacy" property to continue using the legacy form marku
1215
1254
  }];
1216
1255
  }
1217
1256
  }
1218
- const isOptionSelected = (currentValue, compareValue, compareWith) => {
1219
- if (currentValue === undefined) {
1220
- return false;
1221
- }
1222
- if (Array.isArray(currentValue)) {
1223
- return currentValue.some((val) => compareOptions(val, compareValue, compareWith));
1224
- }
1225
- else {
1226
- return compareOptions(currentValue, compareValue, compareWith);
1227
- }
1228
- };
1229
1257
  const getOptionValue = (el) => {
1230
1258
  const value = el.value;
1231
1259
  return value === undefined ? el.textContent || '' : value;
@@ -1239,17 +1267,6 @@ const parseValue = (value) => {
1239
1267
  }
1240
1268
  return value.toString();
1241
1269
  };
1242
- const compareOptions = (currentValue, compareValue, compareWith) => {
1243
- if (typeof compareWith === 'function') {
1244
- return compareWith(currentValue, compareValue);
1245
- }
1246
- else if (typeof compareWith === 'string') {
1247
- return currentValue[compareWith] === compareValue[compareWith];
1248
- }
1249
- else {
1250
- return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
1251
- }
1252
- };
1253
1270
  const generateText = (opts, value, compareWith) => {
1254
1271
  if (value === undefined) {
1255
1272
  return '';
@@ -132,6 +132,10 @@
132
132
  overflow: hidden;
133
133
  }
134
134
 
135
+ :host(.in-item:not(.legacy-select)) {
136
+ flex: 1 1 0;
137
+ }
138
+
135
139
  :host(.in-item.legacy-select) {
136
140
  position: static;
137
141
  max-width: 45%;
@@ -232,6 +236,12 @@ button {
232
236
  margin-top: 0;
233
237
  margin-bottom: 0;
234
238
  position: relative;
239
+ /**
240
+ * Prevent the icon from shrinking when the label and/or
241
+ * selected item text is long enough to fill the rest of
242
+ * the container.
243
+ */
244
+ flex-shrink: 0;
235
245
  }
236
246
 
237
247
  /**
@@ -315,6 +325,23 @@ button {
315
325
  transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
316
326
  }
317
327
 
328
+ .select-wrapper-inner {
329
+ display: flex;
330
+ align-items: center;
331
+ overflow: hidden;
332
+ }
333
+
334
+ :host(.select-label-placement-stacked) .select-wrapper-inner,
335
+ :host(.select-label-placement-floating) .select-wrapper-inner {
336
+ /**
337
+ * When using a stacked/floating label, the inner wrapper is
338
+ * stacked vertically under the label container. This line
339
+ * ensures that the inner wrapper fills all the remaining height
340
+ * of the component.
341
+ */
342
+ flex-grow: 1;
343
+ }
344
+
318
345
  :host(.ion-touched.ion-invalid) {
319
346
  --highlight-color: var(--highlight-color-invalid);
320
347
  }
@@ -555,11 +582,23 @@ button {
555
582
  * The placeholder should be hidden when the label
556
583
  * is on top of the select. This prevents the label
557
584
  * from overlapping any placeholder value.
585
+ *
586
+ * TODO(FW-5592): Remove :not(.label-floating) piece
558
587
  */
559
- :host(.select-label-placement-floating) .native-wrapper .select-placeholder {
588
+ :host(.select-label-placement-floating:not(.label-floating)) .native-wrapper .select-placeholder {
560
589
  opacity: 0;
561
590
  }
562
591
 
592
+ /**
593
+ * We don't use .label-floating here because that would
594
+ * also include the case where the label is floating due
595
+ * to content in the start/end slot. We want the opacity
596
+ * to remain at the default in this case, since the select
597
+ * isn't being actively interacted with.
598
+ *
599
+ * TODO(FW-5592): Change entire selector to:
600
+ * :host(.label-floating.select-label-placement-floating) .native-wrapper .select-placeholder
601
+ */
563
602
  :host(.select-expanded.select-label-placement-floating) .native-wrapper .select-placeholder,
564
603
  :host(.ion-focused.select-label-placement-floating) .native-wrapper .select-placeholder,
565
604
  :host(.has-value.select-label-placement-floating) .native-wrapper .select-placeholder {
@@ -569,10 +608,7 @@ button {
569
608
  /**
570
609
  * This makes the label sit above the input.
571
610
  */
572
- :host(.select-label-placement-stacked) .label-text-wrapper,
573
- :host(.select-expanded.select-label-placement-floating) .label-text-wrapper,
574
- :host(.ion-focused.select-label-placement-floating) .label-text-wrapper,
575
- :host(.has-value.select-label-placement-floating) .label-text-wrapper {
611
+ :host(.label-floating) .label-text-wrapper {
576
612
  transform: translateY(50%) scale(0.75);
577
613
  /**
578
614
  * Label text should not extend
@@ -581,6 +617,23 @@ button {
581
617
  max-width: calc(100% / 0.75);
582
618
  }
583
619
 
620
+ ::slotted([slot=start]), ::slotted([slot=end]) {
621
+ /**
622
+ * Prevent the slots from shrinking when the label and/or
623
+ * selected item text is long enough to fill the rest of
624
+ * the container.
625
+ */
626
+ flex-shrink: 0;
627
+ }
628
+
629
+ ::slotted([slot=start]) {
630
+ margin-inline-end: 16px;
631
+ }
632
+
633
+ ::slotted([slot=end]) {
634
+ margin-inline-start: 16px;
635
+ }
636
+
584
637
  /**
585
638
  * Convert a font size to a dynamic font size.
586
639
  * Fonts that participate in Dynamic Type should use
@@ -824,10 +877,7 @@ button {
824
877
  }
825
878
  }
826
879
 
827
- :host(.select-fill-solid.select-label-placement-stacked) .label-text-wrapper,
828
- :host(.select-expanded.select-fill-solid.select-label-placement-floating) .label-text-wrapper,
829
- :host(.ion-focused.select-fill-solid.select-label-placement-floating) .label-text-wrapper,
830
- :host(.has-value.select-fill-solid.select-label-placement-floating) .label-text-wrapper {
880
+ :host(.label-floating.select-fill-solid) .label-text-wrapper {
831
881
  /**
832
882
  * Label text should not extend
833
883
  * beyond the bounds of the select.
@@ -983,10 +1033,7 @@ button {
983
1033
  /**
984
1034
  * This makes the label sit above the select.
985
1035
  */
986
- :host(.select-expanded.select-fill-outline.select-label-placement-floating) .label-text-wrapper,
987
- :host(.ion-focused.select-fill-outline.select-label-placement-floating) .label-text-wrapper,
988
- :host(.has-value.select-fill-outline.select-label-placement-floating) .label-text-wrapper,
989
- :host(.select-fill-outline.select-label-placement-stacked) .label-text-wrapper {
1036
+ :host(.label-floating.select-fill-outline) .label-text-wrapper {
990
1037
  transform: translateY(-32%) scale(0.75);
991
1038
  margin-left: 0;
992
1039
  margin-right: 0;
@@ -1131,10 +1178,7 @@ button {
1131
1178
  * the floating/stacked label. We simulate this "cut out"
1132
1179
  * by removing the top border from the notch fragment.
1133
1180
  */
1134
- :host(.select-expanded.select-fill-outline.select-label-placement-floating) .select-outline-notch,
1135
- :host(.ion-focused.select-fill-outline.select-label-placement-floating) .select-outline-notch,
1136
- :host(.has-value.select-fill-outline.select-label-placement-floating) .select-outline-notch,
1137
- :host(.select-fill-outline.select-label-placement-stacked) .select-outline-notch {
1181
+ :host(.label-floating.select-fill-outline) .select-outline-notch {
1138
1182
  border-top: none;
1139
1183
  }
1140
1184
 
@@ -1299,8 +1343,8 @@ button {
1299
1343
  --border-radius: 16px;
1300
1344
  }
1301
1345
 
1302
- :host(.select-label-placement-stacked) .native-wrapper,
1303
- :host(.select-label-placement-floating) .native-wrapper {
1346
+ :host(.select-label-placement-stacked) .select-wrapper-inner,
1347
+ :host(.select-label-placement-floating) .select-wrapper-inner {
1304
1348
  width: calc(100% - 0.8125rem - 4px);
1305
1349
  }
1306
1350
 
@@ -37,5 +37,20 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
37
37
  const list = page.locator('ion-list');
38
38
  await expect(list).toHaveScreenshot(screenshot(`select-inset-list-no-fill`));
39
39
  });
40
+ test('should render correctly in an item inside of a flex container', async ({ page }) => {
41
+ await page.setContent(`
42
+ <div id="container" style="display: flex">
43
+ <ion-list>
44
+ <ion-item>
45
+ <ion-select label="Fruit" value="apple">
46
+ <ion-select-option value="apple">Apple</ion-select-option>
47
+ </ion-select>
48
+ </ion-item>
49
+ <ion-list>
50
+ </div>
51
+ `, config);
52
+ const container = page.locator('#container');
53
+ await expect(container).toHaveScreenshot(screenshot(`select-item-flex-container`));
54
+ });
40
55
  });
41
56
  });
@@ -121,7 +121,7 @@ configs().forEach(({ title, screenshot, config }) => {
121
121
  });
122
122
  test('label should appear on top of the select when the select is expanded', async ({ page }) => {
123
123
  await page.setContent(`
124
- <ion-select class="select-expanded" label="Label" label-placement="floating" placeholder="Select a Fruit">
124
+ <ion-select class="select-expanded label-floating" label="Label" label-placement="floating" placeholder="Select a Fruit">
125
125
  <ion-select-option value="apples">Apples</ion-select-option>
126
126
  </ion-select>
127
127
  `, config);
@@ -27,7 +27,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
27
27
  await page.setContent(`
28
28
  <ion-item>
29
29
  <ion-label>Really long label should not wrap</ion-label>
30
- <ion-select value="wrap" class="ion-text-wrap">
30
+ <ion-select value="wrap" legacy="true" class="ion-text-wrap">
31
31
  <ion-select-option value="wrap">Should wrap value only when label exists and really long text exists to make it wrap the text</ion-select-option>
32
32
  </ion-select>
33
33
  </ion-item>
@@ -0,0 +1,67 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ import { expect } from "@playwright/test";
5
+ import { configs, test } from "../../../../utils/test/playwright/index";
6
+ configs().forEach(({ title, screenshot, config }) => {
7
+ test.describe(title('select: start and end slots (visual checks)'), () => {
8
+ test('should not have visual regressions with a start-positioned label', async ({ page }) => {
9
+ await page.setContent(`
10
+ <ion-select label-placement="start" fill="solid" placeholder="Select weight" label="Weight">
11
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
12
+ <ion-label slot="end">lbs</ion-label>
13
+ </ion-select>
14
+ `, config);
15
+ const select = page.locator('ion-select');
16
+ await expect(select).toHaveScreenshot(screenshot(`select-slots-label-start`));
17
+ });
18
+ test('should not have visual regressions with a floating label', async ({ page }) => {
19
+ await page.setContent(`
20
+ <ion-select label-placement="floating" fill="solid" placeholder="Select weight" label="Weight">
21
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
22
+ <ion-label slot="end">lbs</ion-label>
23
+ </ion-select>
24
+ `, config);
25
+ const select = page.locator('ion-select');
26
+ await expect(select).toHaveScreenshot(screenshot(`select-slots-label-floating`));
27
+ });
28
+ });
29
+ });
30
+ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
31
+ test.describe(title('select: start and end slots (functionality checks)'), () => {
32
+ test('should raise floating label when there is content in the start slot', async ({ page }) => {
33
+ await page.setContent(`
34
+ <ion-select label-placement="floating" fill="solid" label="Weight">
35
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
36
+ </ion-select>
37
+ `, config);
38
+ const select = page.locator('ion-select');
39
+ await expect(select).toHaveClass(/label-floating/);
40
+ });
41
+ test('should raise floating label when there is content in the end slot', async ({ page }) => {
42
+ await page.setContent(`
43
+ <ion-select label-placement="floating" fill="solid" label="Weight">
44
+ <ion-icon slot="end" name="barbell" aria-hidden="true"></ion-icon>
45
+ </ion-select>
46
+ `, config);
47
+ const select = page.locator('ion-select');
48
+ await expect(select).toHaveClass(/label-floating/);
49
+ });
50
+ test('should not open select when slotted buttons are clicked', async ({ page }) => {
51
+ await page.setContent(`
52
+ <ion-select label="Favorite Pizza" placeholder="Select a pizza">
53
+ <ion-select-option value="pepperoni">Pepperoni</ion-select-option>
54
+ <ion-select-option value="supreme">Supreme</ion-select-option>
55
+ <ion-select-option value="chicken">Chicken</ion-select-option>
56
+ <ion-button fill="clear" slot="end" aria-label="Show/hide password">
57
+ <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
58
+ </ion-button>
59
+ </ion-select>
60
+ `, config);
61
+ await page.click('ion-select ion-button[slot="end"]');
62
+ await page.waitForChanges();
63
+ const select = page.locator('ion-select');
64
+ await expect(select).not.toHaveClass(/select-expanded/);
65
+ });
66
+ });
67
+ });
@@ -9,6 +9,18 @@ export class SkeletonText {
9
9
  constructor() {
10
10
  this.animated = false;
11
11
  }
12
+ componentWillLoad() {
13
+ this.emitStyle();
14
+ }
15
+ emitStyle() {
16
+ // The emitted property is used by item in order
17
+ // to add the item-skeleton-text class which applies
18
+ // overflow: hidden to its label
19
+ const style = {
20
+ 'skeleton-text': true,
21
+ };
22
+ this.ionStyle.emit(style);
23
+ }
12
24
  render() {
13
25
  const animated = this.animated && config.getBoolean('animated', true);
14
26
  const inMedia = hostContext('ion-avatar', this.el) || hostContext('ion-thumbnail', this.el);
@@ -53,5 +65,32 @@ export class SkeletonText {
53
65
  }
54
66
  };
55
67
  }
68
+ static get events() {
69
+ return [{
70
+ "method": "ionStyle",
71
+ "name": "ionStyle",
72
+ "bubbles": true,
73
+ "cancelable": true,
74
+ "composed": true,
75
+ "docs": {
76
+ "tags": [{
77
+ "name": "internal",
78
+ "text": undefined
79
+ }],
80
+ "text": "Emitted when the styles change."
81
+ },
82
+ "complexType": {
83
+ "original": "StyleEventDetail",
84
+ "resolved": "StyleEventDetail",
85
+ "references": {
86
+ "StyleEventDetail": {
87
+ "location": "import",
88
+ "path": "../../interface",
89
+ "id": "src/interface.d.ts::StyleEventDetail"
90
+ }
91
+ }
92
+ }
93
+ }];
94
+ }
56
95
  static get elementRef() { return "el"; }
57
96
  }
@@ -0,0 +1,51 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ import { expect } from "@playwright/test";
5
+ import { configs, test } from "../../../../utils/test/playwright/index";
6
+ configs().forEach(({ title, screenshot, config }) => {
7
+ test.describe(title('textarea: start and end slots (visual checks)'), () => {
8
+ test('should not have visual regressions with a start-positioned label', async ({ page }) => {
9
+ await page.setContent(`
10
+ <ion-textarea label-placement="start" fill="solid" value="100" label="Weight">
11
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
12
+ <ion-label slot="end">lbs</ion-label>
13
+ </ion-textarea>
14
+ `, config);
15
+ const textarea = page.locator('ion-textarea');
16
+ await expect(textarea).toHaveScreenshot(screenshot(`textarea-slots-label-start`));
17
+ });
18
+ test('should not have visual regressions with a floating label', async ({ page }) => {
19
+ await page.setContent(`
20
+ <ion-textarea label-placement="floating" fill="solid" value="100" label="Weight">
21
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
22
+ <ion-label slot="end">lbs</ion-label>
23
+ </ion-textarea>
24
+ `, config);
25
+ const textarea = page.locator('ion-textarea');
26
+ await expect(textarea).toHaveScreenshot(screenshot(`textarea-slots-label-floating`));
27
+ });
28
+ });
29
+ });
30
+ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
31
+ test.describe(title('textarea: start and end slots (functionality checks)'), () => {
32
+ test('should raise floating label when there is content in the start slot', async ({ page }) => {
33
+ await page.setContent(`
34
+ <ion-textarea label-placement="floating" fill="solid" label="Weight">
35
+ <ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
36
+ </ion-textarea>
37
+ `, config);
38
+ const textarea = page.locator('ion-textarea');
39
+ await expect(textarea).toHaveClass(/label-floating/);
40
+ });
41
+ test('should raise floating label when there is content in the end slot', async ({ page }) => {
42
+ await page.setContent(`
43
+ <ion-textarea label-placement="floating" fill="solid" label="Weight">
44
+ <ion-icon slot="end" name="barbell" aria-hidden="true"></ion-icon>
45
+ </ion-textarea>
46
+ `, config);
47
+ const textarea = page.locator('ion-textarea');
48
+ await expect(textarea).toHaveClass(/label-floating/);
49
+ });
50
+ });
51
+ });
@@ -356,9 +356,7 @@
356
356
  }
357
357
 
358
358
  .native-wrapper {
359
- display: flex;
360
359
  position: relative;
361
- flex-grow: 1;
362
360
  width: 100%;
363
361
  height: 100%;
364
362
  }
@@ -429,6 +427,12 @@
429
427
  word-break: break-word;
430
428
  }
431
429
 
430
+ .textarea-wrapper-inner {
431
+ display: flex;
432
+ width: 100%;
433
+ min-height: inherit;
434
+ }
435
+
432
436
  :host(.ion-touched.ion-invalid) {
433
437
  --highlight-color: var(--highlight-color-invalid);
434
438
  }
@@ -704,6 +708,13 @@
704
708
  margin-bottom: 0px;
705
709
  }
706
710
 
711
+ :host(.textarea-label-placement-stacked) ::slotted([slot=start]),
712
+ :host(.textarea-label-placement-stacked) ::slotted([slot=end]),
713
+ :host(.textarea-label-placement-floating) ::slotted([slot=start]),
714
+ :host(.textarea-label-placement-floating) ::slotted([slot=end]) {
715
+ margin-top: 8px;
716
+ }
717
+
707
718
  /**
708
719
  * This makes the label sit over the textarea
709
720
  * when the textarea is blurred and has no value.
@@ -729,9 +740,7 @@
729
740
  /**
730
741
  * This makes the label sit above the textarea.
731
742
  */
732
- :host(.textarea-label-placement-stacked) .label-text-wrapper,
733
- :host(.has-focus.textarea-label-placement-floating) .label-text-wrapper,
734
- :host(.has-value.textarea-label-placement-floating) .label-text-wrapper {
743
+ :host(.label-floating) .label-text-wrapper {
735
744
  transform: translateY(50%) scale(0.75);
736
745
  /**
737
746
  * Label text should not extend
@@ -740,6 +749,29 @@
740
749
  max-width: calc(100% / 0.75);
741
750
  }
742
751
 
752
+ .start-slot-wrapper, .end-slot-wrapper {
753
+ padding-left: 0;
754
+ padding-right: 0;
755
+ padding-top: var(--padding-top);
756
+ padding-bottom: var(--padding-bottom);
757
+ display: flex;
758
+ flex-shrink: 0;
759
+ align-self: start;
760
+ }
761
+
762
+ ::slotted([slot=start]),
763
+ ::slotted([slot=end]) {
764
+ margin-top: 0;
765
+ }
766
+
767
+ ::slotted([slot=start]) {
768
+ margin-inline-end: 16px;
769
+ }
770
+
771
+ ::slotted([slot=end]) {
772
+ margin-inline-start: 16px;
773
+ }
774
+
743
775
  /**
744
776
  * Convert a font size to a dynamic font size.
745
777
  * Fonts that participate in Dynamic Type should use
@@ -13,6 +13,8 @@ import { getCounterText } from "../input/input.utils";
13
13
  * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
14
14
  *
15
15
  * @slot label - The label text to associate with the textarea. Use the `labelPlacement` property to control where the label is placed relative to the textarea. Use this if you need to render a label with custom HTML. (EXPERIMENTAL)
16
+ * @slot start - Content to display at the leading edge of the textarea. (EXPERIMENTAL)
17
+ * @slot end - Content to display at the trailing edge of the textarea. (EXPERIMENTAL)
16
18
  */
17
19
  export class Textarea {
18
20
  constructor() {
@@ -121,7 +123,7 @@ export class Textarea {
121
123
  connectedCallback() {
122
124
  const { el } = this;
123
125
  this.legacyFormController = createLegacyFormController(el);
124
- this.slotMutationController = createSlotMutationController(el, 'label', () => forceUpdate(this));
126
+ this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
125
127
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
126
128
  this.emitStyle();
127
129
  this.debounceChanged();
@@ -191,6 +193,8 @@ export class Textarea {
191
193
  'has-placeholder': this.placeholder !== undefined,
192
194
  'has-value': this.hasValue(),
193
195
  'has-focus': this.hasFocus,
196
+ // TODO(FW-2876): remove this
197
+ legacy: !!this.legacy,
194
198
  });
195
199
  }
196
200
  }
@@ -362,20 +366,41 @@ Developers can use the "legacy" property to continue using the legacy form marku
362
366
  return (h("div", { class: "textarea-bottom" }, this.renderHintText(), this.renderCounter()));
363
367
  }
364
368
  renderTextarea() {
365
- const { inputId, disabled, fill, shape, labelPlacement } = this;
369
+ const { inputId, disabled, fill, shape, labelPlacement, el, hasFocus } = this;
366
370
  const mode = getIonMode(this);
367
371
  const value = this.getValue();
368
372
  const inItem = hostContext('ion-item', this.el);
369
373
  const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
374
+ const hasValue = this.hasValue();
375
+ const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
376
+ /**
377
+ * If the label is stacked, it should always sit above the textarea.
378
+ * For floating labels, the label should move above the textarea if
379
+ * the textarea has a value, is focused, or has anything in either
380
+ * the start or end slot.
381
+ *
382
+ * If there is content in the start slot, the label would overlap
383
+ * it if not forced to float. This is also applied to the end slot
384
+ * because with the default or solid fills, the textarea is not
385
+ * vertically centered in the container, but the label is. This
386
+ * causes the slots and label to appear vertically offset from each
387
+ * other when the label isn't floating above the input. This doesn't
388
+ * apply to the outline fill, but this was not accounted for to keep
389
+ * things consistent.
390
+ *
391
+ * TODO(FW-5592): Remove hasStartEndSlots condition
392
+ */
393
+ const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
370
394
  return (h(Host, { class: createColorClasses(this.color, {
371
395
  [mode]: true,
372
- 'has-value': this.hasValue(),
373
- 'has-focus': this.hasFocus,
396
+ 'has-value': hasValue,
397
+ 'has-focus': hasFocus,
398
+ 'label-floating': labelShouldFloat,
374
399
  [`textarea-fill-${fill}`]: fill !== undefined,
375
400
  [`textarea-shape-${shape}`]: shape !== undefined,
376
401
  [`textarea-label-placement-${labelPlacement}`]: true,
377
402
  'textarea-disabled': disabled,
378
- }) }, h("label", { class: "textarea-wrapper" }, this.renderLabelContainer(), h("div", { class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ 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 }, this.inheritedAttributes), value)), shouldRenderHighlight && h("div", { class: "textarea-highlight" })), this.renderBottomContent()));
403
+ }) }, h("label", { class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { class: "textarea-wrapper-inner" }, h("div", { class: "start-slot-wrapper" }, h("slot", { name: "start" })), h("div", { class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ 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 }, this.inheritedAttributes), value)), h("div", { class: "end-slot-wrapper" }, h("slot", { name: "end" }))), shouldRenderHighlight && h("div", { class: "textarea-highlight" })), this.renderBottomContent()));
379
404
  }
380
405
  render() {
381
406
  const { legacyFormController } = this;