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
@@ -6,7 +6,6 @@ import { attachComponent } from "../../utils/framework-delegate";
6
6
  import { printIonError } from "../../utils/logging/index";
7
7
  export class Tab {
8
8
  constructor() {
9
- this.loaded = false;
10
9
  /** @internal */
11
10
  this.active = false;
12
11
  }
@@ -30,27 +29,35 @@ export class Tab {
30
29
  }
31
30
  changeActive(isActive) {
32
31
  if (isActive) {
33
- this.prepareLazyLoaded();
32
+ this.prepareLazyLoaded().catch((e) => {
33
+ printIonError('[ion-tab] - Exception in prepareLazyLoaded:', e);
34
+ });
34
35
  }
35
36
  }
36
37
  prepareLazyLoaded() {
37
- if (!this.loaded && this.component != null) {
38
- this.loaded = true;
39
- try {
40
- return attachComponent(this.delegate, this.el, this.component, ['ion-page']);
41
- }
42
- catch (e) {
43
- printIonError('[ion-tab] - Exception in prepareLazyLoaded:', e);
44
- }
38
+ if (this.component == null) {
39
+ return Promise.resolve(undefined);
40
+ }
41
+ /**
42
+ * The attach is cached so that concurrent activations share a single
43
+ * attempt and later activations reuse the result. The cache is cleared
44
+ * only when the attach fails, so a tab whose component failed to load
45
+ * can be retried the next time it is activated.
46
+ */
47
+ if (this.loadPromise === undefined) {
48
+ this.loadPromise = attachComponent(this.delegate, this.el, this.component, ['ion-page']).catch((e) => {
49
+ this.loadPromise = undefined;
50
+ throw e;
51
+ });
45
52
  }
46
- return Promise.resolve(undefined);
53
+ return this.loadPromise;
47
54
  }
48
55
  render() {
49
56
  const { tab, active, component } = this;
50
- return (h(Host, { key: 'fbd837bad7a0632336d46a597ace23673b153e48', role: "tabpanel", "aria-hidden": !active ? 'true' : null, "aria-labelledby": `tab-button-${tab}`, class: {
57
+ return (h(Host, { key: 'c35c9f96ed3a73270b23f30d9e279dcad9032e28', role: "tabpanel", "aria-hidden": !active ? 'true' : null, "aria-labelledby": `tab-button-${tab}`, class: {
51
58
  'ion-page': component === undefined,
52
59
  'tab-hidden': !active,
53
- } }, h("slot", { key: '35c218169fda826c9c1337558e0278d0c7f5f26a' })));
60
+ } }, h("slot", { key: '1c03523327e571f9ba140be0ccd6fe089e16a77a' })));
54
61
  }
55
62
  static get is() { return "ion-tab"; }
56
63
  static get encapsulation() { return "shadow"; }
@@ -98,10 +98,6 @@
98
98
  width: auto;
99
99
  }
100
100
 
101
- :host(.ion-focused) input {
102
- border: 2px solid #5e9ed6;
103
- }
104
-
105
101
  :host(.toggle-disabled) {
106
102
  pointer-events: none;
107
103
  }
@@ -114,6 +110,10 @@ input {
114
110
  display: none;
115
111
  }
116
112
 
113
+ :host(:focus) {
114
+ outline: none;
115
+ }
116
+
117
117
  .toggle-wrapper {
118
118
  display: flex;
119
119
  position: relative;
@@ -635,4 +635,12 @@ input {
635
635
 
636
636
  :host(.toggle-disabled) {
637
637
  opacity: 0.3;
638
+ }
639
+
640
+ :host(.ion-focused:not(.toggle-defers-indicator)) .toggle-icon {
641
+ box-shadow: 0 0 0 4px rgba(var(--ion-color-primary-rgb, 0, 84, 233), 0.2);
642
+ }
643
+
644
+ :host(.ion-color.ion-focused.toggle-checked:not(.toggle-defers-indicator)) .toggle-icon {
645
+ box-shadow: 0 0 0 4px rgba(var(--ion-color-base-rgb), 0.2);
638
646
  }
@@ -1,8 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Build, Host, h } from "@stencil/core";
5
- import { checkInvalidState } from "../../utils/forms/index";
4
+ import { Build, Host, forceUpdate, h } from "@stencil/core";
5
+ import { checkInvalidState, createItemMultipleInputsObserver } from "../../utils/forms/index";
6
6
  import { renderHiddenInput, inheritAriaAttributes } from "../../utils/helpers";
7
7
  import { hapticSelection } from "../../utils/native/haptic";
8
8
  import { isPlatform } from "../../utils/platform";
@@ -196,6 +196,10 @@ export class Toggle {
196
196
  }
197
197
  // Always set initial state
198
198
  this.isInvalid = checkInvalidState(el);
199
+ this.itemFocusObserver = createItemMultipleInputsObserver(el, () => forceUpdate(this), [
200
+ 'item-multiple-inputs',
201
+ 'ion-activatable',
202
+ ]);
199
203
  }
200
204
  componentDidLoad() {
201
205
  this.setupGesture();
@@ -206,6 +210,10 @@ export class Toggle {
206
210
  this.gesture.destroy();
207
211
  this.gesture = undefined;
208
212
  }
213
+ if (this.itemFocusObserver) {
214
+ this.itemFocusObserver.disconnect();
215
+ this.itemFocusObserver = undefined;
216
+ }
209
217
  // Clean up validation observer to prevent memory leaks.
210
218
  if (this.validationObserver) {
211
219
  this.validationObserver.disconnect();
@@ -286,10 +294,20 @@ export class Toggle {
286
294
  const mode = getIonMode(this);
287
295
  const value = this.getValue();
288
296
  const rtl = isRTL(el) ? 'rtl' : 'ltr';
297
+ const inItem = hostContext('ion-item', el);
298
+ const inMultipleInputsItem = hostContext('ion-item.item-multiple-inputs', el);
299
+ // A clickable item is a second tab stop painting the same row indicator, which
300
+ // `item-multiple-inputs` misses because it counts cover elements, not toggles.
301
+ // The attributes are matched too because the class needs the item to render.
302
+ const inClickableItem = hostContext('ion-item.ion-activatable, ion-item[button], ion-item[href]', el);
289
303
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
290
- return (h(Host, { key: '736e1dcfca34fd41f6f0632652e7f86d6996a232', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, class: createColorClasses(color, {
304
+ return (h(Host, { key: 'e2fb9e7fb497511a8f763895fab34f20d5f8cb9e', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, class: createColorClasses(color, {
291
305
  [mode]: true,
292
- 'in-item': hostContext('ion-item', el),
306
+ 'in-item': inItem,
307
+ // `ion-focusable` has to stay on because it is what makes the item focusable.
308
+ // When the item draws the row indicator, CSS suppresses ours instead.
309
+ 'ion-focusable': true,
310
+ 'toggle-defers-indicator': inItem && !inMultipleInputsItem && !inClickableItem,
293
311
  'toggle-activated': activated,
294
312
  'toggle-checked': checked,
295
313
  'toggle-disabled': disabled,
@@ -297,10 +315,10 @@ export class Toggle {
297
315
  [`toggle-alignment-${alignment}`]: alignment !== undefined,
298
316
  [`toggle-label-placement-${labelPlacement}`]: true,
299
317
  [`toggle-${rtl}`]: true,
300
- }) }, h("label", { key: 'e697739fd3964c1c6c23331cf7e38a9841c46a6a', class: "toggle-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '3d500b8c1bc879591249d183c1abb3f5f26aff6b', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), h("div", { key: 'e38f3fe0e66b4ca434fd81514f5ef771c2960841', class: {
318
+ }) }, h("label", { key: 'ab95b2af493f6352abefbc8661b112796ed12035', class: "toggle-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: 'f5a96b8f56787d67fa4ed2c29a089cd2fa02cb00', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), h("div", { key: '3bdabb3c25ec4417b0e968bae12f7365a77acb65', class: {
301
319
  'label-text-wrapper': true,
302
320
  'label-text-wrapper-hidden': !hasLabel,
303
- }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '006dd3558264d69ab020c07005b348adc4ecdd5b' }), this.renderHintText()), h("div", { key: 'dd4e52de31caabee41ec1bc58efecd1e213368c9', class: "native-wrapper" }, this.renderToggleControl()))));
321
+ }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '3de9890950b5066173d49a6d615e415d286e4aa5' }), this.renderHintText()), h("div", { key: 'de233f83efd106d043b31cceb5eb9a6e5758d032', class: "native-wrapper" }, this.renderToggleControl()))));
304
322
  }
