@rdlabo/capacitor-native-ui 1.2.1 → 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.
- package/README.md +38 -25
- package/dist/core.d.ts +2 -2
- package/dist/native/activation.d.ts.map +1 -1
- package/dist/native/activation.js +19 -19
- package/dist/native/activation.js.map +1 -1
- package/dist/native/definitions.d.ts +22 -40
- package/dist/native/definitions.d.ts.map +1 -1
- package/dist/native/html.d.ts.map +1 -1
- package/dist/native/html.js +10 -1
- package/dist/native/html.js.map +1 -1
- package/dist/native/index.d.ts +2 -2
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +2 -11
- package/dist/native/index.js.map +1 -1
- package/dist/native/overlays/controller.js +1 -1
- package/dist/native/overlays/controller.js.map +1 -1
- package/dist/native/prehide.js +1 -1
- package/dist/native/prehide.js.map +1 -1
- package/dist/native/runtime.d.ts +1 -1
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +8 -20
- package/dist/native/runtime.js.map +1 -1
- package/dist/native/shared/candidate.d.ts +2 -2
- package/dist/native/shared/candidate.d.ts.map +1 -1
- package/dist/native/shared/candidate.js +2 -2
- package/dist/native/shared/candidate.js.map +1 -1
- package/dist/native/shared/control-options.d.ts +6 -0
- package/dist/native/shared/control-options.d.ts.map +1 -0
- package/dist/native/shared/control-options.js +17 -0
- package/dist/native/shared/control-options.js.map +1 -0
- package/dist/native/shared/toolbar-options.d.ts +6 -2
- package/dist/native/shared/toolbar-options.d.ts.map +1 -1
- package/dist/native/shared/toolbar-options.js +7 -15
- package/dist/native/shared/toolbar-options.js.map +1 -1
- package/dist/native/startup.d.ts.map +1 -1
- package/dist/native/startup.js +0 -16
- package/dist/native/startup.js.map +1 -1
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +3 -2
- package/dist/native/vertical-bars-web.js.map +1 -1
- package/dist/vertical-bars.d.ts +2 -2
- package/docs/appearance.md +45 -0
- package/docs/configuration.md +106 -74
- package/docs/framework-independent.md +35 -10
- package/docs/ionic-vertical-bars.md +173 -0
- package/docs/migration.md +65 -87
- package/docs/native-ui-extensions.md +60 -31
- package/docs/native-ui.md +186 -67
- package/docs/setup.md +43 -24
- package/docs/vertical-bars.md +124 -72
- package/ios/Sources/NativeUIPlugin/NativeUIPlugin.swift +1 -1
- package/package.json +1 -1
- package/src/core.ts +1 -2
- package/src/native/activation.ts +13 -22
- package/src/native/definitions.ts +40 -90
- package/src/native/html.ts +11 -5
- package/src/native/index.ts +2 -11
- package/src/native/overlays/controller.ts +1 -1
- package/src/native/prehide.ts +1 -1
- package/src/native/runtime.ts +7 -16
- package/src/native/shared/candidate.ts +4 -4
- package/src/native/shared/control-options.ts +20 -0
- package/src/native/shared/toolbar-options.ts +8 -12
- package/src/native/startup.ts +0 -16
- package/src/native/vertical-bars-web.ts +4 -2
- package/src/vertical-bars.ts +2 -1
- package/docs/native-mini-player.md +0 -47
package/README.md
CHANGED
|
@@ -1,43 +1,56 @@
|
|
|
1
1
|
# Capacitor Native UI
|
|
2
2
|
|
|
3
|
-
> **Unreleased package:** the first npm release of `@rdlabo/capacitor-native-ui` is pending. The registry install commands in these guides apply after that release. To try the current code now, install a candidate archive as described below.
|
|
4
|
-
|
|
5
3
|
Render supported Web controls with UIKit and SwiftUI on iOS while keeping your application state, routing, and handlers in the WebView. Use existing Ionic markup or explicit HTML buttons; custom SwiftUI or Android Compose content can be added through experimental app-local extensions.
|
|
6
4
|
|
|
7
5
|
Native UI is opt-in and currently a preview. Standard controls on Android, browsers, older iOS, and unsupported controls retain Web presentation. Android supports experimental app-local Compose extensions. Keep your application's styles and layout; the iOS theme is optional.
|
|
8
6
|
|
|
7
|
+
## How projection works
|
|
8
|
+
|
|
9
|
+
A **projection** is another presentation of a Web control. Your application keeps the original DOM element, state and event handlers. The plugin reads supported labels, icons, selected/disabled state and geometry, then associates the rendered control with that source element. It does not convert the page into a native screen or run a second copy of your application.
|
|
10
|
+
|
|
11
|
+
| Web source | Projected presentation |
|
|
12
|
+
| ----------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
13
|
+
| Supported Ionic buttons, tabs and segments | Native UIKit/SwiftUI controls on supported iOS |
|
|
14
|
+
| Explicitly marked HTML buttons and back buttons | Native buttons on supported iOS |
|
|
15
|
+
| Eligible controls placed in Vertical Bars | SwiftUI controls in the available system rail; Web rail presentation otherwise |
|
|
16
|
+
| An app-local extension's DOM element | The SwiftUI or Compose content registered by your app, using props read from that element |
|
|
17
|
+
|
|
18
|
+
The original element **stays in the DOM** while projected. Its bindings and handlers remain attached. Once native presentation is accepted, the runtime hides the source visually, prevents direct pointer interaction and hides it from Web accessibility so there are not two interactive copies. Element projection retains the source's layout box; rail spacing is supplied by your app or theme. A Web rail similarly uses its adapter's Web presentation: Ionic layout/clones or HTML proxy buttons in your `webHost`.
|
|
19
|
+
|
|
20
|
+
For example, tapping a projected Save button activates the original Web button. Its existing handler performs the save and updates application state; supported DOM changes then update the projection. Tabs and other controls likewise route actions through their source adapters. Continue updating the source, rather than managing separate native state. For reader values that change without a DOM mutation, use `markForCheck()`.
|
|
21
|
+
|
|
22
|
+
Projection changes presentation; it does not translate arbitrary HTML or CSS into native views. Native controls use supported data and styling; a custom Web appearance is not guaranteed to be reproduced. Removing or disabling a source prevents further valid actions. If projection is unavailable or stopped, the runtime releases its hiding state and the source returns to Web presentation, or to an available Web rail. Your application's own hidden/disabled state still applies.
|
|
23
|
+
|
|
9
24
|
## Choose what to build
|
|
10
25
|
|
|
11
|
-
| Goal
|
|
12
|
-
|
|
|
13
|
-
| Render existing Ionic controls natively
|
|
14
|
-
| Use native buttons without Ionic
|
|
15
|
-
|
|
|
26
|
+
| Goal | Start here |
|
|
27
|
+
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| Render existing Ionic controls natively | [Ionic setup](docs/setup.md) |
|
|
29
|
+
| Use native buttons without Ionic | [HTML setup](docs/framework-independent.md) |
|
|
30
|
+
| Add a side rail to Ionic while keeping your theme | [Vertical Bars step by step](docs/ionic-vertical-bars.md) |
|
|
31
|
+
| Add a side rail without Ionic | [HTML rail setup](docs/framework-independent.md) |
|
|
16
32
|
| Add native controls to an existing iOS 27 theme app | [Add Native UI to your theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/add-native-ui.md) |
|
|
17
|
-
| Render custom SwiftUI or Android Compose content
|
|
18
|
-
|
|
19
|
-
## Try the unreleased packages
|
|
33
|
+
| Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) |
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
2. Choose the packages for your goal. Replace `VERSION` and the paths with the downloaded filenames:
|
|
35
|
+
## Install from npm
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
Install in your Capacitor 8 application:
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
```sh
|
|
40
|
+
npm install @rdlabo/capacitor-native-ui
|
|
41
|
+
```
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
If you also use the iOS 27 theme, install or update both packages together:
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
```sh
|
|
46
|
+
npm install @rdlabo/capacitor-native-ui @rdlabo/ionic-theme-ios27
|
|
47
|
+
```
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
Both packages share the same release version. Declare Native UI as a direct dependency so Capacitor can discover the plugin. Theme-only apps can install just the theme; its dependency is resolved by npm and does not activate native controls.
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
Continue with [Ionic setup](docs/setup.md), [HTML setup](docs/framework-independent.md), or [extensions](docs/native-ui-extensions.md). After configuring startup, build your application's Web assets, run `npx cap sync` and rebuild the native app. No repository checkout or archive download is needed.
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
Upgrading from the native APIs bundled in theme 1.2.0 or 1.2.1? Follow the [migration guide](docs/migration.md) for import, configuration and native project changes.
|
|
41
54
|
|
|
42
55
|
## Requirements
|
|
43
56
|
|
|
@@ -45,10 +58,10 @@ Native startup requires Capacitor 8 and an iOS or Android project. Android exten
|
|
|
45
58
|
|
|
46
59
|
For standard controls, follow the setup guide for your markup; the Ionic and HTML adapters use different imports. For custom native content, start directly with the [extensions guide](docs/native-ui-extensions.md).
|
|
47
60
|
|
|
48
|
-
## Reference
|
|
61
|
+
## Reference
|
|
49
62
|
|
|
50
63
|
- [Supported Ionic controls](docs/native-ui.md#supported-markup) and fallback behavior.
|
|
51
64
|
- [Configuration](docs/configuration.md): defaults, selected controls, rail-only mode, and live changes.
|
|
65
|
+
- [Light and dark appearance](docs/appearance.md): inherit the Web theme, configure custom HTML, and understand shared rail appearance.
|
|
52
66
|
- [Vertical Bars](docs/vertical-bars.md): eligibility, appearance, and runtime API.
|
|
53
|
-
- [Now Playing extension](docs/native-mini-player.md): a Web and SwiftUI example to adapt.
|
|
54
67
|
- [Migration](docs/migration.md): package names, imports, and native project updates.
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { NativeUIExtension, NativeUIImageReference, NativeUIReadContext, NativeUIValue, defineNativeUI } from "./native/extensions.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ControlProjection, IonicProjectionOptions, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions } from "./native/definitions.js";
|
|
3
3
|
import { enableNativeUI } from "./native/startup.js";
|
|
4
|
-
export { type
|
|
4
|
+
export { type ControlProjection, type IonicProjectionOptions, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, defineNativeUI, enableNativeUI };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activation.d.ts","names":[],"sources":["../../src/native/activation.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"activation.d.ts","names":[],"sources":["../../src/native/activation.ts"],"mappings":";;;UAUU;EACR,WAAW;EACX,WAAW;;KAER,kBACH,KAAK,UACL,QAAQ,gBACR,SAAS,wBACT,SAAS,mBAAmB,uCAC5B,8BACG,QAAQ;qBAkBA,UAAQ;qBAqGR,iCAAsC,QAAQ;qBAuB9C,iBAAkB,UAAS,wBAA6B,iBAAiB,mBAAiB,QAAQ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { controlDefault } from "./shared/control-options.js";
|
|
1
2
|
import { observeVerticalBarsModals } from "./shared/modal.js";
|
|
2
3
|
import { toolbarOptions } from "./shared/toolbar-options.js";
|
|
3
4
|
import { createDiagnostics } from "./shared/diagnostics.js";
|
|
@@ -118,11 +119,6 @@ const webProjection = (doc, options, enabled = () => true) => {
|
|
|
118
119
|
};
|
|
119
120
|
/** Call once at application startup. Ionic markup remains the source of truth. */
|
|
120
121
|
const enableNativeUI = (options = {}, overlayFactory) => {
|
|
121
|
-
if (options.enabled === false) {
|
|
122
|
-
const current = active;
|
|
123
|
-
if (current) stopActivation(current, current.then((handle) => handle.destroy()));
|
|
124
|
-
return (stopping ?? Promise.resolve()).then(() => web("Disabled"));
|
|
125
|
-
}
|
|
126
122
|
if (typeof document === "undefined") return Promise.resolve(web("Requires a document"));
|
|
127
123
|
const extensions = [...options.extensions ?? []];
|
|
128
124
|
const nativeState = options.nativeState = {};
|
|
@@ -131,26 +127,23 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
131
127
|
} catch (error) {
|
|
132
128
|
return Promise.reject(error);
|
|
133
129
|
}
|
|
134
|
-
const controls = options.controls;
|
|
135
130
|
const configuration = JSON.stringify([
|
|
136
131
|
options.source ?? "ionic",
|
|
137
132
|
options.debug === true,
|
|
138
|
-
options.
|
|
133
|
+
options.ionic?.auto !== false,
|
|
139
134
|
options.verticalBars?.enabled !== false,
|
|
140
135
|
options.verticalBars?.placement?.edge,
|
|
141
136
|
options.rail?.edge,
|
|
142
|
-
options.verticalBarsOnly === true,
|
|
143
137
|
toolbarOptions(options, false),
|
|
144
138
|
toolbarOptions(options, true),
|
|
145
|
-
options.buttonProjection ?? "system",
|
|
146
|
-
options.buttonDefaultFill ??
|
|
139
|
+
options.ionic?.verticalBars?.buttonProjection ?? "system",
|
|
140
|
+
options.ionic?.verticalBars?.buttonDefaultFill ?? "glass",
|
|
147
141
|
...[
|
|
148
142
|
"tabs",
|
|
149
|
-
"toolbar",
|
|
150
143
|
"segment",
|
|
151
144
|
"fab"
|
|
152
|
-
].
|
|
153
|
-
|
|
145
|
+
].flatMap((kind) => [controlDefault(options, kind, false), controlDefault(options, kind, true)]),
|
|
146
|
+
options.ionic?.verticalBars?.popover === true
|
|
154
147
|
]);
|
|
155
148
|
if (active && activeFailed) {
|
|
156
149
|
const previous = active;
|
|
@@ -186,7 +179,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
186
179
|
const platform = Capacitor.getPlatform();
|
|
187
180
|
const diagnose = createDiagnostics(options.debug);
|
|
188
181
|
const stopModals = options.source === "html" ? () => {} : observeVerticalBarsModals(document);
|
|
189
|
-
const prehide = platform !== "android" && options.source !== "html" &&
|
|
182
|
+
const prehide = platform !== "android" && options.source !== "html" && options.ionic?.auto !== false ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options) : void 0;
|
|
190
183
|
const fallback = (reason) => {
|
|
191
184
|
diagnose(`web-fallback: ${reason}`);
|
|
192
185
|
return manage(webProjection(document, options), {
|
|
@@ -225,7 +218,14 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
225
218
|
}
|
|
226
219
|
};
|
|
227
220
|
try {
|
|
228
|
-
const
|
|
221
|
+
const ordinaryControls = [
|
|
222
|
+
"button",
|
|
223
|
+
"backButton",
|
|
224
|
+
"tabs",
|
|
225
|
+
"segment",
|
|
226
|
+
"fab"
|
|
227
|
+
].some((kind) => controlDefault(options, kind, false));
|
|
228
|
+
const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));
|
|
229
229
|
if (!capabilities.supported) return fallback("Native UI is unavailable on this device");
|
|
230
230
|
const unavailableExtensions = /* @__PURE__ */ new Map();
|
|
231
231
|
for (const extension of extensions) if (platform === "android" && extension.placement === "tab-accessory") unavailableExtensions.set(extension.name, "unsupported-placement: tab-accessory requires a supported iOS tab host.");
|
|
@@ -236,7 +236,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
236
236
|
source: "extensions",
|
|
237
237
|
extensions,
|
|
238
238
|
unavailableExtensions
|
|
239
|
-
}, () => false,
|
|
239
|
+
}, () => false, runtimeStopped);
|
|
240
240
|
runtime = combine(runtime, webProjection(document, options));
|
|
241
241
|
metricsListener = await boundedListener(plugin.addListener("webViewMetricsChange", () => {
|
|
242
242
|
document.defaultView?.dispatchEvent(new Event("nativeUIRefresh"));
|
|
@@ -265,7 +265,7 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
265
265
|
return (app.classList.contains("ios-theme-vertical-bars-left") ? "left" : "right") === nativeState.edge;
|
|
266
266
|
};
|
|
267
267
|
const applyMetrics = (metrics) => {
|
|
268
|
-
if (
|
|
268
|
+
if (ordinaryControls) nativeWebViewMetrics.radius = metrics.radius;
|
|
269
269
|
deviceEdge = metrics.verticalBarEdge ?? null;
|
|
270
270
|
updateNativeEdge();
|
|
271
271
|
document.defaultView?.dispatchEvent(new Event("nativeUIRefresh"));
|
|
@@ -276,9 +276,9 @@ const enableNativeUI = (options = {}, overlayFactory) => {
|
|
|
276
276
|
...options,
|
|
277
277
|
extensions,
|
|
278
278
|
unavailableExtensions
|
|
279
|
-
}, nativeVerticalBars,
|
|
279
|
+
}, nativeVerticalBars, runtimeStopped);
|
|
280
280
|
runtime = combine(native, webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0));
|
|
281
|
-
if (
|
|
281
|
+
if (options.ionic?.verticalBars?.popover === true && overlayFactory) overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);
|
|
282
282
|
return manage(runtime, {
|
|
283
283
|
suspend: async () => {
|
|
284
284
|
const restorePrehide = prehide?.suspend();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activation.js","names":[],"sources":["../../src/native/activation.ts"],"sourcesContent":["import { toolbarOptions } from './shared/toolbar-options';\nimport { createDiagnostics } from './shared/diagnostics';\nimport { createHTMLWebProjection } from './html';\nimport { validateExtensions, type NativeUIExtension } from './extensions';\nimport { Capacitor, registerPlugin } from '@capacitor/core';\nimport { nativeWebViewMetrics } from '../native-integration';\nimport type { NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIPlugin, VerticalBarEdge, WebViewMetrics } from './definitions';\nimport { createRuntime } from './runtime';\nimport { bounded, boundedListener } from './shared/bridge';\ninterface OverlayController {\n suspend(): Promise<() => void>;\n destroy(): Promise<void>;\n}\ntype OverlayFactory = (\n doc: Document,\n plugin: NativeUIPlugin,\n options: NativeUIRuntimeOptions,\n retain: (deferActivation?: (id: string) => boolean) => () => void,\n nativeRail: () => boolean,\n) => Promise<OverlayController>;\nimport { createVerticalBarsWebProjection } from './vertical-bars-web';\nimport { prehideVerticalBarsToolbarSources } from './prehide';\nimport { observeVerticalBarsModals } from './shared/modal';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue } from './extensions';\nexport type {\n NativeUIComponent,\n NativeUIControls,\n NativeUIRuntimeHandle,\n NativeUIRuntimeOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n WebViewMetrics,\n} from './definitions';\n\nconst plugin = registerPlugin<NativeUIPlugin>('NativeUI');\nexport const NativeUI = plugin;\nlet active: Promise<NativeUIRuntimeHandle> | undefined;\nlet activeFailed = false;\nlet activeRoot: HTMLElement | undefined;\nlet activeRailHost: HTMLElement | undefined;\nlet activeConfiguration: string | undefined;\nlet activeExtensions: readonly NativeUIExtension[] = [];\nlet stopping: Promise<void> | undefined;\n\n// Every stop entry point releases the activation immediately but reserves the\n// shared native/DOM resources until its teardown has settled.\nconst stopActivation = (owner: Promise<NativeUIRuntimeHandle>, destruction: Promise<void>) => {\n if (active !== owner) return;\n active = undefined;\n activeFailed = false;\n activeConfiguration = undefined;\n activeRoot = undefined;\n activeRailHost = undefined;\n activeExtensions = [];\n stopping = destruction;\n const release = () => {\n if (stopping === destruction) stopping = undefined;\n };\n void destruction.then(release, release);\n};\nconst web = (reason: string): NativeUIRuntimeHandle => ({\n markForCheck: () => {},\n getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {},\n});\n\nconst combine = (native: NativeUIRuntimeHandle, fallback: NativeUIRuntimeHandle): NativeUIRuntimeHandle => ({\n markForCheck() {\n native.markForCheck();\n fallback.markForCheck();\n },\n getStatus() {\n const nativeStatus = native.getStatus();\n const fallbackStatus = fallback.getStatus();\n return {\n ...nativeStatus,\n state:\n nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped' ? 'stopped' : nativeStatus.projected > 0 ? 'native' : 'web',\n projected: nativeStatus.projected + fallbackStatus.projected,\n updates: nativeStatus.updates + fallbackStatus.updates,\n };\n },\n async suspend() {\n const [nativeLease, fallbackLease] = await Promise.all([native.suspend(), fallback.suspend()]);\n return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };\n },\n async destroy() {\n await Promise.all([native.destroy(), fallback.destroy()]);\n },\n});\n\n/** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */\nconst manage = (\n handle: NativeUIRuntimeHandle,\n lifecycle: {\n reason?: string;\n suspend?: () => (() => void) | undefined | Promise<(() => void) | undefined>;\n /** Registers teardown before it starts so direct destroy and disable share the same queue. */\n stopping?: (destruction: Promise<void>) => void;\n destroy?: () => void | Promise<void>;\n } = {},\n): NativeUIRuntimeHandle => {\n let destruction: Promise<void> | undefined;\n return {\n markForCheck: () => {\n if (!destruction) handle.markForCheck();\n },\n getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),\n async suspend() {\n const lease = await handle.suspend();\n const resume = await lifecycle.suspend?.();\n return {\n async resume() {\n await lease.resume();\n resume?.();\n },\n };\n },\n destroy() {\n if (!destruction) {\n destruction = Promise.resolve().then(async () => {\n try {\n await handle.destroy();\n } finally {\n await lifecycle.destroy?.();\n }\n });\n lifecycle.stopping?.(destruction);\n }\n return destruction;\n },\n };\n};\n\n/** Reads the current native WebView geometry and applies it to page transitions. */\nexport const configureNativeTransition = async (): Promise<WebViewMetrics> => {\n const metrics =\n typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' && Capacitor.isPluginAvailable('NativeUI')\n ? await bounded(plugin.getWebViewMetrics())\n : { radius: 0 };\n nativeWebViewMetrics.radius = metrics.radius;\n return metrics;\n};\n\n/** Resolves a logical vertical-bar edge to the physical side for the given direction. */\nconst physicalVerticalBarEdge = (edge: Exclude<VerticalBarEdge, null>, rtl: boolean): 'left' | 'right' =>\n (edge === 'leading') !== rtl ? 'left' : 'right';\n\nconst elementRtl = (element: Element): boolean => element.closest('[dir]')?.getAttribute('dir') === 'rtl';\n\nconst webProjection = (doc: Document, options: NativeUIRuntimeOptions, enabled: () => boolean = () => true) => {\n const ionic = () => createVerticalBarsWebProjection(doc, options, () => options.verticalBars?.enabled !== false && enabled());\n if (options.source === 'html') return createHTMLWebProjection(doc, options);\n if (options.source === 'auto') return combine(ionic(), createHTMLWebProjection(doc, options));\n return ionic();\n};\n\n/** Call once at application startup. Ionic markup remains the source of truth. */\nexport const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFactory?: OverlayFactory): Promise<NativeUIRuntimeHandle> => {\n if (options.enabled === false) {\n const current = active;\n if (current)\n stopActivation(\n current,\n current.then((handle) => handle.destroy()),\n );\n return (stopping ?? Promise.resolve()).then(() => web('Disabled'));\n }\n if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));\n const extensions = [...(options.extensions ?? [])];\n const nativeState: NonNullable<NativeUIRuntimeOptions['nativeState']> = (options.nativeState = {});\n try {\n validateExtensions(extensions, document);\n } catch (error) {\n return Promise.reject(error);\n }\n const controls = options.controls;\n const configuration = JSON.stringify([\n options.source ?? 'ionic',\n options.debug === true,\n options.explicitControls !== false,\n options.verticalBars?.enabled !== false,\n options.verticalBars?.placement?.edge,\n options.rail?.edge,\n options.verticalBarsOnly === true,\n toolbarOptions(options, false),\n toolbarOptions(options, true),\n options.buttonProjection ?? 'system',\n // Local source overrides can use this even when the startup projection is system.\n options.buttonDefaultFill ?? null,\n ...(['tabs', 'toolbar', 'segment', 'fab'] as const).map((component) => !controls || controls[component] === true),\n controls?.popover === true,\n ]);\n if (active && activeFailed) {\n const previous = active;\n stopActivation(\n previous,\n previous.then((handle) => handle.destroy()),\n );\n }\n if (\n active &&\n (activeConfiguration !== configuration ||\n activeRoot !== options.root ||\n activeRailHost !== options.rail?.host ||\n extensions.length !== activeExtensions.length ||\n extensions.some((extension, index) => extension !== activeExtensions[index]))\n )\n return Promise.reject(\n new Error('Native UI is already running with different controls or extensions; destroy it before changing configuration.'),\n );\n if (active) return active;\n activeConfiguration = configuration;\n activeRoot = options.root;\n activeRailHost = options.rail?.host;\n activeExtensions = extensions;\n const previousStop = stopping;\n let start: Promise<NativeUIRuntimeHandle>;\n const stop = (destruction: Promise<void>) => stopActivation(start, destruction);\n let releaseNative = async () => {};\n let retired = false;\n const pagehide = () => runtimeStopped('pagehide');\n const runtimeStopped = (cause: 'failure' | 'pagehide') => {\n if (cause === 'failure') {\n // Keep the Web rail usable, but replace this degraded activation on the next enable.\n if (active === start) activeFailed = true;\n void start\n .then(async () => {\n if (!retired) document.defaultView?.addEventListener('pagehide', pagehide, { once: true });\n await releaseNative();\n })\n .catch(() => {});\n return;\n }\n const destruction = start.then((handle) => handle.destroy());\n stop(destruction);\n void destruction.catch(() => {});\n };\n return (start = active =\n (async () => {\n // Native controls and DOM ownership are shared. A replacement activation\n // must wait until the old handle has finished clearing both surfaces.\n if (previousStop) await previousStop.catch(() => {});\n const platform = Capacitor.getPlatform();\n const diagnose = createDiagnostics(options.debug);\n const stopModals = options.source === 'html' ? () => {} : observeVerticalBarsModals(document);\n const prehide =\n platform !== 'android' && options.source !== 'html' && (options.controls === undefined || options.controls.toolbar === true)\n ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)\n : undefined;\n const fallback = (reason: string) => {\n diagnose(`web-fallback: ${reason}`);\n return manage(webProjection(document, options), {\n reason,\n suspend: () => prehide?.suspend(),\n destroy: () => {\n retired = true;\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n },\n stopping: stop,\n });\n };\n if (platform !== 'ios' && platform !== 'android') return fallback('Requires Capacitor iOS or Android');\n if (!Capacitor.isPluginAvailable('NativeUI')) return fallback('Native UI plugin is not registered');\n let runtime: NativeUIRuntimeHandle | undefined;\n let overlays: OverlayController | undefined;\n let metricsListener: Awaited<ReturnType<typeof plugin.addListener>> | undefined;\n let nativeRelease: Promise<void> | undefined;\n releaseNative = () =>\n (nativeRelease ??= (async () => {\n try {\n await overlays?.destroy();\n } finally {\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n }\n })());\n const cleanup = async () => {\n retired = true;\n try {\n await releaseNative();\n } finally {\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n }\n };\n try {\n const capabilities = await bounded(plugin.configure({ verticalBarsOnly: options.verticalBarsOnly === true }));\n if (!capabilities.supported) return fallback('Native UI is unavailable on this device');\n const unavailableExtensions = new Map<string, string>();\n for (const extension of extensions) {\n if (platform === 'android' && extension.placement === 'tab-accessory')\n unavailableExtensions.set(extension.name, 'unsupported-placement: tab-accessory requires a supported iOS tab host.');\n else if (!capabilities.extensions?.includes(extension.name))\n unavailableExtensions.set(\n extension.name,\n 'unregistered-extension: register this name in the native app before enabling the shell, then rebuild.',\n );\n }\n if (platform === 'android') {\n runtime = await createRuntime(\n document,\n plugin,\n {\n ...options,\n source: 'extensions',\n extensions,\n unavailableExtensions,\n },\n () => false,\n options.verticalBarsOnly === true,\n runtimeStopped,\n );\n runtime = combine(runtime, webProjection(document, options));\n metricsListener = await boundedListener(\n plugin.addListener('webViewMetricsChange', () => {\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n }),\n );\n return manage(runtime, {\n stopping: stop,\n destroy: cleanup,\n });\n }\n let deviceEdge: VerticalBarEdge = null;\n const ionicRoot = () => {\n if (options.source === 'html') return null;\n const root = options.root ?? document.body;\n return root.closest<HTMLElement>('ion-app') ?? root.querySelector<HTMLElement>('ion-app');\n };\n const updateNativeEdge = () => {\n const requested = options.verticalBars?.placement?.edge;\n const root = ionicRoot() ?? options.root ?? document.body;\n nativeState.edge =\n deviceEdge && (!requested || requested === deviceEdge) ? physicalVerticalBarEdge(deviceEdge, elementRtl(root)) : undefined;\n };\n // Ionic layout is read-only input; device metrics determine native availability.\n const nativeVerticalBars = () => {\n updateNativeEdge();\n if (options.verticalBars?.enabled === false) return false;\n const app = ionicRoot();\n if (!app?.classList.contains('ios-theme-vertical-bars')) return !!nativeState.edge;\n const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';\n return physicalEdge === nativeState.edge;\n };\n const applyMetrics = (metrics: WebViewMetrics) => {\n if (!options.verticalBarsOnly) nativeWebViewMetrics.radius = metrics.radius;\n deviceEdge = metrics.verticalBarEdge ?? null;\n updateNativeEdge();\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n };\n metricsListener = await boundedListener(plugin.addListener('webViewMetricsChange', applyMetrics));\n await bounded(Promise.resolve().then(() => plugin.getWebViewMetrics()))\n .then(applyMetrics)\n .catch(() => undefined);\n const native = await createRuntime(\n document,\n plugin,\n { ...options, extensions, unavailableExtensions },\n nativeVerticalBars,\n options.verticalBarsOnly === true,\n runtimeStopped,\n );\n runtime = combine(\n native,\n webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),\n );\n if (controls?.popover === true && overlayFactory)\n overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);\n return manage(runtime, {\n suspend: async () => {\n const restorePrehide = prehide?.suspend();\n const resumeOverlays = await overlays?.suspend();\n return () => {\n restorePrehide?.();\n resumeOverlays?.();\n };\n },\n stopping: stop,\n destroy: cleanup,\n });\n } catch (error) {\n await overlays?.destroy().catch(() => {});\n await runtime?.destroy().catch(() => {});\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n return fallback(error instanceof Error ? error.message : String(error));\n }\n })());\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"activation.js","names":[],"sources":["../../src/native/activation.ts"],"sourcesContent":["import { controlDefault } from './shared/control-options';\nimport { toolbarOptions } from './shared/toolbar-options';\nimport { createDiagnostics } from './shared/diagnostics';\nimport { createHTMLWebProjection } from './html';\nimport { validateExtensions, type NativeUIExtension } from './extensions';\nimport { Capacitor, registerPlugin } from '@capacitor/core';\nimport { nativeWebViewMetrics } from '../native-integration';\nimport type { NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIPlugin, VerticalBarEdge, WebViewMetrics } from './definitions';\nimport { createRuntime } from './runtime';\nimport { bounded, boundedListener } from './shared/bridge';\ninterface OverlayController {\n suspend(): Promise<() => void>;\n destroy(): Promise<void>;\n}\ntype OverlayFactory = (\n doc: Document,\n plugin: NativeUIPlugin,\n options: NativeUIRuntimeOptions,\n retain: (deferActivation?: (id: string) => boolean) => () => void,\n nativeRail: () => boolean,\n) => Promise<OverlayController>;\nimport { createVerticalBarsWebProjection } from './vertical-bars-web';\nimport { prehideVerticalBarsToolbarSources } from './prehide';\nimport { observeVerticalBarsModals } from './shared/modal';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue } from './extensions';\nexport type {\n NativeUIComponent,\n NativeUIControls,\n NativeUIRuntimeHandle,\n NativeUIRuntimeOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n WebViewMetrics,\n} from './definitions';\n\nconst plugin = registerPlugin<NativeUIPlugin>('NativeUI');\nexport const NativeUI = plugin;\nlet active: Promise<NativeUIRuntimeHandle> | undefined;\nlet activeFailed = false;\nlet activeRoot: HTMLElement | undefined;\nlet activeRailHost: HTMLElement | undefined;\nlet activeConfiguration: string | undefined;\nlet activeExtensions: readonly NativeUIExtension[] = [];\nlet stopping: Promise<void> | undefined;\n\n// Every stop entry point releases the activation immediately but reserves the\n// shared native/DOM resources until its teardown has settled.\nconst stopActivation = (owner: Promise<NativeUIRuntimeHandle>, destruction: Promise<void>) => {\n if (active !== owner) return;\n active = undefined;\n activeFailed = false;\n activeConfiguration = undefined;\n activeRoot = undefined;\n activeRailHost = undefined;\n activeExtensions = [];\n stopping = destruction;\n const release = () => {\n if (stopping === destruction) stopping = undefined;\n };\n void destruction.then(release, release);\n};\nconst web = (reason: string): NativeUIRuntimeHandle => ({\n markForCheck: () => {},\n getStatus: () => ({ state: 'web', projected: 0, updates: 0, reason }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {},\n});\n\nconst combine = (native: NativeUIRuntimeHandle, fallback: NativeUIRuntimeHandle): NativeUIRuntimeHandle => ({\n markForCheck() {\n native.markForCheck();\n fallback.markForCheck();\n },\n getStatus() {\n const nativeStatus = native.getStatus();\n const fallbackStatus = fallback.getStatus();\n return {\n ...nativeStatus,\n state:\n nativeStatus.state === 'stopped' && fallbackStatus.state === 'stopped' ? 'stopped' : nativeStatus.projected > 0 ? 'native' : 'web',\n projected: nativeStatus.projected + fallbackStatus.projected,\n updates: nativeStatus.updates + fallbackStatus.updates,\n };\n },\n async suspend() {\n const [nativeLease, fallbackLease] = await Promise.all([native.suspend(), fallback.suspend()]);\n return { resume: async () => void (await Promise.all([nativeLease.resume(), fallbackLease.resume()])) };\n },\n async destroy() {\n await Promise.all([native.destroy(), fallback.destroy()]);\n },\n});\n\n/** Wraps a runtime handle with the shared enable-lifecycle: reason, suspend hooks and idempotent destroy. */\nconst manage = (\n handle: NativeUIRuntimeHandle,\n lifecycle: {\n reason?: string;\n suspend?: () => (() => void) | undefined | Promise<(() => void) | undefined>;\n /** Registers teardown before it starts so direct destroy and disable share the same queue. */\n stopping?: (destruction: Promise<void>) => void;\n destroy?: () => void | Promise<void>;\n } = {},\n): NativeUIRuntimeHandle => {\n let destruction: Promise<void> | undefined;\n return {\n markForCheck: () => {\n if (!destruction) handle.markForCheck();\n },\n getStatus: () => (lifecycle.reason ? { ...handle.getStatus(), reason: lifecycle.reason } : handle.getStatus()),\n async suspend() {\n const lease = await handle.suspend();\n const resume = await lifecycle.suspend?.();\n return {\n async resume() {\n await lease.resume();\n resume?.();\n },\n };\n },\n destroy() {\n if (!destruction) {\n destruction = Promise.resolve().then(async () => {\n try {\n await handle.destroy();\n } finally {\n await lifecycle.destroy?.();\n }\n });\n lifecycle.stopping?.(destruction);\n }\n return destruction;\n },\n };\n};\n\n/** Reads the current native WebView geometry and applies it to page transitions. */\nexport const configureNativeTransition = async (): Promise<WebViewMetrics> => {\n const metrics =\n typeof document !== 'undefined' && Capacitor.getPlatform() === 'ios' && Capacitor.isPluginAvailable('NativeUI')\n ? await bounded(plugin.getWebViewMetrics())\n : { radius: 0 };\n nativeWebViewMetrics.radius = metrics.radius;\n return metrics;\n};\n\n/** Resolves a logical vertical-bar edge to the physical side for the given direction. */\nconst physicalVerticalBarEdge = (edge: Exclude<VerticalBarEdge, null>, rtl: boolean): 'left' | 'right' =>\n (edge === 'leading') !== rtl ? 'left' : 'right';\n\nconst elementRtl = (element: Element): boolean => element.closest('[dir]')?.getAttribute('dir') === 'rtl';\n\nconst webProjection = (doc: Document, options: NativeUIRuntimeOptions, enabled: () => boolean = () => true) => {\n const ionic = () => createVerticalBarsWebProjection(doc, options, () => options.verticalBars?.enabled !== false && enabled());\n if (options.source === 'html') return createHTMLWebProjection(doc, options);\n if (options.source === 'auto') return combine(ionic(), createHTMLWebProjection(doc, options));\n return ionic();\n};\n\n/** Call once at application startup. Ionic markup remains the source of truth. */\nexport const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFactory?: OverlayFactory): Promise<NativeUIRuntimeHandle> => {\n if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));\n const extensions = [...(options.extensions ?? [])];\n const nativeState: NonNullable<NativeUIRuntimeOptions['nativeState']> = (options.nativeState = {});\n try {\n validateExtensions(extensions, document);\n } catch (error) {\n return Promise.reject(error);\n }\n const configuration = JSON.stringify([\n options.source ?? 'ionic',\n options.debug === true,\n options.ionic?.auto !== false,\n options.verticalBars?.enabled !== false,\n options.verticalBars?.placement?.edge,\n options.rail?.edge,\n toolbarOptions(options, false),\n toolbarOptions(options, true),\n options.ionic?.verticalBars?.buttonProjection ?? 'system',\n // Local source overrides can use this even when the startup projection is system.\n options.ionic?.verticalBars?.buttonDefaultFill ?? 'glass',\n ...(['tabs', 'segment', 'fab'] as const).flatMap((kind) => [controlDefault(options, kind, false), controlDefault(options, kind, true)]),\n options.ionic?.verticalBars?.popover === true,\n ]);\n if (active && activeFailed) {\n const previous = active;\n stopActivation(\n previous,\n previous.then((handle) => handle.destroy()),\n );\n }\n if (\n active &&\n (activeConfiguration !== configuration ||\n activeRoot !== options.root ||\n activeRailHost !== options.rail?.host ||\n extensions.length !== activeExtensions.length ||\n extensions.some((extension, index) => extension !== activeExtensions[index]))\n )\n return Promise.reject(\n new Error('Native UI is already running with different controls or extensions; destroy it before changing configuration.'),\n );\n if (active) return active;\n activeConfiguration = configuration;\n activeRoot = options.root;\n activeRailHost = options.rail?.host;\n activeExtensions = extensions;\n const previousStop = stopping;\n let start: Promise<NativeUIRuntimeHandle>;\n const stop = (destruction: Promise<void>) => stopActivation(start, destruction);\n let releaseNative = async () => {};\n let retired = false;\n const pagehide = () => runtimeStopped('pagehide');\n const runtimeStopped = (cause: 'failure' | 'pagehide') => {\n if (cause === 'failure') {\n // Keep the Web rail usable, but replace this degraded activation on the next enable.\n if (active === start) activeFailed = true;\n void start\n .then(async () => {\n if (!retired) document.defaultView?.addEventListener('pagehide', pagehide, { once: true });\n await releaseNative();\n })\n .catch(() => {});\n return;\n }\n const destruction = start.then((handle) => handle.destroy());\n stop(destruction);\n void destruction.catch(() => {});\n };\n return (start = active =\n (async () => {\n // Native controls and DOM ownership are shared. A replacement activation\n // must wait until the old handle has finished clearing both surfaces.\n if (previousStop) await previousStop.catch(() => {});\n const platform = Capacitor.getPlatform();\n const diagnose = createDiagnostics(options.debug);\n const stopModals = options.source === 'html' ? () => {} : observeVerticalBarsModals(document);\n const prehide =\n platform !== 'android' && options.source !== 'html' && options.ionic?.auto !== false\n ? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)\n : undefined;\n const fallback = (reason: string) => {\n diagnose(`web-fallback: ${reason}`);\n return manage(webProjection(document, options), {\n reason,\n suspend: () => prehide?.suspend(),\n destroy: () => {\n retired = true;\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n },\n stopping: stop,\n });\n };\n if (platform !== 'ios' && platform !== 'android') return fallback('Requires Capacitor iOS or Android');\n if (!Capacitor.isPluginAvailable('NativeUI')) return fallback('Native UI plugin is not registered');\n let runtime: NativeUIRuntimeHandle | undefined;\n let overlays: OverlayController | undefined;\n let metricsListener: Awaited<ReturnType<typeof plugin.addListener>> | undefined;\n let nativeRelease: Promise<void> | undefined;\n releaseNative = () =>\n (nativeRelease ??= (async () => {\n try {\n await overlays?.destroy();\n } finally {\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n }\n })());\n const cleanup = async () => {\n retired = true;\n try {\n await releaseNative();\n } finally {\n document.defaultView?.removeEventListener('pagehide', pagehide);\n prehide?.stop();\n stopModals();\n }\n };\n try {\n const ordinaryControls = (['button', 'backButton', 'tabs', 'segment', 'fab'] as const).some((kind) =>\n controlDefault(options, kind, false),\n );\n const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));\n if (!capabilities.supported) return fallback('Native UI is unavailable on this device');\n const unavailableExtensions = new Map<string, string>();\n for (const extension of extensions) {\n if (platform === 'android' && extension.placement === 'tab-accessory')\n unavailableExtensions.set(extension.name, 'unsupported-placement: tab-accessory requires a supported iOS tab host.');\n else if (!capabilities.extensions?.includes(extension.name))\n unavailableExtensions.set(\n extension.name,\n 'unregistered-extension: register this name in the native app before enabling the shell, then rebuild.',\n );\n }\n if (platform === 'android') {\n runtime = await createRuntime(\n document,\n plugin,\n {\n ...options,\n source: 'extensions',\n extensions,\n unavailableExtensions,\n },\n () => false,\n runtimeStopped,\n );\n runtime = combine(runtime, webProjection(document, options));\n metricsListener = await boundedListener(\n plugin.addListener('webViewMetricsChange', () => {\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n }),\n );\n return manage(runtime, {\n stopping: stop,\n destroy: cleanup,\n });\n }\n let deviceEdge: VerticalBarEdge = null;\n const ionicRoot = () => {\n if (options.source === 'html') return null;\n const root = options.root ?? document.body;\n return root.closest<HTMLElement>('ion-app') ?? root.querySelector<HTMLElement>('ion-app');\n };\n const updateNativeEdge = () => {\n const requested = options.verticalBars?.placement?.edge;\n const root = ionicRoot() ?? options.root ?? document.body;\n nativeState.edge =\n deviceEdge && (!requested || requested === deviceEdge) ? physicalVerticalBarEdge(deviceEdge, elementRtl(root)) : undefined;\n };\n // Ionic layout is read-only input; device metrics determine native availability.\n const nativeVerticalBars = () => {\n updateNativeEdge();\n if (options.verticalBars?.enabled === false) return false;\n const app = ionicRoot();\n if (!app?.classList.contains('ios-theme-vertical-bars')) return !!nativeState.edge;\n const physicalEdge = app.classList.contains('ios-theme-vertical-bars-left') ? 'left' : 'right';\n return physicalEdge === nativeState.edge;\n };\n const applyMetrics = (metrics: WebViewMetrics) => {\n if (ordinaryControls) nativeWebViewMetrics.radius = metrics.radius;\n deviceEdge = metrics.verticalBarEdge ?? null;\n updateNativeEdge();\n document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));\n };\n metricsListener = await boundedListener(plugin.addListener('webViewMetricsChange', applyMetrics));\n await bounded(Promise.resolve().then(() => plugin.getWebViewMetrics()))\n .then(applyMetrics)\n .catch(() => undefined);\n const native = await createRuntime(\n document,\n plugin,\n { ...options, extensions, unavailableExtensions },\n nativeVerticalBars,\n runtimeStopped,\n );\n runtime = combine(\n native,\n webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),\n );\n if (options.ionic?.verticalBars?.popover === true && overlayFactory)\n overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);\n return manage(runtime, {\n suspend: async () => {\n const restorePrehide = prehide?.suspend();\n const resumeOverlays = await overlays?.suspend();\n return () => {\n restorePrehide?.();\n resumeOverlays?.();\n };\n },\n stopping: stop,\n destroy: cleanup,\n });\n } catch (error) {\n await overlays?.destroy().catch(() => {});\n await runtime?.destroy().catch(() => {});\n if (metricsListener) await bounded(metricsListener.remove()).catch(() => {});\n return fallback(error instanceof Error ? error.message : String(error));\n }\n })());\n};\n"],"mappings":"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"}
|
|
@@ -8,31 +8,34 @@ export interface NativeUIStatus {
|
|
|
8
8
|
updates: number;
|
|
9
9
|
reason?: string;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export type ControlProjection = boolean | 'horizontalOnly' | 'verticalOnly';
|
|
12
|
+
export interface NativeUIControls {
|
|
13
|
+
buttonGroup?: ControlProjection;
|
|
14
|
+
button?: ControlProjection;
|
|
15
|
+
backButton?: ControlProjection;
|
|
16
|
+
tabs?: ControlProjection;
|
|
17
|
+
segment?: ControlProjection;
|
|
18
|
+
fab?: ControlProjection;
|
|
15
19
|
}
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export interface IonicProjectionOptions {
|
|
21
|
+
auto?: boolean;
|
|
22
|
+
verticalBars?: {
|
|
23
|
+
buttonProjection?: 'system' | 'source';
|
|
24
|
+
buttonDefaultFill?: 'glass' | 'solid';
|
|
25
|
+
popover?: boolean;
|
|
26
|
+
};
|
|
23
27
|
}
|
|
24
|
-
export interface NativeUIOptions
|
|
25
|
-
|
|
28
|
+
export interface NativeUIOptions {
|
|
29
|
+
root?: HTMLElement;
|
|
30
|
+
controls?: ControlProjection | NativeUIControls;
|
|
26
31
|
verticalBars?: NativeUIVerticalBarsOptions;
|
|
32
|
+
extensions?: readonly NativeUIExtension[];
|
|
27
33
|
ionic?: IonicProjectionOptions;
|
|
28
|
-
|
|
29
|
-
enabled?: boolean;
|
|
30
|
-
controls?: NativeUIControls;
|
|
31
|
-
verticalBarsOnly?: boolean;
|
|
34
|
+
debug?: boolean;
|
|
32
35
|
}
|
|
36
|
+
export type VerticalControlAreaOptions = NativeUIOptions;
|
|
33
37
|
export interface NativeUIRuntimeOptions extends NativeUIOptions {
|
|
34
38
|
source?: 'ionic' | 'html' | 'auto' | 'extensions';
|
|
35
|
-
explicitControls?: boolean;
|
|
36
39
|
nativeState?: {
|
|
37
40
|
edge?: 'left' | 'right';
|
|
38
41
|
};
|
|
@@ -42,33 +45,12 @@ export interface NativeUIRuntimeOptions extends NativeUIOptions {
|
|
|
42
45
|
edge: 'left' | 'right';
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
|
-
export interface IonicProjectionOptions {
|
|
46
|
-
auto?: boolean | AutoIonicControls;
|
|
47
|
-
}
|
|
48
|
-
export interface AutoIonicControls {
|
|
49
|
-
buttons?: boolean;
|
|
50
|
-
tabs?: boolean;
|
|
51
|
-
segment?: boolean;
|
|
52
|
-
fab?: boolean;
|
|
53
|
-
}
|
|
54
|
-
export interface NativeUIControls {
|
|
55
|
-
buttons?: boolean;
|
|
56
|
-
tabs?: boolean;
|
|
57
|
-
toolbar?: boolean;
|
|
58
|
-
segment?: boolean;
|
|
59
|
-
fab?: boolean;
|
|
60
|
-
popover?: boolean;
|
|
61
|
-
}
|
|
62
48
|
export interface NativeUIHandle extends NativeUIRuntimeHandle {
|
|
63
49
|
enableVerticalControlArea(): Promise<void>;
|
|
64
50
|
disableVerticalControlArea(): Promise<void>;
|
|
65
51
|
}
|
|
66
52
|
export interface NativeUIVerticalBarsOptions {
|
|
67
53
|
enabled?: boolean;
|
|
68
|
-
only?: boolean;
|
|
69
|
-
buttonProjection?: 'system' | 'source';
|
|
70
|
-
buttonDefaultFill?: 'glass' | 'solid';
|
|
71
|
-
popover?: boolean;
|
|
72
54
|
placement?: {
|
|
73
55
|
edge: Exclude<VerticalBarEdge, null>;
|
|
74
56
|
webHost?: HTMLElement;
|
|
@@ -192,7 +174,7 @@ export interface WebViewMetrics {
|
|
|
192
174
|
}
|
|
193
175
|
export interface NativeUIPlugin {
|
|
194
176
|
configure(options?: {
|
|
195
|
-
|
|
177
|
+
suppressScrollEdgeEffect?: boolean;
|
|
196
178
|
}): Promise<{
|
|
197
179
|
supported: boolean;
|
|
198
180
|
extensions?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","names":[],"sources":["../../src/native/definitions.ts"],"mappings":";;;YAGY;iBASK;EACf;EACA;EACA;EACA;;
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","names":[],"sources":["../../src/native/definitions.ts"],"mappings":";;;YAGY;iBASK;EACf;EACA;EACA;EACA;;YAMU;iBAEK;EAEf,cAAc;EAEd,SAAS;EACT,aAAa;EACb,OAAO;EACP,UAAU;EACV,MAAM;;iBAGS;EAEf;EACA;IAEE;IAEA;IAEA;;;iBAIa;EAEf,OAAO;EAEP,WAAW,oBAAoB;EAC/B,eAAe;EAIf,sBAAsB;EACtB,QAAQ;EAER;;YAIU,6BAA6B;iBAGxB,+BAA+B;EAC9C;EACA;IAAgB;;EAChB,wBAAwB;EACxB;IAAS,MAAM;IAAa;;;iBAIb,uBAAuB;EAEtC,6BAA6B;EAE7B,8BAA8B;;iBAGf;EAEf;EAEA;IAAc,MAAM,QAAQ;IAAwB,UAAU;;;iBAG/C;EAEf;EAEA,aAAa;EAEb,WAAW,QAAQ;EAEnB,WAAW;;YAID;iBAEK;EACf,MAAM;EAEN;EAIA,aAAa;;iBAGE,kCAAkC;EAIjD,aAAa,WAAW,kBAAkB,sBAAsB;;iBAGjD;EAEf,UAAU;;iBAGK;EACf;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;;iBAGe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAIU;iBASK,wBAAwB;EACvC;EACA,MAAM;EACN;IAAc;IAAc;IAAe,SAAS;IAAwB;;EAE5E;EAEA;EACA,OAAO;EACP;EACA;EACA;IAAiB;IAAgB;;EACjC,SAAS;;iBAGM;EACf;EACA,OAAO;EACP,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe,4BAA4B;EAC3C;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EAEA;EAEA,mBAAmB;EACnB,UAAU;;iBAGK;EACf;EACA;EACA;EAEA;;iBAGe;EAEf;EAEA,kBAAkB;;iBAGH;EACf,UAAU;IAAY;MAAuC;IAAU;IAAoB;;EAC3F,wBAAwB;IAAW;IAAmB;MAAsB;EAC5E,qBAAqB,QAAQ;EAC7B,wBAAwB;IAAW;IAAuB;MAAkB;IAAU;IAAmB,OAAO,eAAe;;EAC/H,eAAe;IAAW;MAAe;EACzC,eAAe;IAAW;IAAY,eAAe;MAAgC;EACrF,eAAe;IAAW;IAAY;MAAsB;EAC5D,aAAa;IAAW;MAAe;EACvC,gBAAgB;EAChB,OAAO,UAAU,mBAAmB;IAAU;IAAkB;IAA6B;;EAC7F,MAAM;IAAW;MAAqB;EACtC,YAAY,kBAAkB,WAAW,OAAO,8BAA8B,QAAQ;EACtF,YAAY,gBAAgB,WAAW,OAAO,+BAA+B,QAAQ;EACrF,YAAY,8BAA8B,WAAW,OAAO,0BAA0B,QAAQ;EAC9F,YAAY,iBAAiB,WAAW,OAAO,gCAAgC,QAAQ;;iBAGxE;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","names":[],"sources":["../../src/native/html.ts"],"mappings":";;;qBAMa;qBACA,iBAAkB,SAAS;qBAG3B,kBAAmB,SAAS,aAAa,IAAI,UAAU,SAAS,2BAAyB;
|
|
1
|
+
{"version":3,"file":"html.d.ts","names":[],"sources":["../../src/native/html.ts"],"mappings":";;;qBAMa;qBACA,iBAAkB,SAAS;qBAG3B,kBAAmB,SAAS,aAAa,IAAI,UAAU,SAAS,2BAAyB;qBA+BzF,0BAA2B,KAAK,UAAU,SAAS,2BAAyB"}
|
package/dist/native/html.js
CHANGED
|
@@ -10,7 +10,6 @@ const readHTMLControl = (element, id, options) => {
|
|
|
10
10
|
if (!element.matches("button, ion-button, [role=\"button\"]") || !visible(element, htmlRailSource(element))) return;
|
|
11
11
|
if (element.contains(element.ownerDocument.activeElement)) return;
|
|
12
12
|
if (!htmlRailSource(element) && !fitsClip(element)) return;
|
|
13
|
-
if (options.explicitControls === false || options.explicitControls === void 0 && (options.controls?.toolbar === false || options.controls?.buttons === false)) return;
|
|
14
13
|
if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;
|
|
15
14
|
const kind = element.getAttribute("data-native-ui") === "back-button" ? "back-button" : "button";
|
|
16
15
|
const candidate = createCandidate(element, kind, id, options);
|
|
@@ -142,6 +141,16 @@ const createHTMLWebProjection = (doc, options) => {
|
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
for (const source of owned.keys()) if (!wanted.has(source)) restore(source);
|
|
144
|
+
const clones = Array.from(wanted, (source) => owned.get(source).clone);
|
|
145
|
+
const order = Array.from(host.children).filter((child) => clones.includes(child));
|
|
146
|
+
const focused = clones.find((clone) => clone === doc.activeElement);
|
|
147
|
+
clones.forEach((clone, index) => {
|
|
148
|
+
if (order[index] === clone) return;
|
|
149
|
+
host.insertBefore(clone, order[index]);
|
|
150
|
+
order.splice(order.indexOf(clone), 1);
|
|
151
|
+
order.splice(index, 0, clone);
|
|
152
|
+
});
|
|
153
|
+
if (focused && doc.activeElement !== focused) focused.focus({ preventScroll: true });
|
|
145
154
|
observer.takeRecords();
|
|
146
155
|
};
|
|
147
156
|
const schedule = () => {
|
package/dist/native/html.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.js","names":[],"sources":["../../src/native/html.ts"],"sourcesContent":["import { fitsClip } from './shared/clipping';\nimport type { NativeUIRuntimeOptions, NativeUIRuntimeHandle } from './definitions';\nimport { appendItem, createCandidate, type Candidate, type Identify } from './shared/candidate';\nimport { marker, unprojected, visible } from './shared/dom';\n\n/** Standard HTML sources use standard native renderers, not app-local Swift extensions. */\nexport const htmlSelector = '[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]';\nexport const htmlRailSource = (element: Element) =>\n element.matches(htmlSelector) && element.getAttribute('data-native-ui-placement') === 'rail';\n\nexport const readHTMLControl = (element: HTMLElement, id: Identify, options: NativeUIRuntimeOptions): Candidate | undefined => {\n if (\n !(options.root ?? element.ownerDocument.body).contains(element) ||\n !element.matches(htmlSelector) ||\n element.closest('[data-native-ui-proxy]')\n )\n return;\n // Explicit opt-in; custom elements can implement click() and expose role=button.\n if (!element.matches('button, ion-button, [role=\"button\"]') || !visible(element, htmlRailSource(element))) return;\n if (element.contains(element.ownerDocument.activeElement)) return;\n if (!htmlRailSource(element) && !fitsClip(element)) return;\n if (\n options.explicitControls === false ||\n (options.explicitControls === undefined && (options.controls?.toolbar === false || options.controls?.buttons === false))\n )\n return;\n if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;\n const kind = element.getAttribute('data-native-ui') === 'back-button' ? 'back-button' : 'button';\n const candidate = createCandidate(element, kind, id, options);\n const item = appendItem(candidate, element, id);\n if (!item) return;\n const labelledBy = element\n .getAttribute('aria-labelledby')\n ?.trim()\n .split(/\\s+/)\n .map((key) => element.ownerDocument.getElementById(key)?.textContent?.trim() ?? '')\n .filter(Boolean)\n .join(' ');\n item.accessibilityLabel = labelledBy || element.getAttribute('aria-label') || item.label;\n if (!item.accessibilityLabel) return;\n item.disabled ||= element.matches(':disabled, [disabled]') || !!element.closest('[aria-disabled=\"true\"]');\n if (htmlRailSource(element)) candidate.control.placement = 'vertical-bars';\n return candidate;\n};\n\n/** A rail host is supplied and styled by the application, never by the plugin. */\nexport const createHTMLWebProjection = (doc: Document, options: NativeUIRuntimeOptions): NativeUIRuntimeHandle => {\n const host = options.rail?.host;\n if (!host) {\n let stopped = false;\n return {\n markForCheck: () => {},\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: 0, updates: 0 }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {\n stopped = true;\n },\n };\n }\n const win = doc.defaultView!;\n const owned = new Map<HTMLElement, { clone: HTMLButtonElement; aria: string | null; signature: string }>();\n const style = doc.createElement('style');\n style.textContent = `[data-native-ui-web-source] { visibility: hidden !important; pointer-events: none !important; }`;\n doc.head.append(style);\n let frame = 0;\n let stopped = false;\n let suspended = 0;\n let updates = 0;\n const restore = (source: HTMLElement) => {\n const state = owned.get(source);\n if (!state) return;\n const focused = state.clone.contains(doc.activeElement);\n state.clone.remove();\n source.removeAttribute('data-native-ui-web-source');\n if (focused && source.isConnected && !source.hasAttribute(marker)) source.focus();\n owned.delete(source);\n };\n const measure = <T>(source: HTMLElement, read: () => T): T => {\n const hidden = source.hasAttribute('data-native-ui-web-source');\n if (hidden) source.removeAttribute('data-native-ui-web-source');\n try {\n return unprojected([source], read);\n } finally {\n if (hidden) source.setAttribute('data-native-ui-web-source', '');\n }\n };\n const render = () => {\n frame = 0;\n if (stopped) return;\n observer.disconnect();\n observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });\n const wanted = new Set<HTMLElement>();\n if (!suspended && options.verticalBars?.enabled !== false && host?.isConnected && options.root?.isConnected) {\n const sources = Array.from(options.root.querySelectorAll<HTMLElement>(htmlSelector));\n let back = false;\n for (const source of sources) {\n // An accepted native surface owns its source and descendants. Rejected sources\n // lose the marker and can return to the Web rail on the next render.\n if (!htmlRailSource(source) || host.contains(source) || source.closest(`[${marker}]`)) continue;\n const iconRoot = source.querySelector('ion-icon')?.shadowRoot;\n if (iconRoot) observer.observe(iconRoot, { subtree: true, childList: true, attributes: true, characterData: true });\n const candidate = measure(source, () => readHTMLControl(source, () => 'web', options));\n if (!candidate) continue;\n if (candidate.control.kind === 'back-button') {\n if (back) continue;\n back = true;\n }\n wanted.add(source);\n const item = candidate.control.items[0];\n const svg = source.querySelector('svg') ?? iconRoot?.querySelector('svg');\n const signature = JSON.stringify([item, source.innerHTML, svg?.outerHTML]);\n let state = owned.get(source);\n if (!state) {\n const clone = doc.createElement('button');\n clone.type = 'button';\n clone.setAttribute('data-native-ui-proxy', candidate.control.kind);\n state = { clone, aria: source.getAttribute('aria-hidden'), signature: '' };\n owned.set(source, state);\n clone.addEventListener('click', () => {\n const current = !stopped && !suspended && measure(source, () => readHTMLControl(source, () => 'web', options));\n if (\n options.verticalBars?.enabled === false ||\n !current ||\n !htmlRailSource(source) ||\n !host.isConnected ||\n current.control.items[0].disabled ||\n source.closest(`[${marker}]`)\n )\n return;\n source.click(); // Source owns submission, custom handlers and back navigation.\n schedule();\n });\n host.append(clone);\n }\n if (state.signature !== signature) {\n state.signature = signature;\n state.clone.replaceChildren();\n if (svg) {\n const icon = svg.cloneNode(true) as SVGElement;\n icon.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'));\n icon.removeAttribute('id');\n for (const node of [icon, ...Array.from(icon.querySelectorAll('*'))])\n for (const attr of Array.from(node.attributes)) if (attr.name.startsWith('on')) node.removeAttribute(attr.name);\n state.clone.append(icon);\n }\n state.clone.append(doc.createTextNode(item.label));\n state.clone.setAttribute('aria-label', item.accessibilityLabel);\n state.clone.disabled = item.disabled;\n updates++;\n }\n if (!source.hasAttribute('data-native-ui-web-source')) source.setAttribute('data-native-ui-web-source', '');\n }\n }\n for (const source of owned.keys()) if (!wanted.has(source)) restore(source);\n // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh.\n observer.takeRecords();\n };\n const schedule = () => {\n if (!stopped && !frame) frame = win.requestAnimationFrame(render);\n };\n const observer = new MutationObserver(schedule);\n win.addEventListener('resize', schedule);\n win.addEventListener('nativeUIRefresh', schedule);\n render();\n return {\n markForCheck: schedule,\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: owned.size, updates }),\n async suspend() {\n suspended++;\n for (const source of owned.keys()) restore(source);\n let resumed = false;\n return {\n async resume() {\n if (!resumed) {\n resumed = true;\n suspended--;\n render();\n }\n },\n };\n },\n async destroy() {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n win.cancelAnimationFrame(frame);\n win.removeEventListener('resize', schedule);\n win.removeEventListener('nativeUIRefresh', schedule);\n for (const source of owned.keys()) restore(source);\n style.remove();\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"html.js","names":[],"sources":["../../src/native/html.ts"],"sourcesContent":["import { fitsClip } from './shared/clipping';\nimport type { NativeUIRuntimeOptions, NativeUIRuntimeHandle } from './definitions';\nimport { appendItem, createCandidate, type Candidate, type Identify } from './shared/candidate';\nimport { marker, unprojected, visible } from './shared/dom';\n\n/** Standard HTML sources use standard native renderers, not app-local Swift extensions. */\nexport const htmlSelector = '[data-native-ui=\"button\"], [data-native-ui=\"back-button\"]';\nexport const htmlRailSource = (element: Element) =>\n element.matches(htmlSelector) && element.getAttribute('data-native-ui-placement') === 'rail';\n\nexport const readHTMLControl = (element: HTMLElement, id: Identify, options: NativeUIRuntimeOptions): Candidate | undefined => {\n if (\n !(options.root ?? element.ownerDocument.body).contains(element) ||\n !element.matches(htmlSelector) ||\n element.closest('[data-native-ui-proxy]')\n )\n return;\n // Explicit opt-in; custom elements can implement click() and expose role=button.\n if (!element.matches('button, ion-button, [role=\"button\"]') || !visible(element, htmlRailSource(element))) return;\n if (element.contains(element.ownerDocument.activeElement)) return;\n if (!htmlRailSource(element) && !fitsClip(element)) return;\n if (htmlRailSource(element) && options.verticalBars?.enabled === false) return;\n const kind = element.getAttribute('data-native-ui') === 'back-button' ? 'back-button' : 'button';\n const candidate = createCandidate(element, kind, id, options);\n const item = appendItem(candidate, element, id);\n if (!item) return;\n const labelledBy = element\n .getAttribute('aria-labelledby')\n ?.trim()\n .split(/\\s+/)\n .map((key) => element.ownerDocument.getElementById(key)?.textContent?.trim() ?? '')\n .filter(Boolean)\n .join(' ');\n item.accessibilityLabel = labelledBy || element.getAttribute('aria-label') || item.label;\n if (!item.accessibilityLabel) return;\n item.disabled ||= element.matches(':disabled, [disabled]') || !!element.closest('[aria-disabled=\"true\"]');\n if (htmlRailSource(element)) candidate.control.placement = 'vertical-bars';\n return candidate;\n};\n\n/** A rail host is supplied and styled by the application, never by the plugin. */\nexport const createHTMLWebProjection = (doc: Document, options: NativeUIRuntimeOptions): NativeUIRuntimeHandle => {\n const host = options.rail?.host;\n if (!host) {\n let stopped = false;\n return {\n markForCheck: () => {},\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: 0, updates: 0 }),\n suspend: async () => ({ resume: async () => {} }),\n destroy: async () => {\n stopped = true;\n },\n };\n }\n const win = doc.defaultView!;\n const owned = new Map<HTMLElement, { clone: HTMLButtonElement; aria: string | null; signature: string }>();\n const style = doc.createElement('style');\n style.textContent = `[data-native-ui-web-source] { visibility: hidden !important; pointer-events: none !important; }`;\n doc.head.append(style);\n let frame = 0;\n let stopped = false;\n let suspended = 0;\n let updates = 0;\n const restore = (source: HTMLElement) => {\n const state = owned.get(source);\n if (!state) return;\n const focused = state.clone.contains(doc.activeElement);\n state.clone.remove();\n source.removeAttribute('data-native-ui-web-source');\n if (focused && source.isConnected && !source.hasAttribute(marker)) source.focus();\n owned.delete(source);\n };\n const measure = <T>(source: HTMLElement, read: () => T): T => {\n const hidden = source.hasAttribute('data-native-ui-web-source');\n if (hidden) source.removeAttribute('data-native-ui-web-source');\n try {\n return unprojected([source], read);\n } finally {\n if (hidden) source.setAttribute('data-native-ui-web-source', '');\n }\n };\n const render = () => {\n frame = 0;\n if (stopped) return;\n observer.disconnect();\n observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });\n const wanted = new Set<HTMLElement>();\n if (!suspended && options.verticalBars?.enabled !== false && host?.isConnected && options.root?.isConnected) {\n const sources = Array.from(options.root.querySelectorAll<HTMLElement>(htmlSelector));\n let back = false;\n for (const source of sources) {\n // An accepted native surface owns its source and descendants. Rejected sources\n // lose the marker and can return to the Web rail on the next render.\n if (!htmlRailSource(source) || host.contains(source) || source.closest(`[${marker}]`)) continue;\n const iconRoot = source.querySelector('ion-icon')?.shadowRoot;\n if (iconRoot) observer.observe(iconRoot, { subtree: true, childList: true, attributes: true, characterData: true });\n const candidate = measure(source, () => readHTMLControl(source, () => 'web', options));\n if (!candidate) continue;\n if (candidate.control.kind === 'back-button') {\n if (back) continue;\n back = true;\n }\n wanted.add(source);\n const item = candidate.control.items[0];\n const svg = source.querySelector('svg') ?? iconRoot?.querySelector('svg');\n const signature = JSON.stringify([item, source.innerHTML, svg?.outerHTML]);\n let state = owned.get(source);\n if (!state) {\n const clone = doc.createElement('button');\n clone.type = 'button';\n clone.setAttribute('data-native-ui-proxy', candidate.control.kind);\n state = { clone, aria: source.getAttribute('aria-hidden'), signature: '' };\n owned.set(source, state);\n clone.addEventListener('click', () => {\n const current = !stopped && !suspended && measure(source, () => readHTMLControl(source, () => 'web', options));\n if (\n options.verticalBars?.enabled === false ||\n !current ||\n !htmlRailSource(source) ||\n !host.isConnected ||\n current.control.items[0].disabled ||\n source.closest(`[${marker}]`)\n )\n return;\n source.click(); // Source owns submission, custom handlers and back navigation.\n schedule();\n });\n host.append(clone);\n }\n if (state.signature !== signature) {\n state.signature = signature;\n state.clone.replaceChildren();\n if (svg) {\n const icon = svg.cloneNode(true) as SVGElement;\n icon.querySelectorAll('[id]').forEach((node) => node.removeAttribute('id'));\n icon.removeAttribute('id');\n for (const node of [icon, ...Array.from(icon.querySelectorAll('*'))])\n for (const attr of Array.from(node.attributes)) if (attr.name.startsWith('on')) node.removeAttribute(attr.name);\n state.clone.append(icon);\n }\n state.clone.append(doc.createTextNode(item.label));\n state.clone.setAttribute('aria-label', item.accessibilityLabel);\n state.clone.disabled = item.disabled;\n updates++;\n }\n if (!source.hasAttribute('data-native-ui-web-source')) source.setAttribute('data-native-ui-web-source', '');\n }\n }\n for (const source of owned.keys()) if (!wanted.has(source)) restore(source);\n const clones = Array.from(wanted, (source) => owned.get(source)!.clone);\n const order = Array.from(host.children).filter((child): child is HTMLButtonElement => clones.includes(child as HTMLButtonElement));\n const focused = clones.find((clone) => clone === doc.activeElement);\n clones.forEach((clone, index) => {\n if (order[index] === clone) return;\n host.insertBefore(clone, order[index]);\n order.splice(order.indexOf(clone), 1);\n order.splice(index, 0, clone);\n });\n // Moving an existing button can blur it; retain keyboard focus across source reordering.\n if (focused && doc.activeElement !== focused) focused.focus({ preventScroll: true });\n // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh.\n observer.takeRecords();\n };\n const schedule = () => {\n if (!stopped && !frame) frame = win.requestAnimationFrame(render);\n };\n const observer = new MutationObserver(schedule);\n win.addEventListener('resize', schedule);\n win.addEventListener('nativeUIRefresh', schedule);\n render();\n return {\n markForCheck: schedule,\n getStatus: () => ({ state: stopped ? 'stopped' : 'web', projected: owned.size, updates }),\n async suspend() {\n suspended++;\n for (const source of owned.keys()) restore(source);\n let resumed = false;\n return {\n async resume() {\n if (!resumed) {\n resumed = true;\n suspended--;\n render();\n }\n },\n };\n },\n async destroy() {\n if (stopped) return;\n stopped = true;\n observer.disconnect();\n win.cancelAnimationFrame(frame);\n win.removeEventListener('resize', schedule);\n win.removeEventListener('nativeUIRefresh', schedule);\n for (const source of owned.keys()) restore(source);\n style.remove();\n },\n };\n};\n"],"mappings":";;;;;AAMA,MAAa,eAAe;AAC5B,MAAa,kBAAkB,YAC7B,QAAQ,QAAA,+DAAoB,KAAK,QAAQ,aAAa,0BAA0B,MAAM;AAExF,MAAa,mBAAmB,SAAsB,IAAc,YAA2D;CAC7H,IACE,EAAE,QAAQ,QAAQ,QAAQ,cAAc,KAAA,CAAM,SAAS,OAAO,KAC9D,CAAC,QAAQ,QAAA,+DAAoB,KAC7B,QAAQ,QAAQ,wBAAwB,GAExC;CAEF,IAAI,CAAC,QAAQ,QAAQ,uCAAqC,KAAK,CAAC,QAAQ,SAAS,eAAe,OAAO,CAAC,GAAG;CAC3G,IAAI,QAAQ,SAAS,QAAQ,cAAc,aAAa,GAAG;CAC3D,IAAI,CAAC,eAAe,OAAO,KAAK,CAAC,SAAS,OAAO,GAAG;CACpD,IAAI,eAAe,OAAO,KAAK,QAAQ,cAAc,YAAY,OAAO;CACxE,MAAM,OAAO,QAAQ,aAAa,gBAAgB,MAAM,gBAAgB,gBAAgB;CACxF,MAAM,YAAY,gBAAgB,SAAS,MAAM,IAAI,OAAO;CAC5D,MAAM,OAAO,WAAW,WAAW,SAAS,EAAE;CAC9C,IAAI,CAAC,MAAM;CAQX,KAAK,qBAPc,QAChB,aAAa,iBAAiB,CAAC,EAC9B,KAAK,CAAC,CACP,MAAM,KAAK,CAAC,CACZ,KAAK,QAAQ,QAAQ,cAAc,eAAe,GAAG,CAAC,EAAE,aAAa,KAAK,KAAK,EAAE,CAAC,CAClF,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,KAC6B,QAAQ,aAAa,YAAY,KAAK,KAAK;CACnF,IAAI,CAAC,KAAK,oBAAoB;CAC9B,KAAK,aAAL,KAAK,WAAa,QAAQ,QAAQ,uBAAuB,KAAK,CAAC,CAAC,QAAQ,QAAQ,0BAAwB;CACxG,IAAI,eAAe,OAAO,GAAG,UAAU,QAAQ,YAAY;CAC3D,OAAO;AACT;;AAGA,MAAa,2BAA2B,KAAe,YAA2D;CAChH,MAAM,OAAO,QAAQ,MAAM;CAC3B,IAAI,CAAC,MAAM;EACT,IAAI,UAAU;EACd,OAAO;GACL,oBAAoB,CAAC;GACrB,kBAAkB;IAAE,OAAO,UAAU,YAAY;IAAO,WAAW;IAAG,SAAS;GAAE;GACjF,SAAS,aAAa,EAAE,QAAQ,YAAY,CAAC,EAAE;GAC/C,SAAS,YAAY;IACnB,UAAU;GACZ;EACF;CACF;CACA,MAAM,MAAM,IAAI;CAChB,MAAM,wBAAQ,IAAI,IAAuF;CACzG,MAAM,QAAQ,IAAI,cAAc,OAAO;CACvC,MAAM,cAAc;CACpB,IAAI,KAAK,OAAO,KAAK;CACrB,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,UAAU;CACd,MAAM,WAAW,WAAwB;EACvC,MAAM,QAAQ,MAAM,IAAI,MAAM;EAC9B,IAAI,CAAC,OAAO;EACZ,MAAM,UAAU,MAAM,MAAM,SAAS,IAAI,aAAa;EACtD,MAAM,MAAM,OAAO;EACnB,OAAO,gBAAgB,2BAA2B;EAClD,IAAI,WAAW,OAAO,eAAe,CAAC,OAAO,aAAA,0BAAmB,GAAG,OAAO,MAAM;EAChF,MAAM,OAAO,MAAM;CACrB;CACA,MAAM,WAAc,QAAqB,SAAqB;EAC5D,MAAM,SAAS,OAAO,aAAa,2BAA2B;EAC9D,IAAI,QAAQ,OAAO,gBAAgB,2BAA2B;EAC9D,IAAI;GACF,OAAO,YAAY,CAAC,MAAM,GAAG,IAAI;EACnC,UAAU;GACR,IAAI,QAAQ,OAAO,aAAa,6BAA6B,EAAE;EACjE;CACF;CACA,MAAM,eAAe;EACnB,QAAQ;EACR,IAAI,SAAS;EACb,SAAS,WAAW;EACpB,SAAS,QAAQ,IAAI,iBAAiB;GAAE,SAAS;GAAM,WAAW;GAAM,YAAY;GAAM,eAAe;EAAK,CAAC;EAC/G,MAAM,yBAAS,IAAI,IAAiB;EACpC,IAAI,CAAC,aAAa,QAAQ,cAAc,YAAY,SAAS,MAAM,eAAe,QAAQ,MAAM,aAAa;GAC3G,MAAM,UAAU,MAAM,KAAK,QAAQ,KAAK,iBAA8B,YAAY,CAAC;GACnF,IAAI,OAAO;GACX,KAAK,MAAM,UAAU,SAAS;IAG5B,IAAI,CAAC,eAAe,MAAM,KAAK,KAAK,SAAS,MAAM,KAAK,OAAO,QAAQ,4BAAa,GAAG;IACvF,MAAM,WAAW,OAAO,cAAc,UAAU,CAAC,EAAE;IACnD,IAAI,UAAU,SAAS,QAAQ,UAAU;KAAE,SAAS;KAAM,WAAW;KAAM,YAAY;KAAM,eAAe;IAAK,CAAC;IAClH,MAAM,YAAY,QAAQ,cAAc,gBAAgB,cAAc,OAAO,OAAO,CAAC;IACrF,IAAI,CAAC,WAAW;IAChB,IAAI,UAAU,QAAQ,SAAS,eAAe;KAC5C,IAAI,MAAM;KACV,OAAO;IACT;IACA,OAAO,IAAI,MAAM;IACjB,MAAM,OAAO,UAAU,QAAQ,MAAM;IACrC,MAAM,MAAM,OAAO,cAAc,KAAK,KAAK,UAAU,cAAc,KAAK;IACxE,MAAM,YAAY,KAAK,UAAU;KAAC;KAAM,OAAO;KAAW,KAAK;IAAS,CAAC;IACzE,IAAI,QAAQ,MAAM,IAAI,MAAM;IAC5B,IAAI,CAAC,OAAO;KACV,MAAM,QAAQ,IAAI,cAAc,QAAQ;KACxC,MAAM,OAAO;KACb,MAAM,aAAa,wBAAwB,UAAU,QAAQ,IAAI;KACjE,QAAQ;MAAE;MAAO,MAAM,OAAO,aAAa,aAAa;MAAG,WAAW;KAAG;KACzE,MAAM,IAAI,QAAQ,KAAK;KACvB,MAAM,iBAAiB,eAAe;MACpC,MAAM,UAAU,CAAC,WAAW,CAAC,aAAa,QAAQ,cAAc,gBAAgB,cAAc,OAAO,OAAO,CAAC;MAC7G,IACE,QAAQ,cAAc,YAAY,SAClC,CAAC,WACD,CAAC,eAAe,MAAM,KACtB,CAAC,KAAK,eACN,QAAQ,QAAQ,MAAM,EAAE,CAAC,YACzB,OAAO,QAAQ,4BAAa,GAE5B;MACF,OAAO,MAAM;MACb,SAAS;KACX,CAAC;KACD,KAAK,OAAO,KAAK;IACnB;IACA,IAAI,MAAM,cAAc,WAAW;KACjC,MAAM,YAAY;KAClB,MAAM,MAAM,gBAAgB;KAC5B,IAAI,KAAK;MACP,MAAM,OAAO,IAAI,UAAU,IAAI;MAC/B,KAAK,iBAAiB,MAAM,CAAC,CAAC,SAAS,SAAS,KAAK,gBAAgB,IAAI,CAAC;MAC1E,KAAK,gBAAgB,IAAI;MACzB,KAAK,MAAM,QAAQ,CAAC,MAAM,GAAG,MAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC,CAAC,GACjE,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,UAAU,GAAG,IAAI,KAAK,KAAK,WAAW,IAAI,GAAG,KAAK,gBAAgB,KAAK,IAAI;MAChH,MAAM,MAAM,OAAO,IAAI;KACzB;KACA,MAAM,MAAM,OAAO,IAAI,eAAe,KAAK,KAAK,CAAC;KACjD,MAAM,MAAM,aAAa,cAAc,KAAK,kBAAkB;KAC9D,MAAM,MAAM,WAAW,KAAK;KAC5B;IACF;IACA,IAAI,CAAC,OAAO,aAAa,2BAA2B,GAAG,OAAO,aAAa,6BAA6B,EAAE;GAC5G;EACF;EACA,KAAK,MAAM,UAAU,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,GAAG,QAAQ,MAAM;EAC1E,MAAM,SAAS,MAAM,KAAK,SAAS,WAAW,MAAM,IAAI,MAAM,CAAC,CAAE,KAAK;EACtE,MAAM,QAAQ,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,QAAQ,UAAsC,OAAO,SAAS,KAA0B,CAAC;EACjI,MAAM,UAAU,OAAO,MAAM,UAAU,UAAU,IAAI,aAAa;EAClE,OAAO,SAAS,OAAO,UAAU;GAC/B,IAAI,MAAM,WAAW,OAAO;GAC5B,KAAK,aAAa,OAAO,MAAM,MAAM;GACrC,MAAM,OAAO,MAAM,QAAQ,KAAK,GAAG,CAAC;GACpC,MAAM,OAAO,OAAO,GAAG,KAAK;EAC9B,CAAC;EAED,IAAI,WAAW,IAAI,kBAAkB,SAAS,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;EAEnF,SAAS,YAAY;CACvB;CACA,MAAM,iBAAiB;EACrB,IAAI,CAAC,WAAW,CAAC,OAAO,QAAQ,IAAI,sBAAsB,MAAM;CAClE;CACA,MAAM,WAAW,IAAI,iBAAiB,QAAQ;CAC9C,IAAI,iBAAiB,UAAU,QAAQ;CACvC,IAAI,iBAAiB,mBAAmB,QAAQ;CAChD,OAAO;CACP,OAAO;EACL,cAAc;EACd,kBAAkB;GAAE,OAAO,UAAU,YAAY;GAAO,WAAW,MAAM;GAAM;EAAQ;EACvF,MAAM,UAAU;GACd;GACA,KAAK,MAAM,UAAU,MAAM,KAAK,GAAG,QAAQ,MAAM;GACjD,IAAI,UAAU;GACd,OAAO,EACL,MAAM,SAAS;IACb,IAAI,CAAC,SAAS;KACZ,UAAU;KACV;KACA,OAAO;IACT;GACF,EACF;EACF;EACA,MAAM,UAAU;GACd,IAAI,SAAS;GACb,UAAU;GACV,SAAS,WAAW;GACpB,IAAI,qBAAqB,KAAK;GAC9B,IAAI,oBAAoB,UAAU,QAAQ;GAC1C,IAAI,oBAAoB,mBAAmB,QAAQ;GACnD,KAAK,MAAM,UAAU,MAAM,KAAK,GAAG,QAAQ,MAAM;GACjD,MAAM,OAAO;EACf;CACF;AACF"}
|
package/dist/native/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { NativeUIExtension, NativeUIImageReference, NativeUIReadContext, NativeUIValue, defineNativeUI } from "./extensions.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ControlProjection, IonicProjectionOptions, NativeUIComponent, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions, VerticalBarEdge, VerticalBarPlacement, VerticalControlAreaHandle, VerticalControlAreaOptions, WebViewMetrics } from "./definitions.js";
|
|
3
3
|
import { NativeUI, configureNativeTransition } from "./activation.js";
|
|
4
4
|
import { setVerticalControlAreaPlacement } from "./legacy-layout.js";
|
|
5
5
|
//#region src/native/index.d.ts
|
|
6
6
|
export declare const enableNativeUI: (options?: NativeUIOptions) => Promise<NativeUIHandle>;
|
|
7
7
|
export declare const enableVerticalControlArea: (options?: VerticalControlAreaOptions) => Promise<VerticalControlAreaHandle>;
|
|
8
8
|
//#endregion
|
|
9
|
-
export { type
|
|
9
|
+
export { type ControlProjection, type IonicProjectionOptions, NativeUI, type NativeUIComponent, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, type VerticalBarEdge, type VerticalBarPlacement, type VerticalControlAreaHandle, type VerticalControlAreaOptions, type WebViewMetrics, configureNativeTransition, defineNativeUI, setVerticalControlAreaPlacement };
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["_$__definitions0.NativeUIHandle"],"sources":["../../src/native/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["_$__definitions0.NativeUIHandle"],"sources":["../../src/native/index.ts"],"mappings":";;;;;qBAyBa,iBAAkB,UAAS,oBAAoB,QAApBA;qBAG3B,4BAAmC,UAAS,+BAAkC,QAAQ"}
|
package/dist/native/index.js
CHANGED
|
@@ -7,17 +7,8 @@ const enableNativeUI = (options = {}) => startNativeUI(options, async (...args)
|
|
|
7
7
|
const enableVerticalControlArea = async (options = {}) => {
|
|
8
8
|
return {
|
|
9
9
|
...await enableNativeUI({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
vertical: options.vertical,
|
|
13
|
-
buttonProjection: options.buttonProjection,
|
|
14
|
-
buttonDefaultFill: options.buttonDefaultFill,
|
|
15
|
-
controls: {
|
|
16
|
-
tabs: true,
|
|
17
|
-
toolbar: true,
|
|
18
|
-
popover: options.controls?.popover === true
|
|
19
|
-
},
|
|
20
|
-
verticalBarsOnly: true
|
|
10
|
+
...options,
|
|
11
|
+
controls: options.controls ?? "verticalOnly"
|
|
21
12
|
}),
|
|
22
13
|
setPlacement: setVerticalControlAreaPlacement
|
|
23
14
|
};
|
package/dist/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["activate"],"sources":["../../src/native/index.ts"],"sourcesContent":["// Compatibility Ionic entrypoint. Standard HTML consumers can import /core without Ionic.\nimport { startNativeUI as activate } from './startup';\nimport type { NativeUIOptions, VerticalControlAreaOptions, VerticalControlAreaHandle } from './definitions';\nimport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { configureNativeTransition, NativeUI } from './activation';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue, NativeUIReadContext, NativeUIImageReference } from './extensions';\nexport type {\n NativeUIVerticalBarsOptions,\n NativeUIComponent,\n NativeUIControls,\n
|
|
1
|
+
{"version":3,"file":"index.js","names":["activate"],"sources":["../../src/native/index.ts"],"sourcesContent":["// Compatibility Ionic entrypoint. Standard HTML consumers can import /core without Ionic.\nimport { startNativeUI as activate } from './startup';\nimport type { NativeUIOptions, VerticalControlAreaOptions, VerticalControlAreaHandle } from './definitions';\nimport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { setVerticalControlAreaPlacement } from './legacy-layout';\nexport { configureNativeTransition, NativeUI } from './activation';\nexport { defineNativeUI } from './extensions';\nexport type { NativeUIExtension, NativeUIValue, NativeUIReadContext, NativeUIImageReference } from './extensions';\nexport type {\n NativeUIVerticalBarsOptions,\n NativeUIComponent,\n NativeUIControls,\n ControlProjection,\n IonicProjectionOptions,\n NativeUIHandle,\n NativeUIOptions,\n NativeUIStatus,\n NativeUISuspension,\n VerticalBarEdge,\n VerticalBarPlacement,\n VerticalControlAreaHandle,\n VerticalControlAreaOptions,\n WebViewMetrics,\n} from './definitions';\n\nexport const enableNativeUI = (options: NativeUIOptions = {}) =>\n activate(options, async (...args) => (await import('./overlays/controller')).createOverlayController(...args));\n\nexport const enableVerticalControlArea = async (options: VerticalControlAreaOptions = {}): Promise<VerticalControlAreaHandle> => {\n const handle = await enableNativeUI({ ...options, controls: options.controls ?? 'verticalOnly' });\n return { ...handle, setPlacement: setVerticalControlAreaPlacement };\n};\n"],"mappings":";;;;;AAyBA,MAAa,kBAAkB,UAA2B,CAAC,MACzDA,cAAS,SAAS,OAAO,GAAG,UAAU,MAAM,OAAO,4BAAA,CAA0B,wBAAwB,GAAG,IAAI,CAAC;AAE/G,MAAa,4BAA4B,OAAO,UAAsC,CAAC,MAA0C;CAE/H,OAAO;EAAE,GAAG,MADS,eAAe;GAAE,GAAG;GAAS,UAAU,QAAQ,YAAY;EAAe,CAAC;EAC5E,cAAc;CAAgC;AACpE"}
|