@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.
Files changed (125) hide show
  1. package/README.md +32 -0
  2. package/android/build.gradle +132 -0
  3. package/android/gradle.properties +5 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/AndroidManifestNew.xml +2 -0
  6. package/android/src/main/java/com/rcttabview/ImageSource.kt +86 -0
  7. package/android/src/main/java/com/rcttabview/LatestRequestTracker.kt +19 -0
  8. package/android/src/main/java/com/rcttabview/RCTTabView.kt +689 -0
  9. package/android/src/main/java/com/rcttabview/RCTTabViewManager.kt +222 -0
  10. package/android/src/main/java/com/rcttabview/RCTTabViewPackage.kt +19 -0
  11. package/android/src/main/java/com/rcttabview/RCTTabViewPrewarm.kt +40 -0
  12. package/android/src/main/java/com/rcttabview/RebuildBatch.kt +31 -0
  13. package/android/src/main/java/com/rcttabview/TabBarVisibility.kt +14 -0
  14. package/android/src/main/java/com/rcttabview/Utils.kt +31 -0
  15. package/android/src/main/java/com/rcttabview/events/OnNativeLayoutEvent.kt +25 -0
  16. package/android/src/main/java/com/rcttabview/events/OnTabBarMeasuredEvent.kt +24 -0
  17. package/android/src/main/java/com/rcttabview/events/PageSelectedEvent.kt +32 -0
  18. package/android/src/main/java/com/rcttabview/events/TabLongPressedEvent.kt +24 -0
  19. package/android/src/test/java/com/rcttabview/LatestRequestTrackerTest.kt +37 -0
  20. package/android/src/test/java/com/rcttabview/RebuildBatchTest.kt +44 -0
  21. package/android/src/test/java/com/rcttabview/TabBarVisibilityTest.kt +58 -0
  22. package/app.plugin.js +1 -0
  23. package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewComponentDescriptor.h +33 -0
  24. package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.cpp +19 -0
  25. package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewShadowNode.h +35 -0
  26. package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.cpp +15 -0
  27. package/common/cpp/react/renderer/components/RNCTabView/RNCTabViewState.h +25 -0
  28. package/ios/BottomAccessoryProvider.swift +27 -0
  29. package/ios/Extensions.swift +101 -0
  30. package/ios/PlatformAliases.swift +43 -0
  31. package/ios/RCTBottomAccessoryComponentView.h +17 -0
  32. package/ios/RCTBottomAccessoryComponentView.mm +76 -0
  33. package/ios/RCTTabViewComponentView.h +16 -0
  34. package/ios/RCTTabViewComponentView.mm +285 -0
  35. package/ios/RepresentableView.swift +55 -0
  36. package/ios/SVG/CoreSVG.h +19 -0
  37. package/ios/SVG/CoreSVG.mm +177 -0
  38. package/ios/SVG/SvgDecoder.h +10 -0
  39. package/ios/SVG/SvgDecoder.mm +38 -0
  40. package/ios/TabAppearModifier.swift +38 -0
  41. package/ios/TabBarFontSize.swift +88 -0
  42. package/ios/TabItem.swift +35 -0
  43. package/ios/TabItemEventModifier.swift +163 -0
  44. package/ios/TabView/AnyTabView.swift +7 -0
  45. package/ios/TabView/LegacyTabView.swift +70 -0
  46. package/ios/TabView/NewTabView.swift +138 -0
  47. package/ios/TabViewImpl.swift +566 -0
  48. package/ios/TabViewProps.swift +109 -0
  49. package/ios/TabViewProvider.swift +368 -0
  50. package/ios/react-native-bottom-tabs-Bridging-Header.h +4 -0
  51. package/lib/module/BottomAccessoryView.js +47 -0
  52. package/lib/module/BottomAccessoryView.js.map +1 -0
  53. package/lib/module/BottomAccessoryViewNativeComponent.ts +27 -0
  54. package/lib/module/DelayedFreeze.js +31 -0
  55. package/lib/module/DelayedFreeze.js.map +1 -0
  56. package/lib/module/NativeSVGDecoder.js +5 -0
  57. package/lib/module/NativeSVGDecoder.js.map +1 -0
  58. package/lib/module/SceneMap.js +28 -0
  59. package/lib/module/SceneMap.js.map +1 -0
  60. package/lib/module/TabView.js +294 -0
  61. package/lib/module/TabView.js.map +1 -0
  62. package/lib/module/TabViewNativeComponent.ts +73 -0
  63. package/lib/module/expo.js +35 -0
  64. package/lib/module/expo.js.map +1 -0
  65. package/lib/module/index.js +20 -0
  66. package/lib/module/index.js.map +1 -0
  67. package/lib/module/package.json +1 -0
  68. package/lib/module/types.js +4 -0
  69. package/lib/module/types.js.map +1 -0
  70. package/lib/module/utils/BottomTabBarHeightContext.js +5 -0
  71. package/lib/module/utils/BottomTabBarHeightContext.js.map +1 -0
  72. package/lib/module/utils/useBottomTabBarHeight.js +12 -0
  73. package/lib/module/utils/useBottomTabBarHeight.js.map +1 -0
  74. package/lib/typescript/package.json +1 -0
  75. package/lib/typescript/src/BottomAccessoryView.d.ts +7 -0
  76. package/lib/typescript/src/BottomAccessoryView.d.ts.map +1 -0
  77. package/lib/typescript/src/BottomAccessoryView.js +24 -0
  78. package/lib/typescript/src/BottomAccessoryViewNativeComponent.d.ts +15 -0
  79. package/lib/typescript/src/BottomAccessoryViewNativeComponent.d.ts.map +1 -0
  80. package/lib/typescript/src/BottomAccessoryViewNativeComponent.js +4 -0
  81. package/lib/typescript/src/DelayedFreeze.d.ts +7 -0
  82. package/lib/typescript/src/DelayedFreeze.d.ts.map +1 -0
  83. package/lib/typescript/src/DelayedFreeze.js +21 -0
  84. package/lib/typescript/src/NativeSVGDecoder.d.ts +5 -0
  85. package/lib/typescript/src/NativeSVGDecoder.d.ts.map +1 -0
  86. package/lib/typescript/src/NativeSVGDecoder.js +2 -0
  87. package/lib/typescript/src/SceneMap.d.ts +9 -0
  88. package/lib/typescript/src/SceneMap.d.ts.map +1 -0
  89. package/lib/typescript/src/SceneMap.js +15 -0
  90. package/lib/typescript/src/TabView.d.ts +219 -0
  91. package/lib/typescript/src/TabView.d.ts.map +1 -0
  92. package/lib/typescript/src/TabView.js +157 -0
  93. package/lib/typescript/src/TabViewNativeComponent.d.ts +59 -0
  94. package/lib/typescript/src/TabViewNativeComponent.d.ts.map +1 -0
  95. package/lib/typescript/src/TabViewNativeComponent.js +4 -0
  96. package/lib/typescript/src/expo.d.ts +6 -0
  97. package/lib/typescript/src/expo.d.ts.map +1 -0
  98. package/lib/typescript/src/expo.js +34 -0
  99. package/lib/typescript/src/index.d.ts +15 -0
  100. package/lib/typescript/src/index.d.ts.map +1 -0
  101. package/lib/typescript/src/index.js +11 -0
  102. package/lib/typescript/src/types.d.ts +31 -0
  103. package/lib/typescript/src/types.d.ts.map +1 -0
  104. package/lib/typescript/src/types.js +1 -0
  105. package/lib/typescript/src/utils/BottomTabBarHeightContext.d.ts +2 -0
  106. package/lib/typescript/src/utils/BottomTabBarHeightContext.d.ts.map +1 -0
  107. package/lib/typescript/src/utils/BottomTabBarHeightContext.js +2 -0
  108. package/lib/typescript/src/utils/useBottomTabBarHeight.d.ts +1 -0
  109. package/lib/typescript/src/utils/useBottomTabBarHeight.d.ts.map +1 -0
  110. package/lib/typescript/src/utils/useBottomTabBarHeight.js +9 -0
  111. package/lib/typescript/tsconfig.tsbuildinfo +1 -0
  112. package/package.json +135 -0
  113. package/react-native-bottom-tabs.podspec +43 -0
  114. package/src/BottomAccessoryView.tsx +59 -0
  115. package/src/BottomAccessoryViewNativeComponent.ts +27 -0
  116. package/src/DelayedFreeze.tsx +31 -0
  117. package/src/NativeSVGDecoder.ts +5 -0
  118. package/src/SceneMap.tsx +34 -0
  119. package/src/TabView.tsx +558 -0
  120. package/src/TabViewNativeComponent.ts +73 -0
  121. package/src/expo.ts +58 -0
  122. package/src/index.tsx +23 -0
  123. package/src/types.ts +36 -0
  124. package/src/utils/BottomTabBarHeightContext.ts +5 -0
  125. package/src/utils/useBottomTabBarHeight.ts +15 -0