305
323
  static get is() { return "ion-toggle"; }
306
324
  static get encapsulation() { return "shadow"; }
@@ -98,10 +98,6 @@
98
98
  width: auto;
99
99
  }
100
100
 
101
- :host(.ion-focused) input {
102
- border: 2px solid #5e9ed6;
103
- }
104
-
105
101
  :host(.toggle-disabled) {
106
102
  pointer-events: none;
107
103
  }
@@ -114,6 +110,10 @@ input {
114
110
  display: none;
115
111
  }
116
112
 
113
+ :host(:focus) {
114
+ outline: none;
115
+ }
116
+
117
117
  .toggle-wrapper {
118
118
  display: flex;
119
119
  position: relative;
@@ -528,7 +528,7 @@ input {
528
528
  --handle-width: 20px;
529
529
  --handle-height: 20px;
530
530
  --handle-max-height: calc(100% + 6px);
531
- --handle-spacing: 0;
531
+ --handle-spacing: 0px;
532
532
  --handle-transition: transform 160ms cubic-bezier(0.4, 0, 0.2, 1), background-color 160ms cubic-bezier(0.4, 0, 0.2, 1);
533
533
  }
534
534
 
@@ -572,6 +572,43 @@ input {
572
572
  height: 100%;
573
573
  }
574
574
 
575
+ :host(.ion-focused:not(.toggle-defers-indicator)) .toggle-icon-wrapper::after {
576
+ border-radius: 50%;
577
+ display: block;
578
+ position: absolute;
579
+ width: 36px;
580
+ height: 36px;
581
+ transform: translateY(-50%);
582
+ background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.12);
583
+ content: "";
584
+ z-index: -1;
585
+ inset-block-start: 50%;
586
+ }
587
+
588
+ :host(.toggle-ltr.ion-focused) .toggle-icon-wrapper::after {
589
+ left: calc(var(--handle-spacing) + (var(--handle-width) - 36px) / 2);
590
+ }
591
+
592
+ :host(.toggle-rtl.ion-focused) .toggle-icon-wrapper::after {
593
+ right: calc(var(--handle-spacing) + (var(--handle-width) - 36px) / 2);
594
+ }
595
+
596
+ :host(.toggle-ltr.ion-focused.toggle-checked) .toggle-icon-wrapper::after {
597
+ transform: translate(calc(var(--handle-spacing) * -2), -50%);
598
+ }
599
+
600
+ :host(.toggle-rtl.ion-focused.toggle-checked) .toggle-icon-wrapper::after {
601
+ transform: translate(calc(var(--handle-spacing) * 2), -50%);
602
+ }
603
+
604
+ :host(.ion-focused.toggle-checked) .toggle-icon-wrapper::after {
605
+ background: rgba(var(--ion-color-primary-rgb, 0, 84, 233), 0.2);
606
+ }
607
+
608
+ :host(.ion-color.ion-focused.toggle-checked) .toggle-icon-wrapper::after {
609
+ background: rgba(var(--ion-color-base-rgb), 0.2);
610
+ }
611
+
575
612
  :host(.toggle-disabled) {
576
613
  opacity: 0.38;
577
614
  }
