@capgo/capacitor-native-navigation 8.4.2 → 8.4.4

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 CHANGED
@@ -1,21 +1,12 @@
1
1
  # @capgo/capacitor-native-navigation
2
2
 
3
- <a href="https://capgo.app/">
4
- <img
5
- src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-native-navigation"
6
- alt="Capgo - Instant updates for Capacitor"
7
- />
8
- </a>
3
+ <a href="https://capgo.app/?ref=plugin_native_navigation"><img src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-native-navigation" alt="Capgo - Instant updates for Capacitor" /></a>
9
4
 
10
5
  <div align="center">
11
- <h2>
12
- <a href="https://capgo.app/?ref=plugin_native_navigation">Get instant updates for your app with Capgo</a>
13
- </h2>
14
- <h2>
15
- <a href="https://capgo.app/consulting/?ref=plugin_native_navigation">
16
- Missing a feature? We can build the plugin for you
17
- </a>
18
- </h2>
6
+ <p><b>Capgo</b>: open-source live updates for Ionic and Capacitor apps. Ship OTA fixes and features instantly, without waiting for app store review.</p>
7
+ <h2><a href="https://capgo.app/register/?ref=plugin_native_navigation">➡️ Get started for free</a></h2>
8
+ <p>14-day unlimited free trial. No credit card required</p>
9
+ <p><a href="https://capgo.app/consulting/?ref=plugin_native_navigation">Missing a feature? We'll build the plugin for you 💪</a></p>
19
10
  </div>
20
11
 
21
12
  Native navbar, tabbar, safe-area handling, and WebView snapshot transitions for Capacitor apps. Your web framework keeps routing and page rendering while this plugin owns the platform surfaces users expect to feel native.