@@ -0,0 +1,109 @@
1
+ import Foundation
2
+ import SwiftUI
3
+
4
+ internal enum MinimizeBehavior: String {
5
+ case automatic
6
+ case never
7
+ case onScrollUp
8
+ case onScrollDown
9
+
10
+ #if compiler(>=6.2)
11
+ @available(iOS 26.0, macOS 26.0, tvOS 26.0, *)
12
+ func convert() -> TabBarMinimizeBehavior {
13
+ #if os(macOS) || os(tvOS)
14
+ return .automatic
15
+ #else
16
+ switch self {
17
+ case .automatic:
18
+ return .automatic
19
+ case .never:
20
+ return .never
21
+ case .onScrollUp:
22
+ return .onScrollUp
23
+ case .onScrollDown:
24
+ return .onScrollDown
25
+ }
26
+ #endif
27
+ }
28
+ #endif
29
+ }
30
+
31
+ public enum TabBarRole: String {
32
+ case search
33
+
34
+ @available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
35
+ func convert() -> TabRole {
36
+ switch self {
37
+ case .search:
38
+ return .search
39
+ }
40
+ }
41
+ }
42
+
43
+ struct IdentifiablePlatformView: Identifiable, Equatable {
44
+ let id = UUID()
45
+ let view: PlatformView
46
+
47
+ init(_ view: PlatformView) {
48
+ self.view = view
49
+ }
50
+ }
51
+
52
+ /// Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes.
53
+ class TabViewProps: ObservableObject {
54
+ @Published var children: [IdentifiablePlatformView] = []
55
+ @Published var items: [TabInfo] = []
56
+ @Published var selectedPage: String?
57
+ @Published var icons: [Int: PlatformImage] = [:]
58
+ @Published var focusedIcons: [Int: PlatformImage] = [:]
59
+ @Published var iconsRevision: Int = 0
60
+ @Published var sidebarAdaptable: Bool?
61
+ @Published var labeled: Bool = false
62
+ @Published var minimizeBehavior: MinimizeBehavior?
63
+ @Published var scrollEdgeAppearance: String?
64
+ @Published var barTintColor: PlatformColor?
65
+ @Published var activeTintColor: PlatformColor?
66
+ @Published var inactiveTintColor: PlatformColor?
67
+ @Published var experimentalBakedTintColors: Bool = false
68
+ @Published var translucent: Bool = true
69
+ @Published var disablePageAnimations: Bool = false
70
+ @Published var hapticFeedbackEnabled: Bool = false
71
+ @Published var layoutDirection: String?
72
+ @Published var fontSize: Int?
73
+ @Published var fontFamily: String?
74
+ @Published var fontWeight: String?
75
+ @Published var tabBarHidden: Bool = false
76
+
77
+ var selectedActiveTintColor: PlatformColor? {
78
+ if let selectedPage,
79
+ let tabData = items.findByKey(selectedPage),
80
+ let activeTintColor = tabData.activeTintColor
81
+ {
82
+ return activeTintColor
83
+ }
84
+
85
+ return activeTintColor
86
+ }
87
+
88
+ var hasCustomTintColors: Bool {
89
+ activeTintColor != nil
90
+ || inactiveTintColor != nil
91
+ || items.contains(where: { $0.activeTintColor != nil })
92
+ }
93
+
94
+ var effectiveInactiveTintColor: PlatformColor? {
95
+ #if os(iOS)
96
+ if ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 26 {
97
+ return nil
98
+ }
99
+ #endif
100
+
101
+ return inactiveTintColor
102
+ }
103
+
104
+ var filteredItems: [TabInfo] {
105
+ items.filter {
106
+ !$0.hidden || $0.key == selectedPage
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,368 @@
1
+ import Foundation
2
+ import React
3
+ import SwiftUI
4
+
5
+ @objcMembers
6
+ public final class TabInfo: NSObject {
7
+ public let key: String
8
+ public let title: String
9
+ public let badge: String?
10
+ public let sfSymbol: String
11
+ public let focusedSfSymbol: String?
12
+ public let activeTintColor: PlatformColor?
13
+ public let iconRenderingMode: String?
14
+ public let hidden: Bool
15
+ public let testID: String?
16
+ public let role: TabBarRole?
17
+ public let preventsDefault: Bool
18
+
19
+ public init(
20
+ key: String,
21
+ title: String,
22
+ badge: String?,
23
+ sfSymbol: String,
24
+ focusedSfSymbol: String?,
25
+ activeTintColor: PlatformColor?,
26
+ iconRenderingMode: String?,
27
+ hidden: Bool,
28
+ testID: String?,
29
+ role: String?,
30
+ preventsDefault: Bool = false
31
+ ) {
32
+ self.key = key
33
+ self.title = title
34
+ self.badge = badge
35
+ self.sfSymbol = sfSymbol
36
+ self.focusedSfSymbol = focusedSfSymbol
37
+ self.activeTintColor = activeTintColor
38
+ self.iconRenderingMode = iconRenderingMode
39
+ self.hidden = hidden
40
+ self.testID = testID
41
+ self.role = TabBarRole(rawValue: role ?? "")
42
+ self.preventsDefault = preventsDefault
43
+ super.init()
44
+ }
45
+ }
46
+
47
+ @objc public protocol TabViewProviderDelegate {
48
+ func onPageSelected(key: String, reactTag: NSNumber?)
49
+ func onLongPress(key: String, reactTag: NSNumber?)
50
+ func onTabBarMeasured(height: Int, reactTag: NSNumber?)
51
+ func onLayout(size: CGSize, reactTag: NSNumber?)
52
+ }
53
+
54
+ @objc public class TabViewProvider: PlatformView {
55
+ private var imageLoader: RCTImageLoaderProtocol?
56
+ private weak var delegate: TabViewProviderDelegate?
57
+ private var props = TabViewProps()
58
+ private var hostingController: TabViewHostingController?
59
+ private var appearanceFixScheduled = false
60
+ private var coalescingKey: UInt16 = 0
61
+ private var iconSize = CGSize(width: 27, height: 27)
62
+
63
+ @objc var onPageSelected: RCTDirectEventBlock?
64
+
65
+ @objc var onTabLongPress: RCTDirectEventBlock?
66
+ @objc var onTabBarMeasured: RCTDirectEventBlock?
67
+ @objc var onNativeLayout: RCTDirectEventBlock?
68
+
69
+ @objc public var icons: NSArray? {
70
+ didSet {
71
+ loadIcons(icons, focused: false)
72
+ }
73
+ }
74
+
75
+ @objc public var focusedIcons: NSArray? {
76
+ didSet {
77
+ loadIcons(focusedIcons, focused: true)
78
+ }
79
+ }
80
+
81
+ @objc public var sidebarAdaptable: Bool = false {
82
+ didSet {
83
+ props.sidebarAdaptable = sidebarAdaptable
84
+ }
85
+ }
86
+
87
+ @objc public var disablePageAnimations: Bool = false {
88
+ didSet {
89
+ props.disablePageAnimations = disablePageAnimations
90
+ }
91
+ }
92
+
93
+ @objc public var labeled: Bool = false {
94
+ didSet {
95
+ props.labeled = labeled
96
+ }
97
+ }
98
+
99
+ @objc public var selectedPage: NSString? {
100
+ didSet {
101
+ props.selectedPage = selectedPage as? String
102
+ }
103
+ }
104
+
105
+ @objc public var hapticFeedbackEnabled: Bool = false {
106
+ didSet {
107
+ props.hapticFeedbackEnabled = hapticFeedbackEnabled
108
+ }
109
+ }
110
+
111
+ @objc public var layoutDirection: NSString? {
112
+ didSet {
113
+ props.layoutDirection = layoutDirection as? String
114
+ }
115
+ }
116
+ @objc public var scrollEdgeAppearance: NSString? {
117
+ didSet {
118
+ props.scrollEdgeAppearance = scrollEdgeAppearance as? String
119
+ }
120
+ }
121
+
122
+ @objc public var minimizeBehavior: NSString? {
123
+ didSet {
124
+ props.minimizeBehavior = MinimizeBehavior(rawValue: minimizeBehavior as? String ?? "")
125
+ }
126
+ }
127
+
128
+ @objc public var translucent: Bool = true {
129
+ didSet {
130
+ props.translucent = translucent
131
+ }
132
+ }
133
+
134
+ @objc public var barTintColor: PlatformColor? {
135
+ didSet {
136
+ props.barTintColor = barTintColor
137
+ }
138
+ }
139
+
140
+ @objc public var activeTintColor: PlatformColor? {
141
+ didSet {
142
+ props.activeTintColor = activeTintColor
143
+ }
144
+ }
145
+
146
+ @objc public var inactiveTintColor: PlatformColor? {
147
+ didSet {
148
+ props.inactiveTintColor = inactiveTintColor
149
+ }
150
+ }
151
+
152
+ @objc public var experimentalBakedTintColors: Bool = false {
153
+ didSet {
154
+ props.experimentalBakedTintColors = experimentalBakedTintColors
155
+ }
156
+ }
157
+
158
+ @objc public var fontFamily: NSString? {
159
+ didSet {
160
+ props.fontFamily = fontFamily as? String
161
+ }
162
+ }
163
+
164
+ @objc public var fontWeight: NSString? {
165
+ didSet {
166
+ props.fontWeight = fontWeight as? String
167
+ }
168
+ }
169
+
170
+ @objc public var fontSize: NSNumber? {
171
+ didSet {
172
+ props.fontSize = fontSize as? Int
173
+ }
174
+ }
175
+
176
+ @objc public var tabBarHidden: Bool = false {
177
+ didSet {
178
+ props.tabBarHidden = tabBarHidden
179
+ }
180
+ }
181
+
182
+ @objc public var itemsData: [TabInfo] = [] {
183
+ didSet {
184
+ props.items = itemsData
185
+ }
186
+ }
187
+
188
+ @objc public convenience init(delegate: TabViewProviderDelegate) {
189
+ self.init()
190
+ self.delegate = delegate
191
+ }
192
+
193
+ @objc public func setImageLoader(_ imageLoader: RCTImageLoader) {
194
+ self.imageLoader = imageLoader
195
+ loadIcons(icons, focused: false)
196
+ loadIcons(focusedIcons, focused: true)
197
+ }
198
+
199
+ override public func didUpdateReactSubviews() {
200
+ props.children = reactSubviews().map(IdentifiablePlatformView.init)
201
+ }
202
+
203
+ #if os(macOS)
204
+ override public func layout() {
205
+ super.layout()
206
+ setupView()
207
+ }
208
+ #else
209
+ override public func layoutSubviews() {
210
+ super.layoutSubviews()
211
+ setupView()
212
+ ensureHostingControllerAppeared()
213
+ }
214
+
215
+ /**
216
+ UIKit forwards appearance callbacks to a child controller added while its
217
+ parent is on screen, but not to one added while the parent is still
218
+ transitioning in, e.g. when the tab view mounts during a native-stack
219
+ replace animation. The host then stays "disappeared" and its view keeps
220
+ zero safe-area insets, so the tab bar and the tabs' navigation bars ignore
221
+ the notch and home indicator. Runs the missing transition once the parent
222
+ has settled.
223
+ */
224
+ private func ensureHostingControllerAppeared() {
225
+ guard let host = hostingController, !host.hasAppeared, !appearanceFixScheduled, window != nil else {
226
+ return
227
+ }
228
+ appearanceFixScheduled = true
229
+ if let coordinator = host.parent?.transitionCoordinator {
230
+ coordinator.animate(alongsideTransition: nil) { [weak self] _ in
231
+ self?.appearanceFixScheduled = false
232
+ self?.ensureHostingControllerAppeared()
233
+ }
234
+ return
235
+ }
236
+ // Deferred a run-loop turn so an appearance transition UIKit is about to
237
+ // forward itself takes precedence over the manual one.
238
+ DispatchQueue.main.async { [weak self] in
239
+ guard let self, let host = self.hostingController else { return }
240
+ self.appearanceFixScheduled = false
241
+ guard !host.hasAppeared, self.window != nil, let parent = host.parent else { return }
242
+ if parent.transitionCoordinator != nil {
243
+ self.ensureHostingControllerAppeared()
244
+ return
245
+ }
246
+ host.beginAppearanceTransition(true, animated: false)
247
+ host.endAppearanceTransition()
248
+ }
249
+ }
250
+ #endif
251
+
252
+ private func setupView() {
253
+ if self.hostingController != nil {
254
+ return
255
+ }
256
+
257
+ self.hostingController = TabViewHostingController(rootView: TabViewImpl(props: props) { key in
258
+ self.delegate?.onPageSelected(key: key, reactTag: self.reactTag)
259
+ } onLongPress: { key in
260
+ self.delegate?.onLongPress(key: key, reactTag: self.reactTag)
261
+ } onLayout: { size in
262
+ self.delegate?.onLayout(size: size, reactTag: self.reactTag)
263
+ } onTabBarMeasured: { height in
264
+ self.delegate?.onTabBarMeasured(height: height, reactTag: self.reactTag)
265
+ })
266
+
267
+ if let hostingController = self.hostingController, let parentViewController = reactViewController() {
268
+ parentViewController.addChild(hostingController)
269
+ #if !os(macOS)
270
+ hostingController.view.backgroundColor = .clear
271
+ #endif
272
+ addSubview(hostingController.view)
273
+ hostingController.view.translatesAutoresizingMaskIntoConstraints = false
274
+ hostingController.view.pinEdges(to: self)
275
+ #if !os(macOS)
276
+ hostingController.didMove(toParent: parentViewController)
277
+ #endif
278
+ }
279
+ }
280
+
281
+ @objc(insertChild:atIndex:)
282
+ public func insertChild(_ child: PlatformView, at index: Int) {
283
+ guard index >= 0 && index <= props.children.count else {
284
+ return
285
+ }
286
+ props.children.insert(IdentifiablePlatformView(child), at: index)
287
+ }
288
+
289
+ @objc(removeChildAtIndex:)
290
+ public func removeChild(at index: Int) {
291
+ guard index >= 0 && index < props.children.count else {
292
+ return
293
+ }
294
+ props.children.remove(at: index)
295
+ }
296
+
297
+ private func loadIcons(_ icons: NSArray?, focused: Bool) {
298
+ guard let imageLoader else { return }
299
+
300
+ // TODO: Diff the arrays and update only changed items.
301
+ // Now if the user passes `unfocusedIcon` we update every item.
302
+ if let imageSources = icons as? [RCTImageSource?] {
303
+ for (index, imageSource) in imageSources.enumerated() {
304
+ guard let imageSource else { continue }
305
+ // Images in the app bundle, including asset catalog images referenced by
306
+ // name (`{ uri: 'tab_home' }`), are loaded right here so the first frame
307
+ // shows them. The image loader only handles what has to be fetched or
308
+ // decoded asynchronously.
309
+ if let url = imageSource.request.url, url.isFileURL, let image = RCTImageFromLocalAssetURL(url) {
310
+ setIcon(image, at: index, focused: focused)
311
+ continue
312
+ }
313
+ imageLoader.loadImage(
314
+ with: imageSource.request,
315
+ size: imageSource.size,
316
+ scale: imageSource.scale,
317
+ clipped: true,
318
+ resizeMode: RCTResizeMode.contain,
319
+ progressBlock: { _, _ in },
320
+ partialLoad: { _ in },
321
+ completionBlock: { error, image in
322
+ if error != nil {
323
+ print("[TabView] Error loading image: \(error!.localizedDescription)")
324
+ return
325
+ }
326
+ guard let image else { return }
327
+ DispatchQueue.main.async { [weak self] in
328
+ self?.setIcon(image, at: index, focused: focused)
329
+ }
330
+ })
331
+ }
332
+ }
333
+ }
334
+
335
+ private func setIcon(_ image: PlatformImage, at index: Int, focused: Bool) {
336
+ var icon = image.resizeImageTo(size: iconSize)
337
+ #if os(iOS)
338
+ if props.experimentalBakedTintColors {
339
+ icon = icon?.withRenderingMode(.alwaysTemplate)
340
+ }
341
+ #endif
342
+ if focused {
343
+ props.focusedIcons[index] = icon
344
+ } else {
345
+ props.icons[index] = icon
346
+ }
347
+ props.iconsRevision += 1
348
+ }
349
+ }
350
+
351
+ #if os(macOS)
352
+ typealias TabViewHostingController = NSHostingController<TabViewImpl>
353
+ #else
354
+ /// Tracks appearance so `TabViewProvider` can detect a host UIKit never transitioned in.
355
+ final class TabViewHostingController: UIHostingController<TabViewImpl> {
356
+ private(set) var hasAppeared = false
357
+
358
+ override func viewDidAppear(_ animated: Bool) {
359
+ super.viewDidAppear(animated)
360
+ hasAppeared = true
361
+ }
362
+
363
+ override func viewDidDisappear(_ animated: Bool) {
364
+ super.viewDidDisappear(animated)
365
+ hasAppeared = false
366
+ }
367
+ }
368
+ #endif
@@ -0,0 +1,4 @@
1
+ #import <React/RCTViewManager.h>
2
+ #import <React/RCTUIManager.h>
3
+ #import <React/UIView+React.h>
4
+ #import <React/RCTUtils.h>
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import BottomAccessoryViewNativeComponent from './BottomAccessoryViewNativeComponent';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const defaultStyle = {
7
+ position: 'absolute',
8
+ top: 0,
9
+ left: 0
10
+ };
11
+ export const BottomAccessoryView = props => {
12
+ const {
13
+ renderBottomAccessoryView
14
+ } = props;
15
+ const [bottomAccessoryDimensions, setBottomAccessoryDimensions] = React.useState({
16
+ width: '100%',
17
+ height: '100%'
18
+ });
19
+ const [placement, setPlacement] = React.useState('none');
20
+ const handleNativeLayout = React.useCallback(({
21
+ nativeEvent: {
22
+ width,
23
+ height
24
+ }
25
+ }) => {
26
+ setBottomAccessoryDimensions({
27
+ width,
28
+ height
29
+ });
30
+ }, [setBottomAccessoryDimensions]);
31
+ const handlePlacementChanged = React.useCallback(({
32
+ nativeEvent
33
+ }) => {
34
+ if (nativeEvent.placement === 'inline' || nativeEvent.placement === 'expanded' || nativeEvent.placement === 'none') {
35
+ setPlacement(nativeEvent.placement);
36
+ }
37
+ }, [setPlacement]);
38
+ return /*#__PURE__*/_jsx(BottomAccessoryViewNativeComponent, {
39
+ style: [defaultStyle, bottomAccessoryDimensions],
40
+ onNativeLayout: handleNativeLayout,
41
+ onPlacementChanged: handlePlacementChanged,
42
+ children: renderBottomAccessoryView({
43
+ placement
44
+ })
45
+ });
46
+ };
47
+ //# sourceMappingURL=BottomAccessoryView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","BottomAccessoryViewNativeComponent","jsx","_jsx","defaultStyle","position","top","left","BottomAccessoryView","props","renderBottomAccessoryView","bottomAccessoryDimensions","setBottomAccessoryDimensions","useState","width","height","placement","setPlacement","handleNativeLayout","useCallback","nativeEvent","handlePlacementChanged","style","onNativeLayout","onPlacementChanged","children"],"sourceRoot":"../../src","sources":["BottomAccessoryView.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AAEzB,OAAOC,kCAAkC,MAGlC,sCAAsC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE9C,MAAMC,YAAuB,GAAG;EAC9BC,QAAQ,EAAE,UAAU;EACpBC,GAAG,EAAE,CAAC;EACNC,IAAI,EAAE;AACR,CAAC;AAQD,OAAO,MAAMC,mBAAmB,GAAIC,KAA+B,IAAK;EACtE,MAAM;IAAEC;EAA0B,CAAC,GAAGD,KAAK;EAC3C,MAAM,CAACE,yBAAyB,EAAEC,4BAA4B,CAAC,GAC7DZ,KAAK,CAACa,QAAQ,CAEZ;IAAEC,KAAK,EAAE,MAAM;IAAEC,MAAM,EAAE;EAAO,CAAC,CAAC;EACtC,MAAM,CAACC,SAAS,EAAEC,YAAY,CAAC,GAAGjB,KAAK,CAACa,QAAQ,CAE9C,MAAM,CAAC;EAET,MAAMK,kBAAkB,GAAGlB,KAAK,CAACmB,WAAW,CAC1C,CAAC;IAAEC,WAAW,EAAE;MAAEN,KAAK;MAAEC;IAAO;EAAmC,CAAC,KAAK;IACvEH,4BAA4B,CAAC;MAAEE,KAAK;MAAEC;IAAO,CAAC,CAAC;EACjD,CAAC,EACD,CAACH,4BAA4B,CAC/B,CAAC;EAED,MAAMS,sBAAsB,GAAGrB,KAAK,CAACmB,WAAW,CAC9C,CAAC;IAAEC;EAAiD,CAAC,KAAK;IACxD,IACEA,WAAW,CAACJ,SAAS,KAAK,QAAQ,IAClCI,WAAW,CAACJ,SAAS,KAAK,UAAU,IACpCI,WAAW,CAACJ,SAAS,KAAK,MAAM,EAChC;MACAC,YAAY,CAACG,WAAW,CAACJ,SAAS,CAAC;IACrC;EACF,CAAC,EACD,CAACC,YAAY,CACf,CAAC;EAED,oBACEd,IAAA,CAACF,kCAAkC;IACjCqB,KAAK,EAAE,CAAClB,YAAY,EAAEO,yBAAyB,CAAE;IACjDY,cAAc,EAAEL,kBAAmB;IACnCM,kBAAkB,EAAEH,sBAAuB;IAAAI,QAAA,EAE1Cf,yBAAyB,CAAC;MAAEM;IAAU,CAAC;EAAC,CACP,CAAC;AAEzC,CAAC","ignoreList":[]}
@@ -0,0 +1,27 @@
1
+ import { codegenNativeComponent } from 'react-native';
2
+ import type { ViewProps } from 'react-native';
3
+ import type {
4
+ DirectEventHandler,
5
+ Double,
6
+ } from 'react-native/Libraries/Types/CodegenTypes';
7
+
8
+ export type OnNativeLayout = Readonly<{
9
+ width: Double;
10
+ height: Double;
11
+ }>;
12
+
13
+ export type OnPlacementChanged = Readonly<{
14
+ placement: string;
15
+ }>;
16
+
17
+ export interface BottomAccessoryViewNativeProps extends ViewProps {
18
+ onNativeLayout?: DirectEventHandler<OnNativeLayout>;
19
+ onPlacementChanged?: DirectEventHandler<OnPlacementChanged>;
20
+ }
21
+
22
+ export default codegenNativeComponent<BottomAccessoryViewNativeProps>(
23
+ 'BottomAccessoryView',
24
+ {
25
+ excludedPlatforms: ['android'],
26
+ }
27
+ );
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { Freeze } from 'react-freeze';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ // Animation delay for freeze effect.
7
+ const ANIMATION_DELAY = 200;
8
+
9
+ // This component delays the freeze effect by animation delay.
10
+ // So that the screen is not frozen immediately causing background flash.
11
+ function DelayedFreeze({
12
+ freeze,
13
+ children
14
+ }) {
15
+ // flag used for determining whether freeze should be enabled
16
+ const [freezeState, setFreezeState] = React.useState(false);
17
+ React.useEffect(() => {
18
+ const id = setTimeout(() => {
19
+ setFreezeState(freeze);
20
+ }, ANIMATION_DELAY);
21
+ return () => {
22
+ clearTimeout(id);
23
+ };
24
+ }, [freeze]);
25
+ return /*#__PURE__*/_jsx(Freeze, {
26
+ freeze: freeze ? freezeState : false,
27
+ children: children
28
+ });
29
+ }
30
+ export default DelayedFreeze;
31
+ //# sourceMappingURL=DelayedFreeze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","Freeze","jsx","_jsx","ANIMATION_DELAY","DelayedFreeze","freeze","children","freezeState","setFreezeState","useState","useEffect","id","setTimeout","clearTimeout"],"sourceRoot":"../../src","sources":["DelayedFreeze.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,MAAM,QAAQ,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAOtC;AACA,MAAMC,eAAe,GAAG,GAAG;;AAE3B;AACA;AACA,SAASC,aAAaA,CAAC;EAAEC,MAAM;EAAEC;AAA6B,CAAC,EAAE;EAC/D;EACA,MAAM,CAACC,WAAW,EAAEC,cAAc,CAAC,GAAGT,KAAK,CAACU,QAAQ,CAAC,KAAK,CAAC;EAE3DV,KAAK,CAACW,SAAS,CAAC,MAAM;IACpB,MAAMC,EAAE,GAAGC,UAAU,CAAC,MAAM;MAC1BJ,cAAc,CAACH,MAAM,CAAC;IACxB,CAAC,EAAEF,eAAe,CAAC;IAEnB,OAAO,MAAM;MACXU,YAAY,CAACF,EAAE,CAAC;IAClB,CAAC;EACH,CAAC,EAAE,CAACN,MAAM,CAAC,CAAC;EAEZ,oBAAOH,IAAA,CAACF,MAAM;IAACK,MAAM,EAAEA,MAAM,GAAGE,WAAW,GAAG,KAAM;IAAAD,QAAA,EAAEA;EAAQ,CAAS,CAAC;AAC1E;AAEA,eAAeF,aAAa","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('SvgDecoder');
5
+ //# sourceMappingURL=NativeSVGDecoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeSVGDecoder.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAIpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,YAAY,CAAC","ignoreList":[]}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ import * as React from 'react';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ const SceneComponent = /*#__PURE__*/React.memo(({
6
+ component,
7
+ ...rest
8
+ }) => {
9
+ return /*#__PURE__*/React.createElement(component, rest);
10
+ });
11
+ SceneComponent.displayName = 'SceneComponent';
12
+ export function SceneMap(scenes) {
13
+ return ({
14
+ route,
15
+ jumpTo
16
+ }) => {
17
+ const component = scenes[route.key];
18
+ if (!component) {
19
+ return null;
20
+ }
21
+ return /*#__PURE__*/_jsx(SceneComponent, {
22
+ jumpTo: jumpTo,
23
+ component: component,
24
+ route: route
25
+ }, route.key);
26
+ };
27
+ }
28
+ //# sourceMappingURL=SceneMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","jsx","_jsx","SceneComponent","memo","component","rest","createElement","displayName","SceneMap","scenes","route","jumpTo","key"],"sourceRoot":"../../src","sources":["SceneMap.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAO/B,MAAMC,cAAc,gBAAGH,KAAK,CAACI,IAAI,CAC/B,CAAiE;EAC/DC,SAAS;EACT,GAAGC;AACF,CAAC,KAAK;EACP,oBAAON,KAAK,CAACO,aAAa,CAACF,SAAS,EAAEC,IAAI,CAAC;AAC7C,CACF,CAAC;AAEDH,cAAc,CAACK,WAAW,GAAG,gBAAgB;AAE7C,OAAO,SAASC,QAAQA,CAAIC,MAAiD,EAAE;EAC7E,OAAO,CAAC;IAAEC,KAAK;IAAEC;EAAmB,CAAC,KAAK;IACxC,MAAMP,SAAS,GAAGK,MAAM,CAACC,KAAK,CAACE,GAAG,CAAC;IACnC,IAAI,CAACR,SAAS,EAAE;MACd,OAAO,IAAI;IACb;IACA,oBACEH,IAAA,CAACC,cAAc;MAEbS,MAAM,EAAEA,MAAO;MACfP,SAAS,EAAEA,SAAU;MACrBM,KAAK,EAAEA;IAAM,GAHRA,KAAK,CAACE,GAIZ,CAAC;EAEN,CAAC;AACH","ignoreList":[]}