@@ -3,4 +3,5 @@
3
3
  */
4
4
  export * from './notch-controller';
5
5
  export * from './compare-with-utils';
6
+ export * from './item-multiple-inputs';
6
7
  export * from './validity';
@@ -0,0 +1,33 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ import { Build } from "@stencil/core";
5
+ /**
6
+ * `item-multiple-inputs` is toggled on the item after its form controls render,
7
+ * so a control whose focus styling depends on it needs to re-render when it flips.
8
+ *
9
+ * @internal
10
+ * @param el The form control whose closest `ion-item` should be observed.
11
+ * @param onChange Called whenever one of `classNames` is added or removed.
12
+ * @param classNames The item classes the caller's rendering depends on. A caller
13
+ * reading more than one must list them all, or changes to the rest are ignored.
14
+ * @returns The observer to disconnect in `disconnectedCallback`, or `undefined`
15
+ * if it could not be created.
16
+ */
17
+ export const createItemMultipleInputsObserver = (el, onChange, classNames = ['item-multiple-inputs']) => {
18
+ const item = el.closest('ion-item');
19
+ if (!item || !Build.isBrowser || typeof MutationObserver === 'undefined') {
20
+ return undefined;
21
+ }
22
+ const readClasses = () => classNames.map((name) => item.classList.contains(name)).join(',');
23
+ let previousClasses = readClasses();
24
+ const observer = new MutationObserver(() => {
25
+ const currentClasses = readClasses();
26
+ if (currentClasses !== previousClasses) {
27
+ previousClasses = currentClasses;
28
+ onChange();
29
+ }
30
+ });
31
+ observer.observe(item, { attributes: true, attributeFilter: ['class'] });
32
+ return observer;
33
+ };
@@ -497,7 +497,30 @@ export const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation
497
497
  * to the overlay container.