@@ -105,6 +105,8 @@ public class NativeNavigationPlugin extends Plugin {
105
105
  private final Map<Integer, String> menuActionPlacements = new HashMap<>();
106
106
  private final List<NativeTabItem> tabItems = new ArrayList<>();
107
107
  private int selectedTabIndex = 0;
108
+ private boolean tabbarDisableIndicator = false;
109
+ private Integer tabbarIndicatorColor = null;
108
110
 
109
111
  @Override
110
112
  public void load() {
@@ -244,6 +246,8 @@ public class NativeNavigationPlugin extends Plugin {
244
246
  tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
245
247
  badgeBackgroundColor = colorOption(call, colors, "badgeBackgroundColor", "badgeBackground", Color.rgb(255, 59, 48));
246
248
  badgeTextColor = colorOption(call, colors, "badgeTextColor", "badgeText", Color.WHITE);
249
+ tabbarDisableIndicator = call.getBoolean("disableIndicator", false);
250
+ tabbarIndicatorColor = colorOption(call, colors, "indicatorColor", "indicator", null);
247
251
 
248
252
  for (int sourceIndex = 0; sourceIndex < tabs.length(); sourceIndex++) {
249
253
  JSONObject tab = tabs.optJSONObject(sourceIndex);
@@ -644,6 +648,68 @@ public class NativeNavigationPlugin extends Plugin {
644
648
  };
645
649
  }
646
650
 
651
+ private void applyTabbarContainerOutline() {
652
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || tabbarContainer == null) {
653
+ return;
654
+ }
655
+
656
+ if (tabbarStyle.isCurve()) {
657
+ tabbarContainer.setClipToOutline(false);
658
+ tabbarContainer.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
659
+ if (tabbarGlassBackdrop != null) {
660
+ tabbarGlassBackdrop.setClipToOutline(false);
661
+ tabbarGlassBackdrop.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
662
+ }
663
+ if (tabbarGlassSurface != null) {
664
+ tabbarGlassSurface.setClipToOutline(false);
665
+ tabbarGlassSurface.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
666
+ }
667
+ return;
668
+ }
669
+
670
+ ViewOutlineProvider floatingOutlineProvider = floatingTabbarOutlineProvider();
671
+ tabbarContainer.setClipToOutline(false);
672
+ tabbarContainer.setOutlineProvider(floatingOutlineProvider);
673
+ if (tabbarGlassBackdrop != null) {
674
+ tabbarGlassBackdrop.setClipToOutline(true);
675
+ tabbarGlassBackdrop.setOutlineProvider(floatingOutlineProvider);
676
+ }
677
+ if (tabbarGlassSurface != null) {
678
+ tabbarGlassSurface.setClipToOutline(true);
679
+ tabbarGlassSurface.setOutlineProvider(floatingOutlineProvider);
680
+ }
681
+ }
682
+
683
+ private ViewOutlineProvider floatingTabbarOutlineProvider() {
684
+ return new ViewOutlineProvider() {
685
+ @Override
686
+ public void getOutline(View view, Outline outline) {
687
+ int width = view.getWidth();
688
+ int height = view.getHeight();
689
+ if (width <= 0 || height <= 0 || tabbar == null) {
690
+ outline.setEmpty();
691
+ return;
692
+ }
693
+
694
+ Path path = tabbar.chromeBackgroundPath(width, height);
695
+ if (TabbarChromeSupport.canApplyPathOutline()) {
696
+ outline.setPath(path);
697
+ return;
698
+ }
699
+
700
+ boolean hasDetachedTrailing = tabItems.stream().anyMatch((item) -> item.detachedTrailing);
701
+ int capsuleWidth = TabbarChromeSupport.floatingCapsuleWidth(
702
+ width,
703
+ dp(tabbarStyle.height),
704
+ dp(10),
705
+ hasDetachedTrailing
706
+ );
707
+ float radius = Math.min(dp(tabbarStyle.cornerRadius), height / 2f);
708
+ outline.setRoundRect(0, 0, capsuleWidth, height, radius);
709
+ }
710
+ };
711
+ }
712
+
647
713
  private Toolbar ensureToolbar() {
648
714
  if (toolbar != null) {
649
715
  return toolbar;
@@ -698,7 +764,6 @@ public class NativeNavigationPlugin extends Plugin {
698
764
  tabbarGlassSurface = new View(getContext());
699
765
  tabbarGlassBackdrop.setVisibility(View.GONE);
700
766
  tabbarGlassSurface.setVisibility(View.GONE);
701
-
702
767
  tabbar = new NativeTabbarLayout(getContext());
703
768
  tabbar.setClipChildren(false);
704
769
  tabbar.setClipToPadding(false);
@@ -722,6 +787,7 @@ public class NativeNavigationPlugin extends Plugin {
722
787
  new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(DEFAULT_TABBAR_DP))
723
788
  );
724
789
  }
790
+ applyTabbarContainerOutline();
725
791
  return tabbar;
726
792
  }
727
793
 
@@ -788,10 +854,10 @@ public class NativeNavigationPlugin extends Plugin {
788
854
  button.addView(centerFill, new FrameLayout.LayoutParams(centerFillDiameter, centerFillDiameter, Gravity.CENTER));
789
855
  }
790
856
 
791
- if (!center && selected) {
857
+ if (!center && selected && !tabbarDisableIndicator) {
792
858
  GradientDrawable selectedBackground = new GradientDrawable();
793
859
  selectedBackground.setShape(GradientDrawable.OVAL);
794
- selectedBackground.setColor(withAlpha(tintColor, 34));
860
+ selectedBackground.setColor(TabbarChromeSupport.resolveSelectedIndicatorColor(tabbarIndicatorColor, tintColor));
795
861
  View selectedCircle = new View(getContext());
796
862
  selectedCircle.setBackground(selectedBackground);
797
863
  button.addView(selectedCircle, new FrameLayout.LayoutParams(dp(58), dp(58), Gravity.CENTER));
@@ -1245,25 +1311,21 @@ public class NativeNavigationPlugin extends Plugin {
1245
1311
  );
1246
1312
  }
1247
1313
 
1314
+ Path chromeBackgroundPath(int width, int height) {
1315
+ return backgroundPath(width, height);
1316
+ }
1317
+
1248
1318
  private Path backgroundPath(int width, int height) {
1249
1319
  Path path = new Path();
1250
1320
  if (!style.isCurve()) {
1251
- float radius = dp(style.cornerRadius);
1252
- int capsuleWidth = capsuleWidth(width);
1253
- path.addRoundRect(new RectF(0, 0, capsuleWidth, height), radius, radius, Path.Direction.CW);
1254
- int trailingIndex = detachedTrailingIndex();
1255
- if (trailingIndex >= 0) {
1256
- float diameter = dp(style.height);
1257
- float left = width - diameter;
1258
- float top = (height - diameter) / 2f;
1259
- path.addRoundRect(
1260
- new RectF(left, top, left + diameter, top + diameter),
1261
- diameter / 2f,
1262
- diameter / 2f,
1263
- Path.Direction.CW
1264
- );
1265
- }
1266
- return path;
1321
+ return TabbarChromeSupport.buildFloatingTabbarPath(
1322
+ width,
1323
+ height,
1324
+ dp(style.cornerRadius),
1325
+ dp(style.height),
1326
+ dp(10),
1327
+ detachedTrailingIndex() >= 0
1328
+ );
1267
1329
  }
1268
1330
  float barTop = dp(style.barTop());
1269
1331
  float barHeight = dp(style.height);
@@ -1607,6 +1669,7 @@ public class NativeNavigationPlugin extends Plugin {
1607
1669
  }
1608
1670
 
1609
1671
  tabbar.setTabbarStyle(tabbarStyle, drawColor, centerIndex);
1672
+ applyTabbarContainerOutline();
1610
1673
  }
1611
1674
 
1612
1675
  private int resolvedTabbarSurfaceColor() {
@@ -1789,7 +1852,9 @@ public class NativeNavigationPlugin extends Plugin {
1789
1852
  }
1790
1853
 
1791
1854
  if (tabbarBackdrop != null) {
1792
- int backdropHeight = tabbarVisible ? bottom + dp(tabbarStyle.bottomGap) : 0;
1855
+ // Only the edge-to-edge curve bar extends its surface under the gesture area. A floating
1856
+ // tabbar must leave the page visible below it, or it sits on a solid band.
1857
+ int backdropHeight = tabbarVisible && tabbarStyle.isCurve() ? bottom + dp(tabbarStyle.bottomGap) : 0;
1793
1858
  FrameLayout.LayoutParams backdropParams = new FrameLayout.LayoutParams(
1794
1859
  ViewGroup.LayoutParams.MATCH_PARENT,
1795
1860
  backdropHeight,
@@ -1815,6 +1880,15 @@ public class NativeNavigationPlugin extends Plugin {
1815
1880
  tabbarContainer.setLayoutParams(tabbarContainerParams);
1816
1881
  fillContainer(tabbarGlassBackdrop);
1817
1882
  fillContainer(tabbarGlassSurface);
1883
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
1884
+ tabbarContainer.invalidateOutline();
1885
+ if (tabbarGlassBackdrop != null) {
1886
+ tabbarGlassBackdrop.invalidateOutline();
1887
+ }
1888
+ if (tabbarGlassSurface != null) {
1889
+ tabbarGlassSurface.invalidateOutline();
1890
+ }
1891
+ }
1818
1892
  }
1819
1893
 
1820
1894
  if (tabbar != null) {
@@ -0,0 +1,66 @@
1
+ package app.capgo.nativenavigation;
2
+
3
+ import android.graphics.Path;
4
+ import android.graphics.RectF;
5
+ import android.os.Build;
6
+
7
+ public final class TabbarChromeSupport {
8
+
9
+ public enum TabbarOutlineMode {
10
+ FLOATING_SHAPE,
11
+ CURVE_DEFAULT
12
+ }
13
+
14
+ private TabbarChromeSupport() {}
15
+
16
+ public static int resolveSelectedIndicatorColor(Integer explicitColor, int tintColor) {
17
+ if (explicitColor != null) {
18
+ return explicitColor;
19
+ }
20
+ return (34 << 24) | (tintColor & 0x00FFFFFF);
21
+ }
22
+
23
+ public static TabbarOutlineMode outlineModeForShape(String shape) {
24
+ return "curve".equals(shape) ? TabbarOutlineMode.CURVE_DEFAULT : TabbarOutlineMode.FLOATING_SHAPE;
25
+ }
26
+
27
+ public static boolean shouldClipGlassToOutline(TabbarOutlineMode mode) {
28
+ return mode == TabbarOutlineMode.FLOATING_SHAPE;
29
+ }
30
+
31
+ public static int floatingCapsuleWidth(int totalWidth, int barHeightPx, int trailingGapPx, boolean hasDetachedTrailing) {
32
+ if (!hasDetachedTrailing) {
33
+ return totalWidth;
34
+ }
35
+ return Math.max(0, totalWidth - barHeightPx - trailingGapPx);
36
+ }
37
+
38
+ public static Path buildFloatingTabbarPath(
39
+ int width,
40
+ int height,
41
+ float cornerRadiusPx,
42
+ float barHeightPx,
43
+ float trailingGapPx,
44
+ boolean hasDetachedTrailing
45
+ ) {
46
+ Path path = new Path();
47
+ int capsuleWidth = floatingCapsuleWidth(width, (int) barHeightPx, (int) trailingGapPx, hasDetachedTrailing);
48
+ path.addRoundRect(new RectF(0, 0, capsuleWidth, height), cornerRadiusPx, cornerRadiusPx, Path.Direction.CW);
49
+ if (hasDetachedTrailing) {
50
+ float diameter = barHeightPx;
51
+ float left = width - diameter;
52
+ float top = (height - diameter) / 2f;
53
+ path.addRoundRect(
54
+ new RectF(left, top, left + diameter, top + diameter),
55
+ diameter / 2f,
56
+ diameter / 2f,
57
+ Path.Direction.CW
58
+ );
59
+ }
60
+ return path;
61
+ }
62
+
63
+ public static boolean canApplyPathOutline() {
64
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
65
+ }
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.4.2",
3
+ "version": "8.4.4",
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",