@rdlabo/ionic-theme-ios27 1.2.1-beta.pr275.shad339e7688db7 → 2.0.0-1

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 (248) hide show
  1. package/README.md +24 -280
  2. package/dist/css/components/ion-toolbar.css +1 -1
  3. package/dist/css/ionic-theme-ios27.css +1 -1
  4. package/dist/css/vertical-bars.css +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/layout/index.d.ts +18 -0
  10. package/dist/layout/index.d.ts.map +1 -0
  11. package/dist/layout/index.js +26 -0
  12. package/dist/layout/index.js.map +1 -0
  13. package/dist/segment/index.js +3 -3
  14. package/dist/segment/index.js.map +1 -1
  15. package/dist/sheets-of-glass/index.js +7 -7
  16. package/dist/sheets-of-glass/index.js.map +1 -1
  17. package/dist/tab-bar-searchable/dismiss-button.js +53 -3
  18. package/dist/tab-bar-searchable/dismiss-button.js.map +1 -1
  19. package/dist/tab-bar-searchable/index.js +18 -3
  20. package/dist/tab-bar-searchable/index.js.map +1 -1
  21. package/dist/transition/ios.transition.d.ts.map +1 -1
  22. package/dist/transition/ios.transition.js +7 -4
  23. package/dist/transition/ios.transition.js.map +1 -1
  24. package/dist/transition/native-ui.d.ts +6 -0
  25. package/dist/transition/native-ui.d.ts.map +1 -0
  26. package/dist/{native-integration/index.js → transition/native-ui.js} +16 -30
  27. package/dist/transition/native-ui.js.map +1 -0
  28. package/docs/adaptive-themes.md +161 -0
  29. package/docs/add-native-ui.md +53 -0
  30. package/docs/animation.md +19 -3
  31. package/docs/eslint.md +3 -1
  32. package/docs/features.md +9 -5
  33. package/docs/iphone-duo.md +62 -35
  34. package/docs/migration.md +17 -26
  35. package/docs/setup.md +92 -0
  36. package/docs/special-markup.md +41 -10
  37. package/docs/using-ion-item-group.md +2 -2
  38. package/docs/vertical-bars-layout.md +67 -0
  39. package/package.json +12 -42
  40. package/src/index.ts +3 -1
  41. package/src/layout/index.ts +29 -0
  42. package/src/segment/index.ts +3 -3
  43. package/src/sheets-of-glass/index.ts +7 -7
  44. package/src/styles/components/ion-toolbar.scss +1 -1
  45. package/src/styles/vertical-bars.scss +15 -18
  46. package/src/tab-bar-searchable/dismiss-button.ts +73 -4
  47. package/src/tab-bar-searchable/index.ts +20 -3
  48. package/src/transition/ios.transition.ts +7 -4
  49. package/src/{native-integration/index.ts → transition/native-ui.ts} +17 -46
  50. package/Package.swift +0 -21
  51. package/dist/_virtual/_rolldown/runtime.js +0 -13
  52. package/dist/native/components/index.d.ts +0 -19
  53. package/dist/native/components/index.d.ts.map +0 -1
  54. package/dist/native/components/index.js +0 -40
  55. package/dist/native/components/index.js.map +0 -1
  56. package/dist/native/components/ion-back-button.d.ts +0 -9
  57. package/dist/native/components/ion-back-button.d.ts.map +0 -1
  58. package/dist/native/components/ion-back-button.js +0 -20
  59. package/dist/native/components/ion-back-button.js.map +0 -1
  60. package/dist/native/components/ion-button.d.ts +0 -10
  61. package/dist/native/components/ion-button.d.ts.map +0 -1
  62. package/dist/native/components/ion-button.js +0 -23
  63. package/dist/native/components/ion-button.js.map +0 -1
  64. package/dist/native/components/ion-buttons.d.ts +0 -11
  65. package/dist/native/components/ion-buttons.d.ts.map +0 -1
  66. package/dist/native/components/ion-buttons.js +0 -30
  67. package/dist/native/components/ion-buttons.js.map +0 -1
  68. package/dist/native/components/ion-fab.d.ts +0 -10
  69. package/dist/native/components/ion-fab.d.ts.map +0 -1
  70. package/dist/native/components/ion-fab.js +0 -132
  71. package/dist/native/components/ion-fab.js.map +0 -1
  72. package/dist/native/components/ion-menu-button.d.ts +0 -13
  73. package/dist/native/components/ion-menu-button.d.ts.map +0 -1
  74. package/dist/native/components/ion-menu-button.js +0 -31
  75. package/dist/native/components/ion-menu-button.js.map +0 -1
  76. package/dist/native/components/ion-segment.d.ts +0 -11
  77. package/dist/native/components/ion-segment.d.ts.map +0 -1
  78. package/dist/native/components/ion-segment.js +0 -30
  79. package/dist/native/components/ion-segment.js.map +0 -1
  80. package/dist/native/components/ion-tab-bar.d.ts +0 -11
  81. package/dist/native/components/ion-tab-bar.d.ts.map +0 -1
  82. package/dist/native/components/ion-tab-bar.js +0 -50
  83. package/dist/native/components/ion-tab-bar.js.map +0 -1
  84. package/dist/native/components/searchable-tabs.d.ts +0 -20
  85. package/dist/native/components/searchable-tabs.d.ts.map +0 -1
  86. package/dist/native/components/searchable-tabs.js +0 -383
  87. package/dist/native/components/searchable-tabs.js.map +0 -1
  88. package/dist/native/definitions.d.ts +0 -191
  89. package/dist/native/definitions.d.ts.map +0 -1
  90. package/dist/native/definitions.js +0 -0
  91. package/dist/native/foldable/fold-state.d.ts +0 -5
  92. package/dist/native/foldable/fold-state.d.ts.map +0 -1
  93. package/dist/native/foldable/fold-state.js +0 -14
  94. package/dist/native/foldable/fold-state.js.map +0 -1
  95. package/dist/native/foldable/types.d.ts +0 -12
  96. package/dist/native/foldable/types.d.ts.map +0 -1
  97. package/dist/native/foldable/types.js +0 -0
  98. package/dist/native/index.d.ts +0 -11
  99. package/dist/native/index.d.ts.map +0 -1
  100. package/dist/native/index.js +0 -267
  101. package/dist/native/index.js.map +0 -1
  102. package/dist/native/overlays/content.d.ts +0 -4
  103. package/dist/native/overlays/content.d.ts.map +0 -1
  104. package/dist/native/overlays/content.js +0 -51
  105. package/dist/native/overlays/content.js.map +0 -1
  106. package/dist/native/overlays/controller.d.ts +0 -8
  107. package/dist/native/overlays/controller.d.ts.map +0 -1
  108. package/dist/native/overlays/controller.js +0 -285
  109. package/dist/native/overlays/controller.js.map +0 -1
  110. package/dist/native/overlays/dialog-animation.d.ts +0 -9
  111. package/dist/native/overlays/dialog-animation.d.ts.map +0 -1
  112. package/dist/native/overlays/dialog-animation.js +0 -53
  113. package/dist/native/overlays/dialog-animation.js.map +0 -1
  114. package/dist/native/overlays/dismiss-animation.d.ts +0 -4
  115. package/dist/native/overlays/dismiss-animation.d.ts.map +0 -1
  116. package/dist/native/overlays/dismiss-animation.js +0 -18
  117. package/dist/native/overlays/dismiss-animation.js.map +0 -1
  118. package/dist/native/overlays/focus.d.ts +0 -5
  119. package/dist/native/overlays/focus.d.ts.map +0 -1
  120. package/dist/native/overlays/focus.js +0 -59
  121. package/dist/native/overlays/focus.js.map +0 -1
  122. package/dist/native/overlays/popover.d.ts +0 -12
  123. package/dist/native/overlays/popover.d.ts.map +0 -1
  124. package/dist/native/overlays/popover.js +0 -110
  125. package/dist/native/overlays/popover.js.map +0 -1
  126. package/dist/native/overlays/styles.d.ts +0 -11
  127. package/dist/native/overlays/styles.d.ts.map +0 -1
  128. package/dist/native/overlays/styles.js +0 -167
  129. package/dist/native/overlays/styles.js.map +0 -1
  130. package/dist/native/prehide.d.ts +0 -7
  131. package/dist/native/prehide.d.ts.map +0 -1
  132. package/dist/native/prehide.js +0 -230
  133. package/dist/native/prehide.js.map +0 -1
  134. package/dist/native/runtime.d.ts +0 -7
  135. package/dist/native/runtime.d.ts.map +0 -1
  136. package/dist/native/runtime.js +0 -710
  137. package/dist/native/runtime.js.map +0 -1
  138. package/dist/native/shared/bridge.d.ts +0 -6
  139. package/dist/native/shared/bridge.d.ts.map +0 -1
  140. package/dist/native/shared/bridge.js +0 -18
  141. package/dist/native/shared/bridge.js.map +0 -1
  142. package/dist/native/shared/candidate.d.ts +0 -21
  143. package/dist/native/shared/candidate.d.ts.map +0 -1
  144. package/dist/native/shared/candidate.js +0 -98
  145. package/dist/native/shared/candidate.js.map +0 -1
  146. package/dist/native/shared/crossfade.d.ts +0 -12
  147. package/dist/native/shared/crossfade.d.ts.map +0 -1
  148. package/dist/native/shared/crossfade.js +0 -67
  149. package/dist/native/shared/crossfade.js.map +0 -1
  150. package/dist/native/shared/dom.d.ts +0 -46
  151. package/dist/native/shared/dom.d.ts.map +0 -1
  152. package/dist/native/shared/dom.js +0 -172
  153. package/dist/native/shared/dom.js.map +0 -1
  154. package/dist/native/shared/events.d.ts +0 -11
  155. package/dist/native/shared/events.d.ts.map +0 -1
  156. package/dist/native/shared/events.js +0 -13
  157. package/dist/native/shared/events.js.map +0 -1
  158. package/dist/native/shared/icons.d.ts +0 -8
  159. package/dist/native/shared/icons.d.ts.map +0 -1
  160. package/dist/native/shared/icons.js +0 -61
  161. package/dist/native/shared/icons.js.map +0 -1
  162. package/dist/native/shared/modal.d.ts +0 -10
  163. package/dist/native/shared/modal.d.ts.map +0 -1
  164. package/dist/native/shared/modal.js +0 -160
  165. package/dist/native/shared/modal.js.map +0 -1
  166. package/dist/native/shared/projection-id.d.ts +0 -4
  167. package/dist/native/shared/projection-id.d.ts.map +0 -1
  168. package/dist/native/shared/projection-id.js +0 -7
  169. package/dist/native/shared/projection-id.js.map +0 -1
  170. package/dist/native/shared/vertical-bars-geometry.d.ts +0 -10
  171. package/dist/native/shared/vertical-bars-geometry.d.ts.map +0 -1
  172. package/dist/native/shared/vertical-bars-geometry.js +0 -85
  173. package/dist/native/shared/vertical-bars-geometry.js.map +0 -1
  174. package/dist/native/vertical-bars-web-search.d.ts +0 -10
  175. package/dist/native/vertical-bars-web-search.d.ts.map +0 -1
  176. package/dist/native/vertical-bars-web-search.js +0 -90
  177. package/dist/native/vertical-bars-web-search.js.map +0 -1
  178. package/dist/native/vertical-bars-web.d.ts +0 -5
  179. package/dist/native/vertical-bars-web.d.ts.map +0 -1
  180. package/dist/native/vertical-bars-web.js +0 -411
  181. package/dist/native/vertical-bars-web.js.map +0 -1
  182. package/dist/native-integration/index.d.ts +0 -23
  183. package/dist/native-integration/index.d.ts.map +0 -1
  184. package/dist/native-integration/index.js.map +0 -1
  185. package/dist/native-integration/transition.d.ts +0 -5
  186. package/dist/native-integration/transition.d.ts.map +0 -1
  187. package/dist/native-integration/transition.js +0 -16
  188. package/dist/native-integration/transition.js.map +0 -1
  189. package/dist/vertical-bars.d.ts +0 -5
  190. package/dist/vertical-bars.js +0 -4
  191. package/docs/e2e-testing.md +0 -51
  192. package/docs/feedback.md +0 -65
  193. package/docs/iphone-duo-with-original-theme.md +0 -158
  194. package/docs/native-ui-shell.md +0 -325
  195. package/docs/vertical-bars.md +0 -300
  196. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButton.swift +0 -71
  197. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButtons.swift +0 -24
  198. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellComponents.swift +0 -18
  199. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellFab.swift +0 -80
  200. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +0 -432
  201. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSegment.swift +0 -91
  202. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +0 -218
  203. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +0 -565
  204. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +0 -118
  205. package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +0 -502
  206. package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayController.swift +0 -119
  207. package/ios/Sources/IonicNativeUIShellPlugin/Overlays/ShellOverlayHost.swift +0 -75
  208. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellCrossfade.swift +0 -35
  209. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellHost.swift +0 -8
  210. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellRendering.swift +0 -27
  211. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +0 -135
  212. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellVerticalBarsGeometry.swift +0 -52
  213. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +0 -788
  214. package/src/native/components/index.ts +0 -53
  215. package/src/native/components/ion-back-button.ts +0 -19
  216. package/src/native/components/ion-button.ts +0 -24
  217. package/src/native/components/ion-buttons.ts +0 -38
  218. package/src/native/components/ion-fab.ts +0 -151
  219. package/src/native/components/ion-menu-button.ts +0 -24
  220. package/src/native/components/ion-segment.ts +0 -27
  221. package/src/native/components/ion-tab-bar.ts +0 -50
  222. package/src/native/components/searchable-tabs.ts +0 -459
  223. package/src/native/definitions.ts +0 -218
  224. package/src/native/foldable/fold-state.ts +0 -9
  225. package/src/native/foldable/types.ts +0 -4
  226. package/src/native/index.ts +0 -315
  227. package/src/native/overlays/content.ts +0 -51
  228. package/src/native/overlays/controller.ts +0 -349
  229. package/src/native/overlays/dialog-animation.ts +0 -53
  230. package/src/native/overlays/dismiss-animation.ts +0 -15
  231. package/src/native/overlays/focus.ts +0 -64
  232. package/src/native/overlays/popover.ts +0 -120
  233. package/src/native/overlays/styles.ts +0 -181
  234. package/src/native/prehide.ts +0 -265
  235. package/src/native/runtime.ts +0 -885
  236. package/src/native/shared/bridge.ts +0 -18
  237. package/src/native/shared/candidate.ts +0 -150
  238. package/src/native/shared/crossfade.ts +0 -67
  239. package/src/native/shared/dom.ts +0 -286
  240. package/src/native/shared/events.ts +0 -8
  241. package/src/native/shared/icons.ts +0 -57
  242. package/src/native/shared/modal.ts +0 -157
  243. package/src/native/shared/projection-id.ts +0 -2
  244. package/src/native/shared/vertical-bars-geometry.ts +0 -80
  245. package/src/native/vertical-bars-web-search.ts +0 -86
  246. package/src/native/vertical-bars-web.ts +0 -485
  247. package/src/native-integration/transition.ts +0 -15
  248. package/src/vertical-bars.ts +0 -11
