@rdlabo/capacitor-native-ui 0.0.0-stage → 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/LICENSE +21 -0
- package/Package.swift +23 -0
- package/README.md +66 -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 +237 -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 +197 -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 +19 -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 +776 -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/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/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 +13 -0
- package/dist/native/shared/toolbar-options.d.ts.map +1 -0
- package/dist/native/shared/toolbar-options.js +28 -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 +70 -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 +422 -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/appearance.md +45 -0
- package/docs/configuration.md +219 -0
- package/docs/framework-independent.md +107 -0
- package/docs/ionic-vertical-bars.md +173 -0
- package/docs/migration.md +184 -0
- package/docs/native-ui-extensions.md +394 -0
- package/docs/native-ui.md +528 -0
- package/docs/setup.md +224 -0
- package/docs/vertical-bars.md +431 -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 +14 -0
- package/src/native/activation.ts +385 -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 +274 -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 +199 -0
- package/src/native/index.ts +32 -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 +979 -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/control-options.ts +20 -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 +49 -0
- package/src/native/shared/vertical-bars-geometry.ts +80 -0
- package/src/native/startup.ts +68 -0
- package/src/native/vertical-bars-web-search.ts +86 -0
- package/src/native/vertical-bars-web.ts +500 -0
- package/src/native-integration/index.ts +49 -0
- package/src/vertical-bars.ts +12 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Configure Native UI
|
|
2
|
+
|
|
3
|
+
This is the settings reference. Start with [how projection works](../README.md#how-projection-works), then [Ionic setup](setup.md) or [HTML setup](framework-independent.md) for installation.
|
|
4
|
+
|
|
5
|
+
Call `enableNativeUI()` once after your application's DOM is mounted. Ionic and explicitly marked HTML controls share the standard Swift renderers. The theme is optional; Web layout, safe areas and spacing belong to your app or theme.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { enableNativeUI } from '@rdlabo/capacitor-native-ui';
|
|
9
|
+
|
|
10
|
+
const shell = await enableNativeUI();
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Without Ionic, import the same API from `@rdlabo/capacitor-native-ui/core`. That entry does not load Ionic's optional popover integration. See [plain HTML](framework-independent.md).
|
|
14
|
+
|
|
15
|
+
## Platform behavior
|
|
16
|
+
|
|
17
|
+
| Feature | Supported iOS | Android | Browser / older iOS |
|
|
18
|
+
| --------------------------------------- | ----------------------------------------- | --------------------- | --------------------- |
|
|
19
|
+
| Automatic Ionic controls (`ionic.auto`) | Native | Original Web controls | Original Web controls |
|
|
20
|
+
| Explicit element buttons | Native | Original Web controls | Original Web controls |
|
|
21
|
+
| Element extensions | SwiftUI | Kotlin / Compose | Original Web content |
|
|
22
|
+
| Vertical Bars | Native when available; otherwise Web rail | Web rail | Web rail |
|
|
23
|
+
| `tab-accessory` extensions | Supported iOS tab host only | Original Web content | Original Web content |
|
|
24
|
+
|
|
25
|
+
Web rails require the Ionic rail layout or an application-owned HTML `webHost`. Settings never create a device capability.
|
|
26
|
+
|
|
27
|
+
For HTML-derived light/dark behavior, see [Light and dark appearance](appearance.md). Appearance is configured in Web CSS, not through a startup option.
|
|
28
|
+
|
|
29
|
+
## All settings
|
|
30
|
+
|
|
31
|
+
`controls` chooses selection and grouping defaults. `verticalBars` configures the rail. `ionic` configures Ionic discovery and appearance. Omitted fields default to `true` in `controls`, including partial objects.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const shell = await enableNativeUI({
|
|
35
|
+
root: document.body,
|
|
36
|
+
controls: {
|
|
37
|
+
buttonGroup: true,
|
|
38
|
+
button: true,
|
|
39
|
+
backButton: true,
|
|
40
|
+
tabs: true,
|
|
41
|
+
segment: true,
|
|
42
|
+
fab: true,
|
|
43
|
+
},
|
|
44
|
+
verticalBars: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
// Optional: placement: { edge: 'trailing', webHost: appOwnedHost },
|
|
47
|
+
},
|
|
48
|
+
extensions: [],
|
|
49
|
+
ionic: {
|
|
50
|
+
auto: true,
|
|
51
|
+
verticalBars: {
|
|
52
|
+
buttonProjection: 'system',
|
|
53
|
+
buttonDefaultFill: 'glass',
|
|
54
|
+
popover: false,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
debug: false,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Selection defaults
|
|
62
|
+
|
|
63
|
+
Every control accepts `ControlProjection`. `controls` itself accepts the same values as a shorthand for all six fields.
|
|
64
|
+
|
|
65
|
+
| Value | Ordinary placement | Vertical rail placement |
|
|
66
|
+
| ------------------ | ------------------ | ----------------------- |
|
|
67
|
+
| `true` (default) | Selected | Selected |
|
|
68
|
+
| `false` | Not selected | Not selected |
|
|
69
|
+
| `'horizontalOnly'` | Selected | Not selected |
|
|
70
|
+
| `'verticalOnly'` | Not selected | Selected |
|
|
71
|
+
|
|
72
|
+
Horizontal and vertical describe **control placement**, not screen orientation or device model. These defaults do not move a control, provide a missing device capability, or make unsupported markup eligible.
|
|
73
|
+
|
|
74
|
+
| Control | Responsibility |
|
|
75
|
+
| ------------- | -------------------------------------------------------------------------------- |
|
|
76
|
+
| `buttonGroup` | Group already selected actions when the group qualifies. Never selects children. |
|
|
77
|
+
| `button` | Select ordinary buttons and Ionic menu buttons. |
|
|
78
|
+
| `backButton` | Select back navigation, independently of its parent group. |
|
|
79
|
+
| `tabs` | Select tabs and their registered search presentation. |
|
|
80
|
+
| `segment` | Select supported segments. |
|
|
81
|
+
| `fab` | Select supported floating action buttons. |
|
|
82
|
+
|
|
83
|
+
Local `data-horizontal-bars="true|false"` and `data-vertical-bars="true|false"` override the matching selection default. On `ion-buttons` they control grouping only. Explicit `data-native-ui` sources are independent of these defaults. Structural exclusions and rail availability still apply. See [toolbar selection and grouping](native-ui.md#toolbar-selection-and-grouping).
|
|
84
|
+
|
|
85
|
+
### Other settings
|
|
86
|
+
|
|
87
|
+
| Setting | Default | Meaning |
|
|
88
|
+
| -------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `root` | `document.body` | Connected source subtree. Controls outside it are not projected. |
|
|
90
|
+
| `verticalBars.enabled` | `true` | Allow rail projection when available. |
|
|
91
|
+
| `verticalBars.placement` | omitted | Optional `{ edge: 'leading' \| 'trailing', webHost?: HTMLElement }`. Edge is a preference, not a capability declaration. |
|
|
92
|
+
| `extensions` | `[]` | **Experimental.** App-specific SwiftUI / Compose extensions. Independent of `controls` and `ionic.auto`; the app decides when its reader returns content. |
|
|
93
|
+
| `ionic.auto` | `true` | Enable automatic Ionic discovery. `false` disables it, including local layout opt-ins. Explicit `data-native-ui` sources and extensions remain independent. |
|
|
94
|
+
| `ionic.verticalBars.buttonProjection` | `'system'` | `'system'` uses native styling; `'source'` projects supported Ionic styling. Local appearance overrides take precedence. |
|
|
95
|
+
| `ionic.verticalBars.buttonDefaultFill` | `'glass'` | `'glass'` or `'solid'` for source-style standalone Ionic buttons without explicit fill. Explicit fills and the clear default inside groups take precedence. |
|
|
96
|
+
| `ionic.verticalBars.popover` | `false` | Opt into Ionic popovers anchored to native rail buttons. Requires the Ionic entry and its optional peers. |
|
|
97
|
+
| `debug` | `false` | Log source elements and fallback reasons to the developer console. |
|
|
98
|
+
|
|
99
|
+
### HTML fallback rail host
|
|
100
|
+
|
|
101
|
+
`webHost` is an optional, application-styled HTML fallback rail. Its requirements are:
|
|
102
|
+
|
|
103
|
+
- Connected to the same document as `root`.
|
|
104
|
+
- Outside `root`; neither element may contain the other.
|
|
105
|
+
- Styled and positioned by the application, including any reserved content spacing.
|
|
106
|
+
|
|
107
|
+
Use a limited `root`, such as your `<main>`, with a sibling host; the default `document.body` cannot have such a host. Without a host, unavailable HTML rails leave original controls visible. Ionic's existing rail layout uses its own fallback adapter.
|
|
108
|
+
|
|
109
|
+
HTML currently supports buttons and back buttons. Tab, segment and FAB readers remain Ionic adapters; enabling those settings does not infer equivalent structures from arbitrary HTML.
|
|
110
|
+
|
|
111
|
+
## Common configurations
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// Ionic or HTML: all supported controls, rail support enabled by default.
|
|
115
|
+
const shell = await enableNativeUI();
|
|
116
|
+
|
|
117
|
+
// Alternative: automatic selection only in the rail.
|
|
118
|
+
const railOnly = await enableNativeUI({ controls: 'verticalOnly' });
|
|
119
|
+
|
|
120
|
+
// Alternative: automatically select only tabs.
|
|
121
|
+
const tabsOnly = await enableNativeUI({
|
|
122
|
+
controls: { buttonGroup: false, button: false, backButton: false, tabs: true, segment: false, fab: false },
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
These are alternatives: start only one shell. Repeating equivalent startup settings returns its handle; different settings require destroying the previous shell.
|
|
127
|
+
|
|
128
|
+
`controls: false` disables all default selection and grouping, while local attributes can still opt in. `ionic: { auto: false }` instead disables automatic Ionic discovery altogether. Neither disables explicit HTML sources or extensions.
|
|
129
|
+
|
|
130
|
+
`verticalBars.enabled: false` leaves rail controls on the Web regardless of selection. It does not affect ordinary controls. `data-native-ui-placement="element"` preserves an explicit control's original position, even with `controls: 'verticalOnly'`. For Ionic groups, put placement on the whole `ion-buttons` group so its shared native surface has one placement.
|
|
131
|
+
|
|
132
|
+
## Select controls explicitly in a customized Ionic app
|
|
133
|
+
|
|
134
|
+
Disable automatic Ionic projection while retaining the native rail and your custom extensions:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const shell = await enableNativeUI({
|
|
138
|
+
root: document.querySelector<HTMLElement>('#content')!,
|
|
139
|
+
ionic: { auto: false },
|
|
140
|
+
verticalBars: {
|
|
141
|
+
enabled: true,
|
|
142
|
+
placement: {
|
|
143
|
+
edge: 'trailing',
|
|
144
|
+
webHost: document.querySelector<HTMLElement>('#rail')!,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
extensions: [myExtension],
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<main id="content">
|
|
153
|
+
<ion-button>Keep this on the Web</ion-button>
|
|
154
|
+
<ion-button data-native-ui="button" data-native-ui-placement="rail">Project this action</ion-button>
|
|
155
|
+
<button data-native-ui="button">Project at its original position</button>
|
|
156
|
+
</main>
|
|
157
|
+
<aside id="rail" aria-label="Actions"></aside>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Keep your original handlers on each source. Explicit `ion-button` uses the standard button reader, so supported content and styling rules still apply; the attribute does not make arbitrary custom markup supported. `data-native-ui-placement="rail"` requests the system rail when available. Style the optional `webHost` and reserve space in your app; without it, unavailable explicit rails keep their source controls visible. Unmarked Ionic controls remain Web-rendered. An automatically projected group containing an explicitly marked source stays on the Web to avoid covering that source twice.
|
|
161
|
+
|
|
162
|
+
Explicit built-in sources support individual `button`, `ion-button`, and `[role="button"]` elements with `data-native-ui="button"` or `"back-button"`. This does not opt existing `ion-back-button`, `ion-menu-button`, button groups, tabs, segments, or FABs into their Ionic-specific readers. Keep automatic projection enabled for the corresponding kind, use a standard explicit button with your own click handler, or register an extension for custom UI.
|
|
163
|
+
|
|
164
|
+
`verticalBars.enabled` still governs rail availability. To leave a specific source on the Web, remove its explicit marker or use `data-native-ui="disabled"`.
|
|
165
|
+
|
|
166
|
+
## Change rail use after startup
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
await shell.disableVerticalControlArea();
|
|
170
|
+
await shell.enableVerticalControlArea();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
These methods change `verticalBars.enabled` on this shell and preserve appearance, controls and placement. They retire existing projection before transferring ownership, without creating another shell. Calls are serialized. Calling either method after `destroy()` rejects.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
const lease = await shell.suspend(); // temporarily restore all Web controls
|
|
177
|
+
await lease.resume();
|
|
178
|
+
await shell.destroy(); // stop the shell and restore sources
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Disabling rail projection does not remove application CSS or change application-owned spacing. Update your own layout alongside this setting if needed.
|
|
182
|
+
|
|
183
|
+
## Request a check after an unobserved change
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
shell.markForCheck();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Use this after changing a property or reader state that does not cause a DOM mutation. It schedules a check, batches repeated requests, and sends only changed native snapshots. It returns `void`; it does not wait for native completion or render your framework's DOM. Calls after destruction are ignored. See [source checks and observation](native-ui-extensions.md#schedule-a-source-check).
|
|
190
|
+
|
|
191
|
+
## Understand why a source stays on the Web
|
|
192
|
+
|
|
193
|
+
Temporarily add `debug: true` to your existing startup options, then rebuild the Web app. Do not start a second shell to enable diagnostics:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
const shell = await enableNativeUI({ debug: true, extensions: [myExtension] });
|
|
197
|
+
console.info(shell.getStatus());
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Open the WebView developer console and filter for `[Native UI]`. Diagnostics include the DOM source when available. Each reason is logged once per source during that activation, so scrolling or repeated `markForCheck()` calls do not flood the console. Props, action payloads and application exception messages are not logged.
|
|
201
|
+
|
|
202
|
+
| Diagnostic | What to check |
|
|
203
|
+
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `web-fallback` | Platform support, native plugin registration, sync and native rebuild. |
|
|
205
|
+
| `unregistered-extension` | Native registration name matches the DOM definition and runs before startup. Newly inserted sources are checked too. |
|
|
206
|
+
| `reader-error` / `invalid-props` | Reader implementation, JSON-only values and the 64K size limit. |
|
|
207
|
+
| `unsupported-placement` / `missing-tab-owner` | Platform and owning native tab host for a tab accessory. |
|
|
208
|
+
| `source-hidden` / `source-clipped` / `source-focused` | Visibility, clipping/transformed ancestors, or a focused Web descendant. |
|
|
209
|
+
| `reader-opt-out` | Your reader returned null. |
|
|
210
|
+
| `unsupported-control` | Supported markup, accessible name, fixed placement and styling. |
|
|
211
|
+
| `native-rejected` | Native props decoding, placement or device support. The bridge reports rejection, not the native exception. |
|
|
212
|
+
|
|
213
|
+
`getStatus()` summarizes the shell; it is not a per-element diagnostic report. Normal navigation, suspension and unsupported content can intentionally retain Web presentation. Turn debug off after investigating; it does not change projection eligibility.
|
|
214
|
+
|
|
215
|
+
## Existing applications
|
|
216
|
+
|
|
217
|
+
The standalone `enableVerticalControlArea()` defaults selection to `'verticalOnly'`. Its `setPlacement()` helper is a deprecated Web CSS adapter; use the theme's `setVerticalBarsLayout({ edge, inset })` for Web layout. Native availability comes from the plugin's device metrics.
|
|
218
|
+
|
|
219
|
+
See [upgrading from 1.2.1](migration.md) for package separation, removed options and changed partial-control defaults.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Native UI without Ionic
|
|
2
|
+
|
|
3
|
+
Add native buttons to an existing Web app without Ionic or theme CSS. Keep labels, state, and click handlers on ordinary HTML elements. Standard buttons require no custom Swift registration.
|
|
4
|
+
|
|
5
|
+
Use a Capacitor 8 app with an iOS project, Xcode 26+, and an iOS 26+ device or simulator. Browsers and Android retain Web controls. For custom Android native content, use [Kotlin/Compose extensions](native-ui-extensions.md#2b-register-compose-content-on-android).
|
|
6
|
+
|
|
7
|
+
Start with [how projection works](../README.md#how-projection-works): the HTML element remains the source while native controls or Web proxies provide its presentation.
|
|
8
|
+
|
|
9
|
+
**Want only Vertical Bars?** Follow steps 1–2 to install and mark your HTML buttons, then use the [Web rail configuration](#optional-web-rail) in place of step 3. Remove explicit markers from buttons you want to leave on the Web. Style the rail host in your application and build with step 4 for iOS. You do not need Ionic, `@rdlabo/ionic-theme-ios27`, or `vertical-bars.css`.
|
|
10
|
+
|
|
11
|
+
For Vertical Bars in an app using `ion-tabs`, `ion-toolbar` or other Ionic components with a custom theme, use the [Ionic Vertical Bars setup](ionic-vertical-bars.md#try-it-in-your-existing-ionic-app) instead. The framework (Angular, React or Vue) alone does not determine which adapter to use.
|
|
12
|
+
|
|
13
|
+
## 1. Install the plugin
|
|
14
|
+
|
|
15
|
+
Keep your existing Capacitor 8 installation and add the plugin directly:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @rdlabo/capacitor-native-ui
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The `/core` entry requires `@capacitor/core` but no Ionic dependencies. If this is currently a browser-only application, configure its Capacitor 8 iOS project before running native controls.
|
|
22
|
+
|
|
23
|
+
## 2. Mark source controls
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<main id="app">
|
|
27
|
+
<button type="button" data-native-ui="button" data-native-ui-placement="element">Add</button>
|
|
28
|
+
<button type="button" data-native-ui="back-button" data-native-ui-placement="rail">Back</button>
|
|
29
|
+
</main>
|
|
30
|
+
<aside id="rail" aria-label="Navigation"></aside>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Attribute | Value | Effect |
|
|
34
|
+
| -------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
35
|
+
| `data-native-ui` | `button` | Project a standard action button. |
|
|
36
|
+
| `data-native-ui` | `back-button` | Project a standard back button. The source click handler owns navigation; the plugin never calls `history.back()`. |
|
|
37
|
+
| `data-native-ui` | `disabled` | Exclude the element and its subtree from projection. |
|
|
38
|
+
| `data-native-ui-placement` | `element` (default) | Use the source element's rectangle. |
|
|
39
|
+
| `data-native-ui-placement` | `rail` | Request Vertical Bars; use the optional Web rail host when native rail presentation is unavailable. |
|
|
40
|
+
|
|
41
|
+
Apply `button` and `back-button` to `<button>`, explicitly marked `ion-button`, or elements exposing `role="button"` and click behavior. Unmarked HTML is never inferred to be a control. `data-native-ui-placement` chooses placement for a declared source; it does not opt an unmarked HTML element into projection.
|
|
42
|
+
|
|
43
|
+
Keep the control's data and behavior on its source:
|
|
44
|
+
|
|
45
|
+
| Source property | Projection behavior |
|
|
46
|
+
| -------------------- | ------------------------------------------------------------------------------------------ |
|
|
47
|
+
| Accessible name | Resolves from `aria-labelledby`, then `aria-label`, then text. |
|
|
48
|
+
| Artwork | Uses supported static SVG. |
|
|
49
|
+
| Disabled state | Respects native `disabled`, a disabled fieldset, and `aria-disabled="true"`. |
|
|
50
|
+
| Visibility and scope | Hidden, removed, excluded or out-of-scope sources are retired; stale actions are rejected. |
|
|
51
|
+
| Application handlers | Remain on the original element. |
|
|
52
|
+
|
|
53
|
+
Native and Web-proxy actions click the original element. An ordinary `<button type="submit">` therefore keeps its original form behavior. Use `type="button"` for actions that must not submit. The native bridge does not preserve browser user-activation privileges for APIs such as popup opening.
|
|
54
|
+
|
|
55
|
+
## 3. Start after mounting the controls
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { enableNativeUI } from '@rdlabo/capacitor-native-ui/core';
|
|
59
|
+
|
|
60
|
+
const shell = await enableNativeUI({
|
|
61
|
+
root: document.querySelector<HTMLElement>('#app')!,
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Call once after the source root exists. Keep the handle and call `await shell.destroy()` when that root is disposed. The plugin does not add page padding or safe-area rules; retain your application's CSS. For Angular, React, or Vue, use the [app-root lifecycle examples](setup.md#place-startup-in-your-frameworks-app-root) with the `/core` import. For animated navigation, use [suspension through completion and cancellation](setup.md#navigation-without-the-theme).
|
|
66
|
+
|
|
67
|
+
### Match the HTML theme
|
|
68
|
+
|
|
69
|
+
For a custom HTML theme, declare `color-scheme: light` or `color-scheme: dark` in the same CSS that selects your Web palette. Background colors alone do not select native appearance. See [Light and dark appearance](appearance.md) for a manual-toggle example, precedence when Ionic/theme variables are present, and shared rail behavior.
|
|
70
|
+
|
|
71
|
+
## 4. Build and run on iOS
|
|
72
|
+
|
|
73
|
+
Build your Web app, run `npx cap sync ios`, and launch the iOS project from Xcode. With the step 3 configuration, Add renders natively and invokes its original click handler. With the rail configuration below, Add retains native element placement and Back uses the native rail when available. In a browser, the same button remains an ordinary Web button. Unmarked HTML remains unchanged.
|
|
74
|
+
|
|
75
|
+
If a button stays on the Web on supported iOS, check that it is visible within the configured root, has an accessible name, and that the direct plugin dependency was synced and the iOS app rebuilt.
|
|
76
|
+
|
|
77
|
+
In an app that also uses Ionic, set `ionic: { auto: false }` to project only explicit sources and registered extensions. See [selective Ionic projection](configuration.md#select-controls-explicitly-in-a-customized-ionic-app).
|
|
78
|
+
|
|
79
|
+
## Optional Web rail
|
|
80
|
+
|
|
81
|
+
For a Web fallback rail, use this instead of the startup call in step 3. Call once after both the source root and rail host are mounted; call `await shell.destroy()` when their owner is disposed. Do not start a second shell:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { enableNativeUI } from '@rdlabo/capacitor-native-ui/core';
|
|
85
|
+
|
|
86
|
+
const shell = await enableNativeUI({
|
|
87
|
+
root: document.querySelector<HTMLElement>('#app')!,
|
|
88
|
+
verticalBars: {
|
|
89
|
+
placement: {
|
|
90
|
+
edge: 'trailing',
|
|
91
|
+
webHost: document.querySelector<HTMLElement>('#rail')!,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The plugin uses device-reported rail availability and resolves leading/trailing through the document direction. A placement preference never forces an unsupported system rail. Native rejection restores Web presentation. With `webHost`, explicitly rail-marked HTML controls have Web proxies when native projection is unavailable. Without it, their originals remain visible. The application styles and positions the host and reserves any content spacing. The host must be outside the source root.
|
|
98
|
+
|
|
99
|
+
Explicit markup chooses placement: Back goes to the rail and Add keeps native element placement. To project only Back, remove `data-native-ui="button"` from Add. `controls` does not disable explicit source declarations. To change rail use later, call `await shell.disableVerticalControlArea()` or `await shell.enableVerticalControlArea()`. Disabling restores original rail controls and removes Web rail proxies without destroying ordinary native projection.
|
|
100
|
+
|
|
101
|
+
Currently the HTML adapter supports buttons and back buttons. Arbitrary HTML tabs, segments, searchbars and FAB structures are not translated. Ionic adapters continue to support their existing controls. Custom element extensions work with the same scoped root; the existing `tab-accessory` extension placement still requires an Ionic tab-bar owner.
|
|
102
|
+
|
|
103
|
+
For device-driven spacing and placement, see [Use with Capacitor Foldable](vertical-bars.md#use-with-capacitor-foldable). Read its initial placement and subscribe to changes, applying the edge and inset to your own CSS; the theme package is not required.
|
|
104
|
+
|
|
105
|
+
## Example
|
|
106
|
+
|
|
107
|
+
The [plain HTML example](https://github.com/rdlabo-dev/ionic-theme-ios27/tree/main/examples/native-ui-html) demonstrates Add, routerless Home/Details/Back navigation, optional Web rail projection and cleanup. It uses no Ionic or theme CSS.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Vertical Bars while keeping your theme
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Add Vertical Bars while keeping your theme
|
|
6
|
+
|
|
7
|
+
Keep your app's existing appearance and add Vertical Bars. Choose the setup for your app's components, whether you use Angular, React, Vue or another framework.
|
|
8
|
+
|
|
9
|
+
Read [how projection works](../README.md#how-projection-works) for the relationship between source DOM, projected controls and actions. Then choose your setup below.
|
|
10
|
+
|
|
11
|
+
## Choose your setup
|
|
12
|
+
|
|
13
|
+
| Your app | Start here | What you add |
|
|
14
|
+
| ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
15
|
+
| Ionic components with your own theme, Ionic's default styles, or another theme | [Ionic setup below](#try-it-in-your-existing-ionic-app) | Standalone rail CSS and the Ionic Native UI adapter. Keep your existing theme. |
|
|
16
|
+
| No Ionic components (plain HTML, React, Vue, Angular, etc.) | [HTML setup](framework-independent.md), then its [Web rail configuration](framework-independent.md#optional-web-rail) | Only `@rdlabo/capacitor-native-ui`, using its `/core` entry. Your app supplies the rail layout. |
|
|
17
|
+
| Already using the iOS 27 theme and Native UI | [Connect device placement](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail) | Reuse your existing runtime. Do not start another one. |
|
|
18
|
+
|
|
19
|
+
**Without Ionic, follow the HTML guide instead of the steps below.** You do not need `@rdlabo/ionic-theme-ios27`, `vertical-bars.css`, `ion-app`, or Ionic navigation animations. Explicitly marked HTML buttons and back buttons are supported; arbitrary HTML tabs are not automatically converted. A browser preview requires an application-styled `webHost`; native rail presentation also requires supported iOS hardware and software.
|
|
20
|
+
|
|
21
|
+
The rest of this page is for **Ionic apps**. Both Ionic `ios` and `md` modes are supported. Tabs and supported toolbar actions move to the side; content and horizontal controls keep their existing appearance.
|
|
22
|
+
|
|
23
|
+
iPhone Duo support and Vertical Bars are stable starting with version 2.0.0.
|
|
24
|
+
|
|
25
|
+
## Try it in your existing Ionic app
|
|
26
|
+
|
|
27
|
+
### 1. Install and load the standalone stylesheet
|
|
28
|
+
|
|
29
|
+
Start with your existing Ionic app (Ionic `>=8.8.1 <10`, Capacitor 8). Add the two packages:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @rdlabo/ionic-theme-ios27 @rdlabo/capacitor-native-ui
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Keep your existing theme imports. Add this to your global Sass file:
|
|
36
|
+
|
|
37
|
+
```scss
|
|
38
|
+
@use '@rdlabo/ionic-theme-ios27/dist/css/vertical-bars.css';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The full iOS 27 theme stylesheets are not required. Preview these steps in Chrome before connecting an iPhone Duo.
|
|
42
|
+
|
|
43
|
+
### 2. Opt your app into the side layout
|
|
44
|
+
|
|
45
|
+
Add the class to your existing app root and keep the content inside it:
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<ion-app class="ios-theme-vertical-bars">
|
|
49
|
+
<!-- Keep your existing pages, tabs, and toolbar controls here. -->
|
|
50
|
+
</ion-app>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The preview reserves `80px` on the physical right. To preview the left side, also add `ios-theme-vertical-bars-left`.
|
|
54
|
+
|
|
55
|
+
### 3. Connect your navigation animation
|
|
56
|
+
|
|
57
|
+
Configure `navAnimation` before Ionic initializes. Starting the rail runtime does not register this option. The adapter waits for native control retirement and coordinates swipe progress and cancellation while keeping your existing animation.
|
|
58
|
+
|
|
59
|
+
#### Keep Ionic's default animation
|
|
60
|
+
|
|
61
|
+
If you have not configured `navAnimation`, wrap Ionic's standard builders. Select the builder from Ionic's transition `mode` so both `ios` and `md` keep their usual animation:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { iosTransitionAnimation, mdTransitionAnimation, type AnimationBuilder } from '@ionic/core';
|
|
65
|
+
import { withNativeUITransition } from '@rdlabo/ionic-theme-ios27';
|
|
66
|
+
|
|
67
|
+
const defaultTransition: AnimationBuilder = (baseEl, opts) =>
|
|
68
|
+
(opts.mode === 'ios' ? iosTransitionAnimation : mdTransitionAnimation)(baseEl, opts);
|
|
69
|
+
|
|
70
|
+
const ionicConfig = {
|
|
71
|
+
navAnimation: withNativeUITransition(defaultTransition),
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Merge this option into your existing Ionic configuration before initialization: pass it to Angular's `provideIonicAngular()`, React's `setupIonicReact()`, or Vue's `IonicVue` plugin options. Keep your existing theme stylesheet imports. No iOS 27 theme stylesheet is required.
|
|
76
|
+
|
|
77
|
+
#### Use the theme's iOS animation
|
|
78
|
+
|
|
79
|
+
If you already use the iOS 27 transition, keep this configuration. It includes the native adapter and excludes the horizontal back-button effect in vertical layouts; no additional wrapper is needed. Importing this JavaScript entry point does not load the theme stylesheets.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { iosTransitionAnimation } from '@rdlabo/ionic-theme-ios27';
|
|
83
|
+
|
|
84
|
+
const ionicConfig = {
|
|
85
|
+
navAnimation: iosTransitionAnimation,
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Apply this option to your existing iOS-mode configuration and keep your MD configuration.
|
|
90
|
+
|
|
91
|
+
#### Keep your custom animation
|
|
92
|
+
|
|
93
|
+
If your app uses another builder for `navAnimation`, wrap it:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import type { AnimationBuilder } from '@ionic/core';
|
|
97
|
+
import { withNativeUITransition } from '@rdlabo/ionic-theme-ios27';
|
|
98
|
+
|
|
99
|
+
// Pass the animation builder your app already uses.
|
|
100
|
+
const configureNavigation = (existingTransition: AnimationBuilder) => ({
|
|
101
|
+
navAnimation: withNativeUITransition(existingTransition),
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The adapter returns the original `Animation`, preserving its effects, duration, and easing. Use it only for navigation, not modal or popover animations. The builder must return a fresh `Animation` for each navigation; Ionic destroys it after the transition. Keep lifecycle events for control registration and transitions without animation.
|
|
106
|
+
|
|
107
|
+
The adapter keeps the builder's animation targets, including any horizontal back-button effect. If you need the iOS 27 transition with that effect excluded in vertical layouts, use `iosTransitionAnimation` from `@rdlabo/ionic-theme-ios27` as your `navAnimation` instead. It already includes the adapter, so no wrapper is needed.
|
|
108
|
+
|
|
109
|
+
### 4. Start the controls after the app root is mounted
|
|
110
|
+
|
|
111
|
+
Call this once from your application startup after `ion-app` exists in the DOM:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { enableNativeUI } from '@rdlabo/capacitor-native-ui';
|
|
115
|
+
|
|
116
|
+
const rail = await enableNativeUI({ controls: 'verticalOnly' });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**What you should see:** your existing tab bar moves to the side, and fixed-toolbar buttons with an `ion-icon` or SVG using `slot="icon-only"` appear there too. Content keeps its existing theme and leaves room for the controls. The Web tab rail displays icons; pressing and dragging reveals tab labels.
|
|
120
|
+
|
|
121
|
+
Use your existing Ionic click handlers, routing, and form associations. All button fills (`default`, `clear`, `solid`, and `outline`) use the same `icon-only` rule, including submit buttons. Actions without that slot remain horizontal. Add `.ios-theme-horizontal-only` to an `ion-buttons` group or individual `ion-button` to keep an action in the horizontal toolbar.
|
|
122
|
+
|
|
123
|
+
When the application owner is disposed, call `await rail.destroy()` to restore the original controls and release the runtime. If you already use `enableNativeUI()`, keep that runtime and follow the [shared placement guide](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail).
|
|
124
|
+
|
|
125
|
+
### Optional: choose native button appearance
|
|
126
|
+
|
|
127
|
+
See [Choose button appearance](vertical-bars.md#choose-button-appearance) for appearance settings and local overrides.
|
|
128
|
+
|
|
129
|
+
The default is `system`: SwiftUI styles vertical buttons and tints their icons. If your existing theme should supply their fill and colors, replace the startup call in step 4 with:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const rail = await enableNativeUI({
|
|
133
|
+
controls: 'verticalOnly',
|
|
134
|
+
ionic: { verticalBars: { buttonProjection: 'source', buttonDefaultFill: 'solid' } },
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The `solid` default suits ordinary Ionic buttons. Buttons inside `ion-buttons` still default to clear; set `fill="solid"` explicitly to project their background. For one-off exceptions, use the [local projection settings](vertical-bars.md#override-individual-buttons-or-groups). These settings affect native vertical buttons only; Web clones keep their existing appearance.
|
|
139
|
+
|
|
140
|
+
### If the preview does not appear
|
|
141
|
+
|
|
142
|
+
| What you see | What to check |
|
|
143
|
+
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| No space at the side | Load `vertical-bars.css` and put the class on `ion-app`. |
|
|
145
|
+
| Space appears, but controls stay horizontal | Start `enableNativeUI({ controls: 'verticalOnly' })` after mounting the app root. Use existing tabs or `slot="icon-only"` actions in a fixed header/footer toolbar. |
|
|
146
|
+
| One action stays horizontal | Check for `slot="icon-only"` on the icon and a fixed toolbar outside scrolling content. Explicitly excluded controls and controls in centered modals stay horizontal; `fill` and `type="submit"` do not prevent movement. See [control requirements](vertical-bars.md#toolbar-actions). |
|
|
147
|
+
|
|
148
|
+
## Connect an iPhone Duo
|
|
149
|
+
|
|
150
|
+
Install [`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable) for device state:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install @erkamyaman/capacitor-foldable
|
|
154
|
+
npx cap sync ios
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Use Capacitor 8.5 or later and build with Xcode 27.1 or newer for actual rail placement and hinge posture on iOS 27.1. Keep your existing SPM or CocoaPods integration; see [Native UI setup](setup.md#4-build-sync-and-run). Keep the theme's `vertical-bars.css`; the device plugin's `ionic-tabs.css` is not needed with our rail projection.
|
|
158
|
+
|
|
159
|
+
Replace the browser-only startup with the [device placement setup](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail). That setup sends initial values and `barPlacementChange` events to `setVerticalBarsLayout({ edge: verticalBarEdge, inset })`, with subscriptions and cleanup kept in your application.
|
|
160
|
+
|
|
161
|
+
Pass Foldable's reported logical edge and measured inset to `setVerticalBarsLayout`. The helper resolves RTL. A null edge restores the ordinary Web layout; Native UI detects native rail availability independently.
|
|
162
|
+
|
|
163
|
+
On supported iOS, controls in the rail use native SwiftUI rendering; your custom Web styling still applies to ordinary content and horizontal controls. Web and Android use Web clones.
|
|
164
|
+
|
|
165
|
+
## Use hinge posture without projecting controls
|
|
166
|
+
|
|
167
|
+
If your existing theme needs only a posture-driven split pane or a layout switch, do not start a projection runtime or add `.ios-theme-vertical-bars`. Pass `Foldable.getFoldState()` results and `foldStateChange` events to `applyFoldStateClasses(root, fold)`, removing the listener when finished. There is no separate start/stop monitoring call.
|
|
168
|
+
|
|
169
|
+
See [Read the device layout](../../theme-ios27/docs/iphone-duo.md#read-the-device-layout) for the subscription example, null values, and monitoring lifetime. See [Adapt the split pane](../../theme-ios27/docs/iphone-duo.md#adapt-the-split-pane) for the opt-in width rules and half-open state.
|
|
170
|
+
|
|
171
|
+
## Shared layout rules and API
|
|
172
|
+
|
|
173
|
+
Safe-area handling, overlays, RTL, control eligibility, Web simulation, and the handle API are documented in [Vertical Bars](vertical-bars.md). Read that reference when you need to customize this setup; it is not an additional installation step.
|