@rdlabo/capacitor-native-ui 1.2.1 → 2.0.0-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -24
- package/dist/core.d.ts +2 -2
- package/dist/native/activation.d.ts.map +1 -1
- package/dist/native/activation.js +19 -19
- package/dist/native/activation.js.map +1 -1
- package/dist/native/definitions.d.ts +22 -40
- package/dist/native/definitions.d.ts.map +1 -1
- package/dist/native/html.d.ts.map +1 -1
- package/dist/native/html.js +10 -1
- package/dist/native/html.js.map +1 -1
- package/dist/native/index.d.ts +2 -2
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +2 -11
- package/dist/native/index.js.map +1 -1
- package/dist/native/overlays/controller.js +1 -1
- package/dist/native/overlays/controller.js.map +1 -1
- package/dist/native/prehide.js +1 -1
- package/dist/native/prehide.js.map +1 -1
- package/dist/native/runtime.d.ts +1 -1
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +8 -20
- package/dist/native/runtime.js.map +1 -1
- package/dist/native/shared/candidate.d.ts +2 -2
- package/dist/native/shared/candidate.d.ts.map +1 -1
- package/dist/native/shared/candidate.js +2 -2
- package/dist/native/shared/candidate.js.map +1 -1
- package/dist/native/shared/control-options.d.ts +6 -0
- package/dist/native/shared/control-options.d.ts.map +1 -0
- package/dist/native/shared/control-options.js +17 -0
- package/dist/native/shared/control-options.js.map +1 -0
- package/dist/native/shared/toolbar-options.d.ts +6 -2
- package/dist/native/shared/toolbar-options.d.ts.map +1 -1
- package/dist/native/shared/toolbar-options.js +7 -15
- package/dist/native/shared/toolbar-options.js.map +1 -1
- package/dist/native/startup.d.ts.map +1 -1
- package/dist/native/startup.js +0 -16
- package/dist/native/startup.js.map +1 -1
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +3 -2
- package/dist/native/vertical-bars-web.js.map +1 -1
- package/dist/vertical-bars.d.ts +2 -2
- package/docs/appearance.md +45 -0
- package/docs/configuration.md +106 -74
- package/docs/framework-independent.md +52 -13
- package/docs/ionic-vertical-bars.md +186 -0
- package/docs/migration.md +67 -89
- package/docs/native-ui-extensions.md +61 -32
- package/docs/native-ui.md +187 -68
- package/docs/setup.md +46 -25
- package/docs/vertical-bars.md +118 -79
- package/ios/Sources/NativeUIPlugin/NativeUIPlugin.swift +1 -1
- package/package.json +1 -1
- package/src/core.ts +1 -2
- package/src/native/activation.ts +13 -22
- package/src/native/definitions.ts +40 -90
- package/src/native/html.ts +11 -5
- package/src/native/index.ts +2 -11
- package/src/native/overlays/controller.ts +1 -1
- package/src/native/prehide.ts +1 -1
- package/src/native/runtime.ts +7 -16
- package/src/native/shared/candidate.ts +4 -4
- package/src/native/shared/control-options.ts +20 -0
- package/src/native/shared/toolbar-options.ts +8 -12
- package/src/native/startup.ts +0 -16
- package/src/native/vertical-bars-web.ts +4 -2
- package/src/vertical-bars.ts +2 -1
- package/docs/native-mini-player.md +0 -47
package/docs/vertical-bars.md
CHANGED
|
@@ -1,46 +1,86 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Vertical Bars
|
|
2
|
+
title: Vertical Bars
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Vertical Bars
|
|
5
|
+
# Vertical Bars
|
|
6
6
|
|
|
7
|
-
Vertical Bars moves eligible Ionic
|
|
7
|
+
Vertical Bars moves eligible Ionic controls or explicitly marked HTML buttons into a side rail while keeping the original elements as the source of labels, icons and behavior. It works with the iOS theme or an existing Ionic theme, independently of hinge posture and the full Native UI.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Choose your setup before using this reference:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- **Ionic with a custom or default theme:** [Ionic rail setup](ionic-vertical-bars.md#try-it-in-your-existing-ionic-app).
|
|
12
|
+
- **No Ionic:** [HTML setup](framework-independent.md) and its [device rail setup](framework-independent.md#device-placement-for-vertical-bars). Use `/core` and your own layout; the Ionic setup below does not apply.
|
|
13
|
+
- **Existing iOS 27 theme and Native UI:** [Connect device placement](../../capacitor-native-ui/docs/vertical-bars.md#use-with-capacitor-foldable) using your existing runtime.
|
|
14
|
+
|
|
15
|
+
This page covers device-plugin integration, control eligibility, native appearance and runtime options. For posture-driven Ionic split panes, see [Device layout and split panes](../../theme-ios27/docs/vertical-bars-layout.md).
|
|
16
|
+
|
|
17
|
+
Vertical Bars and iPhone Duo support are stable starting with version 2.0.0.
|
|
12
18
|
|
|
13
19
|
See [shared configuration](configuration.md) for all defaults and live `rail.enableVerticalControlArea()` / `rail.disableVerticalControlArea()` methods. [Plain HTML controls](framework-independent.md) use the same native rail renderers.
|
|
14
20
|
|
|
15
21
|
## Enable Vertical Bars
|
|
16
22
|
|
|
17
|
-
Install the
|
|
23
|
+
Install Native UI and the required Foldable device-state plugin as direct application dependencies:
|
|
18
24
|
|
|
19
25
|
```sh
|
|
20
|
-
npm install @rdlabo/capacitor-native-ui
|
|
26
|
+
npm install @rdlabo/capacitor-native-ui @erkamyaman/capacitor-foldable
|
|
21
27
|
```
|
|
22
28
|
|
|
23
|
-
For
|
|
29
|
+
For Ionic, follow the [standard setup](ionic-vertical-bars.md): load your rail CSS, configure navigation, then connect Foldable's initial placement and `barPlacementChange` events before starting one runtime. The [integration below](#use-with-capacitor-foldable) also covers existing runtimes and application-owned CSS. Build your Web assets, run `npx cap sync ios`, and rebuild the native app.
|
|
30
|
+
|
|
31
|
+
A runtime alone does not reserve Web space. Keep the device subscription for the standard setup; manual placement belongs to the [optional browser preview](ionic-vertical-bars.md#optional-browser-preview). If Native UI is already active, reuse it rather than starting another runtime.
|
|
32
|
+
|
|
33
|
+
The `/vertical-bars` entry point requires `@capacitor/core`, including in browser builds. Using only the theme’s rail CSS needs no native runtime. For native setup and navigation transitions, follow the [existing-theme guide](ionic-vertical-bars.md).
|
|
34
|
+
|
|
35
|
+
On supported Capacitor iOS, eligible controls use native SwiftUI rendering. Web, Android and unavailable native projection use Web clones. A browser preview can verify layout and actions, but cannot compare native button appearance.
|
|
36
|
+
|
|
37
|
+
## Placement and Web layout
|
|
38
|
+
|
|
39
|
+
The application or theme owns Web spacing, rail positioning, and fallback styles. The plugin reads the supplied Ionic layout and placement; it does not reserve Web content space on its own. With the iOS27 theme, use its `setVerticalBarsLayout({ edge, inset })` helper. Native-only applications use their own CSS; no theme helper is required. Native availability comes from device metrics, independently of Web layout.
|
|
40
|
+
|
|
41
|
+
The [theme's optional rail stylesheet](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/vertical-bars-layout.md) supplies one ready-made layout. Its classes and CSS variables are not a requirement to install the theme package: applications may provide equivalent layout themselves. The runtime currently uses the `ios-theme-vertical-bars` class on `ion-app` to identify the rail layout, regardless of who supplies its styles.
|
|
42
|
+
|
|
43
|
+
## Use with Capacitor Foldable
|
|
44
|
+
|
|
45
|
+
[`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable) is required in the standard Vertical Bars setup. Install it with Native UI as shown above, and connect its device placement to your Web layout. Native UI does not subscribe to Foldable automatically.
|
|
46
|
+
|
|
47
|
+
| Owner | Responsibility |
|
|
48
|
+
| -------------------- | ------------------------------------------------------------------------------------------- |
|
|
49
|
+
| Foldable | Report initial device state and subsequent placement/posture changes. |
|
|
50
|
+
| Application or theme | Reserve Web space and choose the layout using the reported logical edge and measured inset. |
|
|
51
|
+
| Native UI | Check native rail availability independently and project eligible source controls. |
|
|
24
52
|
|
|
25
|
-
|
|
53
|
+
### With the theme's Ionic rail layout
|
|
54
|
+
|
|
55
|
+
After loading [the rail stylesheet](../../theme-ios27/docs/vertical-bars-layout.md) and mounting `ion-app`, connect device placement and start the runtime once:
|
|
26
56
|
|
|
27
57
|
```ts
|
|
58
|
+
import { Foldable, type BarPlacement } from '@erkamyaman/capacitor-foldable';
|
|
59
|
+
import { setVerticalBarsLayout } from '@rdlabo/ionic-theme-ios27';
|
|
28
60
|
import { enableNativeUI } from '@rdlabo/capacitor-native-ui';
|
|
29
61
|
|
|
30
|
-
const
|
|
62
|
+
const applyPlacement = ({ verticalBarEdge, inset }: BarPlacement) => setVerticalBarsLayout({ edge: verticalBarEdge, inset });
|
|
63
|
+
const listener = await Foldable.addListener('barPlacementChange', applyPlacement);
|
|
64
|
+
applyPlacement(await Foldable.getBarPlacement());
|
|
65
|
+
const rail = await enableNativeUI({ controls: 'verticalOnly' });
|
|
31
66
|
```
|
|
32
67
|
|
|
33
|
-
|
|
68
|
+
If a runtime is already active, keep it and add only the placement subscription. `setVerticalBarsLayout` resolves logical edges for RTL; a null edge restores the ordinary Web layout. Use the measured inset instead of a fixed width. Keep the theme's rail stylesheet; Foldable's `ionic-tabs.css` is not needed for this integration.
|
|
34
69
|
|
|
35
|
-
|
|
70
|
+
When the application owner is disposed, remove its subscription and destroy the runtime it owns:
|
|
36
71
|
|
|
37
|
-
|
|
72
|
+
```ts
|
|
73
|
+
await listener.remove();
|
|
74
|
+
await rail.destroy();
|
|
75
|
+
```
|
|
38
76
|
|
|
39
|
-
|
|
77
|
+
### With application-owned CSS or without Ionic
|
|
40
78
|
|
|
41
|
-
|
|
79
|
+
Use the same `getBarPlacement()` / `barPlacementChange` subscription, but apply the edge and inset to your own CSS instead of calling `setVerticalBarsLayout`. A null edge means your layout should restore its ordinary spacing. For Ionic, retain the rail layout classes described above; for plain HTML, use the `/core` entry and [device rail setup](framework-independent.md#device-placement-for-vertical-bars). Neither Foldable nor Native UI supplies the application's CSS. Explicit HTML rail markers remain active independently of CSS; to return those sources to their original placement on a null edge, call `disableVerticalControlArea()`, then `enableVerticalControlArea()` when a rail edge returns. Serialize these changes with your layout updates.
|
|
42
80
|
|
|
43
|
-
|
|
81
|
+
Keep Web layout updates separate from native availability: do not pass Foldable's edge through the deprecated `nativeEdge` option. Native UI reads that capability itself. Only an optional browser layout preview substitutes simulated placement for this subscription.
|
|
82
|
+
|
|
83
|
+
For hinge-driven content layout, read `getFoldState()` and subscribe to `foldStateChange` separately. Hinge posture does not select controls or enable the rail; see the theme's [posture subscription and split-pane example](../../theme-ios27/docs/vertical-bars-layout.md#read-the-device-layout).
|
|
44
84
|
|
|
45
85
|
## Native rendering
|
|
46
86
|
|
|
@@ -48,7 +88,6 @@ On supported iOS versions, adding `.ios-theme-vertical-bars` changes only contro
|
|
|
48
88
|
|
|
49
89
|
The SwiftUI surface is clipped and hit-tested to the system rail. Web content remains visible and interactive outside that physical region. The runtime optimistically updates tab selection before forwarding the action to the original `ion-tab-button`, using the same event and stale-revision protection as the other native controls. Overlay layout follows the [optional theme rail layout](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/vertical-bars-layout.md#reserve-the-vertical-rail) when that stylesheet is used; custom layouts remain the application’s responsibility.
|
|
50
90
|
|
|
51
|
-
|
|
52
91
|
### Native popovers (Vertical Bars only)
|
|
53
92
|
|
|
54
93
|
Native popover presentation is limited to popovers anchored to accepted native Vertical Bars `ion-button` controls. It is available with the standalone Vertical Bars runtime; the full Native UI is not required. Enable it explicitly when starting the runtime (`popover` defaults to `false`):
|
|
@@ -57,24 +96,24 @@ Native popover presentation is limited to popovers anchored to accepted native V
|
|
|
57
96
|
import { enableNativeUI } from '@rdlabo/capacitor-native-ui';
|
|
58
97
|
|
|
59
98
|
const rail = await enableNativeUI({
|
|
60
|
-
|
|
99
|
+
controls: 'verticalOnly',
|
|
100
|
+
ionic: { verticalBars: { popover: true } },
|
|
61
101
|
});
|
|
62
102
|
```
|
|
63
103
|
|
|
64
104
|
Use an `ion-popover` whose `trigger` identifies the projected button, or pass that button's click event to `PopoverController.create({ event, ... })`. Ordinary toolbar and body popovers remain on the Web. If a rail-anchored popover falls back to Web rendering, the rail also returns to Web projection until dismissal. See [native popover behavior and limitations](native-ui.md#popovers-anchored-to-vertical-bars).
|
|
65
105
|
|
|
66
|
-
|
|
67
106
|
## Toolbar actions
|
|
68
107
|
|
|
69
108
|
A standard `ion-back-button` can project from outside a fixed toolbar, including routed content or a persistent app shell. `ion-menu-button` and other toolbar actions require a fixed toolbar.
|
|
70
109
|
|
|
71
110
|
By default, an `ion-button` moves into the rail when it contains an `ion-icon` or SVG with `slot="icon-only"`. The button must be in a fixed `ion-toolbar` directly inside `ion-header` or `ion-footer`, outside scrolling `ion-content`.
|
|
72
111
|
|
|
73
|
-
| Icon markup
|
|
74
|
-
|
|
|
75
|
-
| `slot="icon-only"`
|
|
112
|
+
| Icon markup | Placement |
|
|
113
|
+
| ---------------------------------------- | --------------------------- |
|
|
114
|
+
| `slot="icon-only"` | Vertical rail |
|
|
76
115
|
| `slot="start"`, `slot="end"`, or no slot | Original horizontal toolbar |
|
|
77
|
-
| No icon
|
|
116
|
+
| No icon | Original horizontal toolbar |
|
|
78
117
|
|
|
79
118
|
```html
|
|
80
119
|
<ion-header>
|
|
@@ -96,29 +135,22 @@ A toolbar whose content has all moved into the rail collapses while projection i
|
|
|
96
135
|
|
|
97
136
|
### Select actions and grouping independently
|
|
98
137
|
|
|
99
|
-
Use `
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
for horizontal settings, boolean shorthands and defaults.
|
|
107
|
-
|
|
108
|
-
| Option | Responsibility | What it does not do |
|
|
109
|
-
| --- | --- | --- |
|
|
110
|
-
| `button` | Automatically select eligible `ion-button` and `ion-menu-button` actions, whether inside or outside `ion-buttons`. | Does not select back buttons. |
|
|
111
|
-
| `buttons` | Allow selected actions in `ion-buttons` to project as a group when the existing grouping rules allow it. | Does not select, enable or disable any child action. |
|
|
112
|
-
| `backButton` | Automatically select eligible `ion-back-button` elements, independently of their parent group. | Does not group back buttons with other actions. |
|
|
138
|
+
Use `controls` to configure toolbar selection and grouping. Each field accepts `true`, `false`, `'horizontalOnly'`, or `'verticalOnly'`, with omitted fields defaulting to `true`. Use `controls: 'verticalOnly'` for all default selection in the rail. Local attributes can override selection; explicit sources and extensions remain independent. See [all selection defaults](configuration.md#selection-defaults).
|
|
139
|
+
|
|
140
|
+
| Option | Responsibility | What it does not do |
|
|
141
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- |
|
|
142
|
+
| `button` | Automatically select eligible `ion-button` and `ion-menu-button` actions, whether inside or outside `ion-buttons`. | Does not select back buttons. |
|
|
143
|
+
| `buttonGroup` | Allow selected actions in `ion-buttons` to project as a group when the existing grouping rules allow it. | Does not select, enable or disable any child action. |
|
|
144
|
+
| `backButton` | Automatically select eligible `ion-back-button` elements, independently of their parent group. | Does not group back buttons with other actions. |
|
|
113
145
|
|
|
114
146
|
For ordinary icon-only buttons in a group, with no local attributes:
|
|
115
147
|
|
|
116
|
-
| `
|
|
117
|
-
|
|
|
118
|
-
| `true`
|
|
119
|
-
| `false`
|
|
120
|
-
| `true`
|
|
121
|
-
| `false`
|
|
148
|
+
| `buttonGroup` | `button` | Result |
|
|
149
|
+
| ------------- | -------- | ------------------------------------------------------------ |
|
|
150
|
+
| `true` | `true` | Eligible buttons project together when the group qualifies. |
|
|
151
|
+
| `false` | `true` | Eligible buttons project individually. |
|
|
152
|
+
| `true` | `false` | No ordinary buttons project; grouping alone selects nothing. |
|
|
153
|
+
| `false` | `false` | No ordinary buttons project. |
|
|
122
154
|
|
|
123
155
|
These results concern ordinary actions only. Back navigation, tabs and native
|
|
124
156
|
search are independent. For example, to select only back navigation among toolbar
|
|
@@ -126,8 +158,7 @@ actions:
|
|
|
126
158
|
|
|
127
159
|
```ts
|
|
128
160
|
const rail = await enableNativeUI({
|
|
129
|
-
|
|
130
|
-
vertical: { buttons: false, button: false, backButton: true },
|
|
161
|
+
controls: { buttonGroup: false, button: false, backButton: 'verticalOnly', tabs: 'verticalOnly', segment: false, fab: false },
|
|
131
162
|
});
|
|
132
163
|
```
|
|
133
164
|
|
|
@@ -138,19 +169,21 @@ This does not disable tab projection or tab rail CSS.
|
|
|
138
169
|
Use `data-vertical-bars="true|false"` on the element whose behavior you want to
|
|
139
170
|
override. The attribute is **not inherited**.
|
|
140
171
|
|
|
141
|
-
| Element |
|
|
142
|
-
|
|
|
143
|
-
| `ion-button` / `ion-menu-button` | Select
|
|
144
|
-
| `ion-buttons`
|
|
145
|
-
| `ion-back-button`
|
|
172
|
+
| Element | `data-vertical-bars="true"` | `data-vertical-bars="false"` | Default when absent or invalid |
|
|
173
|
+
| -------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | -------------------------------------------------- |
|
|
174
|
+
| `ion-button` / `ion-menu-button` | Select the action, including supported text actions without an icon-only slot. | Exclude the action from the rail. | `controls.button` and automatic shape rules. |
|
|
175
|
+
| `ion-buttons` | Allow grouping of already-selected actions. | Project selected actions individually. | `controls.buttonGroup` and grouping eligibility. |
|
|
176
|
+
| `ion-back-button` | Select back navigation independently of its group. | Exclude this back button from the rail. | `controls.backButton` and back-button eligibility. |
|
|
177
|
+
| `ion-tab-bar` | Select eligible rail tabs and their native search presentation. | Disable native rail tab selection; existing Web layout remains. | `controls.tabs` and tab-bar eligibility. |
|
|
178
|
+
|
|
179
|
+
The group's attribute never selects or excludes its children. All opt-ins remain subject to structural exclusions and rail availability.
|
|
146
180
|
|
|
147
181
|
Empty or unknown attribute values are ignored. To disable automatic action
|
|
148
182
|
selection and explicitly select a text button:
|
|
149
183
|
|
|
150
184
|
```ts
|
|
151
185
|
const rail = await enableNativeUI({
|
|
152
|
-
|
|
153
|
-
vertical: { buttons: true, button: false, backButton: false },
|
|
186
|
+
controls: { buttonGroup: 'verticalOnly', button: false, backButton: false, tabs: 'verticalOnly', segment: false, fab: false },
|
|
154
187
|
});
|
|
155
188
|
```
|
|
156
189
|
|
|
@@ -164,7 +197,7 @@ const rail = await enableNativeUI({
|
|
|
164
197
|
Only Save moves; Cancel stays in the horizontal toolbar. Adding
|
|
165
198
|
`data-vertical-bars="true"` to the group would not select Cancel.
|
|
166
199
|
|
|
167
|
-
To group two explicitly selected actions even when `
|
|
200
|
+
To group two explicitly selected actions even when `buttonGroup: false`:
|
|
168
201
|
|
|
169
202
|
```html
|
|
170
203
|
<ion-buttons data-vertical-bars="true">
|
|
@@ -188,7 +221,7 @@ startup options.
|
|
|
188
221
|
### Back buttons inside groups
|
|
189
222
|
|
|
190
223
|
An `ion-back-button` remains a navigation action even inside `ion-buttons`.
|
|
191
|
-
It ignores `
|
|
224
|
+
It ignores `buttonGroup`, `button`, and the group's `data-vertical-bars` attribute.
|
|
192
225
|
Its own attribute overrides `backButton`. It uses the dedicated back position,
|
|
193
226
|
never an ordinary action group, and is not duplicated as a toolbar action.
|
|
194
227
|
|
|
@@ -210,35 +243,41 @@ remain opt-outs; those are distinct from the grouping setting.
|
|
|
210
243
|
|
|
211
244
|
### Placement versus appearance
|
|
212
245
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
246
|
+
| Setting | Values | Responsibility | Applies to |
|
|
247
|
+
| -------------------------- | ----------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
248
|
+
| `data-native-ui` | `button`, `back-button`, `disabled` | Declare an explicit source or exclude a subtree. | [Explicit source elements](framework-independent.md#2-mark-source-controls); `disabled` also applies to ancestors. |
|
|
249
|
+
| `data-native-ui-placement` | `element`, `rail` | Choose the original element position or request the rail. | Explicit sources; `element` also keeps automatic Ionic controls at their original position. |
|
|
250
|
+
| `data-vertical-bars` | `true`, `false` | Override rail selection or grouping defaults. | Ionic actions and groups listed above. |
|
|
251
|
+
| `data-horizontal-bars` | `true`, `false` | Override ordinary-placement selection or grouping defaults. | [Ordinary Ionic controls](native-ui.md#toolbar-selection-and-grouping). |
|
|
252
|
+
| `data-projection` | `system`, `source` | Choose native appearance without selecting an action. | Native vertical `ion-button` and `ion-menu-button`; inherited from the nearest `ion-buttons`. |
|
|
253
|
+
|
|
254
|
+
Selection attributes are not inherited. Appearance follows the button-then-group priority described below. An attribute never makes unsupported markup or a missing native rail available.
|
|
255
|
+
|
|
256
|
+
The rail shares one light/dark appearance across projected controls. See [Shared Vertical Bars appearance](appearance.md#shared-vertical-bars-appearance) when configuring your Web theme.
|
|
216
257
|
|
|
217
258
|
### Choose button appearance
|
|
218
259
|
|
|
219
|
-
`buttonProjection`
|
|
260
|
+
Use `ionic.verticalBars.buttonProjection` to choose native button appearance.
|
|
220
261
|
|
|
221
262
|
For native vertical `ion-button` and `ion-menu-button` actions, choose who controls appearance:
|
|
222
263
|
|
|
223
|
-
| `buttonProjection`
|
|
224
|
-
|
|
|
225
|
-
| `'system'` (default) | SwiftUI styles the buttons and tints their icons. Ionic fill, colors and borders are not applied.
|
|
226
|
-
| `'source'`
|
|
264
|
+
| `buttonProjection` | Appearance |
|
|
265
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
266
|
+
| `'system'` (default) | SwiftUI styles the buttons and tints their icons. Ionic fill, colors and borders are not applied. |
|
|
267
|
+
| `'source'` | Projects the supported Ionic fill and computed colors described in [Source fill rules](#source-fill-rules). |
|
|
227
268
|
|
|
228
269
|
```ts
|
|
229
|
-
const rail = await enableNativeUI({
|
|
270
|
+
const rail = await enableNativeUI({ controls: 'verticalOnly', ionic: { verticalBars: { buttonProjection: 'source' } } });
|
|
230
271
|
```
|
|
231
272
|
|
|
232
|
-
`enableNativeUI()`
|
|
233
|
-
|
|
234
|
-
**Migration from the experimental releases:** the default changes from source styling to `system`. Set `buttonProjection: 'source'` to retain the previous projection behavior.
|
|
273
|
+
`enableNativeUI()` and the standalone `enableVerticalControlArea()` accept these settings inside `ionic.verticalBars`. Use one runtime, and destroy it before restarting with different options. Either mode preserves actions, disabled state and grouping. Disabled icons use the native disabled appearance. These appearance settings do not affect horizontal controls, source elements or Web fallback clones, so compare the native appearance on supported iOS.
|
|
235
274
|
|
|
236
275
|
### Override individual buttons or groups
|
|
237
276
|
|
|
238
277
|
Use `data-projection` for local exceptions. Existing classes remain supported:
|
|
239
278
|
|
|
240
|
-
| Attribute
|
|
241
|
-
|
|
|
279
|
+
| Attribute | Equivalent class |
|
|
280
|
+
| -------------------------- | ----------------------------- |
|
|
242
281
|
| `data-projection="source"` | `ios-theme-projection-source` |
|
|
243
282
|
| `data-projection="system"` | `ios-theme-projection-system` |
|
|
244
283
|
|
|
@@ -267,17 +306,17 @@ Only native vertical `ion-button` and `ion-menu-button` actions interpret these
|
|
|
267
306
|
|
|
268
307
|
For an `ion-button` resolved to `source`, fill is chosen separately from the projection mode:
|
|
269
308
|
|
|
270
|
-
| Button
|
|
271
|
-
|
|
|
272
|
-
| Explicit `fill="clear"`, `"solid"` or `"outline"`
|
|
273
|
-
| Omitted fill or `fill="default"` inside `ion-buttons`
|
|
309
|
+
| Button | Effective fill |
|
|
310
|
+
| ------------------------------------------------------ | ------------------- |
|
|
311
|
+
| Explicit `fill="clear"`, `"solid"` or `"outline"` | The explicit value |
|
|
312
|
+
| Omitted fill or `fill="default"` inside `ion-buttons` | `clear` |
|
|
274
313
|
| Omitted fill or `fill="default"` outside `ion-buttons` | `buttonDefaultFill` |
|
|
275
314
|
|
|
276
315
|
`verticalBars.buttonDefaultFill` accepts `'solid'` or `'glass'`; omission is equivalent to `'glass'`. Use `'solid'` for Ionic's default button design, or `'glass'` for a glass design. It applies to every button resolved to `source`, including a local exception under a global `system` setting.
|
|
277
316
|
|
|
278
317
|
```ts
|
|
279
318
|
// Keep system styling globally; local source buttons use Ionic's default fill.
|
|
280
|
-
const rail = await enableNativeUI({
|
|
319
|
+
const rail = await enableNativeUI({ controls: 'verticalOnly', ionic: { verticalBars: { buttonDefaultFill: 'solid' } } });
|
|
281
320
|
```
|
|
282
321
|
|
|
283
322
|
```html
|
|
@@ -287,12 +326,12 @@ const rail = await enableNativeUI({ verticalBars: { only: true, buttonDefaultFil
|
|
|
287
326
|
</ion-button>
|
|
288
327
|
```
|
|
289
328
|
|
|
290
|
-
| Effective fill | Native appearance in `source` mode
|
|
291
|
-
|
|
|
292
|
-
| `clear`
|
|
293
|
-
| `solid`
|
|
294
|
-
| `outline`
|
|
295
|
-
| `null`
|
|
329
|
+
| Effective fill | Native appearance in `source` mode |
|
|
330
|
+
| -------------- | ------------------------------------------------------------------- |
|
|
331
|
+
| `clear` | Icon color, without a glass background; CSS backgrounds are ignored |
|
|
332
|
+
| `solid` | Computed background color tints a prominent glass button |
|
|
333
|
+
| `outline` | Computed border color and width, with native glass |
|
|
334
|
+
| `null` | Native glass with source icon colors |
|
|
296
335
|
|
|
297
336
|
Inside `ion-buttons`, explicitly set `fill="solid"` to project a background; `buttonDefaultFill: 'solid'` does not override the group's clear default. Native Liquid Glass tinting can differ from the CSS color, especially for translucent backgrounds. With the iOS theme, ordinary `ion-buttons` retain group projection; `ion-buttons.ios-theme-disabled` projects eligible buttons individually. Local projection settings do not change that grouping rule.
|
|
298
337
|
|
|
@@ -210,7 +210,7 @@ public class NativeUIPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarDelegate {
|
|
|
210
210
|
self.bridge?.webView?.layoutIfNeeded()
|
|
211
211
|
// Ionic already paints the header edge; a second native effect can
|
|
212
212
|
// add a dark scrim when the OS and Web themes differ.
|
|
213
|
-
if call.getBool("
|
|
213
|
+
if call.getBool("suppressScrollEdgeEffect") == true, let effect = self.bridge?.webView?.scrollView.topEdgeEffect {
|
|
214
214
|
let hidden = effect.isHidden
|
|
215
215
|
effect.isHidden = true
|
|
216
216
|
self.restoreTopEdge = { [weak effect] in effect?.isHidden = hidden }
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
package/src/native/activation.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { controlDefault } from './shared/control-options';
|
|
1
2
|
import { toolbarOptions } from './shared/toolbar-options';
|
|
2
3
|
import { createDiagnostics } from './shared/diagnostics';
|
|
3
4
|
import { createHTMLWebProjection } from './html';
|
|
@@ -160,15 +161,6 @@ const webProjection = (doc: Document, options: NativeUIRuntimeOptions, enabled:
|
|
|
160
161
|
|
|
161
162
|
/** Call once at application startup. Ionic markup remains the source of truth. */
|
|
162
163
|
export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFactory?: OverlayFactory): Promise<NativeUIRuntimeHandle> => {
|
|
163
|
-
if (options.enabled === false) {
|
|
164
|
-
const current = active;
|
|
165
|
-
if (current)
|
|
166
|
-
stopActivation(
|
|
167
|
-
current,
|
|
168
|
-
current.then((handle) => handle.destroy()),
|
|
169
|
-
);
|
|
170
|
-
return (stopping ?? Promise.resolve()).then(() => web('Disabled'));
|
|
171
|
-
}
|
|
172
164
|
if (typeof document === 'undefined') return Promise.resolve(web('Requires a document'));
|
|
173
165
|
const extensions = [...(options.extensions ?? [])];
|
|
174
166
|
const nativeState: NonNullable<NativeUIRuntimeOptions['nativeState']> = (options.nativeState = {});
|
|
@@ -177,22 +169,20 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
177
169
|
} catch (error) {
|
|
178
170
|
return Promise.reject(error);
|
|
179
171
|
}
|
|
180
|
-
const controls = options.controls;
|
|
181
172
|
const configuration = JSON.stringify([
|
|
182
173
|
options.source ?? 'ionic',
|
|
183
174
|
options.debug === true,
|
|
184
|
-
options.
|
|
175
|
+
options.ionic?.auto !== false,
|
|
185
176
|
options.verticalBars?.enabled !== false,
|
|
186
177
|
options.verticalBars?.placement?.edge,
|
|
187
178
|
options.rail?.edge,
|
|
188
|
-
options.verticalBarsOnly === true,
|
|
189
179
|
toolbarOptions(options, false),
|
|
190
180
|
toolbarOptions(options, true),
|
|
191
|
-
options.buttonProjection ?? 'system',
|
|
181
|
+
options.ionic?.verticalBars?.buttonProjection ?? 'system',
|
|
192
182
|
// Local source overrides can use this even when the startup projection is system.
|
|
193
|
-
options.buttonDefaultFill ??
|
|
194
|
-
...(['tabs', '
|
|
195
|
-
|
|
183
|
+
options.ionic?.verticalBars?.buttonDefaultFill ?? 'glass',
|
|
184
|
+
...(['tabs', 'segment', 'fab'] as const).flatMap((kind) => [controlDefault(options, kind, false), controlDefault(options, kind, true)]),
|
|
185
|
+
options.ionic?.verticalBars?.popover === true,
|
|
196
186
|
]);
|
|
197
187
|
if (active && activeFailed) {
|
|
198
188
|
const previous = active;
|
|
@@ -248,7 +238,7 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
248
238
|
const diagnose = createDiagnostics(options.debug);
|
|
249
239
|
const stopModals = options.source === 'html' ? () => {} : observeVerticalBarsModals(document);
|
|
250
240
|
const prehide =
|
|
251
|
-
platform !== 'android' && options.source !== 'html' &&
|
|
241
|
+
platform !== 'android' && options.source !== 'html' && options.ionic?.auto !== false
|
|
252
242
|
? prehideVerticalBarsToolbarSources(document, () => options.verticalBars?.enabled !== false, options.root, options)
|
|
253
243
|
: undefined;
|
|
254
244
|
const fallback = (reason: string) => {
|
|
@@ -290,7 +280,10 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
290
280
|
}
|
|
291
281
|
};
|
|
292
282
|
try {
|
|
293
|
-
const
|
|
283
|
+
const ordinaryControls = (['button', 'backButton', 'tabs', 'segment', 'fab'] as const).some((kind) =>
|
|
284
|
+
controlDefault(options, kind, false),
|
|
285
|
+
);
|
|
286
|
+
const capabilities = await bounded(plugin.configure({ suppressScrollEdgeEffect: ordinaryControls }));
|
|
294
287
|
if (!capabilities.supported) return fallback('Native UI is unavailable on this device');
|
|
295
288
|
const unavailableExtensions = new Map<string, string>();
|
|
296
289
|
for (const extension of extensions) {
|
|
@@ -313,7 +306,6 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
313
306
|
unavailableExtensions,
|
|
314
307
|
},
|
|
315
308
|
() => false,
|
|
316
|
-
options.verticalBarsOnly === true,
|
|
317
309
|
runtimeStopped,
|
|
318
310
|
);
|
|
319
311
|
runtime = combine(runtime, webProjection(document, options));
|
|
@@ -349,7 +341,7 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
349
341
|
return physicalEdge === nativeState.edge;
|
|
350
342
|
};
|
|
351
343
|
const applyMetrics = (metrics: WebViewMetrics) => {
|
|
352
|
-
if (
|
|
344
|
+
if (ordinaryControls) nativeWebViewMetrics.radius = metrics.radius;
|
|
353
345
|
deviceEdge = metrics.verticalBarEdge ?? null;
|
|
354
346
|
updateNativeEdge();
|
|
355
347
|
document.defaultView?.dispatchEvent(new Event('nativeUIRefresh'));
|
|
@@ -363,14 +355,13 @@ export const enableNativeUI = (options: NativeUIRuntimeOptions = {}, overlayFact
|
|
|
363
355
|
plugin,
|
|
364
356
|
{ ...options, extensions, unavailableExtensions },
|
|
365
357
|
nativeVerticalBars,
|
|
366
|
-
options.verticalBarsOnly === true,
|
|
367
358
|
runtimeStopped,
|
|
368
359
|
);
|
|
369
360
|
runtime = combine(
|
|
370
361
|
native,
|
|
371
362
|
webProjection(document, options, () => !nativeVerticalBars() || native.getStatus().projected === 0),
|
|
372
363
|
);
|
|
373
|
-
if (
|
|
364
|
+
if (options.ionic?.verticalBars?.popover === true && overlayFactory)
|
|
374
365
|
overlays = await overlayFactory(document, plugin, options, native.retain, nativeVerticalBars);
|
|
375
366
|
return manage(runtime, {
|
|
376
367
|
suspend: async () => {
|