498
498
  */
499
499
  if (overlay.keyboardClose && (document.activeElement === null || !overlay.el.contains(document.activeElement))) {
500
- overlay.el.focus();
500
+ /**
501
+ * Some overlays (e.g. modal) put the dialog role and accessible label
502
+ * on the `.ion-overlay-wrapper` instead of the host. Screen readers
503
+ * need focus on the element with `role="dialog"` to properly announce
504
+ * and navigate the dialog.
505
+ *
506
+ * We only target wrappers with `tabindex`, since `role="dialog"` alone
507
+ * does not make an element focusable. If no focusable dialog wrapper
508
+ * exists (e.g. picker-legacy), we fall back to the host.
509
+ */
510
+ const overlayWrapper = getElementRoot(overlay.el).querySelector('[role="dialog"][tabindex]');
511
+ const focusTarget = overlayWrapper !== null && overlayWrapper !== void 0 ? overlayWrapper : overlay.el;
512
+ /**
513
+ * `preventScroll` keeps this a pure focus move so the viewport does not
514
+ * jump when the wrapper is partially off-screen (e.g. a sheet modal).
515
+ * Guard the options call so an older engine that mishandles it can never
516
+ * reject present(); we fall back to a plain focus() in that case.
517
+ */
518
+ try {
519
+ focusTarget.focus({ preventScroll: true });
520
+ }
521
+ catch (_c) {
522
+ focusTarget.focus();
523
+ }
501
524
  }
