@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,315 +0,0 @@
1
- import { Capacitor, registerPlugin } from '@capacitor/core';
2
- import { setConfig } from '../transition/ios.transition';
3
- import type {
4
- NativeUIShellHandle,
5
- NativeUIShellOptions,
6
- NativeUIShellPlugin,
7
- VerticalBarEdge,
8
- VerticalBarPlacement,
9
- VerticalControlAreaHandle,
10
- VerticalControlAreaOptions,
11
- WebViewMetrics,
12
- } from './definitions';
13
- import { createRuntime } from './runtime';
14
- import { bounded, boundedListener } from './shared/bridge';
15
- import { createOverlayController } from './overlays/controller';
16
- import { createVerticalBarsWebProjection } from './vertical-bars-web';
17
- import { prehideVerticalBarsToolbarSources } from './prehide';
18
- import { observeVerticalBarsModals } from './shared/modal';
19
- export type {
20
- NativeUIShellComponent,
21
- NativeUIShellControls,
22
- NativeUIShellHandle,
23
- NativeUIShellOptions,
24
- NativeUIShellStatus,
25
- NativeUIShellSuspension,
26
- VerticalBarEdge,
27
- VerticalBarPlacement,
28
- VerticalControlAreaHandle,
29
- VerticalControlAreaOptions,
30
- WebViewMetrics,
31
- } from './definitions';
32
-
33
- const plugin = registerPlugin<NativeUIShellPlugin>('IonicNativeUIShell');
34
- export const IonicNativeUIShell = plugin;
35
- let active: Promise<NativeUIShellHandle> | undefined;
36
- let activeConfiguration: string | undefined;
37
- let stopping: Promise<void> | undefined;
38
-
39
- // Every stop entry point releases the activation immediately but reserves the
40
- // shared native/DOM resources until its teardown has settled.
41
- const stopActivation = (owner: Promise<NativeUIShellHandle>, destruction: Promise<void>) => {
42
- if (active !== owner) return;
43
- active = undefined;
44
- activeConfiguration = undefined;
45
- stopping = destruction;
46
- const release = () => {
47
- if (stopping === destruction) stopping = undefined;
48
- };
49
- void destruction.then(release, release);
50
- };
51
- const web = (reason: string): NativeUIShellHandle => ({
52
- getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),
53
- suspend: async () => ({ resume: async () => {} }),
54
- destroy: async () => {},
55
- });
56
-
57
- const combine = (native: NativeUIShellHandle, fallback: NativeUIShellHandle): NativeUIShellHandle => ({
58
- getStatus() {
59
- const nativeStatus = native.getStatus();
60
- const fallbackStatus = fallback.getStatus();
61
- return {
62
- ...nativeStatus,
63
- state:
64
- nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped' ? 'stopped' : nativeStatus.projected > 0 ? 'native' : 'web',
65
- projected: nativeStatus.projected + fallbackStatus.projected,
66
- updates: nativeStatus.updates + fallbackStatus.updates,
67
- };
68
- },
69
- async suspend() {
70
- const [nativeLease, fallbackLease] = await Promise.all([native.suspend(), fallback.suspend()]);
71
- return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };
72
- },
73
- async destroy() {
74
- await Promise.all([native.destroy(), fallback.destroy()]);
75
- },
76
- });
77
-
78
- /** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */
79
- const manage = (
80
- handle: NativeUIShellHandle,
81
- lifecycle: {
82
- reason?: string;
83
- suspend?: () => (() => void) | undefined | Promise<(() => void) | undefined>;
84
- /** Registers teardown before it starts so direct destroy and disable share the same queue. */
85
- stopping?: (destruction: Promise<void>) => void;
86
- destroy?: () => void | Promise<void>;
87
- } = {},
88
- ): NativeUIShellHandle => {
89
- let destruction: Promise<void> | undefined;
90
- return {
91
- getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),
92
- async suspend() {
93
- const lease = await handle.suspend();
94
- const resume = await lifecycle.suspend?.();
95
- return {
96
- async resume() {
97
- await lease.resume();
98
- resume?.();
99
- },
100
- };
101
- },
102
- destroy() {
103
- if (!destruction) {
104
- destruction = Promise.resolve().then(async () => {
105
- try {
106
- await handle.destroy();
107
- } finally {
108
- await lifecycle.destroy?.();
109
- }
110
- });
111
- lifecycle.stopping?.(destruction);
112
- }
113
- return destruction;
114
- },
115
- };
116
- };
117
-
118
- /** Reads the current native WebView geometry and applies it to page transitions. */
119
- export const configureNativeTransition = async (): Promise<WebViewMetrics> => {
120
- const metrics =
121
- typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' ? await bounded(plugin.getWebViewMetrics()) : { radius: 0 };
122
- setConfig({ radius: metrics.radius });
123
- return metrics;
124
- };
125
-
126
- /** Resolves a logical vertical-bar edge to the physical side for the given direction. */
127
- const physicalVerticalBarEdge = (edge: Exclude<VerticalBarEdge, null>, rtl: boolean): 'left' | 'right' =>
128
- (edge === 'leading') !== rtl ? 'left' : 'right';
129
-
130
- const elementRtl = (element: Element): boolean => element.closest('[dir]')?.getAttribute('dir') === 'rtl';
131
-
132
- // Device facts are supplied by the application; the theme only compares placement.
133
- const nativePlacements = new WeakMap<HTMLElement, { edge: VerticalBarEdge; rtl?: boolean }>();
134
- const horizontalFallbackAttribute = 'data-native-ui-shell-vertical-bars-suspended';
135
-
136
- // Keep the requested rail while ordinary Native UI Shell temporarily uses its
137
- // horizontal layout. Removing the effective class restores normal measurements,
138
- // toolbar ownership and safe areas throughout the existing rendering pipeline.
139
- const observeNativeVerticalBarsLayout = (doc: Document): (() => void) => {
140
- const reconcile = () => {
141
- const app = doc.querySelector<HTMLElement>('ion-app');
142
- if (!app) return;
143
- const requested = app.classList.contains('ios-theme-vertical-bars') || app.hasAttribute(horizontalFallbackAttribute);
144
- const suspended = requested && nativePlacements.get(app)?.edge == null;
145
- if (app.hasAttribute(horizontalFallbackAttribute) !== suspended) app.toggleAttribute(horizontalFallbackAttribute, suspended);
146
- if (app.classList.contains('ios-theme-vertical-bars') !== (requested && !suspended)) {
147
- app.classList.toggle('ios-theme-vertical-bars', requested && !suspended);
148
- }
149
- };
150
- const observer = new MutationObserver(reconcile);
151
- observer.observe(doc.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });
152
- doc.defaultView?.addEventListener('nativeUIShellRefresh', reconcile);
153
- reconcile();
154
- return () => {
155
- observer.disconnect();
156
- doc.defaultView?.removeEventListener('nativeUIShellRefresh', reconcile);
157
- const app = doc.querySelector<HTMLElement>(`ion-app[${horizontalFallbackAttribute}]`);
158
- if (app) {
159
- app.removeAttribute(horizontalFallbackAttribute);
160
- app.classList.add('ios-theme-vertical-bars');
161
- }
162
- };
163
- };
164
-
165
- /**
166
- * Applies one placement to the CSS layout and both Web/native projections.
167
- * Pass `rtl` when the document direction is known; otherwise the nearest `dir` attribute is used.
168
- */
169
- export const setVerticalControlAreaPlacement = (placement: VerticalBarEdge | VerticalBarPlacement, rtl?: boolean): void => {
170
- if (typeof document === 'undefined') return;
171
- const app = document.querySelector<HTMLElement>('ion-app');
172
- if (!app) throw new Error('Vertical Control Area requires ion-app');
173
- const { edge, inset = 0 } = placement && typeof placement === 'object' ? placement : { edge: placement, inset: 0 };
174
- if (placement && typeof placement === 'object' && placement.nativeEdge !== undefined) {
175
- nativePlacements.set(app, { edge: placement.nativeEdge, rtl });
176
- }
177
- app.removeAttribute(horizontalFallbackAttribute);
178
- app.classList.toggle('ios-theme-vertical-bars', edge !== null);
179
- app.classList.toggle('ios-theme-vertical-bars-left', edge !== null && physicalVerticalBarEdge(edge, rtl ?? elementRtl(app)) === 'left');
180
- if (edge && Number.isFinite(inset) && inset > 0) app.style.setProperty('--ios-theme-vertical-bars-native-inset', `${inset}px`);
181
- else app.style.removeProperty('--ios-theme-vertical-bars-native-inset');
182
- document.defaultView?.dispatchEvent(new Event('nativeUIShellRefresh'));
183
- };
184
-
185
- /**
186
- * Call once after ion-app is mounted. Ionic markup remains the source of truth.
187
- * Native vertical buttons default to SwiftUI appearance.
188
- * See VerticalControlAreaOptions for source styling and local overrides.
189
- */
190
- export const enableVerticalControlArea = async (options: VerticalControlAreaOptions = {}): Promise<VerticalControlAreaHandle> => {
191
- const handle = await enableNativeUIShell({
192
- buttonProjection: options.buttonProjection,
193
- buttonDefaultFill: options.buttonDefaultFill,
194
- controls: { tabs: true, toolbar: true, popover: options.controls?.popover === true },
195
- verticalBarsOnly: true,
196
- });
197
- return {
198
- getStatus: () => handle.getStatus(),
199
- suspend: () => handle.suspend(),
200
- destroy: () => handle.destroy(),
201
- setPlacement: setVerticalControlAreaPlacement,
202
- };
203
- };
204
-
205
- /** Call once at application startup. Ionic markup remains the source of truth. */
206
- export const enableNativeUIShell = (options: NativeUIShellOptions = {}): Promise<NativeUIShellHandle> => {
207
- if (options.enabled === false) {
208
- const current = active;
209
- if (current)
210
- stopActivation(
211
- current,
212
- current.then((handle) => handle.destroy()),
213
- );
214
- return (stopping ?? Promise.resolve()).then(() => web('Disabled'));
215
- }
216
- if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));
217
- const controls = options.controls;
218
- const configuration = JSON.stringify([
219
- options.verticalBarsOnly === true,
220
- options.buttonProjection ?? 'system',
221
- // Local source overrides can use this even when the startup projection is system.
222
- options.buttonDefaultFill ?? null,
223
- ...(['tabs', 'toolbar', 'segment', 'fab'] as const).map((component) => !controls || controls[component] === true),
224
- controls?.popover === true,
225
- ]);
226
- if (active && activeConfiguration !== configuration)
227
- return Promise.reject(
228
- new Error('Native UI Shell is already running with different controls; destroy it before changing configuration.'),
229
- );
230
- if (active) return active;
231
- activeConfiguration = configuration;
232
- const previousStop = stopping;
233
- let start: Promise<NativeUIShellHandle>;
234
- const stop = (destruction: Promise<void>) => stopActivation(start, destruction);
235
- return (start = active =
236
- (async () => {
237
- // Native controls and DOM ownership are shared. A replacement activation
238
- // must wait until the old handle has finished clearing both surfaces.
239
- if (previousStop) await previousStop.catch(() => {});
240
- const stopModals = observeVerticalBarsModals(document);
241
- const prehide =
242
- options.controls === undefined || options.controls.toolbar === true ? prehideVerticalBarsToolbarSources(document) : undefined;
243
- const fallback = (reason: string) =>
244
- manage(createVerticalBarsWebProjection(document, options), {
245
- reason,
246
- suspend: () => prehide?.suspend(),
247
- destroy: () => {
248
- prehide?.stop();
249
- stopModals();
250
- },
251
- stopping: stop,
252
- });
253
- if (Capacitor.getPlatform() !== 'ios') return fallback('Requires Capacitor iOS');
254
- let runtime: NativeUIShellHandle | undefined;
255
- let overlays: Awaited<ReturnType<typeof createOverlayController>> | undefined;
256
- let metricsListener: Awaited<ReturnType<typeof plugin.addListener>> | undefined;
257
- let stopVerticalBarsLayout: (() => void) | undefined;
258
- try {
259
- const capabilities = await bounded(plugin.configure({ verticalBarsOnly: options.verticalBarsOnly === true }));
260
- if (!capabilities.supported) return fallback('Requires iOS 26 or later');
261
- if (!options.verticalBarsOnly) stopVerticalBarsLayout = observeNativeVerticalBarsLayout(document);
262
- // The application owns device state and selects the rail through placement classes.
263
- const nativeVerticalBars = () => {
264
- const app = document.querySelector<HTMLElement>('ion-app.ios-theme-vertical-bars');
265
- if (!app) return false;
266
- const reported = nativePlacements.get(app);
267
- if (!reported?.edge) return false;
268
- const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';
269
- return physicalEdge === physicalVerticalBarEdge(reported.edge, reported.rtl ?? elementRtl(app));
270
- };
271
- if (!options.verticalBarsOnly) {
272
- metricsListener = await boundedListener(
273
- plugin.addListener('webViewMetricsChange', (metrics) => {
274
- setConfig({ radius: metrics.radius });
275
- }),
276
- );
277
- await configureNativeTransition().catch(() => undefined);
278
- }
279
- const native = await createRuntime(document, plugin, options, nativeVerticalBars, options.verticalBarsOnly === true);
280
- runtime = combine(
281
- native,
282
- createVerticalBarsWebProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),
283
- );
284
- if (controls?.popover === true)
285
- overlays = await createOverlayController(document, plugin, options, native.retain, nativeVerticalBars);
286
- return manage(runtime, {
287
- suspend: async () => {
288
- const restorePrehide = prehide?.suspend();
289
- const resumeOverlays = await overlays?.suspend();
290
- return () => {
291
- restorePrehide?.();
292
- resumeOverlays?.();
293
- };
294
- },
295
- stopping: stop,
296
- destroy: async () => {
297
- try {
298
- await overlays?.destroy();
299
- } finally {
300
- if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});
301
- stopVerticalBarsLayout?.();
302
- prehide?.stop();
303
- stopModals();
304
- }
305
- },
306
- });
307
- } catch (error) {
308
- await overlays?.destroy().catch(() => {});
309
- await runtime?.destroy().catch(() => {});
310
- if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});
311
- stopVerticalBarsLayout?.();
312
- return fallback(error instanceof Error ? error.message : String(error));
313
- }
314
- })());
315
- };
@@ -1,51 +0,0 @@
1
- import { sheetText } from './styles';
2
-
3
- /** Constructed stylesheets cannot cross documents with their nodes. */
4
- export const moveContent = (content: HTMLElement[], destination: HTMLElement): (() => void) => {
5
- const home = content.map((element) => ({ element, parent: element.parentNode!, next: element.nextSibling }));
6
- const roots: { root: ShadowRoot; css: string; sheets: CSSStyleSheet[] }[] = [];
7
- const collect = (node: Element) => {
8
- if (node.shadowRoot) {
9
- roots.push({
10
- root: node.shadowRoot,
11
- css: sheetText(node.shadowRoot.adoptedStyleSheets),
12
- sheets: [...node.shadowRoot.adoptedStyleSheets],
13
- });
14
- for (const child of Array.from(node.shadowRoot.children)) collect(child);
15
- }
16
- for (const child of Array.from(node.children)) collect(child);
17
- };
18
- content.forEach(collect);
19
- for (const element of content) destination.append(destination.ownerDocument.adoptNode(element));
20
- const styles = roots
21
- .filter(({ css }) => css)
22
- .map(({ root, css }) => {
23
- const style = destination.ownerDocument.createElement('style');
24
- style.textContent = css;
25
- root.append(style);
26
- return style;
27
- });
28
- return () => {
29
- styles.forEach((style) => style.remove());
30
- for (const { element, parent, next } of home) {
31
- const connected = element.isConnected;
32
- parent.ownerDocument!.adoptNode(element);
33
- // Ionic may already have destroyed the Angular component on dismiss.
34
- if (connected) parent.insertBefore(element, next?.parentNode === parent ? next : null);
35
- }
36
- for (const { root, css, sheets } of roots) {
37
- // WebKit re-associates adopted sheets with the new document across
38
- // adoptNode, so they may no longer match this root; fall back to the
39
- // inline copy instead of aborting the remaining restore.
40
- try {
41
- root.adoptedStyleSheets = sheets;
42
- } catch {
43
- if (css) {
44
- const style = root.ownerDocument.createElement('style');
45
- style.textContent = css;
46
- root.append(style);
47
- }
48
- }
49
- }
50
- };
51
- };