@capgo/capacitor-native-navigation 8.4.5 → 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 +133 -25
- 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
|
|
|
@@ -124,7 +126,9 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
124
126
|
runOnUiThread(() -> {
|
|
125
127
|
enabled = call.getBoolean("enabled", true);
|
|
126
128
|
contentInsetMode = call.getString("contentInsetMode", contentInsetMode);
|
|
127
|
-
|
|
129
|
+
JSObject configureGlass = call.getObject("glass", null);
|
|
130
|
+
configureSpecifiedGlassEffect = configureGlass != null && configureGlass.has("effect");
|
|
131
|
+
defaultGlassOptions = GlassOptions.from(configureGlass, defaultGlassOptions);
|
|
128
132
|
navbarGlassOptions = GlassOptions.from(navbarGlassConfig, defaultGlassOptions);
|
|
129
133
|
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
130
134
|
Double duration = call.getDouble("animationDuration");
|
|
@@ -218,17 +222,10 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
boolean hidden = call.getBoolean("hidden", false);
|
|
225
|
+
boolean animated = call.getBoolean("animated", false);
|
|
221
226
|
tabbarVisible = !hidden;
|
|
222
227
|
if (hidden) {
|
|
223
|
-
|
|
224
|
-
tabbar.setVisibility(View.GONE);
|
|
225
|
-
}
|
|
226
|
-
if (tabbarContainer != null) {
|
|
227
|
-
tabbarContainer.setVisibility(View.GONE);
|
|
228
|
-
}
|
|
229
|
-
if (tabbarBackdrop != null) {
|
|
230
|
-
tabbarBackdrop.setVisibility(View.GONE);
|
|
231
|
-
}
|
|
228
|
+
setTabbarChromeVisible(false, animated);
|
|
232
229
|
updateInsetsAndNotify();
|
|
233
230
|
call.resolve(insetsResult());
|
|
234
231
|
return;
|
|
@@ -246,7 +243,6 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
246
243
|
String selectedId = call.getString("selectedId", null);
|
|
247
244
|
JSObject colors = call.getObject("colors", new JSObject());
|
|
248
245
|
tabbarGlassConfig = call.getObject("glass", null);
|
|
249
|
-
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
250
246
|
badgeBackgroundColor = colorOption(call, colors, "badgeBackgroundColor", "badgeBackground", Color.rgb(255, 59, 48));
|
|
251
247
|
badgeTextColor = colorOption(call, colors, "badgeTextColor", "badgeText", Color.WHITE);
|
|
252
248
|
tabbarDisableIndicator = call.getBoolean("disableIndicator", false);
|
|
@@ -276,6 +272,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
276
272
|
|
|
277
273
|
applyTabbarColors(call, colors);
|
|
278
274
|
tabbarStyle = makeTabbarStyle(call.getObject("style", new JSObject()));
|
|
275
|
+
tabbarGlassOptions = resolveTabbarGlassOptions(call);
|
|
279
276
|
|
|
280
277
|
// Keep at most one detached trailing action for floating bars.
|
|
281
278
|
// Curve bars ignore role so tab order / center selection stay stable.
|
|
@@ -317,14 +314,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
317
314
|
|
|
318
315
|
applyTabbarBackground(centerTabIndex());
|
|
319
316
|
renderTabbarItems(labelVisibilityMode, icons);
|
|
320
|
-
if (tabbarContainer != null) {
|
|
321
|
-
tabbarContainer.setVisibility(View.VISIBLE);
|
|
322
|
-
}
|
|
323
|
-
if (tabbarBackdrop != null) {
|
|
324
|
-
tabbarBackdrop.setVisibility(View.VISIBLE);
|
|
325
|
-
}
|
|
326
|
-
nativeTabbar.setVisibility(View.VISIBLE);
|
|
327
317
|
layoutChrome();
|
|
318
|
+
setTabbarChromeVisible(true, animated);
|
|
328
319
|
updateInsetsAndNotify();
|
|
329
320
|
call.resolve(insetsResult());
|
|
330
321
|
});
|
|
@@ -762,6 +753,10 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
762
753
|
tabbarContainer.setClipChildren(false);
|
|
763
754
|
tabbarContainer.setClipToPadding(false);
|
|
764
755
|
tabbarContainer.setElevation(dp(12));
|
|
756
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
757
|
+
tabbarContainer.setOutlineAmbientShadowColor(0x33000000);
|
|
758
|
+
tabbarContainer.setOutlineSpotShadowColor(0x66000000);
|
|
759
|
+
}
|
|
765
760
|
|
|
766
761
|
tabbarGlassBackdrop = new GlassBackdropView(getContext());
|
|
767
762
|
tabbarGlassSurface = new View(getContext());
|
|
@@ -1671,15 +1666,36 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1671
1666
|
}
|
|
1672
1667
|
}
|
|
1673
1668
|
|
|
1674
|
-
|
|
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);
|
|
1675
1673
|
applyTabbarContainerOutline();
|
|
1676
1674
|
}
|
|
1677
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
|
+
|
|
1678
1690
|
private int resolvedTabbarSurfaceColor() {
|
|
1679
1691
|
GlassOptions resolvedGlassOptions = tabbarGlassOptions == null ? GlassOptions.defaults() : tabbarGlassOptions;
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1692
|
+
if (resolvedGlassOptions.isLiquidGlass()) {
|
|
1693
|
+
return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
|
|
1694
|
+
}
|
|
1695
|
+
if (!tabbarStyle.isCurve()) {
|
|
1696
|
+
return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
|
|
1697
|
+
}
|
|
1698
|
+
return tabbarBackgroundColor;
|
|
1683
1699
|
}
|
|
1684
1700
|
|
|
1685
1701
|
private void reapplyVisibleChromeBackgrounds() {
|
|
@@ -1834,7 +1850,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1834
1850
|
int bottom = navigationBarInset();
|
|
1835
1851
|
int navbarHeight = navbarVisible ? status + dp(DEFAULT_NAVBAR_DP) : 0;
|
|
1836
1852
|
int tabbarHeight = dp(tabbarStyle.totalHeight());
|
|
1837
|
-
|
|
1853
|
+
boolean tabbarLayoutVisible = tabbarVisible || tabbarChromeHideAnimating;
|
|
1854
|
+
int tabbarBottomMargin = tabbarLayoutVisible ? bottom + dp(tabbarStyle.bottomGap) : bottom;
|
|
1838
1855
|
|
|
1839
1856
|
if (navbarContainer != null) {
|
|
1840
1857
|
FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(
|
|
@@ -1857,7 +1874,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1857
1874
|
if (tabbarBackdrop != null) {
|
|
1858
1875
|
// Only the edge-to-edge curve bar extends its surface under the gesture area. A floating
|
|
1859
1876
|
// tabbar must leave the page visible below it, or it sits on a solid band.
|
|
1860
|
-
int backdropHeight =
|
|
1877
|
+
int backdropHeight = tabbarLayoutVisible && tabbarStyle.isCurve() ? bottom + dp(tabbarStyle.bottomGap) : 0;
|
|
1861
1878
|
FrameLayout.LayoutParams backdropParams = new FrameLayout.LayoutParams(
|
|
1862
1879
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
1863
1880
|
backdropHeight,
|
|
@@ -1865,7 +1882,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1865
1882
|
);
|
|
1866
1883
|
tabbarBackdrop.setLayoutParams(backdropParams);
|
|
1867
1884
|
tabbarBackdrop.setBackgroundColor(resolvedTabbarSurfaceColor());
|
|
1868
|
-
tabbarBackdrop.setVisibility(
|
|
1885
|
+
tabbarBackdrop.setVisibility(tabbarLayoutVisible && backdropHeight > 0 ? View.VISIBLE : View.GONE);
|
|
1869
1886
|
}
|
|
1870
1887
|
if (tabbarContainer != null) {
|
|
1871
1888
|
int rootWidth = root.getWidth() > 0 ? root.getWidth() : Resources.getSystem().getDisplayMetrics().widthPixels;
|
|
@@ -1907,6 +1924,97 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1907
1924
|
bringChromeToFront();
|
|
1908
1925
|
}
|
|
1909
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
|
+
|
|
1910
2018
|
private void bringChromeToFront() {
|
|
1911
2019
|
if (navbarContainer != null) {
|
|
1912
2020
|
navbarContainer.bringToFront();
|
|
@@ -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",
|