voyager-ionic-core 8.8.10 → 8.8.17

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 (247) hide show
  1. package/components/index.js +1 -1
  2. package/components/ion-accordion.js +1 -1
  3. package/components/ion-action-sheet.js +1 -1
  4. package/components/ion-alert.js +1 -1
  5. package/components/ion-app.js +1 -1
  6. package/components/ion-back-button.js +1 -1
  7. package/components/ion-breadcrumb.js +1 -1
  8. package/components/ion-button.js +1 -1
  9. package/components/ion-card.js +1 -1
  10. package/components/ion-checkbox.js +1 -1
  11. package/components/ion-content.js +1 -1
  12. package/components/ion-datetime-button.js +1 -1
  13. package/components/ion-datetime.js +1 -1
  14. package/components/ion-fab-button.js +1 -1
  15. package/components/ion-footer.js +1 -1
  16. package/components/ion-header.js +1 -1
  17. package/components/ion-img.js +1 -1
  18. package/components/ion-infinite-scroll.js +1 -1
  19. package/components/ion-input-otp.js +1 -1
  20. package/components/ion-input-password-toggle.js +1 -1
  21. package/components/ion-input.js +1 -1
  22. package/components/ion-item-options.js +1 -1
  23. package/components/ion-item-sliding.js +1 -1
  24. package/components/ion-item.js +1 -1
  25. package/components/ion-loading.js +1 -1
  26. package/components/ion-menu-button.js +1 -1
  27. package/components/ion-menu-toggle.js +1 -1
  28. package/components/ion-menu.js +1 -1
  29. package/components/ion-modal.js +1 -1
  30. package/components/ion-nav.js +1 -1
  31. package/components/ion-picker-column-option.js +1 -1
  32. package/components/ion-picker-column.js +1 -1
  33. package/components/ion-picker-legacy-column.js +1 -1
  34. package/components/ion-picker-legacy.js +1 -1
  35. package/components/ion-picker.js +1 -1
  36. package/components/ion-popover.js +1 -1
  37. package/components/ion-progress-bar.js +1 -1
  38. package/components/ion-radio-group.js +1 -1
  39. package/components/ion-radio.js +1 -1
  40. package/components/ion-range.js +1 -1
  41. package/components/ion-refresher-content.js +1 -1
  42. package/components/ion-refresher.js +1 -1
  43. package/components/ion-reorder-group.js +1 -1
  44. package/components/ion-router-outlet.js +1 -1
  45. package/components/ion-router.js +1 -1
  46. package/components/ion-searchbar.js +1 -1
  47. package/components/ion-segment-button.js +1 -1
  48. package/components/ion-segment.js +1 -1
  49. package/components/ion-select-modal.js +1 -1
  50. package/components/ion-select-popover.js +1 -1
  51. package/components/ion-select.js +1 -1
  52. package/components/ion-tab-button.js +1 -1
  53. package/components/ion-tab.js +1 -1
  54. package/components/ion-textarea.js +1 -1
  55. package/components/ion-toast.js +1 -1
  56. package/components/ion-toggle.js +1 -1
  57. package/components/{p-CtukzcyX.js → p-A3O24YmY.js} +1 -1
  58. package/components/{p-CU5rbWAW.js → p-ABkNVeU9.js} +1 -1
  59. package/components/{p-BT_5jSqG.js → p-B-JJgYSG.js} +1 -1
  60. package/components/p-BB42Mlnr.js +4 -0
  61. package/components/{p-lE_JGpHD.js → p-BMI53wYL.js} +1 -1
  62. package/components/p-BkKAANMJ.js +4 -0
  63. package/components/p-BlIAuGv2.js +4 -0
  64. package/components/{p-Bth-EzJ0.js → p-Bnsn6KaP.js} +1 -1
  65. package/components/p-BsBCoGa-.js +4 -0
  66. package/components/{p-BnqQlm7c.js → p-CQGnMY3n.js} +1 -1
  67. package/components/{p-DNQI6d-L.js → p-C_hpsif8.js} +1 -1
  68. package/components/p-Cf2jSDS8.js +4 -0
  69. package/components/{p-C4t5ymfq.js → p-ChIZKXUu.js} +1 -1
  70. package/components/p-CkMSOsFF.js +4 -0
  71. package/components/p-CsNJ1ULL.js +4 -0
  72. package/components/{p-BsPelv04.js → p-CzCbeKys.js} +1 -1
  73. package/components/{p-BjfgFSSR.js → p-D-aHdMxW.js} +1 -1
  74. package/components/p-D0d8qTwj.js +4 -0
  75. package/components/{p-Baq1XyAy.js → p-D3xo7hqU.js} +1 -1
  76. package/components/{p-nQi4oKcG.js → p-DCnGU1fF.js} +1 -1
  77. package/components/p-DGZpx6KJ.js +4 -0
  78. package/components/p-DGvrK8dD.js +4 -0
  79. package/components/p-DHTSTb52.js +4 -0
  80. package/components/{p-BirPFsRD.js → p-DJZp0KTQ.js} +1 -1
  81. package/components/{p-ijF0iCrA.js → p-DRugphUN.js} +1 -1
  82. package/components/{p-0NRupjzW.js → p-DV5mPYQS.js} +1 -1
  83. package/components/{p-C1aScSTo.js → p-DWkAgceV.js} +1 -1
  84. package/components/{p-DDWp95gH.js → p-KEFO0w3i.js} +1 -1
  85. package/components/{p-Ciz1-FuE.js → p-Osve4qkc.js} +1 -1
  86. package/components/{p-dhLYXa7A.js → p-SLS1rDZM.js} +1 -1
  87. package/components/{p-D5Ubpm7D.js → p-VWbRkLT7.js} +1 -1
  88. package/components/{p-BTPfOxsU.js → p-aJ6PBUvj.js} +1 -1
  89. package/components/{p-DnL1c9_X.js → p-oD4kcBRX.js} +1 -1
  90. package/components/{p-CI3ncNZm.js → p-scxfI1Dd.js} +1 -1
  91. package/components/p-yzCsOZE2.js +4 -0
  92. package/dist/cjs/{data-DLTUw-KF.js → format-PxvGiOik.js} +8 -532
  93. package/dist/cjs/index.cjs.js +1 -1
  94. package/dist/cjs/ion-action-sheet.cjs.entry.js +1 -1
  95. package/dist/cjs/ion-alert.cjs.entry.js +1 -1
  96. package/dist/cjs/ion-button_2.cjs.entry.js +7 -3
  97. package/dist/cjs/ion-checkbox.cjs.entry.js +17 -7
  98. package/dist/cjs/ion-datetime-button.cjs.entry.js +17 -15
  99. package/dist/cjs/ion-datetime_3.cjs.entry.js +616 -75
  100. package/dist/cjs/ion-fab_3.cjs.entry.js +87 -9
  101. package/dist/cjs/ion-input-otp.cjs.entry.js +3 -3
  102. package/dist/cjs/ion-item_8.cjs.entry.js +84 -4
  103. package/dist/cjs/ion-loading.cjs.entry.js +1 -1
  104. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  105. package/dist/cjs/ion-modal.cjs.entry.js +35 -9
  106. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  107. package/dist/cjs/ion-radio_2.cjs.entry.js +16 -7
  108. package/dist/cjs/ion-refresher_2.cjs.entry.js +31 -4
  109. package/dist/cjs/ion-searchbar.cjs.entry.js +26 -5
  110. package/dist/cjs/ion-select-modal.cjs.entry.js +1 -1
  111. package/dist/cjs/ion-select_3.cjs.entry.js +5 -4
  112. package/dist/cjs/ion-tab_2.cjs.entry.js +20 -13
  113. package/dist/cjs/ion-toast.cjs.entry.js +1 -1
  114. package/dist/cjs/ion-toggle.cjs.entry.js +25 -6
  115. package/dist/cjs/ionic.cjs.js +1 -1
  116. package/dist/cjs/item-multiple-inputs-D-gyg57c.js +38 -0
  117. package/dist/cjs/loader.cjs.js +1 -1
  118. package/dist/cjs/{overlays-TbKsuC-K.js → overlays-DdtIYQUH.js} +24 -1
  119. package/dist/collection/components/button/button.js +7 -3
  120. package/dist/collection/components/checkbox/checkbox.ios.css +18 -0
  121. package/dist/collection/components/checkbox/checkbox.js +16 -7
  122. package/dist/collection/components/checkbox/checkbox.md.css +18 -0
  123. package/dist/collection/components/datetime/datetime.js +51 -6
  124. package/dist/collection/components/datetime-button/datetime-button.js +8 -7
  125. package/dist/collection/components/fab-button/fab-button.js +114 -10
  126. package/dist/collection/components/input-otp/input-otp.js +3 -3
  127. package/dist/collection/components/item/item.ios.css +11 -0
  128. package/dist/collection/components/item/item.js +85 -4
  129. package/dist/collection/components/item/item.md.css +11 -0
  130. package/dist/collection/components/modal/gestures/sheet.js +7 -0
  131. package/dist/collection/components/modal/gestures/swipe-to-close.js +7 -0
  132. package/dist/collection/components/modal/modal.ios.css +8 -0
  133. package/dist/collection/components/modal/modal.js +18 -6
  134. package/dist/collection/components/modal/modal.md.css +8 -0
  135. package/dist/collection/components/radio/radio.ios.css +11 -15
  136. package/dist/collection/components/radio/radio.js +15 -7
  137. package/dist/collection/components/radio/radio.md.css +11 -11
  138. package/dist/collection/components/refresher/refresher.js +31 -4
  139. package/dist/collection/components/searchbar/searchbar.js +26 -5
  140. package/dist/collection/components/select/select.js +4 -3
  141. package/dist/collection/components/tab/tab.js +20 -13
  142. package/dist/collection/components/toggle/toggle.ios.css +12 -4
  143. package/dist/collection/components/toggle/toggle.js +24 -6
  144. package/dist/collection/components/toggle/toggle.md.css +42 -5
  145. package/dist/collection/utils/forms/index.js +1 -0
  146. package/dist/collection/utils/forms/item-multiple-inputs.js +33 -0
  147. package/dist/collection/utils/overlays.js +24 -1
  148. package/dist/collection/utils/test/playwright/apply-keyboard-focus.js +25 -0
  149. package/dist/collection/utils/test/playwright/drag-element.js +15 -0
  150. package/dist/collection/utils/test/playwright/index.js +1 -0
  151. package/dist/docs.json +40 -1
  152. package/dist/esm/{data-DZI70dKr.js → format-CAmvpAez.js} +1 -524
  153. package/dist/esm/index.js +1 -1
  154. package/dist/esm/ion-action-sheet.entry.js +1 -1
  155. package/dist/esm/ion-alert.entry.js +1 -1
  156. package/dist/esm/ion-button_2.entry.js +7 -3
  157. package/dist/esm/ion-checkbox.entry.js +19 -9
  158. package/dist/esm/ion-datetime-button.entry.js +9 -7
  159. package/dist/esm/ion-datetime_3.entry.js +550 -9
  160. package/dist/esm/ion-fab_3.entry.js +89 -11
  161. package/dist/esm/ion-input-otp.entry.js +3 -3
  162. package/dist/esm/ion-item_8.entry.js +84 -4
  163. package/dist/esm/ion-loading.entry.js +1 -1
  164. package/dist/esm/ion-menu_3.entry.js +1 -1
  165. package/dist/esm/ion-modal.entry.js +35 -9
  166. package/dist/esm/ion-popover.entry.js +1 -1
  167. package/dist/esm/ion-radio_2.entry.js +17 -8
  168. package/dist/esm/ion-refresher_2.entry.js +31 -4
  169. package/dist/esm/ion-searchbar.entry.js +26 -5
  170. package/dist/esm/ion-select-modal.entry.js +1 -1
  171. package/dist/esm/ion-select_3.entry.js +5 -4
  172. package/dist/esm/ion-tab_2.entry.js +20 -13
  173. package/dist/esm/ion-toast.entry.js +1 -1
  174. package/dist/esm/ion-toggle.entry.js +27 -8
  175. package/dist/esm/ionic.js +1 -1
  176. package/dist/esm/item-multiple-inputs-DlNuqNJm.js +36 -0
  177. package/dist/esm/loader.js +1 -1
  178. package/dist/esm/{overlays-TbiM4mdr.js → overlays-Dg0q2_tJ.js} +25 -2
  179. package/dist/html.html-data.json +4 -0
  180. package/dist/ionic/index.esm.js +1 -1
  181. package/dist/ionic/ionic.esm.js +1 -1
  182. package/dist/ionic/{p-30bf9737.entry.js → p-0dc33652.entry.js} +1 -1
  183. package/dist/ionic/p-10e833ef.entry.js +4 -0
  184. package/dist/ionic/p-21eb76d5.entry.js +4 -0
  185. package/dist/ionic/{p-4dc1c33c.entry.js → p-2a05aa64.entry.js} +1 -1
  186. package/dist/ionic/p-2b204c59.entry.js +4 -0
  187. package/dist/ionic/p-2fa58e80.entry.js +4 -0
  188. package/dist/ionic/{p-94f2a736.entry.js → p-34586ead.entry.js} +1 -1
  189. package/dist/ionic/p-4654ae1c.entry.js +4 -0
  190. package/dist/ionic/{p-8d6fd502.entry.js → p-4745f534.entry.js} +1 -1
  191. package/dist/ionic/p-4ac4f3d6.entry.js +4 -0
  192. package/dist/ionic/p-5018dfc8.entry.js +4 -0
  193. package/dist/ionic/{p-00dbb17e.entry.js → p-5eb8e86d.entry.js} +1 -1
  194. package/dist/ionic/{p-e847a888.entry.js → p-65ebdf1b.entry.js} +1 -1
  195. package/dist/ionic/p-7c319341.entry.js +4 -0
  196. package/dist/ionic/p-829367ed.entry.js +4 -0
  197. package/dist/ionic/{p-aa5d6b8f.entry.js → p-9f9db3a2.entry.js} +1 -1
  198. package/dist/ionic/p-C0eMdTCW.js +4 -0
  199. package/dist/ionic/p-D-3ycHmd.js +4 -0
  200. package/dist/ionic/p-DqN5EA2O.js +4 -0
  201. package/dist/ionic/p-aaeedb6b.entry.js +4 -0
  202. package/dist/ionic/p-bfc9bb0f.entry.js +4 -0
  203. package/dist/ionic/p-c38b648e.entry.js +4 -0
  204. package/dist/ionic/{p-3a541154.entry.js → p-ca2ab74d.entry.js} +1 -1
  205. package/dist/ionic/p-d889ff6d.entry.js +4 -0
  206. package/dist/types/components/checkbox/checkbox.d.ts +1 -0
  207. package/dist/types/components/datetime/datetime.d.ts +8 -0
  208. package/dist/types/components/fab-button/fab-button.d.ts +15 -0
  209. package/dist/types/components/item/item.d.ts +19 -0
  210. package/dist/types/components/radio/radio.d.ts +1 -0
  211. package/dist/types/components/searchbar/searchbar.d.ts +1 -0
  212. package/dist/types/components/tab/tab.d.ts +1 -1
  213. package/dist/types/components/toggle/toggle.d.ts +1 -0
  214. package/dist/types/components.d.ts +15 -2
  215. package/dist/types/utils/forms/index.d.ts +1 -0
  216. package/dist/types/utils/forms/item-multiple-inputs.d.ts +13 -0
  217. package/dist/types/utils/test/playwright/apply-keyboard-focus.d.ts +16 -0
  218. package/dist/types/utils/test/playwright/index.d.ts +1 -0
  219. package/hydrate/index.js +411 -87
  220. package/hydrate/index.mjs +411 -87
  221. package/package.json +5 -5
  222. package/components/p-5bE_Wz4Y.js +0 -4
  223. package/components/p-B60hm8Qv.js +0 -4
  224. package/components/p-BAYYcg3o.js +0 -4
  225. package/components/p-BjoEPDW5.js +0 -4
  226. package/components/p-C2lKgrXx.js +0 -4
  227. package/components/p-D7bSXQD3.js +0 -4
  228. package/components/p-DIE4pXMl.js +0 -4
  229. package/components/p-W5nO8mX_.js +0 -4
  230. package/components/p-W9CZ2mWX.js +0 -4
  231. package/components/p-XlSIJDJ-.js +0 -4
  232. package/components/p-ryqmO0B-.js +0 -4
  233. package/dist/ionic/p-0d005398.entry.js +0 -4
  234. package/dist/ionic/p-1ae21e4c.entry.js +0 -4
  235. package/dist/ionic/p-2f55dc1a.entry.js +0 -4
  236. package/dist/ionic/p-3a126bca.entry.js +0 -4
  237. package/dist/ionic/p-3a7d1e91.entry.js +0 -4
  238. package/dist/ionic/p-81ea5652.entry.js +0 -4
  239. package/dist/ionic/p-8c8eee7d.entry.js +0 -4
  240. package/dist/ionic/p-9a65ac7f.entry.js +0 -4
  241. package/dist/ionic/p-DAAFkPFT.js +0 -4
  242. package/dist/ionic/p-a21add2f.entry.js +0 -4
  243. package/dist/ionic/p-e686695b.entry.js +0 -4
  244. package/dist/ionic/p-ec090e0a.entry.js +0 -4
  245. package/dist/ionic/p-fabcce6c.entry.js +0 -4
  246. package/dist/ionic/p-facfde02.entry.js +0 -4
  247. package/dist/ionic/p-xOfi7-Zn.js +0 -4
