@capgo/capacitor-native-navigation 8.4.4 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -32
- package/android/src/main/java/app/capgo/nativenavigation/NativeNavigationPlugin.java +191 -27
- package/android/src/main/java/app/capgo/nativenavigation/TabbarChromeSupport.java +13 -0
- package/dist/docs.json +2 -2
- package/dist/esm/definitions.d.ts +6 -2
- package/dist/esm/definitions.js.map +1 -1
- package/docs/android-floating-tabbar-after-300.png +0 -0
- package/docs/android-floating-tabbar-after-full.png +0 -0
- package/ios/Sources/NativeNavigationPlugin/NativeNavigationPlugin.swift +98 -5
- package/ios/Tests/NativeNavigationPluginTests/NativeNavigationTests.swift +24 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,7 +159,7 @@ await NativeNavigation.setTabbar({
|
|
|
159
159
|
|
|
160
160
|
## Android Liquid Glass
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
On Android 12+, floating tabbars use `liquidGlass` automatically when `setTabbar` omits `glass` (pass `glass: { effect: 'none' }` for a flat fill). The live blur uses a platform `RenderEffect` behind the bar shape; older Android versions keep a translucent tint without live blur. Tune `colors.background` alpha and `glass.surfaceAlpha` (default `0.62`) together.
|
|
163
163
|
|
|
164
164
|
```typescript
|
|
165
165
|
await NativeNavigation.configure({
|
|
@@ -650,17 +650,17 @@ Global plugin configuration.
|
|
|
650
650
|
|
|
651
651
|
Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).
|
|
652
652
|
|
|
653
|
-
| Prop | Type | Description
|
|
654
|
-
| --------------------- | -------------------- |
|
|
655
|
-
| **`dynamic`** | <code>boolean</code> | When `true`, Android 12+ derives unspecified bar colors from Material You system palettes. Explicit color fields still win.
|
|
656
|
-
| **`tint`** | <code>string</code> | Tint color for active buttons/items.
|
|
657
|
-
| **`inactiveTint`** | <code>string</code> | Color for inactive tab items. Ignored on iOS 26+ unless `experimentalBakedTintColors` is enabled.
|
|
658
|
-
| **`background`** | <code>string</code> | Optional background tint. Ignored on iOS 26+ so UIKit can preserve the system Liquid Glass navigation appearance.
|
|
659
|
-
| **`foreground`** | <code>string</code> | Title and label text color where the native platform supports it.
|
|
660
|
-
| **`badgeBackground`** | <code>string</code> | Badge background color for native tab badges.
|
|
661
|
-
| **`badgeText`** | <code>string</code> | Badge text color for native tab badges.
|
|
662
|
-
| **`indicator`** | <code>string</code> | Active tab indicator color on Android.
|
|
663
|
-
| **`ripple`** | <code>string</code> | Tab press ripple color on Android.
|
|
653
|
+
| Prop | Type | Description |
|
|
654
|
+
| --------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
655
|
+
| **`dynamic`** | <code>boolean</code> | When `true`, Android 12+ derives unspecified bar colors from Material You system palettes. Explicit color fields still win. |
|
|
656
|
+
| **`tint`** | <code>string</code> | Tint color for active buttons/items. |
|
|
657
|
+
| **`inactiveTint`** | <code>string</code> | Color for inactive tab items. Ignored on iOS 26+ unless `experimentalBakedTintColors` is enabled. |
|
|
658
|
+
| **`background`** | <code>string</code> | Optional background tint. Ignored on iOS 26+ so UIKit can preserve the system Liquid Glass navigation appearance. On Android, alpha is honored. When `glass.effect` is not `liquidGlass`, floating tabbars still apply `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint. |
|
|
659
|
+
| **`foreground`** | <code>string</code> | Title and label text color where the native platform supports it. |
|
|
660
|
+
| **`badgeBackground`** | <code>string</code> | Badge background color for native tab badges. |
|
|
661
|
+
| **`badgeText`** | <code>string</code> | Badge text color for native tab badges. |
|
|
662
|
+
| **`indicator`** | <code>string</code> | Active tab indicator color on Android. |
|
|
663
|
+
| **`ripple`** | <code>string</code> | Tab press ripple color on Android. |
|
|
664
664
|
|
|
665
665
|
|
|
666
666
|
#### NativeNavigationGlassOptions
|
|
@@ -736,26 +736,26 @@ because icons are rendered by native UI.
|
|
|
736
736
|
|
|
737
737
|
Native tabbar state.
|
|
738
738
|
|
|
739
|
-
| Prop | Type | Description
|
|
740
|
-
| ------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
741
|
-
| **`hidden`** | <code>boolean</code> | Hide the native tabbar.
|
|
742
|
-
| **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions.
|
|
743
|
-
| **`selectedId`** | <code>string</code> | Currently selected tab id.
|
|
744
|
-
| **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`.
|
|
745
|
-
| **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided.
|
|
746
|
-
| **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`.
|
|
747
|
-
| **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints.
|
|
748
|
-
| **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available.
|
|
749
|
-
| **`glass`** | <code><a href="#nativenavigationglassoptions">NativeNavigationGlassOptions</a></code> | Optional glass background behavior. Overrides `configure({ glass })` for this tabbar update.
|
|
750
|
-
| **`experimentalBakedTintColors`** | <code>boolean</code> | Opt into the iOS 26 Liquid Glass tint workaround that renders active and inactive tab items into baked images. This can affect badge positioning and icon sizing, so it is disabled by default.
|
|
751
|
-
| **`disableTransparentOnScrollEdge`** | <code>boolean</code> | Keep the iOS scroll-edge tabbar appearance from becoming transparent. Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option. Defaults to `false`.
|
|
752
|
-
| **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator.
|
|
753
|
-
| **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported.
|
|
754
|
-
| **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported.
|
|
755
|
-
| **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported.
|
|
756
|
-
| **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported.
|
|
757
|
-
| **`style`** | <code><a href="#nativenavigationtabbarstyle">NativeNavigationTabbarStyle</a></code> | Optional native tabbar layout and shape customization.
|
|
758
|
-
| **`animated`** | <code>boolean</code> | Animate native tabbar changes.
|
|
739
|
+
| Prop | Type | Description |
|
|
740
|
+
| ------------------------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
741
|
+
| **`hidden`** | <code>boolean</code> | Hide the native tabbar. |
|
|
742
|
+
| **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions. |
|
|
743
|
+
| **`selectedId`** | <code>string</code> | Currently selected tab id. |
|
|
744
|
+
| **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`. |
|
|
745
|
+
| **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided. |
|
|
746
|
+
| **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`. |
|
|
747
|
+
| **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints. |
|
|
748
|
+
| **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available. |
|
|
749
|
+
| **`glass`** | <code><a href="#nativenavigationglassoptions">NativeNavigationGlassOptions</a></code> | Optional glass background behavior. Overrides `configure({ glass })` for this tabbar update. On Android 12+, floating tabbars default to `liquidGlass` (live blur plus tint) when this field is omitted. Pass `effect: 'none'` for a flat translucent fill without blur. |
|
|
750
|
+
| **`experimentalBakedTintColors`** | <code>boolean</code> | Opt into the iOS 26 Liquid Glass tint workaround that renders active and inactive tab items into baked images. This can affect badge positioning and icon sizing, so it is disabled by default. |
|
|
751
|
+
| **`disableTransparentOnScrollEdge`** | <code>boolean</code> | Keep the iOS scroll-edge tabbar appearance from becoming transparent. Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option. Defaults to `false`. |
|
|
752
|
+
| **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator. |
|
|
753
|
+
| **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported. |
|
|
754
|
+
| **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported. |
|
|
755
|
+
| **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported. |
|
|
756
|
+
| **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported. |
|
|
757
|
+
| **`style`** | <code><a href="#nativenavigationtabbarstyle">NativeNavigationTabbarStyle</a></code> | Optional native tabbar layout and shape customization. |
|
|
758
|
+
| **`animated`** | <code>boolean</code> | Animate native tabbar changes. |
|
|
759
759
|
|
|
760
760
|
|
|
761
761
|
#### NativeNavigationTab
|
|
@@ -81,6 +81,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
81
81
|
private boolean tabbarVisible = false;
|
|
82
82
|
private String contentInsetMode = "css";
|
|
83
83
|
private GlassOptions defaultGlassOptions = GlassOptions.defaults();
|
|
84
|
+
private boolean configureSpecifiedGlassEffect = false;
|
|
84
85
|
private GlassOptions navbarGlassOptions = GlassOptions.defaults();
|
|
85
86
|
private GlassOptions tabbarGlassOptions = GlassOptions.defaults();
|
|
86
87
|
private JSObject navbarGlassConfig;
|
|
@@ -105,6 +106,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
105
106
|
private final Map<Integer, String> menuActionPlacements = new HashMap<>();
|
|
106
107
|
private final List<NativeTabItem> tabItems = new ArrayList<>();
|
|
107
108
|
private int selectedTabIndex = 0;
|
|
109
|
+
private boolean tabbarChromeHideAnimating = false;
|
|
108
110
|
private boolean tabbarDisableIndicator = false;
|
|
109
111
|
private Integer tabbarIndicatorColor = null;
|
|
110
112
|
|
|
@@ -112,7 +114,10 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
112
114
|
public void load() {
|
|
113
115
|
Activity activity = getActivity();
|
|
114
116
|
if (activity != null) {
|
|
115
|
-
activity.runOnUiThread(
|
|
117
|
+
activity.runOnUiThread(() -> {
|
|
118
|
+
enableEdgeToEdge();
|
|
119
|
+
watchContentRootBounds();
|
|
120
|
+
});
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
|
|
@@ -121,7 +126,9 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
121
126
|
runOnUiThread(() -> {
|
|
122
127
|
enabled = call.getBoolean("enabled", true);
|
|
123
128
|
contentInsetMode = call.getString("contentInsetMode", contentInsetMode);
|
|
124
|
-
|
|
129
|
+
JSObject configureGlass = call.getObject("glass", null);
|
|
130
|
+
configureSpecifiedGlassEffect = configureGlass != null && configureGlass.has("effect");
|
|
131
|
+
defaultGlassOptions = GlassOptions.from(configureGlass, defaultGlassOptions);
|
|
125
132
|
navbarGlassOptions = GlassOptions.from(navbarGlassConfig, defaultGlassOptions);
|
|
126
133
|
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
127
134
|
Double duration = call.getDouble("animationDuration");
|
|
@@ -215,17 +222,10 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
boolean hidden = call.getBoolean("hidden", false);
|
|
225
|
+
boolean animated = call.getBoolean("animated", false);
|
|
218
226
|
tabbarVisible = !hidden;
|
|
219
227
|
if (hidden) {
|
|
220
|
-
|
|
221
|
-
tabbar.setVisibility(View.GONE);
|
|
222
|
-
}
|
|
223
|
-
if (tabbarContainer != null) {
|
|
224
|
-
tabbarContainer.setVisibility(View.GONE);
|
|
225
|
-
}
|
|
226
|
-
if (tabbarBackdrop != null) {
|
|
227
|
-
tabbarBackdrop.setVisibility(View.GONE);
|
|
228
|
-
}
|
|
228
|
+
setTabbarChromeVisible(false, animated);
|
|
229
229
|
updateInsetsAndNotify();
|
|
230
230
|
call.resolve(insetsResult());
|
|
231
231
|
return;
|
|
@@ -243,7 +243,6 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
243
243
|
String selectedId = call.getString("selectedId", null);
|
|
244
244
|
JSObject colors = call.getObject("colors", new JSObject());
|
|
245
245
|
tabbarGlassConfig = call.getObject("glass", null);
|
|
246
|
-
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
247
246
|
badgeBackgroundColor = colorOption(call, colors, "badgeBackgroundColor", "badgeBackground", Color.rgb(255, 59, 48));
|
|
248
247
|
badgeTextColor = colorOption(call, colors, "badgeTextColor", "badgeText", Color.WHITE);
|
|
249
248
|
tabbarDisableIndicator = call.getBoolean("disableIndicator", false);
|
|
@@ -273,6 +272,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
273
272
|
|
|
274
273
|
applyTabbarColors(call, colors);
|
|
275
274
|
tabbarStyle = makeTabbarStyle(call.getObject("style", new JSObject()));
|
|
275
|
+
tabbarGlassOptions = resolveTabbarGlassOptions(call);
|
|
276
276
|
|
|
277
277
|
// Keep at most one detached trailing action for floating bars.
|
|
278
278
|
// Curve bars ignore role so tab order / center selection stay stable.
|
|
@@ -314,14 +314,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
314
314
|
|
|
315
315
|
applyTabbarBackground(centerTabIndex());
|
|
316
316
|
renderTabbarItems(labelVisibilityMode, icons);
|
|
317
|
-
if (tabbarContainer != null) {
|
|
318
|
-
tabbarContainer.setVisibility(View.VISIBLE);
|
|
319
|
-
}
|
|
320
|
-
if (tabbarBackdrop != null) {
|
|
321
|
-
tabbarBackdrop.setVisibility(View.VISIBLE);
|
|
322
|
-
}
|
|
323
|
-
nativeTabbar.setVisibility(View.VISIBLE);
|
|
324
317
|
layoutChrome();
|
|
318
|
+
setTabbarChromeVisible(true, animated);
|
|
325
319
|
updateInsetsAndNotify();
|
|
326
320
|
call.resolve(insetsResult());
|
|
327
321
|
});
|
|
@@ -759,6 +753,10 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
759
753
|
tabbarContainer.setClipChildren(false);
|
|
760
754
|
tabbarContainer.setClipToPadding(false);
|
|
761
755
|
tabbarContainer.setElevation(dp(12));
|
|
756
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
757
|
+
tabbarContainer.setOutlineAmbientShadowColor(0x33000000);
|
|
758
|
+
tabbarContainer.setOutlineSpotShadowColor(0x66000000);
|
|
759
|
+
}
|
|
762
760
|
|
|
763
761
|
tabbarGlassBackdrop = new GlassBackdropView(getContext());
|
|
764
762
|
tabbarGlassSurface = new View(getContext());
|
|
@@ -1668,15 +1666,36 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1668
1666
|
}
|
|
1669
1667
|
}
|
|
1670
1668
|
|
|
1671
|
-
|
|
1669
|
+
boolean glassBlurActive =
|
|
1670
|
+
resolvedGlassOptions.isLiquidGlass() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
|
|
1671
|
+
int tabbarFillColor = glassBlurActive ? Color.TRANSPARENT : drawColor;
|
|
1672
|
+
tabbar.setTabbarStyle(tabbarStyle, tabbarFillColor, centerIndex);
|
|
1672
1673
|
applyTabbarContainerOutline();
|
|
1673
1674
|
}
|
|
1674
1675
|
|
|
1676
|
+
private GlassOptions resolveTabbarGlassOptions(PluginCall call) {
|
|
1677
|
+
if (tabbarGlassConfig != null) {
|
|
1678
|
+
return GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
1679
|
+
}
|
|
1680
|
+
boolean apiAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
|
|
1681
|
+
if (
|
|
1682
|
+
TabbarChromeSupport.shouldEnableDefaultLiquidGlass(apiAtLeastS, tabbarStyle.isCurve(), false)
|
|
1683
|
+
&& (!configureSpecifiedGlassEffect || defaultGlassOptions.isLiquidGlass())
|
|
1684
|
+
) {
|
|
1685
|
+
return GlassOptions.from(new JSObject().put("effect", "liquidGlass"), defaultGlassOptions);
|
|
1686
|
+
}
|
|
1687
|
+
return GlassOptions.from(null, defaultGlassOptions);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1675
1690
|
private int resolvedTabbarSurfaceColor() {
|
|
1676
1691
|
GlassOptions resolvedGlassOptions = tabbarGlassOptions == null ? GlassOptions.defaults() : tabbarGlassOptions;
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1692
|
+
if (resolvedGlassOptions.isLiquidGlass()) {
|
|
1693
|
+
return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
|
|
1694
|
+
}
|
|
1695
|
+
if (!tabbarStyle.isCurve()) {
|
|
1696
|
+
return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
|
|
1697
|
+
}
|
|
1698
|
+
return tabbarBackgroundColor;
|
|
1680
1699
|
}
|
|
1681
1700
|
|
|
1682
1701
|
private void reapplyVisibleChromeBackgrounds() {
|
|
@@ -1831,7 +1850,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1831
1850
|
int bottom = navigationBarInset();
|
|
1832
1851
|
int navbarHeight = navbarVisible ? status + dp(DEFAULT_NAVBAR_DP) : 0;
|
|
1833
1852
|
int tabbarHeight = dp(tabbarStyle.totalHeight());
|
|
1834
|
-
|
|
1853
|
+
boolean tabbarLayoutVisible = tabbarVisible || tabbarChromeHideAnimating;
|
|
1854
|
+
int tabbarBottomMargin = tabbarLayoutVisible ? bottom + dp(tabbarStyle.bottomGap) : bottom;
|
|
1835
1855
|
|
|
1836
1856
|
if (navbarContainer != null) {
|
|
1837
1857
|
FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(
|
|
@@ -1854,7 +1874,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1854
1874
|
if (tabbarBackdrop != null) {
|
|
1855
1875
|
// Only the edge-to-edge curve bar extends its surface under the gesture area. A floating
|
|
1856
1876
|
// tabbar must leave the page visible below it, or it sits on a solid band.
|
|
1857
|
-
int backdropHeight =
|
|
1877
|
+
int backdropHeight = tabbarLayoutVisible && tabbarStyle.isCurve() ? bottom + dp(tabbarStyle.bottomGap) : 0;
|
|
1858
1878
|
FrameLayout.LayoutParams backdropParams = new FrameLayout.LayoutParams(
|
|
1859
1879
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
1860
1880
|
backdropHeight,
|
|
@@ -1862,7 +1882,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1862
1882
|
);
|
|
1863
1883
|
tabbarBackdrop.setLayoutParams(backdropParams);
|
|
1864
1884
|
tabbarBackdrop.setBackgroundColor(resolvedTabbarSurfaceColor());
|
|
1865
|
-
tabbarBackdrop.setVisibility(
|
|
1885
|
+
tabbarBackdrop.setVisibility(tabbarLayoutVisible && backdropHeight > 0 ? View.VISIBLE : View.GONE);
|
|
1866
1886
|
}
|
|
1867
1887
|
if (tabbarContainer != null) {
|
|
1868
1888
|
int rootWidth = root.getWidth() > 0 ? root.getWidth() : Resources.getSystem().getDisplayMetrics().widthPixels;
|
|
@@ -1904,6 +1924,97 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1904
1924
|
bringChromeToFront();
|
|
1905
1925
|
}
|
|
1906
1926
|
|
|
1927
|
+
private void setTabbarChromeVisible(boolean visible, boolean animated) {
|
|
1928
|
+
if (!animated) {
|
|
1929
|
+
tabbarChromeHideAnimating = false;
|
|
1930
|
+
int visibility = visible ? View.VISIBLE : View.GONE;
|
|
1931
|
+
if (tabbar != null) {
|
|
1932
|
+
tabbar.setVisibility(visibility);
|
|
1933
|
+
}
|
|
1934
|
+
if (tabbarContainer != null) {
|
|
1935
|
+
tabbarContainer.animate().cancel();
|
|
1936
|
+
tabbarContainer.setTranslationY(0f);
|
|
1937
|
+
tabbarContainer.setAlpha(1f);
|
|
1938
|
+
tabbarContainer.setVisibility(visibility);
|
|
1939
|
+
}
|
|
1940
|
+
if (tabbarBackdrop != null) {
|
|
1941
|
+
tabbarBackdrop.animate().cancel();
|
|
1942
|
+
tabbarBackdrop.setAlpha(1f);
|
|
1943
|
+
tabbarBackdrop.setVisibility(visible && tabbarVisible ? View.VISIBLE : View.GONE);
|
|
1944
|
+
}
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
if (tabbarContainer == null) {
|
|
1949
|
+
setTabbarChromeVisible(visible, false);
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
float slideDistance = tabbarSlideDistance();
|
|
1954
|
+
tabbarContainer.animate().cancel();
|
|
1955
|
+
if (tabbarBackdrop != null) {
|
|
1956
|
+
tabbarBackdrop.animate().cancel();
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
if (!visible) {
|
|
1960
|
+
tabbarChromeHideAnimating = true;
|
|
1961
|
+
tabbarContainer
|
|
1962
|
+
.animate()
|
|
1963
|
+
.translationY(slideDistance)
|
|
1964
|
+
.alpha(0f)
|
|
1965
|
+
.setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS)
|
|
1966
|
+
.withEndAction(() -> {
|
|
1967
|
+
tabbarChromeHideAnimating = false;
|
|
1968
|
+
if (tabbar != null) {
|
|
1969
|
+
tabbar.setVisibility(View.GONE);
|
|
1970
|
+
}
|
|
1971
|
+
tabbarContainer.setVisibility(View.GONE);
|
|
1972
|
+
if (tabbarBackdrop != null) {
|
|
1973
|
+
tabbarBackdrop.setVisibility(View.GONE);
|
|
1974
|
+
}
|
|
1975
|
+
})
|
|
1976
|
+
.start();
|
|
1977
|
+
if (tabbarBackdrop != null) {
|
|
1978
|
+
tabbarBackdrop.animate().alpha(0f).setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS).start();
|
|
1979
|
+
}
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
tabbarChromeHideAnimating = false;
|
|
1984
|
+
tabbarContainer.setVisibility(View.VISIBLE);
|
|
1985
|
+
tabbarContainer.setAlpha(0f);
|
|
1986
|
+
tabbarContainer.setTranslationY(slideDistance);
|
|
1987
|
+
if (tabbarBackdrop != null) {
|
|
1988
|
+
tabbarBackdrop.setVisibility(View.VISIBLE);
|
|
1989
|
+
tabbarBackdrop.setAlpha(0f);
|
|
1990
|
+
}
|
|
1991
|
+
if (tabbar != null) {
|
|
1992
|
+
tabbar.setVisibility(View.VISIBLE);
|
|
1993
|
+
}
|
|
1994
|
+
tabbarContainer
|
|
1995
|
+
.animate()
|
|
1996
|
+
.translationY(0f)
|
|
1997
|
+
.alpha(1f)
|
|
1998
|
+
.setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS)
|
|
1999
|
+
.start();
|
|
2000
|
+
if (tabbarBackdrop != null) {
|
|
2001
|
+
tabbarBackdrop.animate().alpha(1f).setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS).start();
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
private float tabbarSlideDistance() {
|
|
2006
|
+
if (tabbarContainer == null) {
|
|
2007
|
+
return dp(tabbarStyle.totalHeight() + tabbarStyle.bottomGap);
|
|
2008
|
+
}
|
|
2009
|
+
ViewGroup.LayoutParams params = tabbarContainer.getLayoutParams();
|
|
2010
|
+
int bottomMargin = params instanceof ViewGroup.MarginLayoutParams ? ((ViewGroup.MarginLayoutParams) params).bottomMargin : 0;
|
|
2011
|
+
return TabbarChromeSupport.tabbarSlideDistancePx(
|
|
2012
|
+
tabbarContainer.getHeight(),
|
|
2013
|
+
bottomMargin,
|
|
2014
|
+
dp(tabbarStyle.totalHeight() + tabbarStyle.bottomGap)
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
1907
2018
|
private void bringChromeToFront() {
|
|
1908
2019
|
if (navbarContainer != null) {
|
|
1909
2020
|
navbarContainer.bringToFront();
|
|
@@ -2062,7 +2173,21 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
2062
2173
|
}
|
|
2063
2174
|
}
|
|
2064
2175
|
|
|
2176
|
+
/**
|
|
2177
|
+
* The chrome is laid out inside {@code android.R.id.content}, so it only needs to clear the part of a
|
|
2178
|
+
* system bar that actually overlaps that view. Capacitor's SystemBars plugin (default
|
|
2179
|
+
* {@code insetsHandling: 'css'} without {@code viewport-fit=cover}) pads the decor view by the system bars,
|
|
2180
|
+
* which already moves the content root clear of them; adding the full inset again doubled the gap.
|
|
2181
|
+
*/
|
|
2065
2182
|
private int statusBarInset() {
|
|
2183
|
+
return Math.max(0, rawStatusBarInset() - contentRootTopOffset());
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
private int navigationBarInset() {
|
|
2187
|
+
return Math.max(0, rawNavigationBarInset() - contentRootBottomOffset());
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
private int rawStatusBarInset() {
|
|
2066
2191
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
2067
2192
|
WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets();
|
|
2068
2193
|
if (insets != null) {
|
|
@@ -2072,7 +2197,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
2072
2197
|
return systemDimension("status_bar_height");
|
|
2073
2198
|
}
|
|
2074
2199
|
|
|
2075
|
-
private int
|
|
2200
|
+
private int rawNavigationBarInset() {
|
|
2076
2201
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
2077
2202
|
WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets();
|
|
2078
2203
|
if (insets != null) {
|
|
@@ -2082,6 +2207,45 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
2082
2207
|
return systemDimension("navigation_bar_height");
|
|
2083
2208
|
}
|
|
2084
2209
|
|
|
2210
|
+
private int contentRootTopOffset() {
|
|
2211
|
+
FrameLayout root = contentRoot();
|
|
2212
|
+
if (root == null || !root.isLaidOut()) {
|
|
2213
|
+
return 0;
|
|
2214
|
+
}
|
|
2215
|
+
int[] location = new int[2];
|
|
2216
|
+
root.getLocationInWindow(location);
|
|
2217
|
+
return Math.max(0, location[1]);
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
private int contentRootBottomOffset() {
|
|
2221
|
+
FrameLayout root = contentRoot();
|
|
2222
|
+
Activity activity = getActivity();
|
|
2223
|
+
if (root == null || activity == null || !root.isLaidOut()) {
|
|
2224
|
+
return 0;
|
|
2225
|
+
}
|
|
2226
|
+
View decor = activity.getWindow().getDecorView();
|
|
2227
|
+
int[] location = new int[2];
|
|
2228
|
+
root.getLocationInWindow(location);
|
|
2229
|
+
return Math.max(0, decor.getHeight() - (location[1] + root.getHeight()));
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
private void watchContentRootBounds() {
|
|
2233
|
+
FrameLayout root = contentRoot();
|
|
2234
|
+
if (root == null) {
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
// SystemBars can add or drop its decor padding after load (for example once viewport-fit=cover is
|
|
2238
|
+
// detected), which moves the content root without a call into this plugin. Re-run layout when it moves.
|
|
2239
|
+
root.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
|
2240
|
+
if (top == oldTop && bottom == oldBottom && left == oldLeft && right == oldRight) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
if (navbarVisible || tabbarVisible) {
|
|
2244
|
+
view.post(this::updateInsetsAndNotify);
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2085
2249
|
private int systemDimension(String name) {
|
|
2086
2250
|
int id = getContext().getResources().getIdentifier(name, "dimen", "android");
|
|
2087
2251
|
return id == 0 ? 0 : getContext().getResources().getDimensionPixelSize(id);
|
|
@@ -6,6 +6,8 @@ import android.os.Build;
|
|
|
6
6
|
|
|
7
7
|
public final class TabbarChromeSupport {
|
|
8
8
|
|
|
9
|
+
public static final int TABBAR_VISIBILITY_ANIMATION_MS = 200;
|
|
10
|
+
|
|
9
11
|
public enum TabbarOutlineMode {
|
|
10
12
|
FLOATING_SHAPE,
|
|
11
13
|
CURVE_DEFAULT
|
|
@@ -13,6 +15,13 @@ public final class TabbarChromeSupport {
|
|
|
13
15
|
|
|
14
16
|
private TabbarChromeSupport() {}
|
|
15
17
|
|
|
18
|
+
public static float tabbarSlideDistancePx(int containerHeight, int bottomMargin, int fallbackDistance) {
|
|
19
|
+
if (containerHeight > 0) {
|
|
20
|
+
return (float) containerHeight + (float) bottomMargin;
|
|
21
|
+
}
|
|
22
|
+
return (float) fallbackDistance;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
public static int resolveSelectedIndicatorColor(Integer explicitColor, int tintColor) {
|
|
17
26
|
if (explicitColor != null) {
|
|
18
27
|
return explicitColor;
|
|
@@ -28,6 +37,10 @@ public final class TabbarChromeSupport {
|
|
|
28
37
|
return mode == TabbarOutlineMode.FLOATING_SHAPE;
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
public static boolean shouldEnableDefaultLiquidGlass(boolean apiAtLeastS, boolean isCurve, boolean glassSpecified) {
|
|
41
|
+
return !glassSpecified && !isCurve && apiAtLeastS;
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
public static int floatingCapsuleWidth(int totalWidth, int barHeightPx, int trailingGapPx, boolean hasDetachedTrailing) {
|
|
32
45
|
if (!hasDetachedTrailing) {
|
|
33
46
|
return totalWidth;
|
package/dist/docs.json
CHANGED
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
{
|
|
420
420
|
"name": "background",
|
|
421
421
|
"tags": [],
|
|
422
|
-
"docs": "Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\nsystem Liquid Glass navigation appearance.",
|
|
422
|
+
"docs": "Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\nsystem Liquid Glass navigation appearance. On Android, alpha is honored.\nWhen `glass.effect` is not `liquidGlass`, floating tabbars still apply\n`glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.",
|
|
423
423
|
"complexTypes": [],
|
|
424
424
|
"type": "string | undefined"
|
|
425
425
|
},
|
|
@@ -791,7 +791,7 @@
|
|
|
791
791
|
{
|
|
792
792
|
"name": "glass",
|
|
793
793
|
"tags": [],
|
|
794
|
-
"docs": "Optional glass background behavior. Overrides `configure({ glass })` for\nthis tabbar update.",
|
|
794
|
+
"docs": "Optional glass background behavior. Overrides `configure({ glass })` for\nthis tabbar update. On Android 12+, floating tabbars default to\n`liquidGlass` (live blur plus tint) when this field is omitted. Pass\n`effect: 'none'` for a flat translucent fill without blur.",
|
|
795
795
|
"complexTypes": [
|
|
796
796
|
"NativeNavigationGlassOptions"
|
|
797
797
|
],
|
|
@@ -150,7 +150,9 @@ export interface NativeNavigationColors {
|
|
|
150
150
|
inactiveTint?: string;
|
|
151
151
|
/**
|
|
152
152
|
* Optional background tint. Ignored on iOS 26+ so UIKit can preserve the
|
|
153
|
-
* system Liquid Glass navigation appearance.
|
|
153
|
+
* system Liquid Glass navigation appearance. On Android, alpha is honored.
|
|
154
|
+
* When `glass.effect` is not `liquidGlass`, floating tabbars still apply
|
|
155
|
+
* `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.
|
|
154
156
|
*/
|
|
155
157
|
background?: string;
|
|
156
158
|
/**
|
|
@@ -439,7 +441,9 @@ export interface NativeNavigationTabbarOptions {
|
|
|
439
441
|
blurEffect?: NativeNavigationBlurEffect;
|
|
440
442
|
/**
|
|
441
443
|
* Optional glass background behavior. Overrides `configure({ glass })` for
|
|
442
|
-
* this tabbar update.
|
|
444
|
+
* this tabbar update. On Android 12+, floating tabbars default to
|
|
445
|
+
* `liquidGlass` (live blur plus tint) when this field is omitted. Pass
|
|
446
|
+
* `effect: 'none'` for a flat translucent fill without blur.
|
|
443
447
|
*/
|
|
444
448
|
glass?: NativeNavigationGlassOptions;
|
|
445
449
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native glass background rendering preference.\n */\nexport type NativeNavigationGlassEffect = 'none' | 'liquidGlass';\n\n/**\n * Native glass background configuration.\n */\nexport interface NativeNavigationGlassOptions {\n /**\n * `liquidGlass` enables the Android 12+ live blurred WebView backdrop for\n * native bars. Android 11 and older keep a translucent surface fallback. iOS\n * uses the platform-owned Liquid Glass behavior.\n */\n effect?: NativeNavigationGlassEffect;\n\n /**\n * Android blur radius in native dp for `liquidGlass`. Defaults to `18`.\n */\n blurRadius?: number;\n\n /**\n * Alpha multiplier for the tint surface over the glass backdrop. Defaults to\n * `0.62`.\n */\n surfaceAlpha?: number;\n}\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Native tabbar background shape.\n */\nexport type NativeNavigationTabbarShape = 'floating' | 'curve';\n\n/**\n * Native tab role for Liquid Glass tab bars.\n *\n * `search` (and `prominent` when the OS supports it) renders as a detached\n * trailing circular action beside the floating tab capsule — the Apple News /\n * Photos pattern. Only one detached trailing role is used; if multiple tabs\n * set `search` or `prominent`, the last one wins. Curve-shaped bars ignore\n * `role` and keep using the included center action instead.\n */\nexport type NativeNavigationTabRole = 'normal' | 'search' | 'prominent';\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items. Ignored on iOS 26+ unless\n * `experimentalBakedTintColors` is enabled.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Shared glass background defaults for native bars.\n */\n glass?: NativeNavigationGlassOptions;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this navbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Hide the tab item from the native tabbar. When the hidden tab is selected,\n * native platform constraints may keep it visible until another tab is\n * selected.\n */\n hidden?: boolean;\n\n /**\n * Optional tab role. On floating tabbars, `search` (or `prominent` when\n * available) becomes a detached trailing circular action beside the capsule\n * — including the iOS 26+ system Liquid Glass tab bar, Android floating\n * layout, and the custom floating capsule path. Only one detached trailing\n * role is used; if multiple tabs set `search` or `prominent`, the last one\n * wins. Curve-shaped bars ignore `role` and keep using the included center\n * action. Defaults to `normal`.\n */\n role?: NativeNavigationTabRole;\n}\n\n/**\n * Native tabbar layout and background shape options.\n */\nexport interface NativeNavigationTabbarStyle {\n /**\n * `floating` keeps the capsule tabbar. On iOS 26+ this uses the\n * system-owned Liquid Glass `UITabBarController` unless a custom capsule\n * path is required; earlier iOS and the custom capsule path use\n * `UIGlassEffect` on iOS 26+ (blur material fallback otherwise). `curve`\n * draws a full-width bar with an included center action.\n */\n shape?: NativeNavigationTabbarShape;\n\n /**\n * Bar height in native points/dp, not including the system bottom inset.\n * Curve defaults to `49` on iOS (83pt once the home indicator is added) and\n * `80` on Android (Material navigation bar). Floating defaults to `64`.\n * Pass `height` to override either platform.\n */\n height?: number;\n\n /**\n * Horizontal margin in native points/dp. Defaults to `24` for `floating` and\n * `0` for `curve`.\n */\n horizontalMargin?: number;\n\n /**\n * Maximum tabbar width in native points/dp. Defaults to `430` for `floating`;\n * `curve` uses the available width unless this is set.\n */\n maxWidth?: number;\n\n /**\n * Bottom gap above the platform safe area in native points/dp. Defaults to\n * `10` for `floating` and `0` for `curve`.\n */\n bottomGap?: number;\n\n /**\n * Background corner radius in native points/dp. Defaults to a capsule radius\n * for `floating` and `0` for `curve`.\n */\n cornerRadius?: number;\n\n /**\n * Tab id promoted into the included center button for `curve`. Defaults to\n * the middle tab.\n */\n centerItemId?: string;\n\n /**\n * Included center button diameter in native points/dp. Defaults to `56`.\n */\n centerButtonDiameter?: number;\n\n /**\n * Distance from the top of the center button to the top edge of the bar in\n * native points/dp. Defaults to half of `centerButtonDiameter`.\n */\n centerButtonLift?: number;\n\n /**\n * Included center button color. Defaults to the active tint color.\n */\n centerButtonColor?: string;\n\n /**\n * Included center button icon color. Defaults to white.\n */\n centerButtonIconColor?: string;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this tabbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Opt into the iOS 26 Liquid Glass tint workaround that renders active and\n * inactive tab items into baked images. This can affect badge positioning\n * and icon sizing, so it is disabled by default.\n */\n experimentalBakedTintColors?: boolean;\n\n /**\n * Keep the iOS scroll-edge tabbar appearance from becoming transparent.\n * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\n * Defaults to `false`.\n */\n disableTransparentOnScrollEdge?: boolean;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Optional native tabbar layout and shape customization.\n */\n style?: NativeNavigationTabbarStyle;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native glass background rendering preference.\n */\nexport type NativeNavigationGlassEffect = 'none' | 'liquidGlass';\n\n/**\n * Native glass background configuration.\n */\nexport interface NativeNavigationGlassOptions {\n /**\n * `liquidGlass` enables the Android 12+ live blurred WebView backdrop for\n * native bars. Android 11 and older keep a translucent surface fallback. iOS\n * uses the platform-owned Liquid Glass behavior.\n */\n effect?: NativeNavigationGlassEffect;\n\n /**\n * Android blur radius in native dp for `liquidGlass`. Defaults to `18`.\n */\n blurRadius?: number;\n\n /**\n * Alpha multiplier for the tint surface over the glass backdrop. Defaults to\n * `0.62`.\n */\n surfaceAlpha?: number;\n}\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Native tabbar background shape.\n */\nexport type NativeNavigationTabbarShape = 'floating' | 'curve';\n\n/**\n * Native tab role for Liquid Glass tab bars.\n *\n * `search` (and `prominent` when the OS supports it) renders as a detached\n * trailing circular action beside the floating tab capsule — the Apple News /\n * Photos pattern. Only one detached trailing role is used; if multiple tabs\n * set `search` or `prominent`, the last one wins. Curve-shaped bars ignore\n * `role` and keep using the included center action instead.\n */\nexport type NativeNavigationTabRole = 'normal' | 'search' | 'prominent';\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items. Ignored on iOS 26+ unless\n * `experimentalBakedTintColors` is enabled.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance. On Android, alpha is honored.\n * When `glass.effect` is not `liquidGlass`, floating tabbars still apply\n * `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Shared glass background defaults for native bars.\n */\n glass?: NativeNavigationGlassOptions;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this navbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Hide the tab item from the native tabbar. When the hidden tab is selected,\n * native platform constraints may keep it visible until another tab is\n * selected.\n */\n hidden?: boolean;\n\n /**\n * Optional tab role. On floating tabbars, `search` (or `prominent` when\n * available) becomes a detached trailing circular action beside the capsule\n * — including the iOS 26+ system Liquid Glass tab bar, Android floating\n * layout, and the custom floating capsule path. Only one detached trailing\n * role is used; if multiple tabs set `search` or `prominent`, the last one\n * wins. Curve-shaped bars ignore `role` and keep using the included center\n * action. Defaults to `normal`.\n */\n role?: NativeNavigationTabRole;\n}\n\n/**\n * Native tabbar layout and background shape options.\n */\nexport interface NativeNavigationTabbarStyle {\n /**\n * `floating` keeps the capsule tabbar. On iOS 26+ this uses the\n * system-owned Liquid Glass `UITabBarController` unless a custom capsule\n * path is required; earlier iOS and the custom capsule path use\n * `UIGlassEffect` on iOS 26+ (blur material fallback otherwise). `curve`\n * draws a full-width bar with an included center action.\n */\n shape?: NativeNavigationTabbarShape;\n\n /**\n * Bar height in native points/dp, not including the system bottom inset.\n * Curve defaults to `49` on iOS (83pt once the home indicator is added) and\n * `80` on Android (Material navigation bar). Floating defaults to `64`.\n * Pass `height` to override either platform.\n */\n height?: number;\n\n /**\n * Horizontal margin in native points/dp. Defaults to `24` for `floating` and\n * `0` for `curve`.\n */\n horizontalMargin?: number;\n\n /**\n * Maximum tabbar width in native points/dp. Defaults to `430` for `floating`;\n * `curve` uses the available width unless this is set.\n */\n maxWidth?: number;\n\n /**\n * Bottom gap above the platform safe area in native points/dp. Defaults to\n * `10` for `floating` and `0` for `curve`.\n */\n bottomGap?: number;\n\n /**\n * Background corner radius in native points/dp. Defaults to a capsule radius\n * for `floating` and `0` for `curve`.\n */\n cornerRadius?: number;\n\n /**\n * Tab id promoted into the included center button for `curve`. Defaults to\n * the middle tab.\n */\n centerItemId?: string;\n\n /**\n * Included center button diameter in native points/dp. Defaults to `56`.\n */\n centerButtonDiameter?: number;\n\n /**\n * Distance from the top of the center button to the top edge of the bar in\n * native points/dp. Defaults to half of `centerButtonDiameter`.\n */\n centerButtonLift?: number;\n\n /**\n * Included center button color. Defaults to the active tint color.\n */\n centerButtonColor?: string;\n\n /**\n * Included center button icon color. Defaults to white.\n */\n centerButtonIconColor?: string;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this tabbar update. On Android 12+, floating tabbars default to\n * `liquidGlass` (live blur plus tint) when this field is omitted. Pass\n * `effect: 'none'` for a flat translucent fill without blur.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Opt into the iOS 26 Liquid Glass tint workaround that renders active and\n * inactive tab items into baked images. This can affect badge positioning\n * and icon sizing, so it is disabled by default.\n */\n experimentalBakedTintColors?: boolean;\n\n /**\n * Keep the iOS scroll-edge tabbar appearance from becoming transparent.\n * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\n * Defaults to `false`.\n */\n disableTransparentOnScrollEdge?: boolean;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Optional native tabbar layout and shape customization.\n */\n style?: NativeNavigationTabbarStyle;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
|
|
Binary file
|
|
Binary file
|
|
@@ -58,6 +58,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
58
58
|
private var tabbarHeight: CGFloat = NativeNavigationTabbarStyleConfig().totalHeight
|
|
59
59
|
private var navbarVisible = false
|
|
60
60
|
private var tabbarVisible = false
|
|
61
|
+
private var tabbarSoftHidden = false
|
|
62
|
+
private var tabbarSkipImmediateSystemReveal = false
|
|
63
|
+
private var floatingTabBarWantsHidden = false
|
|
61
64
|
private var contentInsetMode = "css"
|
|
62
65
|
private var isEnabled = true
|
|
63
66
|
private var defaultTransitionDuration: TimeInterval = 0.35
|
|
@@ -203,15 +206,36 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
203
206
|
}
|
|
204
207
|
|
|
205
208
|
let hidden = call.getBool("hidden", false)
|
|
209
|
+
let animated = call.getBool("animated", false)
|
|
206
210
|
self.tabbarVisible = !hidden
|
|
207
211
|
|
|
208
212
|
guard !hidden else {
|
|
209
|
-
self.hideTabBarChrome()
|
|
213
|
+
self.hideTabBarChrome(animated: animated)
|
|
210
214
|
self.updateInsetsAndNotify()
|
|
211
215
|
call.resolve(self.insetsResult())
|
|
212
216
|
return
|
|
213
217
|
}
|
|
214
218
|
|
|
219
|
+
if self.tabbarSoftHidden {
|
|
220
|
+
self.tabbarSoftHidden = false
|
|
221
|
+
if animated {
|
|
222
|
+
if nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
223
|
+
animated: true,
|
|
224
|
+
usesSystemLiquidGlass: self.usesSystemLiquidGlass,
|
|
225
|
+
shape: self.tabbarStyle.shape == .curve ? "curve" : "floating"
|
|
226
|
+
) {
|
|
227
|
+
self.tabbarSkipImmediateSystemReveal = true
|
|
228
|
+
self.setSystemTabBarHidden(false, animated: true, syncSubviewState: false)
|
|
229
|
+
} else {
|
|
230
|
+
self.setFloatingTabBarVisibility(hidden: false, animated: true)
|
|
231
|
+
}
|
|
232
|
+
} else if self.usesSystemLiquidGlass && self.tabbarStyle.shape != .curve {
|
|
233
|
+
self.setSystemTabBarHidden(false, animated: false, syncSubviewState: true)
|
|
234
|
+
} else {
|
|
235
|
+
self.setFloatingTabBarVisibility(hidden: false, animated: false)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
215
239
|
self.tabbarStyle = self.makeTabbarStyle(from: call)
|
|
216
240
|
self.tabbarHeight = self.tabbarStyle.totalHeight
|
|
217
241
|
|
|
@@ -241,6 +265,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
241
265
|
self.applyTabBarAppearance(tabBar: tabBar, options: call)
|
|
242
266
|
if items.isEmpty {
|
|
243
267
|
self.tabbarVisible = false
|
|
268
|
+
self.tabbarSkipImmediateSystemReveal = false
|
|
244
269
|
self.hideSystemTabBarChromeCompletely()
|
|
245
270
|
} else {
|
|
246
271
|
self.showTabBarChrome(tabBar)
|
|
@@ -904,18 +929,22 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
904
929
|
}
|
|
905
930
|
}
|
|
906
931
|
|
|
907
|
-
private func setSystemTabBarHidden(_ hidden: Bool) {
|
|
932
|
+
private func setSystemTabBarHidden(_ hidden: Bool, animated: Bool = false, syncSubviewState: Bool = true) {
|
|
908
933
|
guard let tabBarController = tabBarController else {
|
|
909
934
|
return
|
|
910
935
|
}
|
|
911
936
|
|
|
912
937
|
let tabBar = tabBarController.tabBar
|
|
913
938
|
if #available(iOS 18.0, *) {
|
|
914
|
-
tabBarController.setTabBarHidden(hidden, animated:
|
|
939
|
+
tabBarController.setTabBarHidden(hidden, animated: animated)
|
|
915
940
|
} else {
|
|
916
941
|
tabBar.isHidden = hidden
|
|
917
942
|
}
|
|
918
943
|
|
|
944
|
+
guard syncSubviewState else {
|
|
945
|
+
return
|
|
946
|
+
}
|
|
947
|
+
|
|
919
948
|
if hidden {
|
|
920
949
|
setTabBarBackgroundSubviewsHidden(true, on: tabBar)
|
|
921
950
|
} else {
|
|
@@ -943,7 +972,24 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
943
972
|
floatingTabBar?.isHidden = true
|
|
944
973
|
}
|
|
945
974
|
|
|
946
|
-
private func hideTabBarChrome() {
|
|
975
|
+
private func hideTabBarChrome(animated: Bool = false) {
|
|
976
|
+
if nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
977
|
+
animated: animated,
|
|
978
|
+
usesSystemLiquidGlass: usesSystemLiquidGlass,
|
|
979
|
+
shape: tabbarStyle.shape == .curve ? "curve" : "floating"
|
|
980
|
+
) {
|
|
981
|
+
tabbarSoftHidden = true
|
|
982
|
+
setSystemTabBarHidden(true, animated: true, syncSubviewState: false)
|
|
983
|
+
return
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if animated {
|
|
987
|
+
tabbarSoftHidden = true
|
|
988
|
+
setFloatingTabBarVisibility(hidden: true, animated: true)
|
|
989
|
+
return
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
tabbarSoftHidden = false
|
|
947
993
|
if usesSystemLiquidGlass && tabbarStyle.shape != .curve {
|
|
948
994
|
hideSystemTabBarChromeCompletely()
|
|
949
995
|
} else {
|
|
@@ -953,12 +999,51 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
953
999
|
}
|
|
954
1000
|
}
|
|
955
1001
|
|
|
1002
|
+
private func setFloatingTabBarVisibility(hidden: Bool, animated: Bool) {
|
|
1003
|
+
floatingTabBarWantsHidden = hidden
|
|
1004
|
+
let targets = [tabContainer, floatingTabBar].compactMap { $0 }
|
|
1005
|
+
guard !targets.isEmpty else {
|
|
1006
|
+
return
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if !animated {
|
|
1010
|
+
targets.forEach { view in
|
|
1011
|
+
view.isHidden = hidden
|
|
1012
|
+
view.alpha = hidden ? 0 : 1
|
|
1013
|
+
}
|
|
1014
|
+
return
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
if hidden {
|
|
1018
|
+
UIView.animate(withDuration: 0.2, animations: {
|
|
1019
|
+
targets.forEach { $0.alpha = 0 }
|
|
1020
|
+
}, completion: { finished in
|
|
1021
|
+
guard finished, self.floatingTabBarWantsHidden else {
|
|
1022
|
+
return
|
|
1023
|
+
}
|
|
1024
|
+
targets.forEach { $0.isHidden = true }
|
|
1025
|
+
})
|
|
1026
|
+
return
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
targets.forEach { view in
|
|
1030
|
+
view.isHidden = false
|
|
1031
|
+
view.alpha = 0
|
|
1032
|
+
}
|
|
1033
|
+
UIView.animate(withDuration: 0.2) {
|
|
1034
|
+
targets.forEach { $0.alpha = 1 }
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
956
1038
|
private func showTabBarChrome(_ tabBar: UITabBar) {
|
|
957
1039
|
tabContainer?.isHidden = true
|
|
958
1040
|
floatingTabBar?.isHidden = true
|
|
959
1041
|
tabBarController?.view.isHidden = false
|
|
960
1042
|
if usesSystemLiquidGlass && tabbarStyle.shape != .curve {
|
|
961
|
-
|
|
1043
|
+
if !tabbarSkipImmediateSystemReveal {
|
|
1044
|
+
setSystemTabBarHidden(false)
|
|
1045
|
+
}
|
|
1046
|
+
tabbarSkipImmediateSystemReveal = false
|
|
962
1047
|
liftWebViewOverlaysAboveSystemTabs()
|
|
963
1048
|
hostWebViewInSelectedSystemTab()
|
|
964
1049
|
} else {
|
|
@@ -2780,6 +2865,14 @@ func nativeNavigationUsesStationaryTransitionCrossfade(direction: String) -> Boo
|
|
|
2780
2865
|
direction == "tab" || direction == "root" || direction == "none"
|
|
2781
2866
|
}
|
|
2782
2867
|
|
|
2868
|
+
func nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
2869
|
+
animated: Bool,
|
|
2870
|
+
usesSystemLiquidGlass: Bool,
|
|
2871
|
+
shape: String
|
|
2872
|
+
) -> Bool {
|
|
2873
|
+
animated && usesSystemLiquidGlass && shape != "curve"
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2783
2876
|
private func nativeNavigationNeedsTransitionSurface(_ color: UIColor?) -> Bool {
|
|
2784
2877
|
guard let color else {
|
|
2785
2878
|
return true
|
|
@@ -97,4 +97,28 @@ class NativeNavigationTests: XCTestCase {
|
|
|
97
97
|
XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "forward"))
|
|
98
98
|
XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "back"))
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
func testAnimatedSystemTabbarVisibilityUsesSoftHide() {
|
|
102
|
+
XCTAssertTrue(
|
|
103
|
+
nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
104
|
+
animated: true,
|
|
105
|
+
usesSystemLiquidGlass: true,
|
|
106
|
+
shape: "floating"
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
XCTAssertFalse(
|
|
110
|
+
nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
111
|
+
animated: false,
|
|
112
|
+
usesSystemLiquidGlass: true,
|
|
113
|
+
shape: "floating"
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
XCTAssertFalse(
|
|
117
|
+
nativeNavigationPrefersAnimatedSystemTabbarVisibility(
|
|
118
|
+
animated: true,
|
|
119
|
+
usesSystemLiquidGlass: true,
|
|
120
|
+
shape: "curve"
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
}
|
|
100
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-native-navigation",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.5.0",
|
|
4
4
|
"description": "Capacitor plugin for native navbar, tabbar, safe-area handling, and WebView snapshot transitions.",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|