@rdlabo/capacitor-native-ui 0.0.0-stage → 2.0.0-beta.pr303.shaf4f5ca16816f
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/LICENSE +21 -0
- package/Package.swift +23 -0
- package/README.md +65 -2
- package/RdlaboCapacitorNativeUi.podspec +17 -0
- package/android/build.gradle +69 -0
- package/android/proguard-rules.pro +0 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/dev/rdlabo/capacitor/nativeui/NativeUI.kt +51 -0
- package/android/src/main/java/dev/rdlabo/capacitor/nativeui/NativeUIPlugin.kt +192 -0
- package/android/src/main/java/dev/rdlabo/capacitor/nativeui/NativeUISnapshot.kt +57 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.js +3 -0
- package/dist/native/activation.d.ts +14 -0
- package/dist/native/activation.d.ts.map +1 -0
- package/dist/native/activation.js +305 -0
- package/dist/native/activation.js.map +1 -0
- package/dist/native/components/index.d.ts +19 -0
- package/dist/native/components/index.d.ts.map +1 -0
- package/dist/native/components/index.js +38 -0
- package/dist/native/components/index.js.map +1 -0
- package/dist/native/components/ion-back-button.d.ts +10 -0
- package/dist/native/components/ion-back-button.d.ts.map +1 -0
- package/dist/native/components/ion-back-button.js +21 -0
- package/dist/native/components/ion-back-button.js.map +1 -0
- package/dist/native/components/ion-button.d.ts +10 -0
- package/dist/native/components/ion-button.d.ts.map +1 -0
- package/dist/native/components/ion-button.js +25 -0
- package/dist/native/components/ion-button.js.map +1 -0
- package/dist/native/components/ion-buttons.d.ts +11 -0
- package/dist/native/components/ion-buttons.d.ts.map +1 -0
- package/dist/native/components/ion-buttons.js +30 -0
- package/dist/native/components/ion-buttons.js.map +1 -0
- package/dist/native/components/ion-fab.d.ts +10 -0
- package/dist/native/components/ion-fab.d.ts.map +1 -0
- package/dist/native/components/ion-fab.js +132 -0
- package/dist/native/components/ion-fab.js.map +1 -0
- package/dist/native/components/ion-menu-button.d.ts +13 -0
- package/dist/native/components/ion-menu-button.d.ts.map +1 -0
- package/dist/native/components/ion-menu-button.js +44 -0
- package/dist/native/components/ion-menu-button.js.map +1 -0
- package/dist/native/components/ion-segment.d.ts +11 -0
- package/dist/native/components/ion-segment.d.ts.map +1 -0
- package/dist/native/components/ion-segment.js +30 -0
- package/dist/native/components/ion-segment.js.map +1 -0
- package/dist/native/components/ion-tab-bar.d.ts +11 -0
- package/dist/native/components/ion-tab-bar.d.ts.map +1 -0
- package/dist/native/components/ion-tab-bar.js +50 -0
- package/dist/native/components/ion-tab-bar.js.map +1 -0
- package/dist/native/components/searchable-tabs.d.ts +20 -0
- package/dist/native/components/searchable-tabs.d.ts.map +1 -0
- package/dist/native/components/searchable-tabs.js +413 -0
- package/dist/native/components/searchable-tabs.js.map +1 -0
- package/dist/native/definitions.d.ts +255 -0
- package/dist/native/definitions.d.ts.map +1 -0
- package/dist/native/definitions.js +0 -0
- package/dist/native/extensions.d.ts +31 -0
- package/dist/native/extensions.d.ts.map +1 -0
- package/dist/native/extensions.js +122 -0
- package/dist/native/extensions.js.map +1 -0
- package/dist/native/foldable/fold-state.d.ts +5 -0
- package/dist/native/foldable/fold-state.d.ts.map +1 -0
- package/dist/native/foldable/fold-state.js +15 -0
- package/dist/native/foldable/fold-state.js.map +1 -0
- package/dist/native/foldable/types.d.ts +12 -0
- package/dist/native/foldable/types.d.ts.map +1 -0
- package/dist/native/foldable/types.js +0 -0
- package/dist/native/html.d.ts +9 -0
- package/dist/native/html.d.ts.map +1 -0
- package/dist/native/html.js +188 -0
- package/dist/native/html.js.map +1 -0
- package/dist/native/index.d.ts +10 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +28 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/legacy-layout.d.ts +5 -0
- package/dist/native/legacy-layout.d.ts.map +1 -0
- package/dist/native/legacy-layout.js +27 -0
- package/dist/native/legacy-layout.js.map +1 -0
- package/dist/native/overlays/content.d.ts +4 -0
- package/dist/native/overlays/content.d.ts.map +1 -0
- package/dist/native/overlays/content.js +51 -0
- package/dist/native/overlays/content.js.map +1 -0
- package/dist/native/overlays/controller.d.ts +8 -0
- package/dist/native/overlays/controller.d.ts.map +1 -0
- package/dist/native/overlays/controller.js +285 -0
- package/dist/native/overlays/controller.js.map +1 -0
- package/dist/native/overlays/dialog-animation.d.ts +9 -0
- package/dist/native/overlays/dialog-animation.d.ts.map +1 -0
- package/dist/native/overlays/dialog-animation.js +53 -0
- package/dist/native/overlays/dialog-animation.js.map +1 -0
- package/dist/native/overlays/dismiss-animation.d.ts +4 -0
- package/dist/native/overlays/dismiss-animation.d.ts.map +1 -0
- package/dist/native/overlays/dismiss-animation.js +18 -0
- package/dist/native/overlays/dismiss-animation.js.map +1 -0
- package/dist/native/overlays/focus.d.ts +5 -0
- package/dist/native/overlays/focus.d.ts.map +1 -0
- package/dist/native/overlays/focus.js +59 -0
- package/dist/native/overlays/focus.js.map +1 -0
- package/dist/native/overlays/popover.d.ts +12 -0
- package/dist/native/overlays/popover.d.ts.map +1 -0
- package/dist/native/overlays/popover.js +110 -0
- package/dist/native/overlays/popover.js.map +1 -0
- package/dist/native/overlays/styles.d.ts +11 -0
- package/dist/native/overlays/styles.d.ts.map +1 -0
- package/dist/native/overlays/styles.js +167 -0
- package/dist/native/overlays/styles.js.map +1 -0
- package/dist/native/prehide.d.ts +8 -0
- package/dist/native/prehide.d.ts.map +1 -0
- package/dist/native/prehide.js +238 -0
- package/dist/native/prehide.js.map +1 -0
- package/dist/native/runtime.d.ts +7 -0
- package/dist/native/runtime.d.ts.map +1 -0
- package/dist/native/runtime.js +788 -0
- package/dist/native/runtime.js.map +1 -0
- package/dist/native/shared/bridge.d.ts +6 -0
- package/dist/native/shared/bridge.d.ts.map +1 -0
- package/dist/native/shared/bridge.js +18 -0
- package/dist/native/shared/bridge.js.map +1 -0
- package/dist/native/shared/candidate.d.ts +25 -0
- package/dist/native/shared/candidate.d.ts.map +1 -0
- package/dist/native/shared/candidate.js +106 -0
- package/dist/native/shared/candidate.js.map +1 -0
- package/dist/native/shared/clipping.d.ts +5 -0
- package/dist/native/shared/clipping.d.ts.map +1 -0
- package/dist/native/shared/clipping.js +26 -0
- package/dist/native/shared/clipping.js.map +1 -0
- package/dist/native/shared/crossfade.d.ts +12 -0
- package/dist/native/shared/crossfade.d.ts.map +1 -0
- package/dist/native/shared/crossfade.js +67 -0
- package/dist/native/shared/crossfade.js.map +1 -0
- package/dist/native/shared/diagnostics.d.ts +4 -0
- package/dist/native/shared/diagnostics.d.ts.map +1 -0
- package/dist/native/shared/diagnostics.js +18 -0
- package/dist/native/shared/diagnostics.js.map +1 -0
- package/dist/native/shared/dom.d.ts +46 -0
- package/dist/native/shared/dom.d.ts.map +1 -0
- package/dist/native/shared/dom.js +180 -0
- package/dist/native/shared/dom.js.map +1 -0
- package/dist/native/shared/events.d.ts +15 -0
- package/dist/native/shared/events.d.ts.map +1 -0
- package/dist/native/shared/events.js +17 -0
- package/dist/native/shared/events.js.map +1 -0
- package/dist/native/shared/icons.d.ts +8 -0
- package/dist/native/shared/icons.d.ts.map +1 -0
- package/dist/native/shared/icons.js +61 -0
- package/dist/native/shared/icons.js.map +1 -0
- package/dist/native/shared/modal.d.ts +10 -0
- package/dist/native/shared/modal.d.ts.map +1 -0
- package/dist/native/shared/modal.js +160 -0
- package/dist/native/shared/modal.js.map +1 -0
- package/dist/native/shared/projection-id.d.ts +4 -0
- package/dist/native/shared/projection-id.d.ts.map +1 -0
- package/dist/native/shared/projection-id.js +7 -0
- package/dist/native/shared/projection-id.js.map +1 -0
- package/dist/native/shared/toolbar-options.d.ts +9 -0
- package/dist/native/shared/toolbar-options.d.ts.map +1 -0
- package/dist/native/shared/toolbar-options.js +36 -0
- package/dist/native/shared/toolbar-options.js.map +1 -0
- package/dist/native/shared/vertical-bars-geometry.d.ts +10 -0
- package/dist/native/shared/vertical-bars-geometry.d.ts.map +1 -0
- package/dist/native/shared/vertical-bars-geometry.js +85 -0
- package/dist/native/shared/vertical-bars-geometry.js.map +1 -0
- package/dist/native/startup.d.ts +8 -0
- package/dist/native/startup.d.ts.map +1 -0
- package/dist/native/startup.js +86 -0
- package/dist/native/startup.js.map +1 -0
- package/dist/native/vertical-bars-web-search.d.ts +10 -0
- package/dist/native/vertical-bars-web-search.d.ts.map +1 -0
- package/dist/native/vertical-bars-web-search.js +90 -0
- package/dist/native/vertical-bars-web-search.js.map +1 -0
- package/dist/native/vertical-bars-web.d.ts +5 -0
- package/dist/native/vertical-bars-web.d.ts.map +1 -0
- package/dist/native/vertical-bars-web.js +421 -0
- package/dist/native/vertical-bars-web.js.map +1 -0
- package/dist/native-integration/index.d.ts +25 -0
- package/dist/native-integration/index.d.ts.map +1 -0
- package/dist/native-integration/index.js +33 -0
- package/dist/native-integration/index.js.map +1 -0
- package/dist/vertical-bars.d.ts +6 -0
- package/dist/vertical-bars.js +5 -0
- package/docs/configuration.md +192 -0
- package/docs/framework-independent.md +89 -0
- package/docs/migration.md +204 -0
- package/docs/native-ui-extensions.md +368 -0
- package/docs/native-ui.md +407 -0
- package/docs/setup.md +217 -0
- package/docs/vertical-bars.md +383 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIButton.swift +71 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIButtons.swift +24 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIComponents.swift +19 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIFab.swift +80 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUISearchController.swift +439 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUISegment.swift +91 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUITabBar.swift +218 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalBars.swift +590 -0
- package/ios/Sources/NativeUIPlugin/Components/NativeUIVerticalSearch.swift +118 -0
- package/ios/Sources/NativeUIPlugin/Extensions/NativeUI.swift +178 -0
- package/ios/Sources/NativeUIPlugin/Extensions/NativeUIRendering.swift +38 -0
- package/ios/Sources/NativeUIPlugin/NativeUIPlugin.swift +615 -0
- package/ios/Sources/NativeUIPlugin/Overlays/NativeUIOverlayController.swift +119 -0
- package/ios/Sources/NativeUIPlugin/Overlays/NativeUIOverlayHost.swift +75 -0
- package/ios/Sources/NativeUIPlugin/Shared/NativeUIControlRendering.swift +21 -0
- package/ios/Sources/NativeUIPlugin/Shared/NativeUICrossfade.swift +35 -0
- package/ios/Sources/NativeUIPlugin/Shared/NativeUIHost.swift +8 -0
- package/ios/Sources/NativeUIPlugin/Shared/NativeUISnapshot.swift +161 -0
- package/ios/Sources/NativeUIPlugin/Shared/NativeUIVerticalBarsGeometry.swift +52 -0
- package/ios/Tests/NativeUIPluginTests/NativeUIExtensionTests.swift +165 -0
- package/ios/Tests/NativeUIPluginTests/NativeUISnapshotTests.swift +805 -0
- package/package.json +89 -4
- package/src/core.ts +15 -0
- package/src/native/activation.ts +394 -0
- package/src/native/components/index.ts +45 -0
- package/src/native/components/ion-back-button.ts +22 -0
- package/src/native/components/ion-button.ts +25 -0
- package/src/native/components/ion-buttons.ts +37 -0
- package/src/native/components/ion-fab.ts +151 -0
- package/src/native/components/ion-menu-button.ts +37 -0
- package/src/native/components/ion-segment.ts +27 -0
- package/src/native/components/ion-tab-bar.ts +50 -0
- package/src/native/components/searchable-tabs.ts +499 -0
- package/src/native/definitions.ts +324 -0
- package/src/native/extensions.ts +176 -0
- package/src/native/foldable/fold-state.ts +10 -0
- package/src/native/foldable/types.ts +4 -0
- package/src/native/html.ts +193 -0
- package/src/native/index.ts +41 -0
- package/src/native/legacy-layout.ts +21 -0
- package/src/native/overlays/content.ts +51 -0
- package/src/native/overlays/controller.ts +349 -0
- package/src/native/overlays/dialog-animation.ts +53 -0
- package/src/native/overlays/dismiss-animation.ts +15 -0
- package/src/native/overlays/focus.ts +64 -0
- package/src/native/overlays/popover.ts +120 -0
- package/src/native/overlays/styles.ts +181 -0
- package/src/native/prehide.ts +285 -0
- package/src/native/runtime.ts +988 -0
- package/src/native/shared/bridge.ts +18 -0
- package/src/native/shared/candidate.ts +162 -0
- package/src/native/shared/clipping.ts +24 -0
- package/src/native/shared/crossfade.ts +67 -0
- package/src/native/shared/diagnostics.ts +13 -0
- package/src/native/shared/dom.ts +304 -0
- package/src/native/shared/events.ts +14 -0
- package/src/native/shared/icons.ts +57 -0
- package/src/native/shared/modal.ts +157 -0
- package/src/native/shared/projection-id.ts +2 -0
- package/src/native/shared/toolbar-options.ts +53 -0
- package/src/native/shared/vertical-bars-geometry.ts +80 -0
- package/src/native/startup.ts +84 -0
- package/src/native/vertical-bars-web-search.ts +86 -0
- package/src/native/vertical-bars-web.ts +498 -0
- package/src/native-integration/index.ts +49 -0
- package/src/vertical-bars.ts +11 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/Package.swift
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "RdlaboCapacitorNativeUi",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(name: "RdlaboCapacitorNativeUi", targets: ["NativeUIPlugin"])
|
|
9
|
+
],
|
|
10
|
+
dependencies: [.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")],
|
|
11
|
+
targets: [.target(
|
|
12
|
+
name: "NativeUIPlugin",
|
|
13
|
+
dependencies: [
|
|
14
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
15
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
16
|
+
],
|
|
17
|
+
path: "ios/Sources/NativeUIPlugin"
|
|
18
|
+
), .testTarget(
|
|
19
|
+
name: "NativeUIPluginTests",
|
|
20
|
+
dependencies: ["NativeUIPlugin"],
|
|
21
|
+
path: "ios/Tests/NativeUIPluginTests"
|
|
22
|
+
)]
|
|
23
|
+
)
|
package/README.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Capacitor Native UI
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
+
|
|
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.
|
|
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
|
+
|
|
24
|
+
## Choose what to build
|
|
25
|
+
|
|
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](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md) |
|
|
31
|
+
| Add a side rail without Ionic | [HTML rail setup](docs/framework-independent.md) |
|
|
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) |
|
|
33
|
+
| Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) |
|
|
34
|
+
|
|
35
|
+
## Install from npm
|
|
36
|
+
|
|
37
|
+
Install in your Capacitor 8 application:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install @rdlabo/capacitor-native-ui
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If you also use the iOS 27 theme, install or update both packages together:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm install @rdlabo/capacitor-native-ui @rdlabo/ionic-theme-ios27
|
|
47
|
+
```
|
|
48
|
+
|
|
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.
|
|
50
|
+
|
|
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.
|
|
52
|
+
|
|
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.
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
Native startup requires Capacitor 8 and an iOS or Android project. Android extensions require API 24+ and Kotlin/Compose. Ordinary native rendering requires iOS 26+ and Xcode 26+. The Ionic adapter supports Ionic `>=8.8.1 <10`; the `/core` HTML adapter requires no Ionic or theme package. [Vertical Bars](docs/vertical-bars.md) and [extension placement](docs/native-ui-extensions.md#content-and-placement-are-independent) have additional device requirements.
|
|
58
|
+
|
|
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).
|
|
60
|
+
|
|
61
|
+
## Reference
|
|
62
|
+
|
|
63
|
+
- [Supported Ionic controls](docs/native-ui.md#supported-markup) and fallback behavior.
|
|
64
|
+
- [Configuration](docs/configuration.md): defaults, selected controls, rail-only mode, and live changes.
|
|
65
|
+
- [Vertical Bars](docs/vertical-bars.md): eligibility, appearance, and runtime API.
|
|
66
|
+
- [Migration](docs/migration.md): package names, imports, and native project updates.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'RdlaboCapacitorNativeUi'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '15.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.9'
|
|
17
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '2.2.20'
|
|
10
|
+
repositories {
|
|
11
|
+
google()
|
|
12
|
+
mavenCentral()
|
|
13
|
+
}
|
|
14
|
+
dependencies {
|
|
15
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
16
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
17
|
+
classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:$kotlin_version"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply plugin: 'com.android.library'
|
|
22
|
+
apply plugin: 'kotlin-android'
|
|
23
|
+
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
|
|
24
|
+
|
|
25
|
+
android {
|
|
26
|
+
namespace = "dev.rdlabo.capacitor.nativeui"
|
|
27
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
28
|
+
defaultConfig {
|
|
29
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
30
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
31
|
+
versionCode 1
|
|
32
|
+
versionName "1.0"
|
|
33
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
34
|
+
}
|
|
35
|
+
buildTypes {
|
|
36
|
+
release {
|
|
37
|
+
minifyEnabled false
|
|
38
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
lintOptions {
|
|
42
|
+
abortOnError = false
|
|
43
|
+
}
|
|
44
|
+
buildFeatures { compose true }
|
|
45
|
+
kotlinOptions { jvmTarget = '21' }
|
|
46
|
+
compileOptions {
|
|
47
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
48
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
repositories {
|
|
53
|
+
google()
|
|
54
|
+
mavenCentral()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
dependencies {
|
|
58
|
+
api platform("androidx.compose:compose-bom:2025.10.00")
|
|
59
|
+
api "androidx.compose.ui:ui"
|
|
60
|
+
api "androidx.compose.foundation:foundation"
|
|
61
|
+
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
|
|
62
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
63
|
+
implementation project(':capacitor-android')
|
|
64
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
65
|
+
testImplementation "junit:junit:$junitVersion"
|
|
66
|
+
testImplementation "org.json:json:20250517"
|
|
67
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
68
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
69
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package dev.rdlabo.capacitor.nativeui
|
|
2
|
+
|
|
3
|
+
import android.os.Looper
|
|
4
|
+
import androidx.compose.runtime.Composable
|
|
5
|
+
import org.json.JSONObject
|
|
6
|
+
import java.util.Collections
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* App-local Compose renderers. Register on the main thread before starting the shell.
|
|
10
|
+
* Experimental API: registration interfaces and behavior may change in future releases.
|
|
11
|
+
*/
|
|
12
|
+
object NativeUI {
|
|
13
|
+
internal val renderers = linkedMapOf<String, (JSONObject, NativeUIActions, NativeUIContext) -> (@Composable () -> Unit)>()
|
|
14
|
+
|
|
15
|
+
fun <Props> register(
|
|
16
|
+
name: String,
|
|
17
|
+
decode: (JSONObject) -> Props,
|
|
18
|
+
content: @Composable (Props, NativeUIActions, NativeUIContext) -> Unit,
|
|
19
|
+
) {
|
|
20
|
+
check(Looper.myLooper() == Looper.getMainLooper()) { "Register Native UI on the main thread" }
|
|
21
|
+
require(name.isNotBlank() && !renderers.containsKey(name)) { "Native UI names must be nonempty and unique" }
|
|
22
|
+
renderers[name] = { json, actions, context ->
|
|
23
|
+
val props = decode(json)
|
|
24
|
+
val view: @Composable () -> Unit = { content(props, actions, context) }
|
|
25
|
+
view
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
data class NativeUIFrame(val x: Double, val y: Double, val width: Double, val height: Double)
|
|
31
|
+
|
|
32
|
+
/** Source geometry, not screen coordinates. Android currently supports element placement only. */
|
|
33
|
+
data class NativeUIContext(
|
|
34
|
+
val sourceID: String,
|
|
35
|
+
val sourceFrame: NativeUIFrame,
|
|
36
|
+
val sourceFrameInDp: NativeUIFrame,
|
|
37
|
+
val viewportWidth: Double,
|
|
38
|
+
val dpPerCSSPixel: Double,
|
|
39
|
+
val dark: Boolean,
|
|
40
|
+
val rtl: Boolean,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
/** Completion acknowledges the DOM click dispatch, not the application's asynchronous work. */
|
|
44
|
+
class NativeUIActions internal constructor(names: Set<String>, private val dispatch: suspend (String) -> Unit) {
|
|
45
|
+
val availableNames: Set<String> = Collections.unmodifiableSet(names.toSet())
|
|
46
|
+
fun isAvailable(name: String): Boolean = name in availableNames
|
|
47
|
+
suspend fun activate(name: String) {
|
|
48
|
+
check(isAvailable(name)) { "Native UI action is unavailable" }
|
|
49
|
+
dispatch(name)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
package dev.rdlabo.capacitor.nativeui
|
|
2
|
+
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.view.ViewGroup
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
import androidx.compose.runtime.Composable
|
|
9
|
+
import androidx.compose.runtime.CompositionLocalProvider
|
|
10
|
+
import androidx.compose.runtime.mutableStateOf
|
|
11
|
+
import androidx.compose.ui.platform.ComposeView
|
|
12
|
+
import androidx.compose.ui.platform.LocalLayoutDirection
|
|
13
|
+
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
14
|
+
import androidx.compose.ui.unit.LayoutDirection
|
|
15
|
+
import com.getcapacitor.JSArray
|
|
16
|
+
import com.getcapacitor.JSObject
|
|
17
|
+
import com.getcapacitor.Plugin
|
|
18
|
+
import com.getcapacitor.PluginCall
|
|
19
|
+
import com.getcapacitor.PluginMethod
|
|
20
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
21
|
+
import kotlinx.coroutines.Dispatchers
|
|
22
|
+
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
23
|
+
import kotlinx.coroutines.withContext
|
|
24
|
+
import org.json.JSONObject
|
|
25
|
+
import java.util.UUID
|
|
26
|
+
import kotlin.coroutines.resume
|
|
27
|
+
import kotlin.coroutines.resumeWithException
|
|
28
|
+
import kotlin.math.roundToInt
|
|
29
|
+
|
|
30
|
+
@CapacitorPlugin(name = "NativeUI")
|
|
31
|
+
class NativeUIPlugin : Plugin() {
|
|
32
|
+
private val handler = Handler(Looper.getMainLooper())
|
|
33
|
+
private var host: FrameLayout? = null
|
|
34
|
+
private var revision = 0L
|
|
35
|
+
private var sequence = 0L
|
|
36
|
+
private var paused = false
|
|
37
|
+
private val entries = linkedMapOf<String, Entry>()
|
|
38
|
+
private val pending = mutableMapOf<String, (Boolean) -> Unit>()
|
|
39
|
+
private val layoutListener = View.OnLayoutChangeListener { _, l, t, r, b, oldL, oldT, oldR, oldB ->
|
|
40
|
+
if (l != oldL || t != oldT || r != oldR || b != oldB) {
|
|
41
|
+
host?.visibility = View.INVISIBLE
|
|
42
|
+
failActions()
|
|
43
|
+
notifyListeners("webViewMetricsChange", JSObject().put("radius", 0))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private class Entry(val view: ComposeView, var node: ExtensionNode) {
|
|
48
|
+
val content = mutableStateOf<(@Composable () -> Unit)?>(null)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override fun load() {
|
|
52
|
+
activity.runOnUiThread { bridge.webView.addOnLayoutChangeListener(layoutListener) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@PluginMethod
|
|
56
|
+
fun configure(call: PluginCall) = onMain(call) {
|
|
57
|
+
clearViews()
|
|
58
|
+
revision = 0
|
|
59
|
+
sequence = 0
|
|
60
|
+
call.resolve(JSObject().put("supported", true).put("extensions", JSArray(NativeUI.renderers.keys.toList())))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@PluginMethod
|
|
64
|
+
fun getWebViewMetrics(call: PluginCall) = call.resolve(JSObject().put("radius", 0))
|
|
65
|
+
|
|
66
|
+
@PluginMethod
|
|
67
|
+
fun update(call: PluginCall) = onMain(call) {
|
|
68
|
+
val snapshot = NativeUISnapshot.parse(call.data)
|
|
69
|
+
require(snapshot.revision > revision) { "Stale Native UI snapshot" }
|
|
70
|
+
val web = bridge.webView
|
|
71
|
+
val parent = web.parent as? ViewGroup ?: error("WebView has no host")
|
|
72
|
+
require(web.width > 0 && web.height > 0 && web.scaleX == 1f && web.scaleY == 1f) { "WebView geometry unavailable" }
|
|
73
|
+
val scale = web.width / snapshot.viewportWidth
|
|
74
|
+
val dpScale = scale / web.resources.displayMetrics.density
|
|
75
|
+
val accepted = linkedMapOf<String, Pair<ExtensionNode, @Composable () -> Unit>>()
|
|
76
|
+
val rejected = snapshot.rejected.toMutableList()
|
|
77
|
+
for (node in snapshot.nodes) {
|
|
78
|
+
val renderer = NativeUI.renderers[node.name]
|
|
79
|
+
if (renderer == null || paused) {
|
|
80
|
+
rejected.add(node.id)
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
val f = node.frame
|
|
85
|
+
require(listOf(f.x * scale, f.y * scale, f.width * scale, f.height * scale).all { it in -1_000_000.0..1_000_000.0 })
|
|
86
|
+
val context = NativeUIContext(node.id, f, NativeUIFrame(f.x * dpScale, f.y * dpScale, f.width * dpScale, f.height * dpScale), snapshot.viewportWidth, dpScale, node.dark, node.rtl)
|
|
87
|
+
val actions = NativeUIActions(node.actions.keys) { action -> activate(node, action) }
|
|
88
|
+
accepted[node.id] = node to renderer(JSONObject(node.props), actions, context)
|
|
89
|
+
} catch (_: Exception) {
|
|
90
|
+
rejected.add(node.id)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
revision = snapshot.revision
|
|
94
|
+
val container = host ?: FrameLayout(activity).also {
|
|
95
|
+
it.clipChildren = true
|
|
96
|
+
it.clipToPadding = true
|
|
97
|
+
parent.addView(it, ViewGroup.LayoutParams(web.width, web.height))
|
|
98
|
+
host = it
|
|
99
|
+
}
|
|
100
|
+
container.layoutParams = container.layoutParams.apply { width = web.width; height = web.height }
|
|
101
|
+
container.x = web.x
|
|
102
|
+
container.y = web.y
|
|
103
|
+
for (id in entries.keys.toList()) if (id !in accepted) removeEntry(id)
|
|
104
|
+
for ((id, pair) in accepted) {
|
|
105
|
+
val (node, content) = pair
|
|
106
|
+
val entry = entries[id] ?: Entry(ComposeView(activity), node).also { entry ->
|
|
107
|
+
entry.view.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
108
|
+
container.addView(entry.view)
|
|
109
|
+
entry.view.setContent { entry.content.value?.invoke() }
|
|
110
|
+
entries[id] = entry
|
|
111
|
+
}
|
|
112
|
+
entry.node = node
|
|
113
|
+
entry.content.value = {
|
|
114
|
+
CompositionLocalProvider(LocalLayoutDirection provides if (node.rtl) LayoutDirection.Rtl else LayoutDirection.Ltr) { content() }
|
|
115
|
+
}
|
|
116
|
+
entry.view.layoutParams = FrameLayout.LayoutParams((node.frame.width * scale).roundToInt().coerceAtLeast(1), (node.frame.height * scale).roundToInt().coerceAtLeast(1)).apply {
|
|
117
|
+
leftMargin = (node.frame.x * scale).roundToInt()
|
|
118
|
+
topMargin = (node.frame.y * scale).roundToInt()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
container.visibility = if (paused) View.INVISIBLE else View.VISIBLE
|
|
122
|
+
call.resolve(JSObject().put("revision", revision).put("rejectedControls", JSArray(rejected)))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private suspend fun activate(node: ExtensionNode, name: String): Unit = withContext(Dispatchers.Main.immediate) {
|
|
126
|
+
check(!paused && host?.visibility == View.VISIBLE && entries[node.id]?.node == node) { "Native UI action is stale" }
|
|
127
|
+
val target = node.actions[name] ?: error("Native UI action is unavailable")
|
|
128
|
+
suspendCancellableCoroutine { continuation ->
|
|
129
|
+
val request = UUID.randomUUID().toString()
|
|
130
|
+
val timeout = Runnable { pending.remove(request)?.invoke(false) }
|
|
131
|
+
pending[request] = { accepted ->
|
|
132
|
+
handler.removeCallbacks(timeout)
|
|
133
|
+
if (continuation.isActive) {
|
|
134
|
+
if (accepted) continuation.resume(Unit)
|
|
135
|
+
else continuation.resumeWithException(IllegalStateException("Native UI action was not accepted"))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
continuation.invokeOnCancellation { handler.post { pending.remove(request); handler.removeCallbacks(timeout) } }
|
|
139
|
+
handler.postDelayed(timeout, 5000)
|
|
140
|
+
notifyListeners("activate", JSObject().put("revision", revision).put("id", target).put("sequence", ++sequence).put("requestId", request))
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@PluginMethod
|
|
145
|
+
fun completeExtensionAction(call: PluginCall) = onMain(call) {
|
|
146
|
+
val request = call.getString("requestId") ?: error("Missing requestId")
|
|
147
|
+
pending.remove(request)?.invoke(call.getBoolean("accepted", false) == true)
|
|
148
|
+
call.resolve()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@PluginMethod
|
|
152
|
+
fun clear(call: PluginCall) = onMain(call) {
|
|
153
|
+
val next = call.data.getLong("revision")
|
|
154
|
+
require(next > revision) { "Stale Native UI clear" }
|
|
155
|
+
revision = next
|
|
156
|
+
clearViews()
|
|
157
|
+
call.resolve()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private fun onMain(call: PluginCall, block: () -> Unit) {
|
|
161
|
+
activity.runOnUiThread {
|
|
162
|
+
try { block() } catch (error: Exception) { call.reject(error.message ?: "Native UI failed", error) }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
private fun failActions() {
|
|
166
|
+
val callbacks = pending.values.toList()
|
|
167
|
+
pending.clear()
|
|
168
|
+
callbacks.forEach { it(false) }
|
|
169
|
+
}
|
|
170
|
+
private fun removeEntry(id: String) {
|
|
171
|
+
entries.remove(id)?.let { it.view.disposeComposition(); host?.removeView(it.view) }
|
|
172
|
+
}
|
|
173
|
+
private fun clearViews() {
|
|
174
|
+
failActions()
|
|
175
|
+
entries.keys.toList().forEach(::removeEntry)
|
|
176
|
+
(host?.parent as? ViewGroup)?.removeView(host)
|
|
177
|
+
host = null
|
|
178
|
+
}
|
|
179
|
+
override fun handleOnPause() {
|
|
180
|
+
paused = true
|
|
181
|
+
host?.visibility = View.INVISIBLE
|
|
182
|
+
failActions()
|
|
183
|
+
}
|
|
184
|
+
override fun handleOnResume() {
|
|
185
|
+
paused = false
|
|
186
|
+
notifyListeners("webViewMetricsChange", JSObject().put("radius", 0))
|
|
187
|
+
}
|
|
188
|
+
override fun handleOnDestroy() {
|
|
189
|
+
bridge.webView.removeOnLayoutChangeListener(layoutListener)
|
|
190
|
+
clearViews()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package dev.rdlabo.capacitor.nativeui
|
|
2
|
+
|
|
3
|
+
import org.json.JSONObject
|
|
4
|
+
|
|
5
|
+
internal data class ExtensionNode(
|
|
6
|
+
val id: String,
|
|
7
|
+
val frame: NativeUIFrame,
|
|
8
|
+
val dark: Boolean,
|
|
9
|
+
val rtl: Boolean,
|
|
10
|
+
val name: String,
|
|
11
|
+
val props: String,
|
|
12
|
+
val actions: Map<String, String>,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
internal data class NativeUISnapshot(val revision: Long, val viewportWidth: Double, val nodes: List<ExtensionNode>, val rejected: List<String>) {
|
|
16
|
+
companion object {
|
|
17
|
+
fun parse(json: JSONObject): NativeUISnapshot {
|
|
18
|
+
val revisionValue = json.getDouble("revision")
|
|
19
|
+
require(revisionValue.isFinite() && revisionValue >= 0 && revisionValue % 1.0 == 0.0 && revisionValue <= 9007199254740991.0)
|
|
20
|
+
val width = json.getDouble("viewportWidth")
|
|
21
|
+
require(width.isFinite() && width > 0)
|
|
22
|
+
val controls = json.getJSONArray("controls")
|
|
23
|
+
require(controls.length() <= 1000)
|
|
24
|
+
val ids = mutableSetOf<String>()
|
|
25
|
+
val nodes = mutableListOf<ExtensionNode>()
|
|
26
|
+
val rejected = mutableListOf<String>()
|
|
27
|
+
for (index in 0 until controls.length()) {
|
|
28
|
+
val item = controls.getJSONObject(index)
|
|
29
|
+
val id = item.getString("id")
|
|
30
|
+
require(id.isNotBlank() && ids.add(id))
|
|
31
|
+
if (item.getString("kind") != "extension") {
|
|
32
|
+
rejected.add(id)
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
val extension = item.getJSONObject("extension")
|
|
36
|
+
if (!extension.isNull("tabBarId") || item.optString("placement") == "vertical-bars") {
|
|
37
|
+
rejected.add(id)
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
val frame = NativeUIFrame(item.getDouble("x"), item.getDouble("y"), item.getDouble("width"), item.getDouble("height"))
|
|
41
|
+
require(listOf(frame.x, frame.y, frame.width, frame.height).all { it.isFinite() })
|
|
42
|
+
require(frame.width > 0 && frame.height > 0)
|
|
43
|
+
val props = extension.getString("props")
|
|
44
|
+
require(props.length <= 65536)
|
|
45
|
+
JSONObject(props)
|
|
46
|
+
val rawActions = extension.getJSONObject("actions")
|
|
47
|
+
require(rawActions.length() <= 30)
|
|
48
|
+
val actions = rawActions.keys().asSequence().associateWith { key ->
|
|
49
|
+
require(key.isNotBlank())
|
|
50
|
+
rawActions.getString(key).also { require(it.isNotBlank()) }
|
|
51
|
+
}
|
|
52
|
+
nodes.add(ExtensionNode(id, frame, item.optBoolean("dark"), item.optBoolean("rtl"), extension.getString("name"), props, actions))
|
|
53
|
+
}
|
|
54
|
+
return NativeUISnapshot(revisionValue.toLong(), width, nodes, rejected)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll };
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { NativeUIExtension, NativeUIImageReference, NativeUIReadContext, NativeUIValue, defineNativeUI } from "./native/extensions.js";
|
|
2
|
+
import { AutoIonicControls, IonicProjectionOptions, NativeUIControls, NativeUIHandle, NativeUIOptions, NativeUIStatus, NativeUISuspension, NativeUIVerticalBarsOptions, ToolbarProjectionOptions } from "./native/definitions.js";
|
|
3
|
+
import { enableNativeUI } from "./native/startup.js";
|
|
4
|
+
export { type AutoIonicControls, type IonicProjectionOptions, type NativeUIControls, type NativeUIExtension, type NativeUIHandle, type NativeUIImageReference, type NativeUIOptions, type NativeUIReadContext, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type NativeUIVerticalBarsOptions, type ToolbarProjectionOptions, defineNativeUI, enableNativeUI };
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NativeUIExtension, NativeUIValue, defineNativeUI } from "./extensions.js";
|
|
2
|
+
import { NativeUIComponent, NativeUIControls, NativeUIPlugin, NativeUIRuntimeHandle, NativeUIRuntimeOptions, NativeUIStatus, NativeUISuspension, VerticalBarEdge, WebViewMetrics } from "./definitions.js";
|
|
3
|
+
//#region src/native/activation.d.ts
|
|
4
|
+
interface OverlayController {
|
|
5
|
+
suspend(): Promise<() => void>;
|
|
6
|
+
destroy(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
type OverlayFactory = (doc: Document, plugin: NativeUIPlugin, options: NativeUIRuntimeOptions, retain: (deferActivation?: (id: string) => boolean) => () => void, nativeRail: () => boolean) => Promise<OverlayController>;
|
|
9
|
+
export declare const NativeUI: NativeUIPlugin;
|
|
10
|
+
export declare const configureNativeTransition: () => Promise<WebViewMetrics>;
|
|
11
|
+
export declare const enableNativeUI: (options?: NativeUIRuntimeOptions, overlayFactory?: OverlayFactory) => Promise<NativeUIRuntimeHandle>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { type NativeUIComponent, type NativeUIControls, type NativeUIExtension, type NativeUIRuntimeHandle, type NativeUIRuntimeOptions, type NativeUIStatus, type NativeUISuspension, type NativeUIValue, type VerticalBarEdge, type WebViewMetrics, defineNativeUI };
|
|
14
|
+
//# sourceMappingURL=activation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activation.d.ts","names":[],"sources":["../../src/native/activation.ts"],"mappings":";;;UASU;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"}
|