@@ -1,11 +1,12 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, forceUpdate, h } from "@stencil/core";
4
+ import { Build, Host, forceUpdate, h } from "@stencil/core";
5
5
  import { inheritAttributes, raf } from "../../utils/helpers";
6
6
  import { createColorClasses, hostContext, openURL } from "../../utils/theme";
7
7
  import { chevronForward } from "ionicons/icons";
8
8
  import { getIonMode } from "../../global/ionic-global";
9
+ const INDICATOR_CONTROL_SELECTOR = 'ion-checkbox, ion-radio, ion-toggle';
9
10
  /**
10
11
  * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
11
12
  *
@@ -23,9 +24,11 @@ export class Item {
23
24
  this.labelColorStyles = {};
24
25
  this.itemStyles = new Map();
25
26
  this.inheritedAriaAttributes = {};
27
+ this.didLoad = false;
26
28
  this.multipleInputs = false;
27
29
  this.focusable = true;
28
30
  this.isInteractive = false;
31
+ this.hasSlottedIndicatorControl = false;
29
32
  /**
30
33
  * If `true`, a button tag will be rendered and the item will be tappable.
31
34
  */
@@ -51,6 +54,7 @@ export class Item {
51
54
  this.updateInteractivityOnSlotChange = () => {
52
55
  this.setIsInteractive();
53
56
  this.setMultipleInputs();
57
+ this.setHasSlottedIndicatorControl();
54
58
  };
55
59
  }