502
525
  /**
503
526
  * If this overlay was previously dismissed without being
@@ -0,0 +1,25 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ import { expect } from "@playwright/test";
5
+ /**
6
+ * Puts an element into the keyboard focus state that draws the focus indicator.
7
+ * Requires an `ion-app` so `startFocusVisible` is running. Its listeners attach
8
+ * asynchronously, so focus is retried until `ion-focused` sticks.
9
+ *
10
+ * `Shift` re-enables keyboard mode without moving focus, in case a pointer event
11
+ * earlier in the test turned it off. Focus moves programmatically because Firefox
12
+ * cannot move it back into the page once blurred, so an early Tab is unretryable.
13
+ *
14
+ * Pass `indicatorLocator` when something other than the focused element gets
15
+ * `ion-focused`. A checkbox or radio alone in an item has no `ion-focusable` class of
16
+ * its own because the item draws the indicator for it, so wait on the item.
17
+ */
18
+ export const applyKeyboardFocus = async (page, locator, indicatorLocator = locator) => {
19
+ await expect(async () => {
20
+ await page.evaluate(() => { var _a; return (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur(); });
21
+ await page.keyboard.press('Shift');
22
+ await locator.focus();
23
+ await expect(indicatorLocator).toHaveClass(/ion-focused/, { timeout: 250 });
24
+ }).toPass({ timeout: 5000 });
25
+ };
@@ -9,6 +9,7 @@
9
9
  * See https://playwright.dev/docs/api/class-mouse#mouse-move for more information.
10
10
  */
11
11
  export const dragElementBy = async (el, page, dragByX = 0, dragByY = 0, startXCoord, startYCoord, releaseDrag = true) => {
12
+ var _a;
12
13
  const boundingBox = await el.boundingBox();
13
14
  if (!boundingBox) {
14
15
  throw new Error('Cannot get a bounding box for an element that is not visible. See https://playwright.dev/docs/api/class-locator#locator-bounding-box for more information');
@@ -18,6 +19,20 @@ export const dragElementBy = async (el, page, dragByX = 0, dragByY = 0, startXCo
18
19
  // Navigate to the start position.
19
20
  await page.mouse.move(startX, startY);
20
21
  await page.mouse.down();
22
+ // Firefox treats focusable elements (tabindex="-1") as draggable text and
23
+ // initiates text selection on mouse.down(). In Playwright, this selection
24
+ // locks in before the gesture detector fires, blocking pointer events.
25
+ // Clearing it here allows the drag gesture to proceed normally.
26
+ //
27
+ // This is scoped to Firefox because the extra page.evaluate() is an awaited
28
+ // round-trip that adds variable latency between mouse.down() and the first
29
+ // mouse.move(). Chromium and WebKit don't need the selection clear, and on
30
+ // WebKit that latency perturbs the timing-derived gesture velocity, causing
31
+ // the item to settle a few pixels off and flaking screenshot comparisons
32
+ // (e.g. item-sliding safe-area tests).
33
+ if (((_a = page.context().browser()) === null || _a === void 0 ? void 0 : _a.browserType().name()) === 'firefox') {
34
+ await page.evaluate(() => { var _a; return (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges(); });
35
+ }
21
36
  // Drag the element.
22
37
  await moveElement(page, startX, startY, dragByX, dragByY);
23
38
  if (releaseDrag) {
@@ -6,6 +6,7 @@ export * from './playwright-declarations';
6
6
  export * from './page/event-spy';
7
7
  export * from './page/utils';
8
8
  export * from './drag-element';
9
+ export * from './apply-keyboard-focus';
9
10
  export * from './matchers';
10
11
  export * from './viewports';
11
12
  export * from './generator';
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-06-12T22:12:03",
2
+ "timestamp": "2026-08-08T17:09:51",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.5",
@@ -10562,6 +10562,40 @@
10562
10562
  "getter": false,
10563
10563
  "setter": false
10564
10564
  },
10565
+ {
10566
+ "name": "form",
10567
+ "type": "HTMLFormElement | string | undefined",
10568
+ "complexType": {
10569
+ "original": "string | HTMLFormElement",
10570
+ "resolved": "HTMLFormElement | string | undefined",
10571
+ "references": {
10572
+ "HTMLFormElement": {
10573
+ "location": "global",
10574
+ "id": "global::HTMLFormElement"
10575
+ }
10576
+ }
10577
+ },
10578
+ "mutable": false,
10579
+ "attr": "form",
10580
+ "reflectToAttr": false,
10581
+ "docs": "The HTML form element or form element id. Used to submit a form when the button is not a child of the form.",
10582
+ "docsTags": [],
10583
+ "values": [
10584
+ {
10585
+ "type": "HTMLFormElement"
10586
+ },
10587
+ {
10588
+ "type": "string"
10589
+ },
10590
+ {
10591
+ "type": "undefined"
10592
+ }
10593
+ ],
10594
+ "optional": true,
10595
+ "required": false,
10596
+ "getter": false,
10597
+ "setter": false
10598
+ },
10565
10599
  {
10566
10600
  "name": "href",
10567
10601
  "type": "string | undefined",
@@ -38144,6 +38178,11 @@
38144
38178
  "docstring": "",
38145
38179
  "path": "src/interface.d.ts"
38146
38180
  },
38181
+ "src/components/datetime/datetime-interface.ts::DatetimeParts": {
38182
+ "declaration": "export interface DatetimeParts {\n month: number;\n day: number | null;\n year: number;\n dayOfWeek?: number | null;\n hour?: number;\n minute?: number;\n ampm?: 'am' | 'pm';\n isAdjacentDay?: boolean;\n}",
38183
+ "docstring": "",
38184
+ "path": "src/components/datetime/datetime-interface.ts"
38185
+ },
38147
38186
  "src/components/picker-column/picker-column-interfaces.ts::PickerColumnChangeEventDetail": {
38148
38187
  "declaration": "export interface PickerColumnChangeEventDetail {\n value: PickerColumnValue;\n}",
38149
38188
  "docstring": "",