@swmansion/react-native-bottom-sheet 0.6.3 → 0.7.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "ReactNativeBottomSheet"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/software-mansion-labs/react-native-bottom-sheet.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
17
+ s.private_header_files = "ios/**/*.h"
18
+ s.swift_version = "5.0"
19
+ s.pod_target_xcconfig = {
20
+ "DEFINES_MODULE" => "YES"
21
+ }
22
+
23
+ install_modules_dependencies(s)
24
+ end
@@ -0,0 +1,68 @@
1
+ buildscript {
2
+ ext.ReactNativeBottomSheet = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return ReactNativeBottomSheet[prop]
15
+ }
16
+
17
+ repositories {
18
+ google()
19
+ mavenCentral()
20
+ }
21
+
22
+ dependencies {
23
+ classpath "com.android.tools.build:gradle:8.7.2"
24
+ // noinspection DifferentKotlinGradleVersion
25
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+
30
+ apply plugin: "com.android.library"
31
+ apply plugin: "kotlin-android"
32
+
33
+ apply plugin: "com.facebook.react"
34
+
35
+ android {
36
+ namespace "com.swmansion.reactnativebottomsheet"
37
+
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
43
+ }
44
+
45
+ buildFeatures {
46
+ buildConfig true
47
+ }
48
+
49
+ buildTypes {
50
+ release {
51
+ minifyEnabled false
52
+ }
53
+ }
54
+
55
+ lint {
56
+ disable "GradleCompatible"
57
+ }
58
+
59
+ compileOptions {
60
+ sourceCompatibility JavaVersion.VERSION_1_8
61
+ targetCompatibility JavaVersion.VERSION_1_8
62
+ }
63
+ }
64
+
65
+ dependencies {
66
+ implementation "com.facebook.react:react-android"
67
+ implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
68
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,14 @@
1
+ package com.swmansion.reactnativebottomsheet
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
+
8
+ class BottomSheetPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10
+ emptyList()
11
+
12
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
13
+ listOf(BottomSheetViewManager())
14
+ }
@@ -0,0 +1,412 @@
1
+ package com.swmansion.reactnativebottomsheet
2
+
3
+ import android.content.Context
4
+ import android.view.Choreographer
5
+ import android.view.MotionEvent
6
+ import android.view.VelocityTracker
7
+ import android.view.View
8
+ import android.view.ViewConfiguration
9
+ import android.view.ViewGroup
10
+ import android.widget.FrameLayout
11
+ import androidx.dynamicanimation.animation.DynamicAnimation
12
+ import androidx.dynamicanimation.animation.SpringAnimation
13
+ import androidx.dynamicanimation.animation.SpringForce
14
+ import com.facebook.react.uimanager.PointerEvents
15
+ import com.facebook.react.views.view.ReactViewGroup
16
+ import kotlin.math.abs
17
+
18
+ private data class DetentSpec(val height: Float, val programmatic: Boolean)
19
+
20
+ interface BottomSheetViewListener {
21
+ fun onIndexChange(index: Int)
22
+ fun onPositionChange(position: Double)
23
+ }
24
+
25
+ class BottomSheetView(context: Context) : ReactViewGroup(context) {
26
+
27
+ // MARK: - Listener
28
+
29
+ var listener: BottomSheetViewListener? = null
30
+
31
+ // MARK: - State
32
+
33
+ private var detentSpecs: List<DetentSpec> = emptyList()
34
+ private var targetIndex: Int = 0
35
+ var animateIn: Boolean = true
36
+ private var pendingIndex: Int? = null
37
+ private var hasLaidOut = false
38
+ private var isPanning = false
39
+
40
+ // MARK: - Internal
41
+
42
+ private val sheetContainer = FrameLayout(context)
43
+ private var activeAnimation: SpringAnimation? = null
44
+ private var velocityTracker: VelocityTracker? = null
45
+ private var choreographerCallback: Choreographer.FrameCallback? = null
46
+ private val density = context.resources.displayMetrics.density
47
+ private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
48
+
49
+ // Touch tracking
50
+ private var initialTouchY = 0f
51
+ private var lastTouchY = 0f
52
+ private var activePointerId = MotionEvent.INVALID_POINTER_ID
53
+
54
+ init {
55
+ clipChildren = false
56
+ clipToPadding = false
57
+ pointerEvents = PointerEvents.BOX_NONE
58
+ sheetContainer.clipChildren = false
59
+ sheetContainer.clipToPadding = false
60
+ super.addView(
61
+ sheetContainer,
62
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT),
63
+ )
64
+ }
65
+
66
+ val sheetChildCount: Int
67
+ get() = sheetContainer.childCount
68
+
69
+ fun getSheetChildAt(index: Int): View? = sheetContainer.getChildAt(index)
70
+
71
+ fun addSheetChild(child: View, index: Int) {
72
+ sheetContainer.addView(child, index)
73
+ }
74
+
75
+ fun removeSheetChildAt(index: Int) {
76
+ sheetContainer.removeViewAt(index)
77
+ }
78
+
79
+ // MARK: - Child view management
80
+
81
+ override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {
82
+ if (child === sheetContainer) {
83
+ super.addView(child, index, params)
84
+ } else {
85
+ sheetContainer.addView(child, index, params)
86
+ }
87
+ }
88
+
89
+ override fun removeView(view: View) {
90
+ if (view === sheetContainer) {
91
+ super.removeView(view)
92
+ } else {
93
+ sheetContainer.removeView(view)
94
+ }
95
+ }
96
+
97
+ override fun removeViewAt(index: Int) {
98
+ sheetContainer.removeViewAt(index)
99
+ }
100
+
101
+ // MARK: - Layout
102
+
103
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
104
+ super.onLayout(changed, l, t, r, b)
105
+ val w = r - l
106
+ val h = b - t
107
+ if (w <= 0 || h <= 0) return
108
+
109
+ layoutSheetContainer(w, h)
110
+
111
+ if (!hasLaidOut && detentSpecs.isNotEmpty()) {
112
+ hasLaidOut = true
113
+ val indexToApply = pendingIndex ?: targetIndex
114
+ pendingIndex = null
115
+ targetIndex = indexToApply.coerceIn(0, detentSpecs.size - 1)
116
+
117
+ if (animateIn) {
118
+ val closedTy = detentSpecs.lastOrNull()?.height ?: h.toFloat()
119
+ sheetContainer.translationY = closedTy
120
+ emitPosition()
121
+ snapToIndex(targetIndex, 0f)
122
+ } else {
123
+ sheetContainer.translationY = translationY(targetIndex)
124
+ emitPosition()
125
+ listener?.onIndexChange(targetIndex)
126
+ }
127
+ return
128
+ }
129
+
130
+ if (activeAnimation != null || isPanning) return
131
+ sheetContainer.translationY = translationY(targetIndex)
132
+ }
133
+
134
+ private fun layoutSheetChildren() {
135
+ for (i in 0 until sheetContainer.childCount) {
136
+ val child = sheetContainer.getChildAt(i)
137
+ child.layout(0, 0, child.measuredWidth, child.measuredHeight)
138
+ }
139
+ }
140
+
141
+ private fun layoutSheetContainer(viewWidth: Int, viewHeight: Int) {
142
+ val maxHeight = detentSpecs.lastOrNull()?.height ?: viewHeight.toFloat()
143
+ val containerTop = (viewHeight - maxHeight).toInt()
144
+ sheetContainer.layout(0, containerTop, viewWidth, containerTop + maxHeight.toInt())
145
+ layoutSheetChildren()
146
+ }
147
+
148
+ // MARK: - Prop setters
149
+
150
+ fun setDetents(raw: List<Map<String, Any>>) {
151
+ detentSpecs = raw.mapNotNull { dict ->
152
+ val height = (dict["height"] as? Number)?.toDouble() ?: return@mapNotNull null
153
+ val programmatic = dict["programmatic"] as? Boolean ?: false
154
+ DetentSpec(height = (height * density).toFloat(), programmatic = programmatic)
155
+ }
156
+
157
+ if (width > 0 && height > 0 && detentSpecs.isNotEmpty()) {
158
+ layoutSheetContainer(width, height)
159
+
160
+ if (hasLaidOut && activeAnimation == null && !isPanning) {
161
+ targetIndex = targetIndex.coerceIn(0, detentSpecs.size - 1)
162
+ sheetContainer.translationY = translationY(targetIndex)
163
+ emitPosition()
164
+ }
165
+ }
166
+
167
+ requestLayout()
168
+ }
169
+
170
+ fun setIndex(newIndex: Int) {
171
+ if (newIndex < 0) return
172
+
173
+ if (!hasLaidOut) {
174
+ pendingIndex = newIndex
175
+ targetIndex = newIndex
176
+ return
177
+ }
178
+
179
+ if (newIndex >= detentSpecs.size || newIndex == targetIndex) return
180
+ snapToIndex(newIndex, 0f)
181
+ }
182
+
183
+ // MARK: - Snap logic
184
+
185
+ private fun translationY(index: Int): Float {
186
+ val maxHeight = detentSpecs.lastOrNull()?.height ?: height.toFloat()
187
+ val snapHeight = detentSpecs.getOrNull(index)?.height ?: 0f
188
+ return maxHeight - snapHeight
189
+ }
190
+
191
+ private val draggableMinTy: Float
192
+ get() {
193
+ val highestIndex = detentSpecs.indices.lastOrNull { !detentSpecs[it].programmatic } ?: 0
194
+ return translationY(highestIndex)
195
+ }
196
+
197
+ private val draggableMaxTy: Float
198
+ get() {
199
+ val lowestIndex = detentSpecs.indices.firstOrNull { !detentSpecs[it].programmatic } ?: 0
200
+ return translationY(lowestIndex)
201
+ }
202
+
203
+ private val isAtMaxDraggable: Boolean
204
+ get() = sheetContainer.translationY <= draggableMinTy + 1f
205
+
206
+ private fun emitPosition() {
207
+ val maxHeight = detentSpecs.lastOrNull()?.height ?: height.toFloat()
208
+ val ty = sheetContainer.translationY
209
+ listener?.onPositionChange(((maxHeight - ty) / density).toDouble())
210
+ }
211
+
212
+ // MARK: - Choreographer (position tracking during animation)
213
+
214
+ private fun startChoreographer() {
215
+ if (choreographerCallback != null) return
216
+ val callback = object : Choreographer.FrameCallback {
217
+ override fun doFrame(frameTimeNanos: Long) {
218
+ emitPosition()
219
+ choreographerCallback?.let { Choreographer.getInstance().postFrameCallback(it) }
220
+ }
221
+ }
222
+ choreographerCallback = callback
223
+ Choreographer.getInstance().postFrameCallback(callback)
224
+ }
225
+
226
+ private fun stopChoreographer() {
227
+ choreographerCallback?.let { Choreographer.getInstance().removeFrameCallback(it) }
228
+ choreographerCallback = null
229
+ }
230
+
231
+ // MARK: - Spring animation
232
+
233
+ private fun snapToIndex(index: Int, velocity: Float) {
234
+ if (index < 0 || index >= detentSpecs.size) return
235
+ targetIndex = index
236
+
237
+ val targetTy = translationY(index)
238
+
239
+ activeAnimation?.cancel()
240
+
241
+ val spring = SpringAnimation(sheetContainer, DynamicAnimation.TRANSLATION_Y, targetTy).apply {
242
+ spring = SpringForce(targetTy).apply {
243
+ dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
244
+ stiffness = SpringForce.STIFFNESS_MEDIUM
245
+ }
246
+ setStartVelocity(velocity)
247
+ addEndListener { _, _, _, _ ->
248
+ stopChoreographer()
249
+ emitPosition()
250
+ activeAnimation = null
251
+ listener?.onIndexChange(index)
252
+ }
253
+ }
254
+
255
+ activeAnimation = spring
256
+ startChoreographer()
257
+ spring.start()
258
+ }
259
+
260
+ private fun bestSnapIndex(currentHeight: Float, velocity: Float): Int {
261
+ val draggable = detentSpecs.withIndex().filter { !it.value.programmatic }
262
+ if (draggable.isEmpty()) return targetIndex
263
+
264
+ val flickThreshold = 600f * density
265
+
266
+ if (velocity < -flickThreshold) {
267
+ return draggable.firstOrNull { it.value.height > currentHeight }?.index
268
+ ?: draggable.lastOrNull()?.index ?: targetIndex
269
+ }
270
+ if (velocity > flickThreshold) {
271
+ return draggable.lastOrNull { it.value.height < currentHeight }?.index
272
+ ?: draggable.firstOrNull()?.index ?: targetIndex
273
+ }
274
+
275
+ return draggable.minByOrNull { abs(it.value.height - currentHeight) }?.index ?: targetIndex
276
+ }
277
+
278
+ // MARK: - Touch handling
279
+
280
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
281
+ val sheetTop = sheetContainer.top + sheetContainer.translationY
282
+ if (ev.y < sheetTop) {
283
+ return false
284
+ }
285
+
286
+ when (ev.actionMasked) {
287
+ MotionEvent.ACTION_DOWN -> {
288
+ initialTouchY = ev.y
289
+ lastTouchY = ev.y
290
+ activePointerId = ev.getPointerId(0)
291
+ }
292
+ MotionEvent.ACTION_MOVE -> {
293
+ if (activePointerId == MotionEvent.INVALID_POINTER_ID) return false
294
+ val pointerIndex = ev.findPointerIndex(activePointerId)
295
+ if (pointerIndex < 0) return false
296
+ val y = ev.getY(pointerIndex)
297
+ val dy = y - initialTouchY
298
+
299
+ if (abs(dy) > touchSlop) {
300
+ if (!isAtMaxDraggable) {
301
+ lastTouchY = y
302
+ requestDisallowInterceptTouchEvent(false)
303
+ return true
304
+ }
305
+ if (dy > 0 && isScrollViewAtTop()) {
306
+ lastTouchY = y
307
+ requestDisallowInterceptTouchEvent(false)
308
+ return true
309
+ }
310
+ }
311
+ }
312
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
313
+ activePointerId = MotionEvent.INVALID_POINTER_ID
314
+ }
315
+ }
316
+ return false
317
+ }
318
+
319
+ override fun onTouchEvent(event: MotionEvent): Boolean {
320
+ val sheetTop = sheetContainer.top + sheetContainer.translationY
321
+ if (event.y < sheetTop) {
322
+ return false
323
+ }
324
+
325
+ when (event.actionMasked) {
326
+ MotionEvent.ACTION_DOWN -> {
327
+ beginPan(event)
328
+ return true
329
+ }
330
+ MotionEvent.ACTION_MOVE -> {
331
+ if (!isPanning) beginPan(event)
332
+ val pointerIndex = event.findPointerIndex(activePointerId)
333
+ if (pointerIndex < 0) return true
334
+ val y = event.getY(pointerIndex)
335
+ velocityTracker?.addMovement(event)
336
+ val dy = y - lastTouchY
337
+ lastTouchY = y
338
+
339
+ val newTy = (sheetContainer.translationY + dy).coerceIn(draggableMinTy, draggableMaxTy)
340
+ sheetContainer.translationY = newTy
341
+ emitPosition()
342
+ return true
343
+ }
344
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
345
+ isPanning = false
346
+ activePointerId = MotionEvent.INVALID_POINTER_ID
347
+ val velocity = velocityTracker?.let { tracker ->
348
+ tracker.computeCurrentVelocity(1000)
349
+ val v = tracker.yVelocity
350
+ tracker.recycle()
351
+ v
352
+ } ?: 0f
353
+ velocityTracker = null
354
+ val maxHeight = detentSpecs.lastOrNull()?.height ?: height.toFloat()
355
+ val currentHeight = maxHeight - sheetContainer.translationY
356
+ val index = bestSnapIndex(currentHeight, velocity)
357
+ snapToIndex(index, velocity)
358
+ return true
359
+ }
360
+ MotionEvent.ACTION_POINTER_UP -> {
361
+ val actionIndex = event.actionIndex
362
+ if (event.getPointerId(actionIndex) == activePointerId) {
363
+ val newIndex = if (actionIndex == 0) 1 else 0
364
+ activePointerId = event.getPointerId(newIndex)
365
+ lastTouchY = event.getY(newIndex)
366
+ }
367
+ return true
368
+ }
369
+ }
370
+ return super.onTouchEvent(event)
371
+ }
372
+
373
+ private fun beginPan(event: MotionEvent) {
374
+ isPanning = true
375
+ activePointerId = event.getPointerId(0)
376
+ lastTouchY = event.y
377
+ velocityTracker?.recycle()
378
+ velocityTracker = VelocityTracker.obtain()
379
+ velocityTracker?.addMovement(event)
380
+ activeAnimation?.let {
381
+ it.cancel()
382
+ activeAnimation = null
383
+ stopChoreographer()
384
+ }
385
+ }
386
+
387
+ // MARK: - Scroll view helpers
388
+
389
+ private fun isScrollViewAtTop(): Boolean {
390
+ val scrollView = findScrollView(sheetContainer) ?: return true
391
+ return !scrollView.canScrollVertically(-1)
392
+ }
393
+
394
+ private fun findScrollView(view: View): View? {
395
+ if (view.canScrollVertically(1) || view.canScrollVertically(-1)) return view
396
+ if (view is ViewGroup) {
397
+ for (i in 0 until view.childCount) {
398
+ findScrollView(view.getChildAt(i))?.let { return it }
399
+ }
400
+ }
401
+ return null
402
+ }
403
+
404
+ // MARK: - Cleanup
405
+
406
+ fun destroy() {
407
+ activeAnimation?.cancel()
408
+ stopChoreographer()
409
+ velocityTracker?.recycle()
410
+ velocityTracker = null
411
+ }
412
+ }
@@ -0,0 +1,105 @@
1
+ package com.swmansion.reactnativebottomsheet
2
+
3
+ import android.view.View
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.facebook.react.uimanager.ThemedReactContext
7
+ import com.facebook.react.uimanager.ViewGroupManager
8
+ import com.facebook.react.uimanager.ViewManagerDelegate
9
+ import com.facebook.react.uimanager.annotations.ReactProp
10
+ import com.facebook.react.viewmanagers.BottomSheetViewManagerDelegate
11
+ import com.facebook.react.viewmanagers.BottomSheetViewManagerInterface
12
+
13
+ @ReactModule(name = BottomSheetViewManager.NAME)
14
+ class BottomSheetViewManager :
15
+ ViewGroupManager<BottomSheetView>(),
16
+ BottomSheetViewManagerInterface<BottomSheetView> {
17
+
18
+ companion object {
19
+ const val NAME = "BottomSheetView"
20
+ }
21
+
22
+ private val delegate = BottomSheetViewManagerDelegate(this)
23
+
24
+ override fun getDelegate(): ViewManagerDelegate<BottomSheetView> = delegate
25
+
26
+ override fun getName(): String = NAME
27
+
28
+ override fun createViewInstance(context: ThemedReactContext): BottomSheetView {
29
+ val view = BottomSheetView(context)
30
+ view.listener = object : BottomSheetViewListener {
31
+ override fun onIndexChange(index: Int) {
32
+ val event = com.facebook.react.bridge.Arguments.createMap().apply {
33
+ putInt("index", index)
34
+ }
35
+ val reactContext = view.context as? ThemedReactContext ?: return
36
+ reactContext
37
+ .getJSModule(com.facebook.react.uimanager.events.RCTEventEmitter::class.java)
38
+ .receiveEvent(view.id, "topIndexChange", event)
39
+ }
40
+
41
+ override fun onPositionChange(position: Double) {
42
+ val event = com.facebook.react.bridge.Arguments.createMap().apply {
43
+ putDouble("position", position)
44
+ }
45
+ val reactContext = view.context as? ThemedReactContext ?: return
46
+ reactContext
47
+ .getJSModule(com.facebook.react.uimanager.events.RCTEventEmitter::class.java)
48
+ .receiveEvent(view.id, "topPositionChange", event)
49
+ }
50
+ }
51
+ return view
52
+ }
53
+
54
+ override fun addView(parent: BottomSheetView, child: View, index: Int) {
55
+ parent.addSheetChild(child, index)
56
+ }
57
+
58
+ override fun getChildCount(parent: BottomSheetView): Int = parent.sheetChildCount
59
+
60
+ override fun getChildAt(parent: BottomSheetView, index: Int): View? = parent.getSheetChildAt(index)
61
+
62
+ override fun removeViewAt(parent: BottomSheetView, index: Int) {
63
+ parent.removeSheetChildAt(index)
64
+ }
65
+
66
+ override fun needsCustomLayoutForChildren(): Boolean = true
67
+
68
+ override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any> {
69
+ return mapOf(
70
+ "topIndexChange" to mapOf("registrationName" to "onIndexChange"),
71
+ "topPositionChange" to mapOf("registrationName" to "onPositionChange"),
72
+ )
73
+ }
74
+
75
+ @ReactProp(name = "detents")
76
+ override fun setDetents(view: BottomSheetView, detents: ReadableArray?) {
77
+ if (detents == null) return
78
+ val list = mutableListOf<Map<String, Any>>()
79
+ for (i in 0 until detents.size()) {
80
+ val map = detents.getMap(i) ?: continue
81
+ list.add(
82
+ mapOf(
83
+ "height" to map.getDouble("height"),
84
+ "programmatic" to map.getBoolean("programmatic"),
85
+ )
86
+ )
87
+ }
88
+ view.setDetents(list)
89
+ }
90
+
91
+ @ReactProp(name = "index")
92
+ override fun setIndex(view: BottomSheetView, index: Int) {
93
+ view.setIndex(index)
94
+ }
95
+
96
+ @ReactProp(name = "animateIn")
97
+ override fun setAnimateIn(view: BottomSheetView, animateIn: Boolean) {
98
+ view.animateIn = animateIn
99
+ }
100
+
101
+ override fun onDropViewInstance(view: BottomSheetView) {
102
+ super.onDropViewInstance(view)
103
+ view.destroy()
104
+ }
105
+ }
@@ -0,0 +1,10 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface BottomSheetComponentView : RCTViewComponentView
7
+
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END