@webview-native-ui/engine 1.0.1 → 1.0.2-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/README.md +14 -0
  2. package/WebViewNativeUIControls.podspec +17 -0
  3. package/dist/controls/activation.d.ts +15 -0
  4. package/dist/controls/activation.js +370 -0
  5. package/dist/controls/components/index.d.ts +16 -0
  6. package/dist/controls/components/index.js +38 -0
  7. package/dist/controls/components/ion-back-button.d.ts +4 -0
  8. package/dist/controls/components/ion-back-button.js +16 -0
  9. package/dist/controls/components/ion-button.d.ts +4 -0
  10. package/dist/controls/components/ion-button.js +26 -0
  11. package/dist/controls/components/ion-buttons.d.ts +5 -0
  12. package/dist/controls/components/ion-buttons.js +34 -0
  13. package/dist/controls/components/ion-fab.d.ts +4 -0
  14. package/dist/controls/components/ion-fab.js +164 -0
  15. package/dist/controls/components/ion-menu-button.d.ts +7 -0
  16. package/dist/controls/components/ion-menu-button.js +41 -0
  17. package/dist/controls/components/ion-segment.d.ts +5 -0
  18. package/dist/controls/components/ion-segment.js +24 -0
  19. package/dist/controls/components/ion-tab-bar.d.ts +5 -0
  20. package/dist/controls/components/ion-tab-bar.js +49 -0
  21. package/dist/controls/components/searchable-tabs.d.ts +20 -0
  22. package/dist/controls/components/searchable-tabs.js +525 -0
  23. package/dist/controls/definitions.d.ts +360 -0
  24. package/dist/controls/definitions.js +1 -0
  25. package/dist/controls/extensions.d.ts +44 -0
  26. package/dist/controls/extensions.js +164 -0
  27. package/dist/controls/foldable/fold-state.d.ts +4 -0
  28. package/dist/controls/foldable/fold-state.js +8 -0
  29. package/dist/controls/foldable/types.d.ts +9 -0
  30. package/dist/controls/foldable/types.js +1 -0
  31. package/dist/controls/html.d.ts +8 -0
  32. package/dist/controls/html.js +260 -0
  33. package/dist/controls/index.d.ts +7 -0
  34. package/dist/controls/index.js +4 -0
  35. package/dist/controls/ionic.d.ts +1 -0
  36. package/dist/controls/ionic.js +1 -0
  37. package/dist/controls/legacy-layout.d.ts +7 -0
  38. package/dist/controls/legacy-layout.js +23 -0
  39. package/dist/controls/overlays/content.d.ts +2 -0
  40. package/dist/controls/overlays/content.js +55 -0
  41. package/dist/controls/overlays/controller.d.ts +7 -0
  42. package/dist/controls/overlays/controller.js +369 -0
  43. package/dist/controls/overlays/covered-overlay.d.ts +4 -0
  44. package/dist/controls/overlays/covered-overlay.js +9 -0
  45. package/dist/controls/overlays/dialog-animation.d.ts +7 -0
  46. package/dist/controls/overlays/dialog-animation.js +49 -0
  47. package/dist/controls/overlays/dismiss-animation.d.ts +2 -0
  48. package/dist/controls/overlays/dismiss-animation.js +14 -0
  49. package/dist/controls/overlays/focus.d.ts +2 -0
  50. package/dist/controls/overlays/focus.js +67 -0
  51. package/dist/controls/overlays/popover.d.ts +10 -0
  52. package/dist/controls/overlays/popover.js +117 -0
  53. package/dist/controls/overlays/styles.d.ts +9 -0
  54. package/dist/controls/overlays/styles.js +194 -0
  55. package/dist/controls/prehide.d.ts +6 -0
  56. package/dist/controls/prehide.js +280 -0
  57. package/dist/controls/runtime.d.ts +5 -0
  58. package/dist/controls/runtime.js +1685 -0
  59. package/dist/controls/shared/bridge.d.ts +4 -0
  60. package/dist/controls/shared/bridge.js +15 -0
  61. package/dist/controls/shared/candidate.d.ts +23 -0
  62. package/dist/controls/shared/candidate.js +128 -0
  63. package/dist/controls/shared/clipping.d.ts +1 -0
  64. package/dist/controls/shared/clipping.js +1 -0
  65. package/dist/controls/shared/connection.d.ts +21 -0
  66. package/dist/controls/shared/connection.js +112 -0
  67. package/dist/controls/shared/control-options.d.ts +4 -0
  68. package/dist/controls/shared/control-options.js +14 -0
  69. package/dist/controls/shared/covered-snapshot.d.ts +35 -0
  70. package/dist/controls/shared/covered-snapshot.js +41 -0
  71. package/dist/controls/shared/covered-source.d.ts +22 -0
  72. package/dist/controls/shared/covered-source.js +72 -0
  73. package/dist/controls/shared/crossfade.d.ts +11 -0
  74. package/dist/controls/shared/crossfade.js +71 -0
  75. package/dist/controls/shared/diagnostics.d.ts +2 -0
  76. package/dist/controls/shared/diagnostics.js +16 -0
  77. package/dist/controls/shared/dom.d.ts +45 -0
  78. package/dist/controls/shared/dom.js +258 -0
  79. package/dist/controls/shared/events.d.ts +12 -0
  80. package/dist/controls/shared/events.js +13 -0
  81. package/dist/controls/shared/horizontal-navigation.d.ts +36 -0
  82. package/dist/controls/shared/horizontal-navigation.js +90 -0
  83. package/dist/controls/shared/icons.d.ts +5 -0
  84. package/dist/controls/shared/icons.js +66 -0
  85. package/dist/controls/shared/modal-animation.d.ts +21 -0
  86. package/dist/controls/shared/modal-animation.js +97 -0
  87. package/dist/controls/shared/modal.d.ts +26 -0
  88. package/dist/controls/shared/modal.js +261 -0
  89. package/dist/controls/shared/projection-id.d.ts +2 -0
  90. package/dist/controls/shared/projection-id.js +2 -0
  91. package/dist/controls/shared/scroll-placement.d.ts +1 -0
  92. package/dist/controls/shared/scroll-placement.js +1 -0
  93. package/dist/controls/shared/tab-bar-width.d.ts +7 -0
  94. package/dist/controls/shared/tab-bar-width.js +72 -0
  95. package/dist/controls/shared/toolbar-options.d.ts +10 -0
  96. package/dist/controls/shared/toolbar-options.js +38 -0
  97. package/dist/controls/shared/transition-identity.d.ts +3 -0
  98. package/dist/controls/shared/transition-identity.js +64 -0
  99. package/dist/controls/shared/vertical-bars-geometry.d.ts +8 -0
  100. package/dist/controls/shared/vertical-bars-geometry.js +82 -0
  101. package/dist/controls/shared/vertical-navigation.d.ts +5 -0
  102. package/dist/controls/shared/vertical-navigation.js +39 -0
  103. package/dist/controls/startup.d.ts +7 -0
  104. package/dist/controls/startup.js +72 -0
  105. package/dist/controls/transport.d.ts +10 -0
  106. package/dist/controls/transport.js +1 -0
  107. package/dist/controls/vertical-bars-web-search.d.ts +8 -0
  108. package/dist/controls/vertical-bars-web-search.js +83 -0
  109. package/dist/controls/vertical-bars-web.d.ts +2 -0
  110. package/dist/controls/vertical-bars-web.js +609 -0
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.js +1 -0
  113. package/dist/integration/index.d.ts +36 -0
  114. package/dist/integration/index.js +25 -0
  115. package/dist/native/session.js +14 -8
  116. package/dist/projection/pause.d.ts +10 -0
  117. package/dist/projection/pause.js +58 -0
  118. package/dist/projection/styles.d.ts +1 -1
  119. package/dist/projection/styles.js +0 -1
  120. package/dist/projection/surfaces.js +2 -1
  121. package/dist/projection/web.js +39 -5
  122. package/dist/types.d.ts +5 -1
  123. package/docs/README.md +1 -0
  124. package/docs/configuration/README.md +3 -1
  125. package/docs/configuration/ionic-overlays.md +36 -2
  126. package/docs/configuration/pause-resume.md +51 -0
  127. package/docs/controls.md +39 -0
  128. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIButton.swift +98 -0
  129. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIButtons.swift +24 -0
  130. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIComponents.swift +19 -0
  131. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIFab.swift +80 -0
  132. package/ios/Sources/WebViewNativeUIControls/Components/NativeUISearchController.swift +449 -0
  133. package/ios/Sources/WebViewNativeUIControls/Components/NativeUISegment.swift +91 -0
  134. package/ios/Sources/WebViewNativeUIControls/Components/NativeUITabBar.swift +213 -0
  135. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIToolbarTransition.swift +186 -0
  136. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIVerticalBars.swift +696 -0
  137. package/ios/Sources/WebViewNativeUIControls/Components/NativeUIVerticalSearch.swift +117 -0
  138. package/ios/Sources/WebViewNativeUIControls/Extensions/NativeUI.swift +180 -0
  139. package/ios/Sources/WebViewNativeUIControls/Extensions/NativeUIRendering.swift +38 -0
  140. package/ios/Sources/WebViewNativeUIControls/NativeUIController.swift +801 -0
  141. package/ios/Sources/WebViewNativeUIControls/Overlays/NativeUIOverlayController.swift +131 -0
  142. package/ios/Sources/WebViewNativeUIControls/Overlays/NativeUIOverlayHost.swift +85 -0
  143. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIControlRendering.swift +21 -0
  144. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUICoveredSnapshots.swift +14 -0
  145. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUICrossfade.swift +35 -0
  146. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIHost.swift +8 -0
  147. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIScrollTracker.swift +2 -0
  148. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUISnapshot.swift +206 -0
  149. package/ios/Sources/WebViewNativeUIControls/Shared/NativeUIVerticalBarsGeometry.swift +93 -0
  150. package/ios/Sources/WebViewNativeUIEngine/CoveredSnapshots.swift +2 -2
  151. package/ios/Sources/WebViewNativeUIEngine/NativeUIProjection.swift +1 -1
  152. package/ios/Sources/WebViewNativeUIEngine/Resources/native-ui-engine.js +3 -4
  153. package/package.json +30 -5
  154. package/src/controls/activation.ts +427 -0
  155. package/src/controls/components/index.ts +45 -0
  156. package/src/controls/components/ion-back-button.ts +22 -0
  157. package/src/controls/components/ion-button.ts +28 -0
  158. package/src/controls/components/ion-buttons.ts +37 -0
  159. package/src/controls/components/ion-fab.ts +151 -0
  160. package/src/controls/components/ion-menu-button.ts +37 -0
  161. package/src/controls/components/ion-segment.ts +27 -0
  162. package/src/controls/components/ion-tab-bar.ts +55 -0
  163. package/src/controls/components/searchable-tabs.ts +534 -0
  164. package/src/controls/definitions.ts +338 -0
  165. package/src/controls/extensions.ts +198 -0
  166. package/src/controls/foldable/fold-state.ts +10 -0
  167. package/src/controls/foldable/types.ts +4 -0
  168. package/src/controls/html.ts +239 -0
  169. package/src/controls/index.ts +7 -0
  170. package/src/controls/ionic.ts +1 -0
  171. package/src/controls/legacy-layout.ts +21 -0
  172. package/src/controls/overlays/content.ts +51 -0
  173. package/src/controls/overlays/controller.ts +394 -0
  174. package/src/controls/overlays/covered-overlay.ts +11 -0
  175. package/src/controls/overlays/dialog-animation.ts +53 -0
  176. package/src/controls/overlays/dismiss-animation.ts +15 -0
  177. package/src/controls/overlays/focus.ts +64 -0
  178. package/src/controls/overlays/popover.ts +116 -0
  179. package/src/controls/overlays/styles.ts +195 -0
  180. package/src/controls/prehide.ts +285 -0
  181. package/src/controls/runtime.ts +1731 -0
  182. package/src/controls/shared/bridge.ts +18 -0
  183. package/src/controls/shared/candidate.ts +163 -0
  184. package/src/controls/shared/clipping.ts +1 -0
  185. package/src/controls/shared/connection.ts +105 -0
  186. package/src/controls/shared/control-options.ts +20 -0
  187. package/src/controls/shared/covered-snapshot.ts +52 -0
  188. package/src/controls/shared/covered-source.ts +78 -0
  189. package/src/controls/shared/crossfade.ts +70 -0
  190. package/src/controls/shared/diagnostics.ts +13 -0
  191. package/src/controls/shared/dom.ts +310 -0
  192. package/src/controls/shared/events.ts +14 -0
  193. package/src/controls/shared/horizontal-navigation.ts +106 -0
  194. package/src/controls/shared/icons.ts +64 -0
  195. package/src/controls/shared/modal-animation.ts +109 -0
  196. package/src/controls/shared/modal.ts +286 -0
  197. package/src/controls/shared/projection-id.ts +2 -0
  198. package/src/controls/shared/scroll-placement.ts +1 -0
  199. package/src/controls/shared/tab-bar-width.ts +66 -0
  200. package/src/controls/shared/toolbar-options.ts +51 -0
  201. package/src/controls/shared/transition-identity.ts +67 -0
  202. package/src/controls/shared/vertical-bars-geometry.ts +92 -0
  203. package/src/controls/shared/vertical-navigation.ts +34 -0
  204. package/src/controls/startup.ts +74 -0
  205. package/src/controls/transport.ts +11 -0
  206. package/src/controls/vertical-bars-web-search.ts +86 -0
  207. package/src/controls/vertical-bars-web.ts +674 -0
  208. package/src/dom/geometry.ts +24 -0
  209. package/src/dom/overlays.ts +196 -0
  210. package/src/dom/scroll-bindings.ts +66 -0
  211. package/src/dom/scroll-placement.ts +92 -0
  212. package/src/dom/visibility.ts +29 -0
  213. package/src/index.ts +19 -0
  214. package/src/integration/index.ts +64 -0
  215. package/src/ionic.ts +34 -0
  216. package/src/native/bootstrap.ts +132 -0
  217. package/src/native/bridge.ts +18 -0
  218. package/src/native/resource-url.ts +11 -0
  219. package/src/native/session.ts +708 -0
  220. package/src/native/updates.ts +103 -0
  221. package/src/native/values.ts +29 -0
  222. package/src/projection/events.ts +58 -0
  223. package/src/projection/pause.ts +58 -0
  224. package/src/projection/placement.ts +34 -0
  225. package/src/projection/scope.ts +13 -0
  226. package/src/projection/styles.ts +26 -0
  227. package/src/projection/surface.ts +88 -0
  228. package/src/projection/surfaces.ts +119 -0
  229. package/src/projection/web.ts +1013 -0
  230. package/src/types.ts +58 -0
