@capgo/capacitor-native-navigation 8.1.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -112,6 +112,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
112
112
|
private String activeTransitionDirection = "forward";
|
|
113
113
|
private RectF activeZoomSourceFrame;
|
|
114
114
|
private float activeZoomCornerRadius = 0f;
|
|
115
|
+
private Drawable activeTransitionRootBackground;
|
|
116
|
+
private boolean activeTransitionRootBackgroundCaptured = false;
|
|
115
117
|
private final Map<Integer, String> menuActionIds = new HashMap<>();
|
|
116
118
|
private final Map<Integer, String> menuActionTitles = new HashMap<>();
|
|
117
119
|
private final Map<Integer, String> menuActionPlacements = new HashMap<>();
|
|
@@ -338,8 +340,11 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
338
340
|
|
|
339
341
|
if (transitionSnapshot != null) {
|
|
340
342
|
root.removeView(transitionSnapshot);
|
|
343
|
+
restoreTransitionRootBackground();
|
|
341
344
|
}
|
|
342
345
|
|
|
346
|
+
int transitionSurface = transitionSurfaceColor(webView);
|
|
347
|
+
prepareTransitionRootBackground(root, transitionSurface);
|
|
343
348
|
Bitmap bitmap = Bitmap.createBitmap(webView.getWidth(), webView.getHeight(), Bitmap.Config.ARGB_8888);
|
|
344
349
|
webView.draw(new Canvas(bitmap));
|
|
345
350
|
if (zoomSourceRect != null) {
|
|
@@ -348,6 +353,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
348
353
|
}
|
|
349
354
|
transitionSnapshot = new ImageView(getContext());
|
|
350
355
|
transitionSnapshot.setImageBitmap(bitmap);
|
|
356
|
+
transitionSnapshot.setBackgroundColor(transitionSurface);
|
|
351
357
|
transitionSnapshot.setScaleType(ImageView.ScaleType.FIT_XY);
|
|
352
358
|
FrameLayout.LayoutParams params =
|
|
353
359
|
activeZoomSourceFrame == null
|
|
@@ -415,8 +421,9 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
415
421
|
snapshotEndTranslation = -width * 0.3f;
|
|
416
422
|
}
|
|
417
423
|
|
|
424
|
+
boolean stationaryTransition = isStationaryTransition(direction);
|
|
418
425
|
webView.setTranslationX(startTranslation);
|
|
419
|
-
webView.setAlpha(
|
|
426
|
+
webView.setAlpha(stationaryTransition ? 1f : 0.01f);
|
|
420
427
|
View snapshot = transitionSnapshot;
|
|
421
428
|
JSObject event = transitionEvent(transitionId, direction, durationMs);
|
|
422
429
|
Runnable finish = () -> {
|
|
@@ -424,6 +431,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
424
431
|
if (root != null && transitionSnapshot != null) {
|
|
425
432
|
root.removeView(transitionSnapshot);
|
|
426
433
|
}
|
|
434
|
+
restoreTransitionRootBackground();
|
|
427
435
|
transitionSnapshot = null;
|
|
428
436
|
activeTransitionId = null;
|
|
429
437
|
activeZoomSourceFrame = null;
|
|
@@ -443,7 +451,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
443
451
|
snapshot
|
|
444
452
|
.animate()
|
|
445
453
|
.translationX(snapshotEndTranslation)
|
|
446
|
-
.alpha(
|
|
454
|
+
.alpha(stationaryTransition ? 0f : 0.75f)
|
|
447
455
|
.setDuration(durationMs)
|
|
448
456
|
.withEndAction(finish)
|
|
449
457
|
.start();
|
|
@@ -480,6 +488,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
480
488
|
if (root != null && transitionSnapshot != null) {
|
|
481
489
|
root.removeView(transitionSnapshot);
|
|
482
490
|
}
|
|
491
|
+
restoreTransitionRootBackground();
|
|
483
492
|
transitionSnapshot = null;
|
|
484
493
|
activeTransitionId = null;
|
|
485
494
|
activeZoomSourceFrame = null;
|
|
@@ -1498,6 +1507,52 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1498
1507
|
return event;
|
|
1499
1508
|
}
|
|
1500
1509
|
|
|
1510
|
+
private boolean isStationaryTransition(String direction) {
|
|
1511
|
+
return "tab".equals(direction) || "root".equals(direction) || "none".equals(direction);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
private void prepareTransitionRootBackground(FrameLayout root, int surfaceColor) {
|
|
1515
|
+
activeTransitionRootBackground = root.getBackground();
|
|
1516
|
+
activeTransitionRootBackgroundCaptured = true;
|
|
1517
|
+
if (needsTransitionSurface(activeTransitionRootBackground)) {
|
|
1518
|
+
root.setBackgroundColor(surfaceColor);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
private void restoreTransitionRootBackground() {
|
|
1523
|
+
if (!activeTransitionRootBackgroundCaptured) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
FrameLayout root = contentRoot();
|
|
1527
|
+
if (root != null) {
|
|
1528
|
+
root.setBackground(activeTransitionRootBackground);
|
|
1529
|
+
}
|
|
1530
|
+
activeTransitionRootBackground = null;
|
|
1531
|
+
activeTransitionRootBackgroundCaptured = false;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
private boolean needsTransitionSurface(Drawable background) {
|
|
1535
|
+
if (background == null) {
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
if (background instanceof ColorDrawable) {
|
|
1539
|
+
return Color.alpha(((ColorDrawable) background).getColor()) < 255;
|
|
1540
|
+
}
|
|
1541
|
+
return false;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
private int transitionSurfaceColor(View webView) {
|
|
1545
|
+
Drawable background = webView.getBackground();
|
|
1546
|
+
if (background instanceof ColorDrawable) {
|
|
1547
|
+
int color = ((ColorDrawable) background).getColor();
|
|
1548
|
+
if (Color.alpha(color) > 0) {
|
|
1549
|
+
return withAlpha(color, 255);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
int fallback = isNightMode() ? Color.rgb(18, 18, 18) : Color.WHITE;
|
|
1553
|
+
return dynamicColor(isNightMode() ? "system_neutral1_900" : "system_neutral1_50", fallback);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1501
1556
|
private FrameLayout contentRoot() {
|
|
1502
1557
|
Activity activity = getActivity();
|
|
1503
1558
|
return activity == null ? null : activity.findViewById(android.R.id.content);
|
|
@@ -70,6 +70,10 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
70
70
|
private var activeTransitionDirection = "forward"
|
|
71
71
|
private var activeZoomSourceFrame: CGRect?
|
|
72
72
|
private var activeZoomCornerRadius: CGFloat = 0
|
|
73
|
+
private weak var activeTransitionContainer: UIView?
|
|
74
|
+
private var activeTransitionContainerBackgroundColor: UIColor?
|
|
75
|
+
private var activeTransitionContainerWasOpaque = false
|
|
76
|
+
private var activeTransitionContainerBackgroundCaptured = false
|
|
73
77
|
|
|
74
78
|
private var usesSystemLiquidGlass: Bool {
|
|
75
79
|
if #available(iOS 26.0, *) {
|
|
@@ -232,9 +236,14 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
232
236
|
let cornerRadius = CGFloat(call.getDouble("cornerRadius") ?? 0)
|
|
233
237
|
|
|
234
238
|
self.transitionSnapshot?.removeFromSuperview()
|
|
239
|
+
self.restoreTransitionContainerBackground()
|
|
240
|
+
let transitionSurface = nativeNavigationFallbackBackground(for: webView).withAlphaComponent(1.0)
|
|
241
|
+
self.prepareTransitionContainerBackground(transitionContainer, surface: transitionSurface)
|
|
235
242
|
let snapshot = self.transitionSnapshotView(from: webView, sourceRect: zoomSourceRect)
|
|
236
243
|
snapshot.frame = zoomSourceFrame ?? webView.frame
|
|
237
244
|
snapshot.autoresizingMask = zoomSourceFrame == nil ? [.flexibleWidth, .flexibleHeight] : []
|
|
245
|
+
snapshot.backgroundColor = transitionSurface
|
|
246
|
+
snapshot.isOpaque = true
|
|
238
247
|
snapshot.layer.cornerRadius = cornerRadius
|
|
239
248
|
snapshot.clipsToBounds = cornerRadius > 0
|
|
240
249
|
transitionContainer.insertSubview(snapshot, aboveSubview: webView)
|
|
@@ -292,8 +301,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
292
301
|
private func finishStandardTransition(_ transition: NativeNavigationTransitionContext) {
|
|
293
302
|
let width = transition.webView.bounds.width
|
|
294
303
|
let transforms = standardTransitionTransforms(direction: transition.direction, width: width)
|
|
304
|
+
let usesStationaryCrossfade = nativeNavigationUsesStationaryTransitionCrossfade(direction: transition.direction)
|
|
295
305
|
transition.webView.transform = transforms.start
|
|
296
|
-
transition.webView.alpha =
|
|
306
|
+
transition.webView.alpha = usesStationaryCrossfade ? 1 : 0.01
|
|
297
307
|
|
|
298
308
|
UIView.animate(
|
|
299
309
|
withDuration: max(transition.duration, 0),
|
|
@@ -303,7 +313,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
303
313
|
transition.webView.transform = .identity
|
|
304
314
|
transition.webView.alpha = 1
|
|
305
315
|
transition.snapshot?.transform = transforms.snapshotEnd
|
|
306
|
-
transition.snapshot?.alpha =
|
|
316
|
+
transition.snapshot?.alpha = usesStationaryCrossfade ? 0 : 0.75
|
|
307
317
|
},
|
|
308
318
|
completion: { _ in
|
|
309
319
|
self.finishTransitionCleanup(transition)
|
|
@@ -407,6 +417,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
407
417
|
transition.webView.layer.cornerRadius = 0
|
|
408
418
|
transition.webView.clipsToBounds = false
|
|
409
419
|
transitionSnapshot = nil
|
|
420
|
+
restoreTransitionContainerBackground()
|
|
410
421
|
activeTransitionId = nil
|
|
411
422
|
activeZoomSourceFrame = nil
|
|
412
423
|
let event: [String: Any] = ["id": transition.id, "direction": transition.direction, "duration": transition.durationMs]
|
|
@@ -414,6 +425,29 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
|
|
|
414
425
|
transition.resolve(event)
|
|
415
426
|
}
|
|
416
427
|
|
|
428
|
+
private func prepareTransitionContainerBackground(_ container: UIView, surface: UIColor) {
|
|
429
|
+
activeTransitionContainer = container
|
|
430
|
+
activeTransitionContainerBackgroundColor = container.backgroundColor
|
|
431
|
+
activeTransitionContainerWasOpaque = container.isOpaque
|
|
432
|
+
activeTransitionContainerBackgroundCaptured = true
|
|
433
|
+
if nativeNavigationNeedsTransitionSurface(container.backgroundColor) {
|
|
434
|
+
container.backgroundColor = surface
|
|
435
|
+
container.isOpaque = true
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private func restoreTransitionContainerBackground() {
|
|
440
|
+
guard activeTransitionContainerBackgroundCaptured else {
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
activeTransitionContainer?.backgroundColor = activeTransitionContainerBackgroundColor
|
|
444
|
+
activeTransitionContainer?.isOpaque = activeTransitionContainerWasOpaque
|
|
445
|
+
activeTransitionContainer = nil
|
|
446
|
+
activeTransitionContainerBackgroundColor = nil
|
|
447
|
+
activeTransitionContainerWasOpaque = false
|
|
448
|
+
activeTransitionContainerBackgroundCaptured = false
|
|
449
|
+
}
|
|
450
|
+
|
|
417
451
|
@objc func getPluginVersion(_ call: CAPPluginCall) {
|
|
418
452
|
call.resolve([
|
|
419
453
|
"version": implementation.getPluginVersion()
|
|
@@ -1611,9 +1645,20 @@ final class NativeNavigationTabContentController: UIViewController {
|
|
|
1611
1645
|
private func nativeNavigationFallbackBackground(for view: UIView) -> UIColor {
|
|
1612
1646
|
if let color = view.backgroundColor,
|
|
1613
1647
|
color.cgColor.alpha > 0 {
|
|
1614
|
-
return color
|
|
1648
|
+
return color.withAlphaComponent(1)
|
|
1649
|
+
}
|
|
1650
|
+
return UIColor.systemBackground.withAlphaComponent(1.0)
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
func nativeNavigationUsesStationaryTransitionCrossfade(direction: String) -> Bool {
|
|
1654
|
+
direction == "tab" || direction == "root" || direction == "none"
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
private func nativeNavigationNeedsTransitionSurface(_ color: UIColor?) -> Bool {
|
|
1658
|
+
guard let color else {
|
|
1659
|
+
return true
|
|
1615
1660
|
}
|
|
1616
|
-
return .
|
|
1661
|
+
return color.cgColor.alpha < 1
|
|
1617
1662
|
}
|
|
1618
1663
|
|
|
1619
1664
|
private func nativeNavigationSnapshotPlaceholder(for view: UIView) -> UIView {
|
|
@@ -89,4 +89,12 @@ class NativeNavigationTests: XCTestCase {
|
|
|
89
89
|
XCTAssertNil(webView.superview)
|
|
90
90
|
XCTAssertEqual(controller.view.superview, webView)
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
func testStationaryTransitionsCrossfadeSnapshotsAway() {
|
|
94
|
+
XCTAssertTrue(nativeNavigationUsesStationaryTransitionCrossfade(direction: "tab"))
|
|
95
|
+
XCTAssertTrue(nativeNavigationUsesStationaryTransitionCrossfade(direction: "root"))
|
|
96
|
+
XCTAssertTrue(nativeNavigationUsesStationaryTransitionCrossfade(direction: "none"))
|
|
97
|
+
XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "forward"))
|
|
98
|
+
XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "back"))
|
|
99
|
+
}
|
|
92
100
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/capacitor-native-navigation",
|
|
3
|
-
"version": "8.1.
|
|
3
|
+
"version": "8.1.1",
|
|
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",
|
|
@@ -53,11 +53,12 @@
|
|
|
53
53
|
"prepublishOnly": "bun run build",
|
|
54
54
|
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs",
|
|
55
55
|
"demo:readme": "bun scripts/generate-readme-demos.mjs",
|
|
56
|
-
"example:build": "bun run build && cd example-app && bun install && bun run build",
|
|
56
|
+
"example:build": "bun run build && cd example-app && bun install --frozen-lockfile && bun run build",
|
|
57
57
|
"example:capgo:deploy": "bun run example:build && bun scripts/deploy-example-capgo.mjs",
|
|
58
58
|
"example:sync": "bun run example:build && cd example-app && bunx cap sync",
|
|
59
59
|
"example:ios": "bun run example:build && cd example-app && ([ -d ios ] || bunx cap add ios) && bunx cap sync ios && bunx cap run ios",
|
|
60
|
-
"example:android": "bun run example:build && cd example-app && ([ -d android ] || bunx cap add android) && bunx cap sync android && bunx cap run android"
|
|
60
|
+
"example:android": "bun run example:build && cd example-app && ([ -d android ] || bunx cap add android) && bunx cap sync android && bunx cap run android",
|
|
61
|
+
"example:install": "cd example-app && bun install --frozen-lockfile"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@capacitor/android": "^8.0.0",
|