@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,222 @@
1
+ package com.rcttabview
2
+
3
+ import android.content.res.ColorStateList
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReadableArray
8
+ import com.facebook.react.module.annotations.ReactModule
9
+ import com.facebook.react.uimanager.ThemedReactContext
10
+ import com.facebook.react.uimanager.UIManagerHelper
11
+ import com.facebook.react.uimanager.ViewGroupManager
12
+ import com.facebook.react.uimanager.ViewManagerDelegate
13
+ import com.facebook.react.viewmanagers.RNCTabViewManagerDelegate
14
+ import com.facebook.react.viewmanagers.RNCTabViewManagerInterface
15
+ import com.rcttabview.events.OnNativeLayoutEvent
16
+ import com.rcttabview.events.OnTabBarMeasuredEvent
17
+ import com.rcttabview.events.PageSelectedEvent
18
+ import com.rcttabview.events.TabLongPressEvent
19
+
20
+ data class TabInfo(
21
+ val key: String,
22
+ val title: String,
23
+ val badge: String?,
24
+ val badgeBackgroundColor: Int?,
25
+ val badgeTextColor: Int?,
26
+ val activeTintColor: Int?,
27
+ val iconRenderingMode: String?,
28
+ val hidden: Boolean,
29
+ val testID: String?
30
+ )
31
+
32
+
33
+ @ReactModule(name = RCTTabViewManager.NAME)
34
+ class RCTTabViewManager(context: ReactApplicationContext) :
35
+ ViewGroupManager<ReactBottomNavigationView>(),
36
+ RNCTabViewManagerInterface<ReactBottomNavigationView> {
37
+
38
+ private val delegate: RNCTabViewManagerDelegate<ReactBottomNavigationView, RCTTabViewManager> =
39
+ RNCTabViewManagerDelegate(this)
40
+
41
+ override fun createViewInstance(context: ThemedReactContext): ReactBottomNavigationView {
42
+ val view = RCTTabViewPrewarm.take(context) ?: ReactBottomNavigationView(context)
43
+ val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
44
+ view.onTabSelectedListener = { key ->
45
+ eventDispatcher?.dispatchEvent(PageSelectedEvent(viewTag = view.id, key))
46
+ }
47
+
48
+ view.onTabLongPressedListener = { key ->
49
+ eventDispatcher?.dispatchEvent(TabLongPressEvent(viewTag = view.id, key))
50
+ }
51
+
52
+ view.onNativeLayoutListener = { width, height ->
53
+ eventDispatcher?.dispatchEvent(OnNativeLayoutEvent(viewTag = view.id, width, height))
54
+ }
55
+ view.onTabBarMeasuredListener = { height ->
56
+ eventDispatcher?.dispatchEvent(OnTabBarMeasuredEvent(viewTag = view.id, height))
57
+ }
58
+ return view
59
+
60
+ }
61
+
62
+ override fun getName(): String {
63
+ return NAME
64
+ }
65
+
66
+ override fun getChildCount(parent: ReactBottomNavigationView): Int {
67
+ return parent.layoutHolder.childCount ?: 0
68
+ }
69
+
70
+ override fun getChildAt(parent: ReactBottomNavigationView, index: Int): View? {
71
+ return parent.layoutHolder.getChildAt(index)
72
+ }
73
+
74
+ override fun removeView(parent: ReactBottomNavigationView, view: View) {
75
+ parent.layoutHolder.removeView(view)
76
+ }
77
+
78
+ override fun removeAllViews(parent: ReactBottomNavigationView) {
79
+ parent.layoutHolder.removeAllViews()
80
+ }
81
+
82
+ override fun removeViewAt(parent: ReactBottomNavigationView, index: Int) {
83
+ parent.layoutHolder.removeViewAt(index)
84
+ }
85
+
86
+ override fun needsCustomLayoutForChildren(): Boolean {
87
+ return true
88
+ }
89
+
90
+ override fun setItems(view: ReactBottomNavigationView?, value: ReadableArray?) {
91
+ if (view != null && value != null) {
92
+ val itemsArray = mutableListOf<TabInfo>()
93
+ for (i in 0 until value.size()) {
94
+ value.getMap(i)?.let { item ->
95
+ itemsArray.add(
96
+ TabInfo(
97
+ key = item.getString("key") ?: "",
98
+ title = item.getString("title") ?: "",
99
+ badge = if (item.hasKey("badge")) item.getString("badge") else null,
100
+ badgeBackgroundColor = if (item.hasKey("badgeBackgroundColor")) item.getInt("badgeBackgroundColor") else null,
101
+ badgeTextColor = if (item.hasKey("badgeTextColor")) item.getInt("badgeTextColor") else null,
102
+ activeTintColor = if (item.hasKey("activeTintColor")) item.getInt("activeTintColor") else null,
103
+ iconRenderingMode = if (item.hasKey("iconRenderingMode")) item.getString("iconRenderingMode") else null,
104
+ hidden = if (item.hasKey("hidden")) item.getBoolean("hidden") else false,
105
+ testID = item.getString("testID")
106
+ )
107
+ )
108
+ }
109
+ }
110
+ view.updateItems(itemsArray)
111
+ }
112
+ }
113
+
114
+ override fun setSelectedPage(view: ReactBottomNavigationView?, value: String?) {
115
+ if (view != null && value != null)
116
+ view.setSelectedItem(value)
117
+ }
118
+
119
+ override fun setIcons(view: ReactBottomNavigationView?, value: ReadableArray?) {
120
+ if (view != null)
121
+ view.setIcons(value)
122
+ }
123
+
124
+ override fun setFocusedIcons(view: ReactBottomNavigationView?, value: ReadableArray?) {
125
+ }
126
+
127
+ override fun setLabeled(view: ReactBottomNavigationView?, value: Boolean) {
128
+ if (view != null)
129
+ view.setLabeled(value)
130
+ }
131
+
132
+ override fun setRippleColor(view: ReactBottomNavigationView?, value: Int?) {
133
+ if (view != null && value != null) {
134
+ val color = ColorStateList.valueOf(value)
135
+ view.setRippleColor(color)
136
+ }
137
+ }
138
+
139
+ override fun setBarTintColor(view: ReactBottomNavigationView?, value: Int?) {
140
+ if (view != null && value != null)
141
+ view.setBarTintColor(value)
142
+ }
143
+
144
+ override fun setActiveTintColor(view: ReactBottomNavigationView?, value: Int?) {
145
+ if (view != null && value != null)
146
+ view.setActiveTintColor(value)
147
+ }
148
+
149
+ override fun setInactiveTintColor(view: ReactBottomNavigationView?, value: Int?) {
150
+ if (view != null && value != null)
151
+ view.setInactiveTintColor(value)
152
+ }
153
+
154
+ override fun setExperimentalBakedTintColors(view: ReactBottomNavigationView?, value: Boolean) {
155
+ }
156
+
157
+ override fun setActiveIndicatorColor(view: ReactBottomNavigationView?, value: Int?) {
158
+ if (view != null && value != null) {
159
+ val color = ColorStateList.valueOf(value)
160
+ view.setActiveIndicatorColor(color)
161
+ }
162
+ }
163
+
164
+ override fun getDelegate(): ViewManagerDelegate<ReactBottomNavigationView> {
165
+ return delegate
166
+ }
167
+
168
+ override fun setHapticFeedbackEnabled(view: ReactBottomNavigationView?, value: Boolean) {
169
+ if (view != null)
170
+ view.isHapticFeedbackEnabled = value
171
+ }
172
+
173
+ override fun setLayoutDirection(view: ReactBottomNavigationView, value: String?) {
174
+ val direction = when (value) {
175
+ "rtl" -> View.LAYOUT_DIRECTION_RTL
176
+ "ltr" -> View.LAYOUT_DIRECTION_LTR
177
+ else -> View.LAYOUT_DIRECTION_LOCALE
178
+ }
179
+ view.applyDirection(direction)
180
+ }
181
+
182
+ override fun setFontFamily(view: ReactBottomNavigationView?, value: String?) {
183
+ view?.setFontFamily(value)
184
+ }
185
+
186
+ override fun setFontWeight(view: ReactBottomNavigationView?, value: String?) {
187
+ view?.setFontWeight(value)
188
+ }
189
+
190
+ override fun setFontSize(view: ReactBottomNavigationView?, value: Int) {
191
+ view?.setFontSize(value)
192
+ }
193
+
194
+ override fun setDisablePageAnimations(view: ReactBottomNavigationView?, value: Boolean) {
195
+ view?.disablePageAnimations = value
196
+ }
197
+
198
+ override fun setTabBarHidden(view: ReactBottomNavigationView?, value: Boolean) {
199
+ view?.setTabBarHidden(value)
200
+ }
201
+
202
+ override fun setTabBarHideOnKeyboard(view: ReactBottomNavigationView?, value: Boolean) {
203
+ view?.setTabBarHideOnKeyboard(value)
204
+ }
205
+
206
+ // iOS Methods
207
+ override fun setTranslucent(view: ReactBottomNavigationView?, value: Boolean) {
208
+ }
209
+
210
+ override fun setSidebarAdaptable(view: ReactBottomNavigationView?, value: Boolean) {
211
+ }
212
+
213
+ override fun setScrollEdgeAppearance(view: ReactBottomNavigationView?, value: String?) {
214
+ }
215
+
216
+ override fun setMinimizeBehavior(view: ReactBottomNavigationView?, value: String?) {
217
+ }
218
+
219
+ companion object {
220
+ const val NAME = "RNCTabView"
221
+ }
222
+ }
@@ -0,0 +1,19 @@
1
+ package com.rcttabview
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+ import java.util.ArrayList
8
+
9
+ class RCTTabViewPackage : ReactPackage {
10
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
11
+ val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
12
+ viewManagers.add(RCTTabViewManager(reactContext))
13
+ return viewManagers
14
+ }
15
+
16
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
17
+ return emptyList()
18
+ }
19
+ }
@@ -0,0 +1,40 @@
1
+ package com.rcttabview
2
+
3
+ import android.app.Activity
4
+ import android.os.Looper
5
+ import com.facebook.react.uimanager.ThemedReactContext
6
+ import java.lang.ref.WeakReference
7
+
8
+ /**
9
+ * Builds a tab bar for an Activity ahead of time, while the main thread is idle during startup,
10
+ * so mounting the real one does not pay Material's inflation inside the frame that shows the
11
+ * first screen. Call [prewarm] from the Activity, e.g. posted right after `onCreate`:
12
+ *
13
+ * ```kotlin
14
+ * Handler(Looper.getMainLooper()).post { RCTTabViewPrewarm.prewarm(this) }
15
+ * ```
16
+ */
17
+ object RCTTabViewPrewarm {
18
+ private var activity: WeakReference<Activity>? = null
19
+ private var view: ReactBottomNavigationView? = null
20
+
21
+ /** Call on the main thread; [itemCount] placeholder tabs are inflated up front. */
22
+ @JvmStatic
23
+ @JvmOverloads
24
+ fun prewarm(activity: Activity, itemCount: Int = 5) {
25
+ check(Looper.myLooper() == Looper.getMainLooper()) { "prewarm must run on the main thread" }
26
+ val prebuilt = ReactBottomNavigationView(activity)
27
+ prebuilt.prewarmItems(itemCount)
28
+ this.activity = WeakReference(activity)
29
+ view = prebuilt
30
+ }
31
+
32
+ /** The prebuilt view for [context]'s Activity, handed out at most once. */
33
+ internal fun take(context: ThemedReactContext): ReactBottomNavigationView? {
34
+ val prebuilt = view ?: return null
35
+ view = null
36
+ val owner = activity?.get()
37
+ activity = null
38
+ return if (owner != null && owner === context.currentActivity) prebuilt else null
39
+ }
40
+ }
@@ -0,0 +1,31 @@
1
+ package com.rcttabview
2
+
3
+ /**
4
+ * Holds back rebuilds requested while a batch of changes is applied, so a rebuild that
5
+ * would otherwise run after every change runs once when the batch ends.
6
+ */
7
+ class RebuildBatch {
8
+ private var batching = false
9
+ private var pending = false
10
+
11
+ /** Whether a rebuild requested now should run. Otherwise it runs when the batch ends. */
12
+ fun shouldRebuildNow(): Boolean {
13
+ if (batching) {
14
+ pending = true
15
+ return false
16
+ }
17
+ return true
18
+ }
19
+
20
+ fun begin() {
21
+ batching = true
22
+ }
23
+
24
+ /** Ends the batch. True when a rebuild was held back and has to run now. */
25
+ fun end(): Boolean {
26
+ batching = false
27
+ val rebuild = pending
28
+ pending = false
29
+ return rebuild
30
+ }
31
+ }
@@ -0,0 +1,14 @@
1
+ package com.rcttabview
2
+
3
+ /**
4
+ * Combines the reasons the tab bar can be hidden: the `tabBarHidden` prop and, when
5
+ * `tabBarHideOnKeyboard` is set, a visible keyboard.
6
+ */
7
+ class TabBarVisibility {
8
+ var hidden = false
9
+ var hideOnKeyboard = false
10
+ var keyboardVisible = false
11
+
12
+ val isVisible: Boolean
13
+ get() = !hidden && !(hideOnKeyboard && keyboardVisible)
14
+ }
@@ -0,0 +1,31 @@
1
+ package com.rcttabview
2
+
3
+ import android.content.Context
4
+ import android.graphics.Typeface
5
+ import android.util.TypedValue
6
+ import androidx.appcompat.content.res.AppCompatResources
7
+
8
+ class Utils {
9
+ companion object {
10
+ fun convertPixelsToDp(context: Context, value: Int): Double {
11
+ val displayDensity = context.resources.displayMetrics.density
12
+ return (value / displayDensity).toDouble()
13
+ }
14
+
15
+ fun getTypefaceStyle(weight: Int?) = when (weight) {
16
+ 700 -> Typeface.BOLD
17
+ else -> Typeface.NORMAL
18
+ }
19
+
20
+ fun getDefaultColorFor(context: Context, baseColorThemeAttr: Int): Int? {
21
+ val value = TypedValue()
22
+ if (!context.theme.resolveAttribute(baseColorThemeAttr, value, true)) {
23
+ return null
24
+ }
25
+ val baseColor = AppCompatResources.getColorStateList(
26
+ context, value.resourceId
27
+ )
28
+ return baseColor.defaultColor
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,25 @@
1
+ package com.rcttabview.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.uimanager.events.Event
5
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+ class OnNativeLayoutEvent(viewTag: Int, private val width: Double, private val height: Double) :
8
+ Event<TabLongPressEvent>(viewTag) {
9
+
10
+ companion object {
11
+ const val EVENT_NAME = "onNativeLayout"
12
+ }
13
+
14
+ override fun getEventName(): String {
15
+ return EVENT_NAME
16
+ }
17
+
18
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
19
+ val event = Arguments.createMap().apply {
20
+ putDouble("width", width)
21
+ putDouble("height", height)
22
+ }
23
+ rctEventEmitter.receiveEvent(viewTag, eventName, event)
24
+ }
25
+ }
@@ -0,0 +1,24 @@
1
+ package com.rcttabview.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.uimanager.events.Event
5
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+ class OnTabBarMeasuredEvent(viewTag: Int, private val height: Int) :
8
+ Event<TabLongPressEvent>(viewTag) {
9
+
10
+ companion object {
11
+ const val EVENT_NAME = "onTabBarMeasured"
12
+ }
13
+
14
+ override fun getEventName(): String {
15
+ return EVENT_NAME
16
+ }
17
+
18
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
19
+ val event = Arguments.createMap().apply {
20
+ putInt("height", height)
21
+ }
22
+ rctEventEmitter.receiveEvent(viewTag, eventName, event)
23
+ }
24
+ }
@@ -0,0 +1,32 @@
1
+ package com.rcttabview.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+ import com.facebook.react.uimanager.events.Event
6
+ import com.facebook.react.uimanager.events.RCTEventEmitter
7
+
8
+
9
+ class PageSelectedEvent(viewTag: Int, private val key: String) : Event<PageSelectedEvent>(viewTag) {
10
+ override fun getEventName(): String {
11
+ return EVENT_NAME
12
+ }
13
+
14
+ override fun canCoalesce(): Boolean {
15
+ return false
16
+ }
17
+
18
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
19
+ rctEventEmitter.receiveEvent(viewTag, eventName, serializeEventData())
20
+ }
21
+
22
+ private fun serializeEventData(): WritableMap {
23
+ val eventData = Arguments.createMap()
24
+ eventData.putString("key", key)
25
+ return eventData
26
+ }
27
+
28
+ companion object {
29
+ const val EVENT_NAME = "topPageSelected"
30
+ }
31
+
32
+ }
@@ -0,0 +1,24 @@
1
+ package com.rcttabview.events
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.uimanager.events.Event
5
+ import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+ class TabLongPressEvent(viewTag: Int, private val key: String) :
8
+ Event<TabLongPressEvent>(viewTag) {
9
+
10
+ companion object {
11
+ const val EVENT_NAME = "onTabLongPress"
12
+ }
13
+
14
+ override fun getEventName(): String {
15
+ return EVENT_NAME
16
+ }
17
+
18
+ override fun dispatch(rctEventEmitter: RCTEventEmitter) {
19
+ val event = Arguments.createMap().apply {
20
+ putString("key", key)
21
+ }
22
+ rctEventEmitter.receiveEvent(viewTag, eventName, event)
23
+ }
24
+ }
@@ -0,0 +1,37 @@
1
+ package com.rcttabview
2
+
3
+ import org.junit.Assert.assertFalse
4
+ import org.junit.Assert.assertTrue
5
+ import org.junit.Test
6
+
7
+ class LatestRequestTrackerTest {
8
+ private val tracker = LatestRequestTracker<Int>()
9
+
10
+ @Test
11
+ fun `the only request for a key is the latest`() {
12
+ val id = tracker.begin(0)
13
+ assertTrue(tracker.isLatest(0, id))
14
+ }
15
+
16
+ @Test
17
+ fun `a newer request supersedes an older one for the same key`() {
18
+ val first = tracker.begin(0)
19
+ val second = tracker.begin(0)
20
+ assertFalse(tracker.isLatest(0, first))
21
+ assertTrue(tracker.isLatest(0, second))
22
+ }
23
+
24
+ @Test
25
+ fun `requests for other keys do not supersede each other`() {
26
+ val home = tracker.begin(0)
27
+ val search = tracker.begin(1)
28
+ assertTrue(tracker.isLatest(0, home))
29
+ assertTrue(tracker.isLatest(1, search))
30
+ }
31
+
32
+ @Test
33
+ fun `an id is never the latest for a key it was not issued for`() {
34
+ val home = tracker.begin(0)
35
+ assertFalse(tracker.isLatest(1, home))
36
+ }
37
+ }
@@ -0,0 +1,44 @@
1
+ package com.rcttabview
2
+
3
+ import org.junit.Assert.assertFalse
4
+ import org.junit.Assert.assertTrue
5
+ import org.junit.Test
6
+
7
+ class RebuildBatchTest {
8
+ private val batch = RebuildBatch()
9
+
10
+ @Test
11
+ fun `rebuilds run at once outside a batch`() {
12
+ assertTrue(batch.shouldRebuildNow())
13
+ }
14
+
15
+ @Test
16
+ fun `rebuilds requested inside a batch are held back`() {
17
+ batch.begin()
18
+ assertFalse(batch.shouldRebuildNow())
19
+ assertFalse(batch.shouldRebuildNow())
20
+ }
21
+
22
+ @Test
23
+ fun `ending a batch releases one rebuild for any number held back`() {
24
+ batch.begin()
25
+ batch.shouldRebuildNow()
26
+ batch.shouldRebuildNow()
27
+ assertTrue(batch.end())
28
+ assertFalse(batch.end())
29
+ }
30
+
31
+ @Test
32
+ fun `ending a batch without requests releases nothing`() {
33
+ batch.begin()
34
+ assertFalse(batch.end())
35
+ }
36
+
37
+ @Test
38
+ fun `rebuilds run at once again after a batch`() {
39
+ batch.begin()
40
+ batch.shouldRebuildNow()
41
+ batch.end()
42
+ assertTrue(batch.shouldRebuildNow())
43
+ }
44
+ }
@@ -0,0 +1,58 @@
1
+ package com.rcttabview
2
+
3
+ import org.junit.Assert.assertFalse
4
+ import org.junit.Assert.assertTrue
5
+ import org.junit.Test
6
+
7
+ class TabBarVisibilityTest {
8
+ private val visibility = TabBarVisibility()
9
+
10
+ @Test
11
+ fun `is visible by default`() {
12
+ assertTrue(visibility.isVisible)
13
+ }
14
+
15
+ @Test
16
+ fun `tabBarHidden hides the bar`() {
17
+ visibility.hidden = true
18
+ assertFalse(visibility.isVisible)
19
+ }
20
+
21
+ @Test
22
+ fun `keyboard alone does not hide the bar`() {
23
+ visibility.keyboardVisible = true
24
+ assertTrue(visibility.isVisible)
25
+ }
26
+
27
+ @Test
28
+ fun `keyboard hides the bar when hideOnKeyboard is set`() {
29
+ visibility.hideOnKeyboard = true
30
+ visibility.keyboardVisible = true
31
+ assertFalse(visibility.isVisible)
32
+ }
33
+
34
+ @Test
35
+ fun `bar returns when the keyboard is dismissed`() {
36
+ visibility.hideOnKeyboard = true
37
+ visibility.keyboardVisible = true
38
+ visibility.keyboardVisible = false
39
+ assertTrue(visibility.isVisible)
40
+ }
41
+
42
+ @Test
43
+ fun `disabling hideOnKeyboard restores the bar while the keyboard is shown`() {
44
+ visibility.hideOnKeyboard = true
45
+ visibility.keyboardVisible = true
46
+ visibility.hideOnKeyboard = false
47
+ assertTrue(visibility.isVisible)
48
+ }
49
+
50
+ @Test
51
+ fun `tabBarHidden keeps the bar hidden after the keyboard is dismissed`() {
52
+ visibility.hidden = true
53
+ visibility.hideOnKeyboard = true
54
+ visibility.keyboardVisible = true
55
+ visibility.keyboardVisible = false
56
+ assertFalse(visibility.isVisible)
57
+ }
58
+ }
package/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./lib/module/expo');
@@ -0,0 +1,33 @@
1
+ #ifdef __cplusplus
2
+
3
+ #pragma once
4
+
5
+ #include <react/renderer/components/RNCTabView/RNCTabViewShadowNode.h>
6
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
7
+ #include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
8
+
9
+ namespace facebook::react {
10
+
11
+ class RNCTabViewComponentDescriptor final
12
+ : public ConcreteComponentDescriptor<RNCTabViewShadowNode> {
13
+ public:
14
+ using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
15
+
16
+ void adopt(ShadowNode& shadowNode) const override {
17
+ ConcreteComponentDescriptor::adopt(shadowNode);
18
+
19
+ #if !defined(ANDROID)
20
+ auto &tabViewShadowNode =
21
+ static_cast<RNCTabViewShadowNode&>(shadowNode);
22
+
23
+ std::weak_ptr<void> imageLoader =
24
+ contextContainer_->at<std::shared_ptr<void>>("RCTImageLoader");
25
+ tabViewShadowNode.setImageLoader(imageLoader);
26
+ #endif
27
+ }
28
+ };
29
+
30
+
31
+ } // namespace facebook::react
32
+
33
+ #endif
@@ -0,0 +1,19 @@
1
+ #include <react/renderer/components/RNCTabView/RNCTabViewShadowNode.h>
2
+
3
+ namespace facebook::react {
4
+
5
+ extern const char RNCTabViewComponentName[] = "RNCTabView";
6
+
7
+ void RNCTabViewShadowNode::setImageLoader(
8
+ std::weak_ptr<void> imageLoader) {
9
+ getStateDataMutable().setImageLoader(imageLoader);
10
+ }
11
+
12
+ RNCTabViewShadowNode::StateData &
13
+ RNCTabViewShadowNode::getStateDataMutable() {
14
+ ensureUnsealed();
15
+ return const_cast<RNCTabViewShadowNode::StateData &>(getStateData());
16
+ }
17
+
18
+
19
+ } // namespace facebook::react