@@ -1,53 +0,0 @@
1
- import type { VerticalControlAreaOptions } from '../definitions';
2
- import * as button from './ion-button';
3
- import * as buttons from './ion-buttons';
4
- import * as backButton from './ion-back-button';
5
- import * as menuButton from './ion-menu-button';
6
- import * as tabBar from './ion-tab-bar';
7
- import * as segment from './ion-segment';
8
- import * as fab from './ion-fab';
9
- import { isDisabledButtonGroupChild, isVerticalBarsSource, visible, withoutPrehide } from '../shared/dom';
10
- import type { Candidate, Identify } from '../shared/candidate';
11
-
12
- // Static composition only. Each component declares its own tag, discovery and reader.
13
- export const components = [button, buttons, backButton, menuButton, tabBar, segment, fab] as const;
14
- export type NativeUIShellComponent = (typeof components)[number]['tag'];
15
- export const selector = components
16
- .map((component) => ('selector' in component ? component.selector : component.tag))
17
- .filter(Boolean)
18
- .join(', ');
19
- export const shadowSelector = [
20
- 'ion-icon',
21
- ...components.flatMap((component) => ('shadowSelector' in component ? [component.shadowSelector] : [])),
22
- ].join(', ');
23
- export const motionSelector = [
24
- '.ion-page',
25
- 'ion-header',
26
- 'ion-footer',
27
- 'ion-toolbar',
28
- ...components.filter((component) => 'tracksMotion' in component && component.tracksMotion).map((component) => component.tag),
29
- ].join(', ');
30
-
31
- export const isVerticalBarsCandidate = isVerticalBarsSource;
32
-
33
- export const readCandidate = (element: HTMLElement, id: Identify, options: VerticalControlAreaOptions = {}): Candidate | undefined => {
34
- const verticalBars = isVerticalBarsCandidate(element);
35
- if (
36
- (!element.classList.contains('ios') && !verticalBars) ||
37
- !visible(element, verticalBars) ||
38
- element.closest('ion-popover') ||
39
- (element.closest('ion-modal') && !verticalBars)
40
- )
41
- return;
42
- const style = getComputedStyle(element);
43
- if (
44
- !isDisabledButtonGroupChild(element) &&
45
- !style.getPropertyValue('--ios-theme-glass-background-rgb').trim() &&
46
- !style.getPropertyValue('--ios26-glass-background-rgb').trim() &&
47
- !verticalBars
48
- )
49
- return;
50
- if (element.contains(element.ownerDocument.activeElement)) return;
51
- // Read icon and control geometry before restoring prehide, which can collapse the toolbar.
52
- return withoutPrehide(element, () => components.find((component) => component.tag === element.localName)?.read(element, id, options));
53
- };
@@ -1,19 +0,0 @@
1
- import { createCandidate, appendItem } from '../shared/candidate';
2
- import type { Candidate, Identify } from '../shared/candidate';
3
- import { inFixedToolbar, isVerticalBarsSource } from '../shared/dom';
4
-
5
- export const tag = 'ion-back-button';
6
-
7
- export const read = (element: HTMLElement, id: Identify): Candidate | undefined => {
8
- const button = element as HTMLIonBackButtonElement;
9
- if (
10
- !button.shadowRoot ||
11
- button.icon !== undefined ||
12
- button.color !== undefined ||
13
- (!isVerticalBarsSource(element) && !inFixedToolbar(element))
14
- )
15
- return;
16
- const candidate = createCandidate(element, tag, id);
17
- const label = button.shadowRoot.querySelector('[part="text"]')?.textContent?.trim() ?? '';
18
- return appendItem(candidate, element, id, button.shadowRoot, label) ? candidate : undefined;
19
- };
@@ -1,24 +0,0 @@
1
- import type { VerticalControlAreaOptions } from '../definitions';
2
- import { modalUsesVerticalBars } from '../shared/modal';
3
- import { createCandidate, appendItem } from '../shared/candidate';
4
- import type { Candidate, Identify } from '../shared/candidate';
5
- import { inFixedToolbar, isVerticalBarsToolbarAction, isVerticalBarsToolbarGroup } from '../shared/dom';
6
-
7
- export const tag = 'ion-button';
8
-
9
- export const read = (element: HTMLElement, id: Identify, options: VerticalControlAreaOptions = {}): Candidate | undefined => {
10
- const button = element as HTMLIonButtonElement;
11
- const verticalBars =
12
- !element.closest('ion-menu, ion-popover') && modalUsesVerticalBars(element) && !!element.closest('ion-app.ios-theme-vertical-bars');
13
- if (verticalBars && element.parentElement && isVerticalBarsToolbarGroup(element.parentElement)) return;
14
- const fill = button.fill ?? 'default';
15
- if (
16
- !inFixedToolbar(element) ||
17
- (verticalBars && !isVerticalBarsToolbarAction(element)) ||
18
- (!verticalBars && fill !== 'default') ||
19
- (!verticalBars && button.classList.contains('ion-color'))
20
- )
21
- return;
22
- const candidate = createCandidate(element, tag, id, options);
23
- return appendItem(candidate, element, id) ? candidate : undefined;
24
- };
@@ -1,38 +0,0 @@
1
- import type { VerticalControlAreaOptions } from '../definitions';
2
- import { modalUsesVerticalBars } from '../shared/modal';
3
- import { createCandidate, appendItem } from '../shared/candidate';
4
- import type { Candidate, Identify } from '../shared/candidate';
5
- import { childElements, verticalBarsToolbarActions, inFixedToolbar, isVerticalBarsToolbarGroup } from '../shared/dom';
6
- import * as menuButton from './ion-menu-button';
7
-
8
- export const tag = 'ion-buttons';
9
- export const tracksMotion = true;
10
-
11
- export const read = (element: HTMLElement, id: Identify, options: VerticalControlAreaOptions = {}): Candidate | undefined => {
12
- if (!inFixedToolbar(element)) return;
13
- let children = childElements(element);
14
- if (children.length === 1) return menuButton.read(element, id, options);
15
- const verticalBars =
16
- !element.closest('ion-menu, ion-popover') && modalUsesVerticalBars(element) && !!element.closest('ion-app.ios-theme-vertical-bars');
17
- if (verticalBars && !isVerticalBarsToolbarGroup(element)) return;
18
- if (verticalBars) children = verticalBarsToolbarActions(element);
19
- if (
20
- !children.length ||
21
- children.some(
22
- (child) =>
23
- !child.matches(`${menuButton.tag}${verticalBars ? '' : '.ios'}`) &&
24
- (!child.matches(`ion-button${verticalBars ? '' : '.ios.button-clear'}`) ||
25
- (!verticalBars && (child as HTMLIonButtonElement).fill !== 'clear')),
26
- )
27
- )
28
- return;
29
- const candidate = createCandidate(element, tag, id, options);
30
- if (verticalBars && children.length !== childElements(element).length) candidate.sources = children;
31
- for (const child of children) {
32
- const supported = child.matches(menuButton.tag)
33
- ? menuButton.append(candidate, child as HTMLIonMenuButtonElement, id)
34
- : appendItem(candidate, child, id);
35
- if (!supported) return;
36
- }
37
- return candidate;
38
- };
@@ -1,151 +0,0 @@
1
- import type { Candidate } from '../shared/candidate';
2
- import { childElements, excluded, isDark, text } from '../shared/dom';
3
- import { iconSource } from '../shared/icons';
4
- import type { ShellItem } from '../definitions';
5
-
6
- export const tag = 'ion-fab';
7
- export const shadowSelector = 'ion-fab-button, ion-fab-list';
8
-
9
- const hasHostMotion = (style: CSSStyleDeclaration): boolean =>
10
- style.animationName !== 'none' || style.transitionDuration.split(',').some((value) => parseFloat(value) > 0);
11
-
12
- // FAB owns the batch; only its buttons draw glass. Closed lists keep their IDs
13
- // and resolved artwork, without inventing layout for display:none descendants.
14
- export const read = (fab: HTMLElement, id: (element: HTMLElement) => string): Candidate | undefined => {
15
- if (fab.slot !== 'fixed' || !fab.parentElement?.matches('ion-content') || fab.contains(fab.ownerDocument.activeElement)) return;
16
- const children = childElements(fab);
17
- const main = children.filter((child) => child.matches('ion-fab-button'));
18
- const lists = children.filter((child) => child.matches('ion-fab-list'));
19
- if (main.length !== 1 || main.length + lists.length !== children.length) return;
20
- if (lists.some((list) => list.closest(excluded) || childElements(list).some((child) => !child.matches('ion-fab-button')))) return;
21
- const buttons = [main[0], ...lists.flatMap((list) => childElements(list))] as HTMLIonFabButtonElement[];
22
- const style = getComputedStyle(fab);
23
- if (hasHostMotion(style)) return;
24
- const transform = new DOMMatrixReadOnly(style.transform);
25
- if (!transform.is2D || transform.a !== 1 || transform.d !== 1 || transform.b || transform.c) return;
26
- const candidate: Candidate = {
27
- element: fab,
28
- control: {
29
- id: id(fab),
30
- kind: tag,
31
- x: 0,
32
- y: 0,
33
- width: innerWidth,
34
- height: innerHeight,
35
- dark: isDark(style),
36
- rtl: style.direction === 'rtl',
37
- items: [],
38
- },
39
- actions: new Map(),
40
- icons: [],
41
- };
42
- for (const button of buttons) {
43
- if (!button.classList.contains('ios') || button.closest(excluded) || button.color || button.type !== 'button' || button.href) return;
44
- if (button.querySelector('input, button, a, img, canvas, video, ion-spinner, ion-avatar, ion-badge')) return;
45
- const native = button.shadowRoot?.querySelector<HTMLElement>('[part=native]');
46
- if (!native) return;
47
- const s = getComputedStyle(button),
48
- n = getComputedStyle(native);
49
- // An opaque custom host/background or custom shape cannot be discarded.
50
- const alpha = n.backgroundColor.startsWith('rgba(') ? Number(n.backgroundColor.slice(5, -1).split(',')[3]) : 1;
51
- if (
52
- s.backgroundColor !== 'rgba(0, 0, 0, 0)' ||
53
- s.backgroundImage !== 'none' ||
54
- n.backgroundImage.includes('url(') ||
55
- !n.backdropFilter.includes('blur') ||
56
- !(alpha > 0 && alpha < 1)
57
- )
58
- return;
59
- const width = parseFloat(s.width),
60
- height = parseFloat(s.height);
61
- if (!(width > 0 && height > 0) || Math.abs(width - height) > 0.5 || !n.borderRadius.includes('50%')) return;
62
- const list = button.parentElement?.matches('ion-fab-list') ? (button.parentElement as HTMLIonFabListElement) : undefined;
63
- const listStyle = list && getComputedStyle(list);
64
- const laidOut = s.display !== 'none' && (!listStyle || listStyle.display !== 'none');
65
- const visible =
66
- laidOut &&
67
- (!list || (list.activated && button.show && listStyle!.visibility === 'visible' && Number(listStyle!.opacity) > 0)) &&
68
- s.visibility === 'visible' &&
69
- Number(s.opacity) > 0;
70
- // Standard scale is centered: even scale(0) retains its actual layout center.
71
- // Static list translation is already included in the DOM rect (including RTL).
72
- const matrix = new DOMMatrixReadOnly(s.transform);
73
- if (!matrix.is2D || matrix.b || matrix.c || matrix.e || matrix.f || matrix.a !== matrix.d || matrix.a < 0) return;
74
- // Only Ionic's hidden list child may use scale(0). Custom static scaling
75
- // changes the actual glass/hit area and must retain its Web rendering.
76
- // Stencil updates show before rendering its host class. Judge the rendered
77
- // scale against that class so this normal one-frame gap keeps its cover.
78
- if (matrix.a !== 1 && (matrix.a !== 0 || !list || button.classList.contains('fab-button-show'))) return;
79
- if (laidOut && !matrix.isIdentity) {
80
- const origin = s.transformOrigin.split(' ').map(parseFloat);
81
- if (Math.abs(origin[0] - width / 2) > 0.5 || Math.abs(origin[1] - height / 2) > 0.5) return;
82
- }
83
- // Ionic's list appearance is staggered show changes, without a host CSS
84
- // transition. Custom host animations keep their complete Web rendering.
85
- if (hasHostMotion(s)) return;
86
- if (listStyle) {
87
- if (hasHostMotion(listStyle)) return;
88
- const transform = new DOMMatrixReadOnly(listStyle.transform);
89
- if (!transform.is2D || transform.a !== 1 || transform.d !== 1 || transform.b || transform.c) return;
90
- }
91
- const rect = button.getBoundingClientRect();
92
- const x = laidOut ? rect.x + rect.width / 2 - width / 2 : 0;
93
- const y = laidOut ? rect.y + rect.height / 2 - height / 2 : 0;
94
- if (visible && (x < -1 || y < -1 || x + width > innerWidth + 1 || y + height > innerHeight + 1)) return;
95
- const label = text(button);
96
- const item: ShellItem = {
97
- id: id(button),
98
- x,
99
- y,
100
- width,
101
- height,
102
- label,
103
- accessibilityLabel: button.getAttribute('aria-label') ?? native.getAttribute('aria-label') ?? label,
104
- disabled: button.disabled || s.pointerEvents === 'none',
105
- selected: button.activated,
106
- visible,
107
- fontSize: parseFloat(s.fontSize),
108
- fontWeight: parseInt(s.fontWeight, 10) || 400,
109
- color: n.color,
110
- };
111
- const normal = Array.from(button.querySelectorAll('ion-icon, svg'));
112
- if (normal.length > 1) return;
113
- const close = button.shadowRoot?.querySelector<HTMLElement>('ion-icon[part=close-icon]');
114
- for (const [index, icon] of [normal[0], close].entries()) {
115
- if (!icon) {
116
- if (index === 1 || !label) return;
117
- else continue;
118
- }
119
- const svg = icon.matches('svg') ? (icon as SVGElement) : icon.shadowRoot?.querySelector<SVGElement>('svg');
120
- if (!svg) return;
121
- const iconStyle = getComputedStyle(icon);
122
- const inner = icon.shadowRoot?.querySelector('.icon-inner');
123
- if (index === 0 && inner && !new DOMMatrixReadOnly(iconStyle.transform).isIdentity) return;
124
- const transform = new DOMMatrixReadOnly(inner ? getComputedStyle(inner).transform : iconStyle.transform);
125
- const mirrored =
126
- transform.is2D && transform.a === -1 && transform.d === 1 && !transform.b && !transform.c && !transform.e && !transform.f;
127
- if (index === 0 && !transform.isIdentity && !mirrored) return;
128
- const source = iconSource(svg, index === 0 && mirrored);
129
- const iconWidth = parseFloat(iconStyle.width),
130
- iconHeight = parseFloat(iconStyle.height);
131
- // The internal close-icon fills the button vertically; its glyph uses font-size.
132
- const size = index === 1 ? parseFloat(iconStyle.fontSize) : iconWidth;
133
- const h = index === 1 ? size : iconHeight;
134
- if (!source || !(size > 0 && h > 0)) return;
135
- if (index === 0) {
136
- item.iconWidth = size;
137
- item.iconHeight = h;
138
- } else {
139
- item.closeIconWidth = size;
140
- item.closeIconHeight = h;
141
- item.iconTransition =
142
- parseFloat(iconStyle.transitionDuration) * (iconStyle.transitionDuration.split(',')[0].trim().endsWith('ms') ? 0.001 : 1);
143
- }
144
- candidate.icons.push({ item, source, ...(index === 1 ? { field: 'closeIcon' as const } : {}) });
145
- }
146
- if (!visible && !list) return;
147
- candidate.control.items.push(item);
148
- if (visible) candidate.actions.set(item.id, button);
149
- }
150
- return candidate;
151
- };
@@ -1,24 +0,0 @@
1
- import { createCandidate, appendItem } from '../shared/candidate';
2
- import type { Candidate, Identify } from '../shared/candidate';
3
- import type { VerticalControlAreaOptions } from '../definitions';
4
- import { childElements, inFixedToolbar } from '../shared/dom';
5
-
6
- export const tag = 'ion-menu-button';
7
- // The glass and projection ownership belong to ion-buttons, even for one menu button.
8
- export const selector = '';
9
- export const shadowSelector = tag;
10
-
11
- export const append = (candidate: Candidate, button: HTMLIonMenuButtonElement, id: Identify): boolean => {
12
- if (button.type !== 'button') return false;
13
- const content = !button.shadowRoot?.querySelector('slot')?.assignedNodes().length ? button.shadowRoot : button;
14
- return !!content && !!appendItem(candidate, button, id, content);
15
- };
16
-
17
- export const read = (group: HTMLElement, id: Identify, options: VerticalControlAreaOptions = {}): Candidate | undefined => {
18
- const children = group.matches('ion-buttons') ? childElements(group) : [];
19
- if (!inFixedToolbar(group) || children.length !== 1) return;
20
- const button = children[0];
21
- if (!button?.matches(`${tag}${group.closest('ion-app.ios-theme-vertical-bars') ? '' : '.ios'}`)) return;
22
- const candidate = createCandidate(group, tag, id, options);
23
- return append(candidate, button as HTMLIonMenuButtonElement, id) ? candidate : undefined;
24
- };
@@ -1,27 +0,0 @@
1
- import { createCandidate, appendItem } from '../shared/candidate';
2
- import type { Candidate, Identify } from '../shared/candidate';
3
- import { inFixedToolbar } from '../shared/dom';
4
-
5
- export const tag = 'ion-segment';
6
- export const shadowSelector = 'ion-segment-button';
7
- export const tracksMotion = true;
8
-
9
- export const read = (element: HTMLElement, id: Identify): Candidate | undefined => {
10
- const segment = element as HTMLIonSegmentElement;
11
- if (
12
- !inFixedToolbar(element) ||
13
- segment.scrollable ||
14
- element.classList.contains('segment-expand') ||
15
- element.querySelector('ion-segment-button[content-id]')
16
- )
17
- return;
18
- const children = Array.from(element.querySelectorAll<HTMLIonSegmentButtonElement>(':scope > ion-segment-button'));
19
- if (!children.length) return;
20
- const candidate = createCandidate(element, tag, id);
21
- for (const child of children) {
22
- const item = appendItem(candidate, child, id);
23
- if (!item || (item.label && item.iconWidth)) return;
24
- item.selected = segment.value === child.value;
25
- }
26
- return candidate;
27
- };
@@ -1,50 +0,0 @@
1
- import { createCandidate, appendItem } from '../shared/candidate';
2
- import type { Candidate, Identify } from '../shared/candidate';
3
-
4
- export const tag = 'ion-tab-bar';
5
- export const shadowSelector = 'ion-tab-button';
6
- export const tracksMotion = true;
7
-
8
- export const read = (element: HTMLElement, id: Identify): Candidate | undefined => {
9
- if (element.closest('ion-content')) return;
10
- const children = Array.from(element.querySelectorAll<HTMLElement>(':scope > ion-tab-button:not(.ion-cloned-element)'));
11
- if (!children.length) return;
12
- // Only equal items with Ionic's default layout map to UIKit's adaptive tabs.
13
- const width = children[0].getBoundingClientRect().width;
14
- if (
15
- children.some(
16
- (child) =>
17
- ((child as HTMLIonTabButtonElement).layout ?? 'icon-top') !== 'icon-top' ||
18
- Math.abs(child.getBoundingClientRect().width - width) > 1,
19
- )
20
- )
21
- return undefined;
22
-
23
- const candidate = createCandidate(element, tag, id);
24
- const style = getComputedStyle(element);
25
- const rtl = style.direction === 'rtl';
26
- const position = ['start', 'center', 'end'].find((value) => element.classList.contains(`tab-bar-position-${value}`));
27
- candidate.control.tabBarAnchor = {
28
- x: position === 'center' ? 0.5 : position ? ((position === 'start') !== rtl ? 0 : 1) : style.left !== 'auto' ? 0 : 1,
29
- y: element.slot === 'bottom' ? 1 : 0,
30
- };
31
-
32
- for (const child of children) {
33
- const item = appendItem(candidate, child, id);
34
- if (!item) return;
35
- const svg = child.querySelector<SVGElement>('svg') ?? child.querySelector('ion-icon')?.shadowRoot?.querySelector<SVGElement>('svg');
36
- if (svg) {
37
- item.iconPosition = 'top';
38
- const color = getComputedStyle(svg).color;
39
- const shapes = Array.from(svg.querySelectorAll('path, rect, circle, ellipse, line, polygon, polyline'));
40
- // Only text-colored SVGs follow UIKit selection tint; preserve explicit artwork colors.
41
- item.iconTemplate =
42
- shapes.length > 0 &&
43
- shapes.every((shape) => {
44
- const paint = getComputedStyle(shape);
45
- return [paint.fill, paint.stroke].every((value) => value === 'none' || value === color);
46
- });
47
- }
48
- }
49
- return candidate;
50
- };