56
60
  buttonChanged() {
@@ -92,6 +96,14 @@ export class Item {
92
96
  }
93
97
  connectedCallback() {
94
98
  this.hasStartEl();
99
+ /**
100
+ * `componentDidLoad` doesn't run again when the item is moved, so re-arm the
101
+ * observer and re-read the light DOM, which may have changed while detached.
102
+ */
103
+ if (this.didLoad) {
104
+ this.watchForIndicatorControls();
105
+ this.updateInteractivityOnSlotChange();
106
+ }
95
107
  }
96
108
  componentWillLoad() {
97
109
  this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
@@ -100,8 +112,17 @@ export class Item {
100
112
  raf(() => {
101
113
  this.setMultipleInputs();
102
114
  this.setIsInteractive();
115
+ this.setHasSlottedIndicatorControl();
103
116
  this.focusable = this.isFocusable();
104
117
  });
118
+ this.watchForIndicatorControls();
119
+ this.didLoad = true;
120
+ }
121
+ disconnectedCallback() {
122
+ if (this.indicatorControlObserver) {
123
+ this.indicatorControlObserver.disconnect();
124
+ this.indicatorControlObserver = undefined;
125
+ }
105
126
  }
106
127
  totalNestedInputs() {
107
128
  // The following elements have a clickable cover that is relative to the entire item
@@ -135,6 +156,50 @@ export class Item {
135
156
  const { covers, inputs, clickables } = this.totalNestedInputs();
136
157
  this.isInteractive = covers.length > 0 || inputs.length > 0 || clickables.length > 0;
137
158
  }
159
+ /**
160
+ * `slotchange` only fires for nodes assigned directly to a slot, so a control
161
+ * inside a slotted wrapper (`<div><ion-toggle>`) never reaches
162
+ * `updateInteractivityOnSlotChange`. The light DOM is observed instead.
163
+ *
164
+ * The callback runs the whole handler because a control below a wrapper can also
165
+ * make the item multi-input, which is what decides whether the controls draw
166
+ * their own indicator at all.
167
+ *
168
+ * `:host(:has())` would avoid the observer, but the `:has()` fallback in
169
+ * `core.scss` is still open as FW-6106 and it's unreliable for slotted content
170
+ * in Android WebView. Worth revisiting when FW-6106 closes.
171
+ */
172
+ watchForIndicatorControls() {
173
+ var _a;
174
+ if (!Build.isBrowser || typeof MutationObserver === 'undefined') {
175
+ return;
176
+ }
177
+ // `Node.moveBefore` relocates the item without either callback firing, so
178
+ // never leave a previous observer behind
179
+ (_a = this.indicatorControlObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
180
+ this.indicatorControlObserver = new MutationObserver((records) => {
181
+ // The subtree observer also fires for text and hidden input churn, so only
182
+ // re-read the DOM when a control was added or removed
183
+ if (records.some(touchesIndicatorControl)) {
184
+ this.updateInteractivityOnSlotChange();
185
+ }
186
+ });
187
+ this.indicatorControlObserver.observe(this.el, { childList: true, subtree: true });
188
+ }
189
+ // These controls paint a focus indicator that overhangs their own bounds, and
190
+ // only the default slot is clipped, so only a control there needs extra room.
191
+ setHasSlottedIndicatorControl() {
192
+ const controls = this.el.querySelectorAll(INDICATOR_CONTROL_SELECTOR);
193
+ this.hasSlottedIndicatorControl = Array.from(controls).some((control) => {
194
+ // The control isn't always a direct child, so walk up to the element the item
195
+ // slots, which is the one carrying the slot name.
196
+ let slotted = control;
197
+ while (slotted !== null && slotted.parentElement !== this.el) {
198
+ slotted = slotted.parentElement;
199
+ }
200
+ return slotted !== null && !slotted.getAttribute('slot');
201
+ });
202
+ }
138
203
  // If the item contains an input including a checkbox, datetime, select, or radio
139
204
  // then the item will have a clickable input cover that covers the item
140
205
  // that should get the hover, focused and activated states UNLESS it has multiple
@@ -236,7 +301,13 @@ export class Item {
236
301
  * However, other form controls such as checkboxes and radios do.
237
302
  */
238
303
  const firstInteractiveNeedsPointerCursor = firstInteractive !== undefined && !['ION-INPUT', 'ION-TEXTAREA'].includes(firstInteractive.tagName);
239
- return (h(Host, { key: '3977491399280a2aa3f796f0549235f3dc8b5681', "aria-disabled": ariaDisabled, class: Object.assign(Object.assign(Object.assign({}, childStyles), labelColorStyles), createColorClasses(this.color, {
304
+ /**
305
+ * A control in a single-input item defers its indicator to the item, so there's
306
+ * nothing to clip and nothing to make room for. It draws its own indicator in a
307
+ * multi-input item, and in a clickable item, which is a second tab stop.
308
+ */
309
+ const slottedIndicatorNeedsRoom = this.hasSlottedIndicatorControl && (multipleInputs || this.isClickable());
310
+ return (h(Host, { key: '0dc161ad7ac2fdedc5005015fceb34ed176010a6', "aria-disabled": ariaDisabled, class: Object.assign(Object.assign(Object.assign({}, childStyles), labelColorStyles), createColorClasses(this.color, {
240
311
  item: true,
241
312
  [mode]: true,
242
313
  'item-lines-default': lines === undefined,
@@ -245,10 +316,11 @@ export class Item {
245
316
  'item-disabled': disabled,
246
317
  'in-list': inList,
247
318
  'item-multiple-inputs': this.multipleInputs,
319
+ 'item-focus-indicator-room': slottedIndicatorNeedsRoom,
248
320
  'ion-activatable': canActivate,
249
321
  'ion-focusable': this.focusable,
250
322
  'item-rtl': document.dir === 'rtl',
251
- })), role: inList ? 'listitem' : null }, h(TagType, Object.assign({ key: '8aff72457004553e83d74a4dda2f9a6b8cf523d7' }, attrs, inheritedAriaAttributes, { class: "item-native", part: "native", disabled: disabled }, clickFn), h("slot", { key: 'aba9a0f2ad101be6598f364c83236ca3de437a04', name: "start", onSlotchange: this.updateInteractivityOnSlotChange }), h("div", { key: '5e7851e49959e4ed114fcd51cb167db9bd8c0f00', class: "item-inner", part: "inner" }, h("div", { key: 'eee6511845d8459450ca856819839d83cd507c0c', class: "input-wrapper", part: "container" }, h("slot", { key: '76e13f86cd144951b1999eb333a2027b42cdf6e2', onSlotchange: this.updateInteractivityOnSlotChange })), h("slot", { key: 'b264f7b4c367e7700a9a05343177abf7b17045d8', name: "end", onSlotchange: this.updateInteractivityOnSlotChange }), showDetail && (h("ion-icon", { key: '66b930bdfebb55ea0b2e784a1c902eb6c64b2370', icon: detailIcon, lazy: false, class: "item-detail-icon", part: "detail-icon", "aria-hidden": "true", "flip-rtl": detailIcon === chevronForward }))), canActivate && mode === 'md' && h("ion-ripple-effect", { key: '06ab8e87917b545f3c221602b9b6d8004a1895ad' }))));
323
+ })), role: inList ? 'listitem' : null }, h(TagType, Object.assign({ key: 'd42583cf62133b2553de2e7a5357168640bb1834' }, attrs, inheritedAriaAttributes, { class: "item-native", part: "native", disabled: disabled }, clickFn), h("slot", { key: 'bd805ce49946cd52280d1c4b370104ee2472f1e2', name: "start", onSlotchange: this.updateInteractivityOnSlotChange }), h("div", { key: '0de589f9c5c13c31b6c8838566a7ad6f78f81028', class: "item-inner", part: "inner" }, h("div", { key: 'faaf6a39e072fb352753c645736ab1f47c2d7d25', class: "input-wrapper", part: "container" }, h("slot", { key: '70a34c31de29a0786a14b4a7d3156ba8d4d92eb4', onSlotchange: this.updateInteractivityOnSlotChange })), h("slot", { key: 'c8e764b62c5c30f67750896741ca266b741d5999', name: "end", onSlotchange: this.updateInteractivityOnSlotChange }), showDetail && (h("ion-icon", { key: 'af006856d8c4ed69174534261344215957cc751a', icon: detailIcon, lazy: false, class: "item-detail-icon", part: "detail-icon", "aria-hidden": "true", "flip-rtl": detailIcon === chevronForward }))), canActivate && mode === 'md' && h("ion-ripple-effect", { key: 'e5e151bafedd38feb8f71a387a0059aa1e9e90de' }))));
252
324
  }
253
325
  static get is() { return "ion-item"; }
254
326
  static get encapsulation() { return "shadow"; }
@@ -543,7 +615,8 @@ export class Item {
543
615
  return {
544
616
  "multipleInputs": {},
545
617
  "focusable": {},
546
- "isInteractive": {}
618
+ "isInteractive": {},
619
+ "hasSlottedIndicatorControl": {}
547
620
  };
548
621
  }
549
622
  static get elementRef() { return "el"; }
@@ -569,3 +642,11 @@ export class Item {
569
642
  }];
570
643
  }
571
644
  }
645
+ const isIndicatorControl = (node) => {
646
+ if (node.nodeType !== Node.ELEMENT_NODE) {
647
+ return false;
648
+ }
649
+ const el = node;
650
+ return el.matches(INDICATOR_CONTROL_SELECTOR) || el.querySelector(INDICATOR_CONTROL_SELECTOR) !== null;
651
+ };
652
+ const touchesIndicatorControl = (record) => Array.from(record.addedNodes).some(isIndicatorControl) || Array.from(record.removedNodes).some(isIndicatorControl);
@@ -387,6 +387,17 @@ a {
387
387
  flex-direction: column;
388
388
  }
389
389
 
390
+ :host(.item-focus-indicator-room) .input-wrapper {
391
+ -webkit-margin-start: -12px;
392
+ margin-inline-start: -12px;
393
+ -webkit-margin-end: -12px;
394
+ margin-inline-end: -12px;
395
+ -webkit-padding-start: 12px;
396
+ padding-inline-start: 12px;
397
+ -webkit-padding-end: 12px;
398
+ padding-inline-end: 12px;
399
+ }
400
+
390
401
  :host(.item-multiple-inputs) ::slotted(ion-checkbox),
391
402
  :host(.item-multiple-inputs) ::slotted(ion-datetime),
392
403
  :host(.item-multiple-inputs) ::slotted(ion-radio) {
@@ -224,6 +224,13 @@ export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpo
224
224
  return true;
225
225
  };
226
226
  const onStart = (detail) => {
227
+ var _a;
228
+ /**
229
+ * Firefox automatically selects the header text during drag
230
+ * due to the focusable wrapper (tabindex="-1"). Remove any
231
+ * selection that may have occurred.
232
+ */
233
+ (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
227
234
  /**
228
235
  * If canDismiss is anything other than `true`
229
236
  * then users should be able to swipe down
@@ -96,7 +96,14 @@ export const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismi
96
96
  return false;
97
97
  };
98
98
  const onStart = (detail) => {
99
+ var _a;
99
100
  const { deltaY } = detail;
101
+ /**
102
+ * Firefox automatically selects the header text during drag
103
+ * due to the focusable wrapper (tabindex="-1"). Remove any
104
+ * selection that may have occurred.
105
+ */
106
+ (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
100
107
  /**
101
108
  * Get the initial scrollY value so
102
109
  * that we can correctly reset the scrollY
@@ -126,6 +126,14 @@ ion-backdrop {
126
126
  z-index: 10;
127
127
  }
128
128
 
129
+ /**
130
+ * The wrapper receives programmatic focus for screen readers but should not
131
+ * show a visible focus ring, which is meant only for keyboard navigation.
132
+ */
133
+ .modal-wrapper {
134
+ outline: none;
135
+ }
136
+
129
137
  .modal-shadow {
130
138
  position: absolute;
131
139
  background: transparent;
@@ -191,9 +191,15 @@ export class Modal {
191
191
  * if it exists and is focusable, otherwise let the focus trap handle it.
192
192
  */
193
193
  this.onModalFocus = (ev) => {
194
+ var _a;
194
195
  const { dragHandleEl, el } = this;
195
- // Only handle focus if the modal itself was focused (not a child element)
196
- if (ev.target === el && dragHandleEl && dragHandleEl.tabIndex !== -1) {
196
+ /**
197
+ * Shadow DOM focus is retargeted to the host, so `ev.target === el` is also
198
+ * true when a shadow child (the dialog wrapper present() focuses) is focused.
199
+ * shadowRoot's activeElement is null only when the host was focused directly,
200
+ * so redirect to the handle only then and leave present()'s wrapper focus intact.
201
+ */
202
+ if (ev.target === el && ((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) == null && dragHandleEl && dragHandleEl.tabIndex !== -1) {
197
203
  dragHandleEl.focus();
198
204
  }
199
205
  };
@@ -1162,20 +1168,26 @@ export class Modal {
1162
1168
  const isCardModal = presentingElement !== undefined && mode === 'ios';
1163
1169
  const isHandleCycle = handleBehavior === 'cycle';
1164
1170
  const isSheetModalWithHandle = isSheetModal && showHandle;
1165
- return (h(Host, Object.assign({ key: 'ab93657f4a5b4c97168d292b398d975e4b5b4af4', "no-router": true,
1171
+ return (h(Host, Object.assign({ key: 'bad33199a939bd1e289b3b3f936c676ba403224f', "no-router": true,
1166
1172
  // Allow the modal to be navigable when the handle is focusable
1167
1173
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
1168
1174
  zIndex: `${20000 + this.overlayIndex}`,
1169
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '5ba6315c7c8c64fa04c62f85fd493da5275b2cbd', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'a76c48d41c67f86c18a7481e980e6925e614f85d', class: "modal-shadow" }), h("div", Object.assign({ key: '3f6c61ba60b063592784d76a9594073b3bf210a9',
1175
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: 'd098688a65fb2d1befb255f0e59fd4c00b401092', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '96cd2e59a7d1c0de7c9afb8f8d366367b09fbc93', class: "modal-shadow" }), h("div", Object.assign({ key: '882ccc39ce344a2c51d6508c11c3f40b26eaf2a7',
1170
1176
  /*
1171
1177
  role and aria-modal must be used on the
1172
1178
  same element. They must also be set inside the
1173
1179
  shadow DOM otherwise ion-button will not be highlighted
1174
1180
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
1181
+
1182
+ tabIndex={-1} is required so present() can move focus to this
1183
+ element (which carries the dialog role) instead of the role-less
1184
+ host. role="dialog" alone does not make an element focusable, so
1185
+ without the tabindex focus() would be a no-op and screen readers
1186
+ may not properly announce the dialog and its content when it opens.
1175
1187
  */
1176
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '7f66b4fdb3dec61a2b9aec4e9dbacf5d013d637c', class: "modal-handle",
1188
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", tabIndex: -1, class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '714d9904fcc8105b21e2bb66cdc032bffd685d72', class: "modal-handle",
1177
1189
  // Prevents the handle from receiving keyboard focus when it does not cycle
1178
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '2dfaaebe3d0b2a46be1224c25f9ab89bf1744332', onSlotchange: this.onSlotChange }))));
1190
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '79dfbeb3fe5eee7dc3a243ad880d3f63184f3ab7', onSlotchange: this.onSlotChange }))));
1179
1191
  }
1180
1192
  static get is() { return "ion-modal"; }
1181
1193
  static get encapsulation() { return "shadow"; }
@@ -126,6 +126,14 @@ ion-backdrop {
126
126
  z-index: 10;
127
127
  }
128
128
 
129
+ /**
130
+ * The wrapper receives programmatic focus for screen readers but should not
131
+ * show a visible focus ring, which is meant only for keyboard navigation.
132
+ */
133
+ .modal-wrapper {
134
+ outline: none;
135
+ }
136
+
129
137
  .modal-shadow {
130
138
  position: absolute;
131
139
  background: transparent;
@@ -130,6 +130,17 @@ input {
130
130
  width: auto;
131
131
  }
132
132
 
133
+ :host(.ion-focused) .radio-icon::after {
134
+ border-radius: var(--inner-border-radius);
135
+ display: block;
136
+ position: absolute;
137
+ width: 36px;
138
+ height: 36px;
139
+ background: var(--ion-color-primary-tint, #1a65eb);
140
+ content: "";
141
+ opacity: 0.2;
142
+ }
143
+
133
144
  .radio-wrapper {
134
145
  display: flex;
135
146
  position: relative;
@@ -456,21 +467,6 @@ input {
456
467
  opacity: 0.3;
457
468
  }
458
469
 
459
- :host(.ion-focused) .radio-icon::after {
460
- border-radius: var(--inner-border-radius);
461
- top: -8px;
462
- display: block;
463
- position: absolute;
464
- width: 36px;
465
- height: 36px;
466
- background: var(--ion-color-primary-tint, #1a65eb);
467
- content: "";
468
- opacity: 0.2;
469
- }
470
- :host(.ion-focused) .radio-icon::after {
471
- inset-inline-start: -9px;
472
- }
473
-
474
470
  .native-wrapper .radio-icon {
475
471
  width: 0.9375rem;
476
472
  height: 1.5rem;
@@ -1,8 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, h } from "@stencil/core";
5
- import { isOptionSelected } from "../../utils/forms/index";
4
+ import { Host, forceUpdate, h } from "@stencil/core";
5
+ import { createItemMultipleInputsObserver, isOptionSelected } from "../../utils/forms/index";
6
6
  import { addEventListener, removeEventListener } from "../../utils/helpers";
7
7
  import { createColorClasses, hostContext } from "../../utils/theme";
8
8
  import { getIonMode } from "../../global/ionic-global";
@@ -118,6 +118,7 @@ export class Radio {
118
118
  this.updateState();
119
119
  addEventListener(radioGroup, 'ionValueChange', this.updateState);
120
120
  }
121
+ this.itemFocusObserver = createItemMultipleInputsObserver(this.el, () => forceUpdate(this));
121
122
  }
122
123
  disconnectedCallback() {
123
124
  const radioGroup = this.radioGroup;
@@ -125,6 +126,10 @@ export class Radio {
125
126
  removeEventListener(radioGroup, 'ionValueChange', this.updateState);
126
127
  this.radioGroup = null;
127
128
  }
129
+ if (this.itemFocusObserver) {
130
+ this.itemFocusObserver.disconnect();
131
+ this.itemFocusObserver = undefined;
132
+ }
128
133
  }
129
134
  get hasLabel() {
130
135
  return this.el.textContent !== '';
@@ -136,7 +141,8 @@ export class Radio {
136
141
  const { checked, disabled, color, el, justify, labelPlacement, hasLabel, buttonTabindex, alignment } = this;
137
142
  const mode = getIonMode(this);
138
143
  const inItem = hostContext('ion-item', el);
139
- return (h(Host, { key: 'af5dc59ed528150872e907ed1036e3e2decba939', onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
144
+ const inMultipleInputsItem = hostContext('ion-item.item-multiple-inputs', el);
145
+ return (h(Host, { key: '8622be22eb4f0bab791b479c813c490e21fd9ad8', onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
140
146
  [mode]: true,
141
147
  'in-item': inItem,
142
148
  'radio-checked': checked,
@@ -144,13 +150,15 @@ export class Radio {
144
150
  [`radio-justify-${justify}`]: justify !== undefined,
145
151
  [`radio-alignment-${alignment}`]: alignment !== undefined,
146
152
  [`radio-label-placement-${labelPlacement}`]: true,
147
- // Focus and active styling should not apply when the radio is in an item
153
+ // A single-input item has the input cover and draws the indicator itself.
154
+ // A multi-input item has no cover, so each control draws its own. Active
155
+ // styling stays suppressed either way.
148
156
  'ion-activatable': !inItem,
149
- 'ion-focusable': !inItem,
150
- }), role: "radio", "aria-checked": checked ? 'true' : 'false', "aria-disabled": disabled ? 'true' : null, tabindex: buttonTabindex }, h("label", { key: '1312aba3259a87fe23a6911cdfa477e309469c8b', class: "radio-wrapper" }, h("div", { key: 'b342aa27e240a300836d2a135658d346b95daf09', class: {
157
+ 'ion-focusable': !inItem || inMultipleInputsItem,
158
+ }), role: "radio", "aria-checked": checked ? 'true' : 'false', "aria-disabled": disabled ? 'true' : null, tabindex: buttonTabindex }, h("label", { key: 'fb358e14faf37142ccad169adb729f3129371406', class: "radio-wrapper" }, h("div", { key: '844ab615aadf6eef46368ad060afaca9c4222cc1', class: {
151
159
  'label-text-wrapper': true,
152
160
  'label-text-wrapper-hidden': !hasLabel,
153
- }, part: "label" }, h("slot", { key: '0a4613d29aa783d1882cf889377f7e4fd4fea51d' })), h("div", { key: '191faea79dc4cd8befc4b873f5bd9f5af8ca2acc', class: "native-wrapper" }, this.renderRadioControl()))));
161
+ }, part: "label" }, h("slot", { key: 'a57e53d65afda0f2082071c911343a0eb02a00a6' })), h("div", { key: '206ff11e9c2c1946a389f96a3e642335e9c3add6', class: "native-wrapper" }, this.renderRadioControl()))));
154
162
  }
155
163
  static get is() { return "ion-radio"; }
156
164
  static get encapsulation() { return "shadow"; }
@@ -130,6 +130,17 @@ input {
130
130
  width: auto;
131
131
  }
132
132
 
133
+ :host(.ion-focused) .radio-icon::after {
134
+ border-radius: var(--inner-border-radius);
135
+ display: block;
136
+ position: absolute;
137
+ width: 36px;
138
+ height: 36px;
139
+ background: var(--ion-color-primary-tint, #1a65eb);
140
+ content: "";
141
+ opacity: 0.2;
142
+ }
143
+
133
144
  .radio-wrapper {
134
145
  display: flex;
135
146
  position: relative;
@@ -477,17 +488,6 @@ input {
477
488
  opacity: 0.63;
478
489
  }
479
490
 
480
- :host(.ion-focused) .radio-icon::after {
481
- border-radius: var(--inner-border-radius);
482
- display: block;
483
- position: absolute;
484
- width: 36px;
485
- height: 36px;
486
- background: var(--ion-color-primary-tint, #1a65eb);
487
- content: "";
488
- opacity: 0.2;
489
- }
490
-
491
491
  .native-wrapper .radio-icon {
492
492
  width: 1.25rem;
493
493
  height: 1.25rem;
@@ -203,7 +203,16 @@ export class Refresher {
203
203
  });
204
204
  };
205
205
  this.scrollEl.addEventListener('scroll', this.scrollListenerCallback);
206
- this.gesture = (await import('../../utils/gesture')).createGesture({
206
+ const { createGesture } = await import('../../utils/gesture');
207
+ /**
208
+ * Awaiting the dynamic import yields to the event loop, so the component can
209
+ * disconnect before it resolves. disconnectedCallback clears scrollEl, so a
210
+ * missing scrollEl here means there is nothing left to attach a gesture to.
211
+ */
212
+ if (!this.scrollEl) {
213
+ return;
214
+ }
215
+ this.gesture = createGesture({
207
216
  el: this.scrollEl,
208
217
  gestureName: 'refresher',
209
218
  gesturePriority: 31,
@@ -263,7 +272,16 @@ export class Refresher {
263
272
  refreshingCircle.style.setProperty('animation-delay', '-655ms');
264
273
  });
265
274
  }
266
- this.gesture = (await import('../../utils/gesture')).createGesture({
275
+ const { createGesture } = await import('../../utils/gesture');
276
+ /**
277
+ * Awaiting the dynamic import yields to the event loop, so the component can
278
+ * disconnect before it resolves. disconnectedCallback clears scrollEl, so a
279
+ * missing scrollEl here means there is nothing left to attach a gesture to.
280
+ */
281
+ if (!this.scrollEl) {
282
+ return;
283
+ }
284
+ this.gesture = createGesture({
267
285
  el: this.scrollEl,
268
286
  gestureName: 'refresher',
269
287
  gesturePriority: 31,
@@ -397,7 +415,16 @@ export class Refresher {
397
415
  this.setupNativeRefresher(contentEl);
398
416
  }
399
417
  else {
400
- this.gesture = (await import('../../utils/gesture')).createGesture({
418
+ const { createGesture } = await import('../../utils/gesture');
419
+ /**
420
+ * Awaiting the dynamic import yields to the event loop, so the component can
421
+ * disconnect before it resolves. A disconnected contentEl means there is
422
+ * nothing left to attach a gesture to.
423
+ */
424
+ if (!contentEl.isConnected) {
425
+ return;
426
+ }
427
+ this.gesture = createGesture({
401
428
  el: contentEl,
402
429
  gestureName: 'refresher',
403
430
  gesturePriority: 31,
@@ -716,7 +743,7 @@ export class Refresher {
716
743
  }
717
744
  render() {
718
745
  const mode = getIonMode(this);
719
- return (h(Host, { key: '80fa313b0d77161680836bdd6aa334d02c549b41', slot: "fixed", class: {
746
+ return (h(Host, { key: '2eaca7f85b3d40418f213077434e519dfacd9d42', slot: "fixed", class: {
720
747
  [mode]: true,
721
748
  // Used internally for styling
722
749
  [`refresher-${mode}`]: true,
@@ -243,12 +243,14 @@ export class Searchbar {
243
243
  }, 300);
244
244
  }
245
245
  disconnectedCallback() {
246
+ var _a;
246
247
  if (this.loadTimeout) {
247
248
  clearTimeout(this.loadTimeout);
248
249
  }
249
250
  if (this.clearTimeout) {
250
251
  clearTimeout(this.clearTimeout);
251
252
  }
253
+ (_a = this.searchIconResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
252
254
  }
253
255
  emitStyle() {
254
256
  this.ionStyle.emit({
@@ -346,6 +348,7 @@ export class Searchbar {
346
348
  doc.body.appendChild(tempSpan);
347
349
  // Get the width of the span then remove it
348
350
  raf(() => {
351
+ var _a, _b;
349
352
  const textWidth = tempSpan.offsetWidth;
350
353
  tempSpan.remove();
351
354
  // Calculate the input padding
@@ -357,7 +360,25 @@ export class Searchbar {
357
360
  * such as Dynamic Type on iOS as well as 8px
358
361
  * of padding.
359
362
  */
360
- const iconLeft = 'calc(50% - ' + (textWidth / 2 + iconEl.clientWidth + 8) + 'px)';
363
+ const iconWidth = iconEl.clientWidth;
364
+ /**
365
+ * Fix for https://github.com/ionic-team/ionic-framework/issues/30434:
366
+ * iconEl.clientWidth can very briefly be 0 when this is called from componentDidLoad.
367
+ * If it is zero, set up a ResizeObserver and call this function when it observes a width greater than 0.
368
+ */
369
+ if (iconWidth === 0) {
370
+ (_a = this.searchIconResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
371
+ this.searchIconResizeObserver = new ResizeObserver((entries) => {
372
+ var _a;
373
+ if (entries[0].contentRect.width > 0) {
374
+ (_a = this.searchIconResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
375
+ this.positionPlaceholder();
376
+ }
377
+ });
378
+ (_b = this.searchIconResizeObserver) === null || _b === void 0 ? void 0 : _b.observe(iconEl);
379
+ return;
380
+ }
381
+ const iconLeft = 'calc(50% - ' + (textWidth / 2 + iconWidth + 8) + 'px)';
361
382
  // Set the input padding start and icon margin start
362
383
  if (rtl) {
363
384
  inputEl.style.paddingRight = inputLeft;
@@ -438,8 +459,8 @@ export class Searchbar {
438
459
  const clearIcon = this.clearIcon || (mode === 'ios' ? closeCircle : closeSharp);
439
460
  const searchIcon = this.searchIcon || (mode === 'ios' ? searchOutline : searchSharp);
440
461
  const shouldShowCancelButton = this.shouldShowCancelButton();
441
- const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '9e9f3a7f6ee66140181f8c3cfa42bbeee75adafc', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: '5f83fbcc0708830637815e8c0b69356cad381870', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
442
- return (h(Host, { key: '219c263c1f3cc63e18bc2d2ba5d672b7806eddd0', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
462
+ const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '226b7a676b4819cda4062b8334fa8d4c4cee6f1a', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: '89adb4efa17a78c973bbd5527cfac91d99ceb9bb', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
463
+ return (h(Host, { key: '237dd767403dba3cb3d7de8905996e275669da3b', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
443
464
  [mode]: true,
444
465
  'searchbar-animated': animated,
445
466
  'searchbar-disabled': this.disabled,
@@ -449,14 +470,14 @@ export class Searchbar {
449
470
  'searchbar-has-focus': this.focused,
450
471
  'searchbar-should-show-clear': this.shouldShowClearButton(),
451
472
  'searchbar-should-show-cancel': this.shouldShowCancelButton(),
452
- }) }, h("div", { key: '3f1c25442d6d87c0df67f79482a925b80dcc403d', class: "searchbar-input-container" }, h("input", Object.assign({ key: 'a386fecafda0fab449ea1488906ba6cfc55e2b5c', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: '5f81642628335def39a14f0726e0fafdc7203776', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: 'da3073646f2d7f639261b556517fbadabc3c10e9', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
473
+ }) }, h("div", { key: '5b4bdf7280bea710304b6975e76390fe6ceb7417', class: "searchbar-input-container" }, h("input", Object.assign({ key: '01a0820a2cf904aec1c7d84b542ab7918d435632', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: 'cd64973206b1cc59d221112861733b7ee25f0323', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: '2061fa8449bbc576cf8c5130ea613378ea492944', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
453
474
  /**
454
475
  * This prevents mobile browsers from
455
476
  * blurring the input when the clear
456
477
  * button is activated.
457
478
  */
458
479
  ev.preventDefault();
459
- }, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: '065cd6c707ae462c0767df1bab04ce8045f340a4', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
480
+ }, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: '00df6f7742bff70b1b088d544b8c00e962e7216c', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
460
481
  }
461
482
  static get is() { return "ion-searchbar"; }
462
483
  static get encapsulation() { return "scoped"; }
@@ -836,8 +836,9 @@ export class Select {
836
836
  *
837
837
  * TODO(FW-5592): Remove hasStartEndSlots condition
838
838
  */
839
- const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
840
- return (h(Host, { key: '5f4ee901b65554eacc9fa52230d06b1a5ff1247c', onClick: this.onClick, class: createColorClasses(this.color, {
839
+ const labelShouldFloat = labelPlacement === 'stacked' ||
840
+ (labelPlacement === 'floating' && (hasValue || hasFocus || isExpanded || hasStartEndSlots));
841
+ return (h(Host, { key: 'f16327d6866ffee76e1618fcd3281590ca0fd56d', onClick: this.onClick, class: createColorClasses(this.color, {
841
842
  [mode]: true,
842
843
  'in-item': inItem,
843
844
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -855,7 +856,7 @@ export class Select {
855
856
  [`select-justify-${justify}`]: justifyEnabled,
856
857
  [`select-shape-${shape}`]: shape !== undefined,
857
858
  [`select-label-placement-${labelPlacement}`]: true,
858
- }) }, h("label", { key: 'd56d89b5b58da2b5eaebada9d1be58ca45c67806', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick, part: "wrapper" }, this.renderLabelContainer(), h("div", { key: 'bc8bf089c693781ad5b3a5db76a12223867d748e', class: "select-wrapper-inner", part: "inner" }, h("slot", { key: 'e1c12b96f1573eb192608c458ed1f3463174cd89', name: "start" }), h("div", { key: '6ee5e3900d8e462f175649b502ac6faa2f2b9909', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'faeaea34aad412e1458beab95d4b1b9ca7b87c41', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'aeff51e51c990844869eff9940575ce6b2223135', class: "select-highlight" })), this.renderBottomContent()));
859
+ }) }, h("label", { key: 'db13f76b384a7a7461c1ad752108eadf9c0213be', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick, part: "wrapper" }, this.renderLabelContainer(), h("div", { key: '2fb0e57bd596c0f525be79293b7da4aca55a9385', class: "select-wrapper-inner", part: "inner" }, h("slot", { key: 'cd694c36242789a89461ed3412af9891ad68411b', name: "start" }), h("div", { key: 'fdf62911be54f4ab29eb3c2eb153f082e76a8f49', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'a32f5c4b101d4d6ad45870f4c4446960009377f5', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: '61f792024a5f221ef4f28a3c249bd3a0de5edeab', class: "select-highlight" })), this.renderBottomContent()));
859
860
  }
860
861
  static get is() { return "ion-select"; }
861
862
  static get encapsulation() { return "shadow"; }