@soundtrackio/react-native-bottom-tabs 1.4.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 -0
- package/android/build.gradle +132 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/rcttabview/ImageSource.kt +86 -0
- package/android/src/main/java/com/rcttabview/LatestRequestTracker.kt +19 -0
- package/android/src/main/java/com/rcttabview/RCTTabView.kt +689 -0
- package/android/src/main/java/com/rcttabview/RCTTabViewManager.kt +222 -0
- package/android/src/main/java/com/rcttabview/RCTTabViewPackage.kt +19 -0
- package/android/src/main/java/com/rcttabview/RCTTabViewPrewarm.kt +40 -0
- package/android/src/main/java/com/rcttabview/RebuildBatch.kt +31 -0
- package/android/src/main/java/com/rcttabview/TabBarVisibility.kt +14 -0
- package/android/src/main/java/com/rcttabview/Utils.kt +31 -0
- package/android/src/main/java/com/rcttabview/events/OnNativeLayoutEvent.kt +25 -0
- package/android/src/main/java/com/rcttabview/events/OnTabBarMeasuredEvent.kt +24 -0
- package/android/src/main/java/com/rcttabview/events/PageSelectedEvent.kt +32 -0
- package/android/src/main/java/com/rcttabview/events/TabLongPressedEvent.kt +24 -0
- package/android/src/test/java/com/rcttabview/LatestRequestTrackerTest.kt +37 -0
- package/android/src/test/java/com/rcttabview/RebuildBatchTest.kt +44 -0
- package/android/src/test/java/com/rcttabview/TabBarVisibilityTest.kt +58 -0
- package/app.plugin.js +1 -0
- package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewComponentDescriptor.h +33 -0
- package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.cpp +19 -0
- package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.h +35 -0
- package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.cpp +15 -0
- package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.h +25 -0
- package/ios/BottomAccessoryProvider.swift +27 -0
- package/ios/Extensions.swift +101 -0
- package/ios/PlatformAliases.swift +43 -0
- package/ios/RCTBottomAccessoryComponentView.h +17 -0
- package/ios/RCTBottomAccessoryComponentView.mm +76 -0
- package/ios/RCTTabViewComponentView.h +16 -0
- package/ios/RCTTabViewComponentView.mm +285 -0
- package/ios/RepresentableView.swift +55 -0
- package/ios/SVG/CoreSVG.h +19 -0
- package/ios/SVG/CoreSVG.mm +177 -0
- package/ios/SVG/SvgDecoder.h +10 -0
- package/ios/SVG/SvgDecoder.mm +38 -0
- package/ios/TabAppearModifier.swift +38 -0
- package/ios/TabBarFontSize.swift +88 -0
- package/ios/TabItem.swift +35 -0
- package/ios/TabItemEventModifier.swift +163 -0
- package/ios/TabView/AnyTabView.swift +7 -0
- package/ios/TabView/LegacyTabView.swift +70 -0
- package/ios/TabView/NewTabView.swift +138 -0
- package/ios/TabViewImpl.swift +566 -0
- package/ios/TabViewProps.swift +109 -0
- package/ios/TabViewProvider.swift +368 -0
- package/ios/react-native-bottom-tabs-Bridging-Header.h +4 -0
- package/lib/module/BottomAccessoryView.js +47 -0
- package/lib/module/BottomAccessoryView.js.map +1 -0
- package/lib/module/BottomAccessoryViewNativeComponent.ts +27 -0
- package/lib/module/DelayedFreeze.js +31 -0
- package/lib/module/DelayedFreeze.js.map +1 -0
- package/lib/module/NativeSVGDecoder.js +5 -0
- package/lib/module/NativeSVGDecoder.js.map +1 -0
- package/lib/module/SceneMap.js +28 -0
- package/lib/module/SceneMap.js.map +1 -0
- package/lib/module/TabView.js +294 -0
- package/lib/module/TabView.js.map +1 -0
- package/lib/module/TabViewNativeComponent.ts +73 -0
- package/lib/module/expo.js +35 -0
- package/lib/module/expo.js.map +1 -0
- package/lib/module/index.js +20 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/BottomTabBarHeightContext.js +5 -0
- package/lib/module/utils/BottomTabBarHeightContext.js.map +1 -0
- package/lib/module/utils/useBottomTabBarHeight.js +12 -0
- package/lib/module/utils/useBottomTabBarHeight.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/BottomAccessoryView.d.ts +7 -0
- package/lib/typescript/src/BottomAccessoryView.d.ts.map +1 -0
- package/lib/typescript/src/BottomAccessoryView.js +24 -0
- package/lib/typescript/src/BottomAccessoryViewNativeComponent.d.ts +15 -0
- package/lib/typescript/src/BottomAccessoryViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/BottomAccessoryViewNativeComponent.js +4 -0
- package/lib/typescript/src/DelayedFreeze.d.ts +7 -0
- package/lib/typescript/src/DelayedFreeze.d.ts.map +1 -0
- package/lib/typescript/src/DelayedFreeze.js +21 -0
- package/lib/typescript/src/NativeSVGDecoder.d.ts +5 -0
- package/lib/typescript/src/NativeSVGDecoder.d.ts.map +1 -0
- package/lib/typescript/src/NativeSVGDecoder.js +2 -0
- package/lib/typescript/src/SceneMap.d.ts +9 -0
- package/lib/typescript/src/SceneMap.d.ts.map +1 -0
- package/lib/typescript/src/SceneMap.js +15 -0
- package/lib/typescript/src/TabView.d.ts +219 -0
- package/lib/typescript/src/TabView.d.ts.map +1 -0
- package/lib/typescript/src/TabView.js +157 -0
- package/lib/typescript/src/TabViewNativeComponent.d.ts +59 -0
- package/lib/typescript/src/TabViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/TabViewNativeComponent.js +4 -0
- package/lib/typescript/src/expo.d.ts +6 -0
- package/lib/typescript/src/expo.d.ts.map +1 -0
- package/lib/typescript/src/expo.js +34 -0
- package/lib/typescript/src/index.d.ts +15 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/index.js +11 -0
- package/lib/typescript/src/types.d.ts +31 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/types.js +1 -0
- package/lib/typescript/src/utils/BottomTabBarHeightContext.d.ts +2 -0
- package/lib/typescript/src/utils/BottomTabBarHeightContext.d.ts.map +1 -0
- package/lib/typescript/src/utils/BottomTabBarHeightContext.js +2 -0
- package/lib/typescript/src/utils/useBottomTabBarHeight.d.ts +1 -0
- package/lib/typescript/src/utils/useBottomTabBarHeight.d.ts.map +1 -0
- package/lib/typescript/src/utils/useBottomTabBarHeight.js +9 -0
- package/lib/typescript/tsconfig.tsbuildinfo +1 -0
- package/package.json +135 -0
- package/react-native-bottom-tabs.podspec +43 -0
- package/src/BottomAccessoryView.tsx +59 -0
- package/src/BottomAccessoryViewNativeComponent.ts +27 -0
- package/src/DelayedFreeze.tsx +31 -0
- package/src/NativeSVGDecoder.ts +5 -0
- package/src/SceneMap.tsx +34 -0
- package/src/TabView.tsx +558 -0
- package/src/TabViewNativeComponent.ts +73 -0
- package/src/expo.ts +58 -0
- package/src/index.tsx +23 -0
- package/src/types.ts +36 -0
- package/src/utils/BottomTabBarHeightContext.ts +5 -0
- package/src/utils/useBottomTabBarHeight.ts +15 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#ifdef __cplusplus
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <react/renderer/components/RNCTabView/EventEmitters.h>
|
|
6
|
+
#include <react/renderer/components/RNCTabView/Props.h>
|
|
7
|
+
#include <react/renderer/components/RNCTabView/RNCTabViewState.h>
|
|
8
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
9
|
+
#include <jsi/jsi.h>
|
|
10
|
+
|
|
11
|
+
namespace facebook::react {
|
|
12
|
+
|
|
13
|
+
JSI_EXPORT extern const char RNCTabViewComponentName[];
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* `ShadowNode` for <RNCTabView> component.
|
|
17
|
+
*/
|
|
18
|
+
class JSI_EXPORT RNCTabViewShadowNode final: public ConcreteViewShadowNode<
|
|
19
|
+
RNCTabViewComponentName,
|
|
20
|
+
RNCTabViewProps,
|
|
21
|
+
RNCTabViewEventEmitter,
|
|
22
|
+
RNCTabViewState> {
|
|
23
|
+
|
|
24
|
+
public:
|
|
25
|
+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
26
|
+
using StateData = ConcreteViewShadowNode::ConcreteStateData;
|
|
27
|
+
|
|
28
|
+
void setImageLoader(std::weak_ptr<void> imageLoader);
|
|
29
|
+
|
|
30
|
+
StateData &getStateDataMutable();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#endif
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#include <react/renderer/components/RNCTabView/RNCTabViewState.h>
|
|
2
|
+
|
|
3
|
+
namespace facebook::react {
|
|
4
|
+
|
|
5
|
+
void RNCTabViewState::setImageLoader(
|
|
6
|
+
std::weak_ptr<void> imageLoader) {
|
|
7
|
+
imageLoader_ = imageLoader;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
std::weak_ptr<void> RNCTabViewState::getImageLoader()
|
|
11
|
+
const noexcept {
|
|
12
|
+
return imageLoader_;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#ifdef __cplusplus
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <react/renderer/core/StateData.h>
|
|
6
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
7
|
+
#include <folly/dynamic.h>
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
namespace facebook::react {
|
|
11
|
+
|
|
12
|
+
class RNCTabViewState final {
|
|
13
|
+
public:
|
|
14
|
+
RNCTabViewState() = default;
|
|
15
|
+
|
|
16
|
+
void setImageLoader(std::weak_ptr<void> imageLoader);
|
|
17
|
+
std::weak_ptr<void> getImageLoader() const noexcept;
|
|
18
|
+
|
|
19
|
+
private:
|
|
20
|
+
std::weak_ptr<void> imageLoader_;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
} // namespace facebook::react
|
|
24
|
+
|
|
25
|
+
#endif
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
@objc public class BottomAccessoryProvider: NSObject {
|
|
4
|
+
private weak var delegate: BottomAccessoryProviderDelegate?
|
|
5
|
+
|
|
6
|
+
@objc public convenience init(delegate: BottomAccessoryProviderDelegate) {
|
|
7
|
+
self.init()
|
|
8
|
+
self.delegate = delegate
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
#if !os(macOS) && compiler(>=6.2)
|
|
12
|
+
@available(iOS 26.0, tvOS 26.0, *)
|
|
13
|
+
public func emitPlacementChanged(_ placement: TabViewBottomAccessoryPlacement?) {
|
|
14
|
+
var placementValue = "none"
|
|
15
|
+
if placement == .inline {
|
|
16
|
+
placementValue = "inline"
|
|
17
|
+
} else if placement == .expanded {
|
|
18
|
+
placementValue = "expanded"
|
|
19
|
+
}
|
|
20
|
+
self.delegate?.onPlacementChanged(placement: placementValue)
|
|
21
|
+
}
|
|
22
|
+
#endif
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc public protocol BottomAccessoryProviderDelegate {
|
|
26
|
+
func onPlacementChanged(placement: String)
|
|
27
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import SwiftUI
|
|
3
|
+
@_spi(Advanced) import SwiftUIIntrospect
|
|
4
|
+
|
|
5
|
+
#if os(macOS)
|
|
6
|
+
import AppKit
|
|
7
|
+
#else
|
|
8
|
+
import UIKit
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
extension Collection {
|
|
12
|
+
// Returns the element at the specified index if it is within bounds, otherwise nil.
|
|
13
|
+
subscript(safe index: Index) -> Element? {
|
|
14
|
+
indices.contains(index) ? self[index] : nil
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
extension Collection where Element == TabInfo {
|
|
19
|
+
func findByKey(_ key: String?) -> Element? {
|
|
20
|
+
guard let key else { return nil }
|
|
21
|
+
guard !isEmpty else { return nil }
|
|
22
|
+
return first { $0.key == key }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
extension PlatformView {
|
|
27
|
+
func pinEdges(to other: PlatformView) {
|
|
28
|
+
NSLayoutConstraint.activate([
|
|
29
|
+
leadingAnchor.constraint(equalTo: other.leadingAnchor),
|
|
30
|
+
trailingAnchor.constraint(equalTo: other.trailingAnchor),
|
|
31
|
+
topAnchor.constraint(equalTo: other.topAnchor),
|
|
32
|
+
bottomAnchor.constraint(equalTo: other.bottomAnchor)
|
|
33
|
+
])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
extension PlatformImage {
|
|
38
|
+
func resizeImageTo(size: CGSize) -> PlatformImage? {
|
|
39
|
+
#if os(macOS)
|
|
40
|
+
return NSImage(size: size, flipped: false) { rect -> Bool in
|
|
41
|
+
self.draw(in: rect,
|
|
42
|
+
from: CGRect(origin: .zero, size: self.size),
|
|
43
|
+
operation: .copy,
|
|
44
|
+
fraction: 1.0)
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
#else
|
|
48
|
+
let renderer = UIGraphicsImageRenderer(size: size)
|
|
49
|
+
return renderer.image { _ in
|
|
50
|
+
self.draw(in: CGRect(origin: .zero, size: size))
|
|
51
|
+
}
|
|
52
|
+
#endif
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
extension View {
|
|
57
|
+
#if os(macOS)
|
|
58
|
+
@MainActor
|
|
59
|
+
@ViewBuilder
|
|
60
|
+
func introspectTabView(closure: @escaping (NSTabView) -> Void) -> some View {
|
|
61
|
+
self
|
|
62
|
+
.introspect(
|
|
63
|
+
.tabView,
|
|
64
|
+
on: .macOS(.v11...),
|
|
65
|
+
customize: closure
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
#else
|
|
69
|
+
@MainActor
|
|
70
|
+
@ViewBuilder
|
|
71
|
+
func introspectTabView(closure: @escaping (UITabBarController) -> Void) -> some View {
|
|
72
|
+
self
|
|
73
|
+
#if !os(visionOS)
|
|
74
|
+
.introspect(
|
|
75
|
+
.tabView,
|
|
76
|
+
on: .iOS(.v14...),
|
|
77
|
+
.tvOS(.v14...),
|
|
78
|
+
customize: closure
|
|
79
|
+
)
|
|
80
|
+
#endif
|
|
81
|
+
}
|
|
82
|
+
#endif
|
|
83
|
+
|
|
84
|
+
@MainActor
|
|
85
|
+
@ViewBuilder
|
|
86
|
+
func measureView(onLayout: @escaping (_ size: CGSize) -> Void) -> some View {
|
|
87
|
+
self
|
|
88
|
+
.background(
|
|
89
|
+
GeometryReader { geometry in
|
|
90
|
+
Color.clear
|
|
91
|
+
.onChange(of: geometry.size) { newValue in
|
|
92
|
+
onLayout(newValue)
|
|
93
|
+
}
|
|
94
|
+
.onAppear {
|
|
95
|
+
onLayout(geometry.size)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
.ignoresSafeArea(.all, edges: .all)
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
#if os(macOS)
|
|
4
|
+
import AppKit
|
|
5
|
+
#else
|
|
6
|
+
import UIKit
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
#if os(macOS)
|
|
10
|
+
public typealias PlatformView = NSView
|
|
11
|
+
#else
|
|
12
|
+
public typealias PlatformView = UIView
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#if os(macOS)
|
|
16
|
+
typealias PlatformImage = NSImage
|
|
17
|
+
#else
|
|
18
|
+
typealias PlatformImage = UIImage
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
#if os(macOS)
|
|
22
|
+
public typealias PlatformColor = NSColor
|
|
23
|
+
#else
|
|
24
|
+
public typealias PlatformColor = UIColor
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#if os(macOS)
|
|
28
|
+
typealias PlatformHostingController = NSHostingController
|
|
29
|
+
#else
|
|
30
|
+
typealias PlatformHostingController = UIHostingController
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
#if os(macOS)
|
|
34
|
+
typealias PlatformViewRepresentable = NSViewRepresentable
|
|
35
|
+
#else
|
|
36
|
+
typealias PlatformViewRepresentable = UIViewRepresentable
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
#if os(macOS)
|
|
40
|
+
typealias PlatformFont = NSFont
|
|
41
|
+
#else
|
|
42
|
+
typealias PlatformFont = UIFont
|
|
43
|
+
#endif
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import <React/RCTViewComponentView.h>
|
|
3
|
+
#if TARGET_OS_OSX
|
|
4
|
+
#import <AppKit/AppKit.h>
|
|
5
|
+
#else
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
10
|
+
|
|
11
|
+
@interface RCTBottomAccessoryComponentView: RCTViewComponentView
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
NS_ASSUME_NONNULL_END
|
|
16
|
+
|
|
17
|
+
#endif /* RCTBottomAccessoryComponentView_h */
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import "RCTBottomAccessoryComponentView.h"
|
|
3
|
+
|
|
4
|
+
#import <react/renderer/components/RNCTabView/ComponentDescriptors.h>
|
|
5
|
+
#import <react/renderer/components/RNCTabView/EventEmitters.h>
|
|
6
|
+
#import <react/renderer/components/RNCTabView/Props.h>
|
|
7
|
+
#import <react/renderer/components/RNCTabView/RCTComponentViewHelpers.h>
|
|
8
|
+
|
|
9
|
+
#import <React/RCTFabricComponentsPlugins.h>
|
|
10
|
+
|
|
11
|
+
#if __has_include("react_native_bottom_tabs/react_native_bottom_tabs-Swift.h")
|
|
12
|
+
#import "react_native_bottom_tabs/react_native_bottom_tabs-Swift.h"
|
|
13
|
+
#else
|
|
14
|
+
#import "react_native_bottom_tabs-Swift.h"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
using namespace facebook::react;
|
|
18
|
+
|
|
19
|
+
@interface RCTBottomAccessoryComponentView () <BottomAccessoryProviderDelegate> {
|
|
20
|
+
BottomAccessoryProvider* bottomAccessoryProvider;
|
|
21
|
+
}
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation RCTBottomAccessoryComponentView
|
|
25
|
+
|
|
26
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
27
|
+
{
|
|
28
|
+
return concreteComponentDescriptorProvider<BottomAccessoryViewComponentDescriptor>();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
32
|
+
{
|
|
33
|
+
if (self = [super initWithFrame:frame]) {
|
|
34
|
+
static const auto defaultProps = std::make_shared<const BottomAccessoryViewProps>();
|
|
35
|
+
if (@available(iOS 26.0, *)) {
|
|
36
|
+
bottomAccessoryProvider = [[BottomAccessoryProvider alloc] initWithDelegate:self];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return self;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (void)setFrame:(CGRect)frame
|
|
44
|
+
{
|
|
45
|
+
[super setFrame:frame];
|
|
46
|
+
auto eventEmitter = std::static_pointer_cast<const BottomAccessoryViewEventEmitter>(_eventEmitter);
|
|
47
|
+
if (eventEmitter) {
|
|
48
|
+
// TODO: Rewrite this to emit synchronous layout events using shadow nodes
|
|
49
|
+
eventEmitter->onNativeLayout(BottomAccessoryViewEventEmitter::OnNativeLayout {
|
|
50
|
+
.height = frame.size.height,
|
|
51
|
+
.width = frame.size.width
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// MARK: BottomAccessoryProviderDelegate
|
|
57
|
+
|
|
58
|
+
- (void)onPlacementChangedWithPlacement:(NSString *)placement
|
|
59
|
+
{
|
|
60
|
+
auto eventEmitter = std::static_pointer_cast<const BottomAccessoryViewEventEmitter>(_eventEmitter);
|
|
61
|
+
if (eventEmitter) {
|
|
62
|
+
eventEmitter->onPlacementChanged(BottomAccessoryViewEventEmitter::OnPlacementChanged {
|
|
63
|
+
.placement = std::string([placement UTF8String])
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Class<RCTComponentViewProtocol> BottomAccessoryViewCls(void)
|
|
70
|
+
{
|
|
71
|
+
return RCTBottomAccessoryComponentView.class;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@end
|
|
75
|
+
|
|
76
|
+
#endif
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import <React/RCTViewComponentView.h>
|
|
3
|
+
#if TARGET_OS_OSX
|
|
4
|
+
#import <AppKit/AppKit.h>
|
|
5
|
+
#else
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
10
|
+
|
|
11
|
+
@interface RCTTabViewComponentView: RCTViewComponentView
|
|
12
|
+
@end
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_END
|
|
15
|
+
|
|
16
|
+
#endif /* RCT_NEW_ARCH_ENABLED */
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import "RCTTabViewComponentView.h"
|
|
3
|
+
|
|
4
|
+
#import <react/renderer/components/RNCTabView/RNCTabViewComponentDescriptor.h>
|
|
5
|
+
#import <react/renderer/components/RNCTabView/EventEmitters.h>
|
|
6
|
+
#import <react/renderer/components/RNCTabView/Props.h>
|
|
7
|
+
#import <react/renderer/components/RNCTabView/RCTComponentViewHelpers.h>
|
|
8
|
+
|
|
9
|
+
#import <React/RCTFabricComponentsPlugins.h>
|
|
10
|
+
|
|
11
|
+
#if __has_include("react_native_bottom_tabs/react_native_bottom_tabs-Swift.h")
|
|
12
|
+
#import "react_native_bottom_tabs/react_native_bottom_tabs-Swift.h"
|
|
13
|
+
#else
|
|
14
|
+
#import "react_native_bottom_tabs-Swift.h"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#import <React/RCTImageLoader.h>
|
|
18
|
+
#import <React/RCTImageSource.h>
|
|
19
|
+
#import <React/RCTBridge+Private.h>
|
|
20
|
+
#import "RCTImagePrimitivesConversions.h"
|
|
21
|
+
#if __has_include(<React/RCTConversions.h>)
|
|
22
|
+
#import <React/RCTConversions.h>
|
|
23
|
+
#else
|
|
24
|
+
#import "RCTConversions.h"
|
|
25
|
+
#endif
|
|
26
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
27
|
+
|
|
28
|
+
#if TARGET_OS_OSX
|
|
29
|
+
typedef NSView PlatformView;
|
|
30
|
+
#else
|
|
31
|
+
typedef UIView PlatformView;
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
// Overload `==` and `!=` operators for `RNCTabViewItemsStruct`
|
|
35
|
+
|
|
36
|
+
namespace facebook::react {
|
|
37
|
+
|
|
38
|
+
bool operator==(const RNCTabViewItemsStruct& lhs, const RNCTabViewItemsStruct& rhs) {
|
|
39
|
+
return lhs.key == rhs.key &&
|
|
40
|
+
lhs.title == rhs.title &&
|
|
41
|
+
lhs.sfSymbol == rhs.sfSymbol &&
|
|
42
|
+
lhs.focusedSfSymbol == rhs.focusedSfSymbol &&
|
|
43
|
+
lhs.badge == rhs.badge &&
|
|
44
|
+
lhs.activeTintColor == rhs.activeTintColor &&
|
|
45
|
+
lhs.iconRenderingMode == rhs.iconRenderingMode &&
|
|
46
|
+
lhs.hidden == rhs.hidden &&
|
|
47
|
+
lhs.testID == rhs.testID &&
|
|
48
|
+
lhs.role == rhs.role &&
|
|
49
|
+
lhs.preventsDefault == rhs.preventsDefault;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
bool operator!=(const RNCTabViewItemsStruct& lhs, const RNCTabViewItemsStruct& rhs) {
|
|
53
|
+
return !(lhs == rhs);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
using namespace facebook::react;
|
|
60
|
+
|
|
61
|
+
@interface RCTTabViewComponentView () <RCTRNCTabViewViewProtocol, TabViewProviderDelegate> {
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@end
|
|
65
|
+
|
|
66
|
+
@implementation RCTTabViewComponentView {
|
|
67
|
+
TabViewProvider *_tabViewProvider;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
71
|
+
{
|
|
72
|
+
return concreteComponentDescriptorProvider<RNCTabViewComponentDescriptor>();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
76
|
+
{
|
|
77
|
+
if (self = [super initWithFrame:frame]) {
|
|
78
|
+
static const auto defaultProps = std::make_shared<const RNCTabViewProps>();
|
|
79
|
+
_tabViewProvider = [[TabViewProvider alloc] initWithDelegate:self];
|
|
80
|
+
self.contentView = _tabViewProvider;
|
|
81
|
+
_props = defaultProps;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return self;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Opt out of recycling for now, it's not working properly.
|
|
88
|
+
+ (BOOL)shouldBeRecycled
|
|
89
|
+
{
|
|
90
|
+
return NO;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
- (void)mountChildComponentView:(PlatformView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
94
|
+
[_tabViewProvider insertChild:childComponentView atIndex:index];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (void)unmountChildComponentView:(PlatformView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
98
|
+
[_tabViewProvider removeChildAtIndex:index];
|
|
99
|
+
[childComponentView removeFromSuperview];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
103
|
+
{
|
|
104
|
+
const auto &oldViewProps = *std::static_pointer_cast<RNCTabViewProps const>(_props);
|
|
105
|
+
const auto &newViewProps = *std::static_pointer_cast<RNCTabViewProps const>(props);
|
|
106
|
+
|
|
107
|
+
if (oldViewProps.items != newViewProps.items) {
|
|
108
|
+
_tabViewProvider.itemsData = convertItemsToArray(newViewProps.items);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (oldViewProps.translucent != newViewProps.translucent) {
|
|
112
|
+
_tabViewProvider.translucent = newViewProps.translucent;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (oldViewProps.icons != newViewProps.icons) {
|
|
116
|
+
auto iconsArray = [[NSMutableArray alloc] init];
|
|
117
|
+
for (auto &source: newViewProps.icons) {
|
|
118
|
+
auto imageSource = [[RCTImageSource alloc] initWithURLRequest:NSURLRequestFromImageSource(source) size:CGSizeMake(source.size.width, source.size.height) scale:source.scale];
|
|
119
|
+
[iconsArray addObject:imageSource];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_tabViewProvider.icons = iconsArray;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (oldViewProps.focusedIcons != newViewProps.focusedIcons) {
|
|
126
|
+
auto focusedIconsArray = [[NSMutableArray alloc] init];
|
|
127
|
+
for (auto &source: newViewProps.focusedIcons) {
|
|
128
|
+
auto imageSource = [[RCTImageSource alloc] initWithURLRequest:NSURLRequestFromImageSource(source) size:CGSizeMake(source.size.width, source.size.height) scale:source.scale];
|
|
129
|
+
[focusedIconsArray addObject:imageSource];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_tabViewProvider.focusedIcons = focusedIconsArray;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (oldViewProps.sidebarAdaptable != newViewProps.sidebarAdaptable) {
|
|
136
|
+
_tabViewProvider.sidebarAdaptable = newViewProps.sidebarAdaptable;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (oldViewProps.minimizeBehavior != newViewProps.minimizeBehavior) {
|
|
140
|
+
_tabViewProvider.minimizeBehavior = RCTNSStringFromString(newViewProps.minimizeBehavior);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (oldViewProps.disablePageAnimations != newViewProps.disablePageAnimations) {
|
|
144
|
+
_tabViewProvider.disablePageAnimations = newViewProps.disablePageAnimations;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (oldViewProps.labeled != newViewProps.labeled) {
|
|
148
|
+
_tabViewProvider.labeled = newViewProps.labeled;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (oldViewProps.selectedPage != newViewProps.selectedPage) {
|
|
152
|
+
_tabViewProvider.selectedPage = RCTNSStringFromString(newViewProps.selectedPage);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (oldViewProps.scrollEdgeAppearance != newViewProps.scrollEdgeAppearance) {
|
|
156
|
+
_tabViewProvider.scrollEdgeAppearance = RCTNSStringFromString(newViewProps.scrollEdgeAppearance);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (oldViewProps.labeled != newViewProps.labeled) {
|
|
160
|
+
_tabViewProvider.labeled = newViewProps.labeled;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (oldViewProps.barTintColor != newViewProps.barTintColor) {
|
|
164
|
+
_tabViewProvider.barTintColor = RCTUIColorFromSharedColor(newViewProps.barTintColor);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (oldViewProps.activeTintColor != newViewProps.activeTintColor) {
|
|
168
|
+
_tabViewProvider.activeTintColor = RCTUIColorFromSharedColor(newViewProps.activeTintColor);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (oldViewProps.inactiveTintColor != newViewProps.inactiveTintColor) {
|
|
172
|
+
_tabViewProvider.inactiveTintColor = RCTUIColorFromSharedColor(newViewProps.inactiveTintColor);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (oldViewProps.experimentalBakedTintColors != newViewProps.experimentalBakedTintColors) {
|
|
176
|
+
_tabViewProvider.experimentalBakedTintColors = newViewProps.experimentalBakedTintColors;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (oldViewProps.hapticFeedbackEnabled != newViewProps.hapticFeedbackEnabled) {
|
|
180
|
+
_tabViewProvider.hapticFeedbackEnabled = newViewProps.hapticFeedbackEnabled;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (oldViewProps.layoutDirection != newViewProps.layoutDirection) {
|
|
184
|
+
_tabViewProvider.layoutDirection = RCTNSStringFromStringNilIfEmpty(newViewProps.layoutDirection);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (oldViewProps.fontSize != newViewProps.fontSize) {
|
|
188
|
+
_tabViewProvider.fontSize = [NSNumber numberWithInt:newViewProps.fontSize];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (oldViewProps.fontWeight != newViewProps.fontWeight) {
|
|
192
|
+
_tabViewProvider.fontWeight = RCTNSStringFromStringNilIfEmpty(newViewProps.fontWeight);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (oldViewProps.fontFamily != newViewProps.fontFamily) {
|
|
196
|
+
_tabViewProvider.fontFamily = RCTNSStringFromStringNilIfEmpty(newViewProps.fontFamily);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (oldViewProps.tabBarHidden != newViewProps.tabBarHidden) {
|
|
200
|
+
_tabViewProvider.tabBarHidden = newViewProps.tabBarHidden;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
[super updateProps:props oldProps:oldProps];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
NSArray* convertItemsToArray(const std::vector<RNCTabViewItemsStruct>& items) {
|
|
208
|
+
NSMutableArray<TabInfo *> *result = [NSMutableArray array];
|
|
209
|
+
|
|
210
|
+
for (const auto& item : items) {
|
|
211
|
+
auto tabInfo = [[TabInfo alloc] initWithKey:RCTNSStringFromString(item.key)
|
|
212
|
+
title:RCTNSStringFromString(item.title)
|
|
213
|
+
badge:RCTNSStringFromStringNilIfEmpty(item.badge)
|
|
214
|
+
sfSymbol:RCTNSStringFromStringNilIfEmpty(item.sfSymbol)
|
|
215
|
+
focusedSfSymbol:RCTNSStringFromStringNilIfEmpty(item.focusedSfSymbol)
|
|
216
|
+
activeTintColor:RCTUIColorFromSharedColor(item.activeTintColor)
|
|
217
|
+
iconRenderingMode:RCTNSStringFromStringNilIfEmpty(item.iconRenderingMode)
|
|
218
|
+
hidden:item.hidden
|
|
219
|
+
testID:RCTNSStringFromStringNilIfEmpty(item.testID)
|
|
220
|
+
role:RCTNSStringFromStringNilIfEmpty(item.role)
|
|
221
|
+
preventsDefault:item.preventsDefault
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
[result addObject:tabInfo];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
- (void)updateState:(const facebook::react::State::Shared &)state oldState:(const facebook::react::State::Shared &)oldState
|
|
231
|
+
{
|
|
232
|
+
auto _state = std::static_pointer_cast<RNCTabViewShadowNode::ConcreteState const>(state);
|
|
233
|
+
auto data = _state->getData();
|
|
234
|
+
if (auto imgLoaderPtr = _state.get()->getData().getImageLoader().lock()) {
|
|
235
|
+
[_tabViewProvider setImageLoader:unwrapManagedObject(imgLoaderPtr)];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// MARK: TabViewProviderDelegate
|
|
240
|
+
|
|
241
|
+
- (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
|
|
242
|
+
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
|
|
243
|
+
if (eventEmitter) {
|
|
244
|
+
eventEmitter->onPageSelected(RNCTabViewEventEmitter::OnPageSelected{
|
|
245
|
+
.key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
- (void)onLongPressWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
|
|
251
|
+
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
|
|
252
|
+
if (eventEmitter) {
|
|
253
|
+
eventEmitter->onTabLongPress(RNCTabViewEventEmitter::OnTabLongPress {
|
|
254
|
+
.key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
- (void)onTabBarMeasuredWithHeight:(NSInteger)height reactTag:(NSNumber *)reactTag {
|
|
260
|
+
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
|
|
261
|
+
if (eventEmitter) {
|
|
262
|
+
eventEmitter->onTabBarMeasured(RNCTabViewEventEmitter::OnTabBarMeasured {
|
|
263
|
+
.height = (int)height
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
- (void)onLayoutWithSize:(CGSize)size reactTag:(NSNumber *)reactTag {
|
|
269
|
+
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
|
|
270
|
+
if (eventEmitter) {
|
|
271
|
+
eventEmitter->onNativeLayout(RNCTabViewEventEmitter::OnNativeLayout {
|
|
272
|
+
.height = size.height,
|
|
273
|
+
.width = size.width
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@end
|
|
279
|
+
|
|
280
|
+
Class<RCTComponentViewProtocol> RNCTabViewCls(void)
|
|
281
|
+
{
|
|
282
|
+
return RCTTabViewComponentView.class;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Helper used to render UIView inside of SwiftUI.
|
|
5
|
+
Wraps each view with an additional wrapper to avoid directly managing React Native views.
|
|
6
|
+
This solves issues where the layout would have weird artifacts..
|
|
7
|
+
*/
|
|
8
|
+
struct RepresentableView: PlatformViewRepresentable {
|
|
9
|
+
var view: PlatformView
|
|
10
|
+
|
|
11
|
+
#if os(macOS)
|
|
12
|
+
|
|
13
|
+
func makeNSView(context: Context) -> PlatformView {
|
|
14
|
+
let wrapper = NSView()
|
|
15
|
+
wrapper.addSubview(view)
|
|
16
|
+
return wrapper
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func updateNSView(_ nsView: PlatformView, context: Context) {}
|
|
20
|
+
|
|
21
|
+
#else
|
|
22
|
+
|
|
23
|
+
func makeUIView(context: Context) -> PlatformView {
|
|
24
|
+
let wrapper = UIView()
|
|
25
|
+
Self.detachStaleViewControllers(in: view)
|
|
26
|
+
wrapper.addSubview(view)
|
|
27
|
+
return wrapper
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func updateUIView(_ uiView: PlatformView, context: Context) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
When a tab is hidden, SwiftUI drops it from the view tree and discards its
|
|
34
|
+
`UIHostingController`, but view controllers that libraries such as
|
|
35
|
+
react-native-screens attached to that host (e.g. `RNSNavigationController`)
|
|
36
|
+
stay parented to it. Those libraries only attach when the controller has no
|
|
37
|
+
parent, so re-adding the same React Native view under a fresh host trips
|
|
38
|
+
UIKit's hierarchy consistency check and crashes. Detaching the stale
|
|
39
|
+
children first lets them re-attach to the new host on their own.
|
|
40
|
+
*/
|
|
41
|
+
static func detachStaleViewControllers(in view: UIView) {
|
|
42
|
+
for subview in view.subviews {
|
|
43
|
+
if let controller = subview.next as? UIViewController, controller.view === subview {
|
|
44
|
+
if controller.parent != nil {
|
|
45
|
+
controller.willMove(toParent: nil)
|
|
46
|
+
controller.removeFromParent()
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
detachStaleViewControllers(in: subview)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#endif
|
|
55
|
+
}
|