@@ -0,0 +1,260 @@
1
+ import { getProjectionState } from './shared/covered-source.js';
2
+ import { fitsClip } from './shared/clipping.js';
3
+ import { appendItem, createCandidate } from './shared/candidate.js';
4
+ import { marker, unprojected, visible } from './shared/dom.js';
5
+ /** Standard HTML sources use standard native renderers, not app-local Swift extensions. */
6
+ export const htmlSelector = '[data-native-ui="button"], [data-native-ui="back-button"]';
7
+ export const htmlRailSource = (element) => element.matches(htmlSelector) && element.getAttribute('data-native-ui-placement') === 'rail';
8
+ export const readHTMLControl = (element, id, options) => {
9
+ if (!(options.root ?? element.ownerDocument.body).contains(element) ||
10
+ !element.matches(htmlSelector) ||
11
+ element.closest('[data-native-ui-proxy]'))
12
+ return;
13
+ // Explicit opt-in; custom elements can implement click() and expose role=button.
14
+ if (!element.matches('button, ion-button, [role="button"]') || !visible(element, htmlRailSource(element)))
15
+ return;
16
+ if (element.contains(element.ownerDocument.activeElement))
17
+ return;
18
+ if (!htmlRailSource(element) && !fitsClip(element))
19
+ return;
20
+ if (htmlRailSource(element) && options.verticalBars?.enabled === false)
21
+ return;
22
+ const kind = element.getAttribute('data-native-ui') === 'back-button' ? 'back-button' : 'button';
23
+ const candidate = createCandidate(element, kind, id, options);
24
+ const item = appendItem(candidate, element, id);
25
+ if (!item)
26
+ return;
27
+ const labelledBy = element
28
+ .getAttribute('aria-labelledby')
29
+ ?.trim()
30
+ .split(/\s+/)
31
+ .map((key) => element.ownerDocument.getElementById(key)?.textContent?.trim() ?? '')
32
+ .filter(Boolean)
33
+ .join(' ');
34
+ item.accessibilityLabel = labelledBy || element.getAttribute('aria-label') || item.label;
35
+ if (!item.accessibilityLabel)
36
+ return;
37
+ item.disabled ||= element.matches(':disabled, [disabled]') || !!element.closest('[aria-disabled="true"]');
38
+ if (htmlRailSource(element))
39
+ candidate.control.placement = 'vertical-bars';
40
+ return candidate;
41
+ };
42
+ /** A rail host is supplied and styled by the application, never by the plugin. */
43
+ export const createHTMLWebProjection = (doc, options) => {
44
+ const host = options.rail?.host;
45
+ if (!host) {
46
+ let stopped = false;
47
+ return {
48
+ markForCheck: () => { },
49
+ getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: 0, updates: 0 }),
50
+ suspend: async () => ({ resume: async () => { } }),
51
+ destroy: async () => {
52
+ stopped = true;
53
+ },
54
+ };
55
+ }
56
+ const projectionState = getProjectionState(doc);
57
+ const { isProjectionMutation, sourcesChangedEvent, targetsChangedEvent } = projectionState;
58
+ const win = doc.defaultView;
59
+ const owned = new Map();
60
+ const style = doc.createElement('style');
61
+ style.textContent = `[data-native-ui-web-source] { visibility: hidden !important; pointer-events: none !important; }
62
+ [data-native-ui-proxy][data-native-ui-proxy-prepared] { position: absolute !important; }
63
+ [data-native-ui-proxy][data-native-ui-proxy-prepared]:not([data-native-ui-snapshot-visible]) { visibility:hidden!important; }`;
64
+ doc.head.append(style);
65
+ let frame = 0;
66
+ let stopped = false;
67
+ let suspended = 0;
68
+ let updates = 0;
69
+ const restore = (source) => {
70
+ const state = owned.get(source);
71
+ if (!state)
72
+ return;
73
+ const focused = state.clone.contains(doc.activeElement);
74
+ state.clone.remove();
75
+ if (stopped && projectionState.getTarget(source) === state.clone)
76
+ projectionState.deleteTarget(source);
77
+ source.removeAttribute('data-native-ui-web-source');
78
+ if (focused && source.isConnected && !source.hasAttribute(marker))
79
+ source.focus();
80
+ owned.delete(source);
81
+ };
82
+ const measure = (source, read) => {
83
+ const hidden = source.hasAttribute('data-native-ui-web-source');
84
+ if (hidden)
85
+ source.removeAttribute('data-native-ui-web-source');
86
+ try {
87
+ return unprojected([source], read);
88
+ }
89
+ finally {
90
+ if (hidden)
91
+ source.setAttribute('data-native-ui-web-source', '');
92
+ }
93
+ };
94
+ const render = () => {
95
+ frame = 0;
96
+ if (stopped)
97
+ return;
98
+ observer.disconnect();
99
+ observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });
100
+ const wanted = new Set();
101
+ if (options.verticalBars?.enabled !== false && host?.isConnected && options.root?.isConnected) {
102
+ const sources = Array.from(options.root.querySelectorAll(htmlSelector));
103
+ let back = false;
104
+ for (const source of sources) {
105
+ // An accepted native surface owns its source and descendants. Rejected sources
106
+ // lose the marker and can return to the Web rail on the next render.
107
+ if (!htmlRailSource(source) || host.contains(source))
108
+ continue;
109
+ const covered = projectionState.isVisible(source) ||
110
+ (projectionState.owns(source) && !!owned.get(source)?.clone.hasAttribute(projectionState.imageMarker));
111
+ // Keep a fixed fallback host available before the first native capture.
112
+ const prepared = (source.hasAttribute(marker) && source.hasAttribute('data-native-ui-vertical-bars')) ||
113
+ projectionState.preparedOwners.includes(source);
114
+ if ((suspended && !covered) || (!covered && !prepared && (source.closest(`[${marker}]`) || projectionState.owns(source))))
115
+ continue;
116
+ // Keep the prepared target stable through coverage and native restoration.
117
+ // Reading a changed/disabled source here must not replace its pre-touch image.
118
+ const existing = owned.get(source);
119
+ if (covered && existing) {
120
+ wanted.add(source);
121
+ existing.clone.inert = true;
122
+ existing.clone.setAttribute('aria-hidden', 'true');
123
+ back ||= existing.clone.getAttribute('data-native-ui-proxy') === 'back-button';
124
+ continue;
125
+ }
126
+ const iconRoot = source.querySelector('ion-icon')?.shadowRoot;
127
+ if (iconRoot)
128
+ observer.observe(iconRoot, { subtree: true, childList: true, attributes: true, characterData: true });
129
+ const candidate = measure(source, () => readHTMLControl(source, () => 'web', options));
130
+ if (!candidate)
131
+ continue;
132
+ if (candidate.control.kind === 'back-button') {
133
+ if (back)
134
+ continue;
135
+ back = true;
136
+ }
137
+ wanted.add(source);
138
+ const item = candidate.control.items[0];
139
+ const svg = source.querySelector('svg') ?? iconRoot?.querySelector('svg');
140
+ const signature = JSON.stringify([item, source.innerHTML, svg?.outerHTML]);
141
+ let state = owned.get(source);
142
+ if (!state) {
143
+ const clone = doc.createElement('button');
144
+ clone.type = 'button';
145
+ clone.setAttribute('data-native-ui-proxy', candidate.control.kind);
146
+ state = { clone, aria: source.getAttribute('aria-hidden'), signature: '' };
147
+ owned.set(source, state);
148
+ clone.addEventListener('click', () => {
149
+ const current = !stopped && !suspended && measure(source, () => readHTMLControl(source, () => 'web', options));
150
+ if (options.verticalBars?.enabled === false ||
151
+ !current ||
152
+ !htmlRailSource(source) ||
153
+ !host.isConnected ||
154
+ current.control.items[0].disabled ||
155
+ source.closest(`[${marker}]`) ||
156
+ projectionState.owns(source))
157
+ return;
158
+ source.click(); // Source owns submission, custom handlers and back navigation.
159
+ schedule();
160
+ });
161
+ clone.toggleAttribute('data-native-ui-proxy-prepared', prepared);
162
+ projectionState.setTarget(source, clone);
163
+ host.append(clone);
164
+ }
165
+ state.clone.toggleAttribute('data-native-ui-proxy-prepared', prepared);
166
+ state.clone.inert = prepared || covered;
167
+ if (prepared || covered)
168
+ state.clone.setAttribute('aria-hidden', 'true');
169
+ else
170
+ state.clone.removeAttribute('aria-hidden');
171
+ if (state.signature !== signature) {
172
+ state.signature = signature;
173
+ state.clone.replaceChildren();
174
+ if (svg) {
175
+ const icon = svg.cloneNode(true);
176
+ icon.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'));
177
+ icon.removeAttribute('id');
178
+ for (const node of [icon, ...Array.from(icon.querySelectorAll('*'))])
179
+ for (const attr of Array.from(node.attributes))
180
+ if (attr.name.startsWith('on'))
181
+ node.removeAttribute(attr.name);
182
+ state.clone.append(icon);
183
+ }
184
+ state.clone.append(doc.createTextNode(item.label));
185
+ state.clone.setAttribute('aria-label', item.accessibilityLabel);
186
+ state.clone.disabled = item.disabled;
187
+ updates++;
188
+ }
189
+ if (!source.hasAttribute('data-native-ui-web-source'))
190
+ source.setAttribute('data-native-ui-web-source', '');
191
+ }
192
+ }
193
+ for (const source of owned.keys())
194
+ if (!wanted.has(source))
195
+ restore(source);
196
+ const clones = Array.from(wanted, (source) => owned.get(source).clone);
197
+ const order = Array.from(host.children).filter((child) => clones.includes(child));
198
+ const focused = clones.find((clone) => clone === doc.activeElement);
199
+ clones.forEach((clone, index) => {
200
+ if (order[index] === clone)
201
+ return;
202
+ host.insertBefore(clone, order[index]);
203
+ order.splice(order.indexOf(clone), 1);
204
+ order.splice(index, 0, clone);
205
+ });
206
+ // Moving an existing button can blur it; retain keyboard focus across source reordering.
207
+ if (focused && doc.activeElement !== focused)
208
+ focused.focus({ preventScroll: true });
209
+ win.dispatchEvent(new Event(targetsChangedEvent));
210
+ // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh.
211
+ observer.takeRecords();
212
+ };
213
+ const schedule = () => {
214
+ if (!stopped && !frame)
215
+ frame = win.requestAnimationFrame(render);
216
+ };
217
+ const observer = new MutationObserver((records) => {
218
+ if (records.some((record) => !isProjectionMutation(record)))
219
+ schedule();
220
+ });
221
+ win.addEventListener('resize', schedule);
222
+ win.addEventListener('nativeUIRefresh', schedule);
223
+ win.addEventListener(sourcesChangedEvent, schedule);
224
+ render();
225
+ return {
226
+ markForCheck: schedule,
227
+ getStatus: () => ({
228
+ state: stopped ? 'stopped' : 'web',
229
+ projected: [...owned.values()].filter(({ clone }) => !clone.inert).length,
230
+ updates,
231
+ }),
232
+ async suspend() {
233
+ suspended++;
234
+ render();
235
+ let resumed = false;
236
+ return {
237
+ async resume() {
238
+ if (!resumed) {
239
+ resumed = true;
240
+ suspended--;
241
+ render();
242
+ }
243
+ },
244
+ };
245
+ },
246
+ async destroy() {
247
+ if (stopped)
248
+ return;
249
+ stopped = true;
250
+ observer.disconnect();
251
+ win.cancelAnimationFrame(frame);
252
+ win.removeEventListener('resize', schedule);
253
+ win.removeEventListener('nativeUIRefresh', schedule);
254
+ win.removeEventListener(sourcesChangedEvent, schedule);
255
+ for (const source of owned.keys())
256
+ restore(source);
257
+ style.remove();
258
+ },
259
+ };
260
+ };
@@ -0,0 +1,7 @@
1
+ export { createNativeUI } from './startup.js';
2
+ export { defineNativeUI } from './extensions.js';
3
+ export { setVerticalControlAreaPlacement } from './legacy-layout.js';
4
+ export { applyFoldStateClasses } from './foldable/fold-state.js';
5
+ export type * from './definitions.js';
6
+ export type * from './extensions.js';
7
+ export type * from './transport.js';
@@ -0,0 +1,4 @@
1
+ export { createNativeUI } from './startup.js';
2
+ export { defineNativeUI } from './extensions.js';
3
+ export { setVerticalControlAreaPlacement } from './legacy-layout.js';
4
+ export { applyFoldStateClasses } from './foldable/fold-state.js';
@@ -0,0 +1 @@
1
+ export { createOverlayController } from './overlays/controller.js';
@@ -0,0 +1 @@
1
+ export { createOverlayController } from './overlays/controller.js';
@@ -0,0 +1,7 @@
1
+ import type { VerticalBarEdge, VerticalBarPlacement } from './definitions.js';
2
+ /**
3
+ * @deprecated Use the theme setVerticalBarsLayout helper. nativeEdge is ignored; device metrics determine availability.
4
+ * Legacy Web layout adapter; does not report native availability.
5
+ * Pass `rtl` when the document direction is known; otherwise the nearest `dir` attribute is used.
6
+ */
7
+ export declare const setVerticalControlAreaPlacement: (placement: VerticalBarEdge | VerticalBarPlacement, rtl?: boolean) => void;
@@ -0,0 +1,23 @@
1
+ const physicalVerticalBarEdge = (edge, rtl) => ((edge === 'leading') !== rtl ? 'left' : 'right');
2
+ const elementRtl = (element) => element.closest('[dir]')?.getAttribute('dir') === 'rtl';
3
+ /**
4
+ * @deprecated Use the theme setVerticalBarsLayout helper. nativeEdge is ignored; device metrics determine availability.
5
+ * Legacy Web layout adapter; does not report native availability.
6
+ * Pass `rtl` when the document direction is known; otherwise the nearest `dir` attribute is used.
7
+ */
8
+ export const setVerticalControlAreaPlacement = (placement, rtl) => {
9
+ if (typeof document === 'undefined')
10
+ return;
11
+ const app = document.querySelector('ion-app');
12
+ if (!app)
13
+ throw new Error('Vertical Control Area requires ion-app');
14
+ const { edge, inset = 0 } = placement && typeof placement === 'object' ? placement : { edge: placement, inset: 0 };
15
+ app.removeAttribute('data-native-ui-vertical-bars-suspended');
16
+ app.classList.toggle('ios-theme-vertical-bars', edge !== null);
17
+ app.classList.toggle('ios-theme-vertical-bars-left', edge !== null && physicalVerticalBarEdge(edge, rtl ?? elementRtl(app)) === 'left');
18
+ if (edge && Number.isFinite(inset) && inset > 0)
19
+ app.style.setProperty('--ios-theme-vertical-bars-native-inset', `${inset}px`);
20
+ else
21
+ app.style.removeProperty('--ios-theme-vertical-bars-native-inset');
22
+ document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));
23
+ };
@@ -0,0 +1,2 @@
1
+ /** Constructed stylesheets cannot cross documents with their nodes. */
2
+ export declare const moveContent: (content: HTMLElement[], destination: HTMLElement) => (() => void);
@@ -0,0 +1,55 @@
1
+ import { sheetText } from './styles.js';
2
+ /** Constructed stylesheets cannot cross documents with their nodes. */
3
+ export const moveContent = (content, destination) => {
4
+ const home = content.map((element) => ({ element, parent: element.parentNode, next: element.nextSibling }));
5
+ const roots = [];
6
+ const collect = (node) => {
7
+ if (node.shadowRoot) {
8
+ roots.push({
9
+ root: node.shadowRoot,
10
+ css: sheetText(node.shadowRoot.adoptedStyleSheets),
11
+ sheets: [...node.shadowRoot.adoptedStyleSheets],
12
+ });
13
+ for (const child of Array.from(node.shadowRoot.children))
14
+ collect(child);
15
+ }
16
+ for (const child of Array.from(node.children))
17
+ collect(child);
18
+ };
19
+ content.forEach(collect);
20
+ for (const element of content)
21
+ destination.append(destination.ownerDocument.adoptNode(element));
22
+ const styles = roots
23
+ .filter(({ css }) => css)
24
+ .map(({ root, css }) => {
25
+ const style = destination.ownerDocument.createElement('style');
26
+ style.textContent = css;
27
+ root.append(style);
28
+ return style;
29
+ });
30
+ return () => {
31
+ styles.forEach((style) => style.remove());
32
+ for (const { element, parent, next } of home) {
33
+ const connected = element.isConnected;
34
+ parent.ownerDocument.adoptNode(element);
35
+ // Ionic may already have destroyed the Angular component on dismiss.
36
+ if (connected)
37
+ parent.insertBefore(element, next?.parentNode === parent ? next : null);
38
+ }
39
+ for (const { root, css, sheets } of roots) {
40
+ // WebKit re-associates adopted sheets with the new document across
41
+ // adoptNode, so they may no longer match this root; fall back to the
42
+ // inline copy instead of aborting the remaining restore.
43
+ try {
44
+ root.adoptedStyleSheets = sheets;
45
+ }
46
+ catch {
47
+ if (css) {
48
+ const style = root.ownerDocument.createElement('style');
49
+ style.textContent = css;
50
+ root.append(style);
51
+ }
52
+ }
53
+ }
54
+ };
55
+ };
@@ -0,0 +1,7 @@
1
+ import type { NativeUIOptions, NativeUIPlugin } from '../definitions.js';
2
+ import type { NativeUIConnection } from '../shared/connection.js';
3
+ /** Ionic owns lifecycle and dismissal; only one overlay is relayed at a time. */
4
+ export declare const createOverlayController: (doc: Document, plugin: NativeUIPlugin, options: NativeUIOptions, retainProjection: (deferActivation?: (id: string) => boolean) => () => void, nativeVerticalBars: () => boolean, connection?: NativeUIConnection) => Promise<{
5
+ suspend(): Promise<() => void>;
6
+ destroy(): Promise<void>;
7
+ }>;