@lodev09/react-native-true-sheet 0.2.1 → 0.3.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 +2 -5
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -7
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +233 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +42 -10
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt +63 -0
- package/android/src/main/java/com/lodev09/truesheet/core/Events.kt +43 -0
- package/android/src/main/java/com/lodev09/truesheet/core/RootViewGroup.kt +136 -0
- package/android/src/main/java/com/lodev09/truesheet/core/SheetBehavior.kt +198 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/maxSize.kt +49 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/toDIP.kt +5 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/withPromise.kt +13 -0
- package/ios/Extensions/UIViewController+detentForSize.swift +22 -13
- package/ios/TrueSheetView.swift +3 -3
- package/ios/TrueSheetViewManager.m +1 -1
- package/lib/commonjs/TrueSheet.js +12 -5
- package/lib/commonjs/TrueSheet.js.map +1 -1
- package/lib/commonjs/TrueSheetModule.js +3 -1
- package/lib/commonjs/TrueSheetModule.js.map +1 -1
- package/lib/module/TrueSheet.js +12 -5
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheetModule.js +3 -1
- package/lib/module/TrueSheetModule.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +1 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetModule.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +4 -3
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/TrueSheet.tsx +21 -10
- package/src/TrueSheetModule.ts +3 -2
- package/src/types.ts +4 -3
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ The true native bottom sheet.
|
|
|
11
11
|
* ✅ **_NOT_** your pure JS, (re)animated View.
|
|
12
12
|
* ✅ Clean, fast and lightweight.
|
|
13
13
|
* ✅ Handles your Sscrolling needs, easy.
|
|
14
|
+
* ✅ Asynchronus `ref` methods.
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -34,7 +35,7 @@ const openSheet = () => {
|
|
|
34
35
|
return (
|
|
35
36
|
<View>
|
|
36
37
|
<Button onPress={openSheet} title="Open Sheet" />
|
|
37
|
-
<TrueSheet ref={sheet}>
|
|
38
|
+
<TrueSheet sizes={['auto', 'large']} ref={sheet}>
|
|
38
39
|
// ...
|
|
39
40
|
</TrueSheet>
|
|
40
41
|
</View>
|
|
@@ -51,7 +52,3 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
|
|
|
51
52
|
## License
|
|
52
53
|
|
|
53
54
|
MIT
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
package/android/build.gradle
CHANGED
|
@@ -5,13 +5,8 @@ import com.facebook.react.bridge.NativeModule
|
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
class TrueSheetPackage : ReactPackage {
|
|
10
|
-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule>
|
|
11
|
-
return emptyList()
|
|
12
|
-
}
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = listOf(TrueSheetViewModule(reactContext))
|
|
13
10
|
|
|
14
|
-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>>
|
|
15
|
-
return listOf(TrueSheetViewManager())
|
|
16
|
-
}
|
|
11
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = listOf(TrueSheetViewManager())
|
|
17
12
|
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
package com.lodev09.truesheet
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.view.ViewStructure
|
|
7
|
+
import android.view.accessibility.AccessibilityEvent
|
|
8
|
+
import android.widget.LinearLayout
|
|
9
|
+
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
10
|
+
import com.facebook.react.bridge.LifecycleEventListener
|
|
11
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
12
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
14
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
15
|
+
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
16
|
+
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
17
|
+
import com.lodev09.truesheet.core.DismissEvent
|
|
18
|
+
import com.lodev09.truesheet.core.PresentEvent
|
|
19
|
+
import com.lodev09.truesheet.core.RootViewGroup
|
|
20
|
+
import com.lodev09.truesheet.core.SheetBehavior
|
|
21
|
+
import com.lodev09.truesheet.core.SizeChangeEvent
|
|
22
|
+
import com.lodev09.truesheet.utils.maxSize
|
|
23
|
+
|
|
24
|
+
class TrueSheetView(context: Context) :
|
|
25
|
+
ViewGroup(context),
|
|
26
|
+
LifecycleEventListener {
|
|
27
|
+
private var eventDispatcher: EventDispatcher? = null
|
|
28
|
+
|
|
29
|
+
private val reactContext: ThemedReactContext
|
|
30
|
+
get() = context as ThemedReactContext
|
|
31
|
+
|
|
32
|
+
private val surfaceId: Int
|
|
33
|
+
get() = UIManagerHelper.getSurfaceId(this)
|
|
34
|
+
|
|
35
|
+
private var sizeIndex: Int = 0
|
|
36
|
+
private var sizes: Array<Any> = arrayOf("medium", "large")
|
|
37
|
+
|
|
38
|
+
private var presentPromise: (() -> Unit)? = null
|
|
39
|
+
private var dismissPromise: (() -> Unit)? = null
|
|
40
|
+
|
|
41
|
+
private val sheetDialog: BottomSheetDialog
|
|
42
|
+
private val sheetLayout: LinearLayout
|
|
43
|
+
private val sheetRootView: RootViewGroup
|
|
44
|
+
|
|
45
|
+
// 1st child of the container view
|
|
46
|
+
private var contentView: ViewGroup? = null
|
|
47
|
+
|
|
48
|
+
// 2nd child of the container view
|
|
49
|
+
private var footerView: ViewGroup? = null
|
|
50
|
+
|
|
51
|
+
private var sheetBehavior: SheetBehavior<ViewGroup>
|
|
52
|
+
|
|
53
|
+
init {
|
|
54
|
+
reactContext.addLifecycleEventListener(this)
|
|
55
|
+
eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
56
|
+
|
|
57
|
+
sheetRootView = RootViewGroup(context)
|
|
58
|
+
sheetRootView.eventDispatcher = eventDispatcher
|
|
59
|
+
|
|
60
|
+
sheetDialog = BottomSheetDialog(context)
|
|
61
|
+
sheetBehavior = SheetBehavior()
|
|
62
|
+
sheetLayout = LinearLayout(context)
|
|
63
|
+
|
|
64
|
+
// Configure Sheet events
|
|
65
|
+
sheetBehavior.apply {
|
|
66
|
+
maxSize = maxSize(context)
|
|
67
|
+
|
|
68
|
+
addBottomSheetCallback(
|
|
69
|
+
object : BottomSheetBehavior.BottomSheetCallback() {
|
|
70
|
+
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
71
|
+
footerView?.let {
|
|
72
|
+
it.y = (sheetView.height - sheetView.top - it.height).toFloat()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override fun onStateChanged(view: View, newState: Int) {
|
|
77
|
+
val sizeInfo = getSizeInfoForState(sizes.size, newState)
|
|
78
|
+
if (sizeInfo != null && sizeInfo.index != sizeIndex) {
|
|
79
|
+
sizeIndex = sizeInfo.index
|
|
80
|
+
|
|
81
|
+
// dispatch onSizeChange event
|
|
82
|
+
eventDispatcher?.dispatchEvent(SizeChangeEvent(surfaceId, id, sizeInfo))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
when (newState) {
|
|
86
|
+
BottomSheetBehavior.STATE_HIDDEN -> sheetDialog.dismiss()
|
|
87
|
+
else -> {}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Configure the sheet layout
|
|
95
|
+
sheetLayout.apply {
|
|
96
|
+
addView(sheetRootView)
|
|
97
|
+
sheetDialog.setContentView(this)
|
|
98
|
+
|
|
99
|
+
val layoutParent = parent as ViewGroup
|
|
100
|
+
(layoutParent.layoutParams as CoordinatorLayout.LayoutParams).behavior = sheetBehavior
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Configure Sheet Dialog
|
|
104
|
+
sheetDialog.apply {
|
|
105
|
+
setOnShowListener {
|
|
106
|
+
UiThreadUtil.runOnUiThread {
|
|
107
|
+
footerView?.let {
|
|
108
|
+
val sheetView = sheetLayout.parent as ViewGroup
|
|
109
|
+
it.y = (sheetView.height - sheetView.top - it.height).toFloat()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
presentPromise?.invoke()
|
|
114
|
+
presentPromise = null
|
|
115
|
+
|
|
116
|
+
// dispatch onPresent event
|
|
117
|
+
eventDispatcher?.dispatchEvent(PresentEvent(surfaceId, id))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setOnDismissListener {
|
|
121
|
+
dismissPromise?.invoke()
|
|
122
|
+
dismissPromise = null
|
|
123
|
+
|
|
124
|
+
// dispatch onDismiss event
|
|
125
|
+
eventDispatcher?.dispatchEvent(DismissEvent(surfaceId, id))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override fun dispatchProvideStructure(structure: ViewStructure) {
|
|
131
|
+
sheetRootView.dispatchProvideStructure(structure)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun onLayout(
|
|
135
|
+
changed: Boolean,
|
|
136
|
+
l: Int,
|
|
137
|
+
t: Int,
|
|
138
|
+
r: Int,
|
|
139
|
+
b: Int
|
|
140
|
+
) {
|
|
141
|
+
// Do nothing as we are laid out by UIManager
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
override fun onDetachedFromWindow() {
|
|
145
|
+
super.onDetachedFromWindow()
|
|
146
|
+
sheetDialog.dismiss()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override fun addView(child: View, index: Int) {
|
|
150
|
+
// Hide this host view
|
|
151
|
+
visibility = GONE
|
|
152
|
+
|
|
153
|
+
(child as ViewGroup).let {
|
|
154
|
+
// Container View's first child is the Content View
|
|
155
|
+
contentView = it.getChildAt(0) as ViewGroup
|
|
156
|
+
footerView = it.getChildAt(1) as ViewGroup
|
|
157
|
+
|
|
158
|
+
sheetBehavior.contentView = contentView
|
|
159
|
+
sheetBehavior.footerView = footerView
|
|
160
|
+
|
|
161
|
+
// rootView's first child is the Container View
|
|
162
|
+
sheetRootView.addView(it, index)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override fun getChildCount(): Int {
|
|
167
|
+
// This method may be called by the parent constructor
|
|
168
|
+
// before rootView is initialized.
|
|
169
|
+
return sheetRootView.childCount
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
override fun getChildAt(index: Int): View = sheetRootView.getChildAt(index)
|
|
173
|
+
|
|
174
|
+
override fun removeView(child: View) {
|
|
175
|
+
sheetRootView.removeView(child)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
override fun removeViewAt(index: Int) {
|
|
179
|
+
val child = getChildAt(index)
|
|
180
|
+
sheetRootView.removeView(child)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {
|
|
184
|
+
// Explicitly override this to prevent accessibility events being passed down to children
|
|
185
|
+
// Those will be handled by the rootView which lives in the dialog
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean {
|
|
189
|
+
// Explicitly override this to prevent accessibility events being passed down to children
|
|
190
|
+
// Those will be handled by the rootView which lives in the dialog
|
|
191
|
+
return false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
override fun onHostResume() {
|
|
195
|
+
// do nothing
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
override fun onHostPause() {
|
|
199
|
+
// do nothing
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
override fun onHostDestroy() {
|
|
203
|
+
// Drop the instance if the host is destroyed which will dismiss the dialog
|
|
204
|
+
reactContext.removeLifecycleEventListener(this)
|
|
205
|
+
sheetDialog.dismiss()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fun setSizes(newSizes: Array<Any>) {
|
|
209
|
+
sizes = newSizes
|
|
210
|
+
sheetBehavior.configure(sizes)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fun present(index: Int, promiseCallback: () -> Unit) {
|
|
214
|
+
sheetBehavior.setStateForSizeIndex(sizes.size, index)
|
|
215
|
+
|
|
216
|
+
if (sheetDialog.isShowing) {
|
|
217
|
+
promiseCallback()
|
|
218
|
+
} else {
|
|
219
|
+
sheetBehavior.configure(sizes)
|
|
220
|
+
presentPromise = promiseCallback
|
|
221
|
+
sheetDialog.show()
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fun dismiss(promiseCallback: () -> Unit) {
|
|
226
|
+
dismissPromise = promiseCallback
|
|
227
|
+
sheetDialog.dismiss()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
companion object {
|
|
231
|
+
const val TAG = "TrueSheetView"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -1,20 +1,52 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
|
-
import android.
|
|
4
|
-
import
|
|
5
|
-
import com.facebook.react.
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
|
5
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
|
+
import com.facebook.react.common.MapBuilder
|
|
6
7
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
+
import com.facebook.react.uimanager.ViewGroupManager
|
|
7
9
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
10
|
+
import com.lodev09.truesheet.core.DismissEvent
|
|
11
|
+
import com.lodev09.truesheet.core.PresentEvent
|
|
12
|
+
import com.lodev09.truesheet.core.SizeChangeEvent
|
|
8
13
|
|
|
9
|
-
class TrueSheetViewManager :
|
|
10
|
-
override fun getName() =
|
|
14
|
+
class TrueSheetViewManager : ViewGroupManager<TrueSheetView>() {
|
|
15
|
+
override fun getName() = TAG
|
|
11
16
|
|
|
12
|
-
override fun createViewInstance(reactContext: ThemedReactContext):
|
|
13
|
-
|
|
17
|
+
override fun createViewInstance(reactContext: ThemedReactContext): TrueSheetView = TrueSheetView(reactContext)
|
|
18
|
+
|
|
19
|
+
override fun onDropViewInstance(view: TrueSheetView) {
|
|
20
|
+
super.onDropViewInstance(view)
|
|
21
|
+
view.onHostDestroy()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? =
|
|
25
|
+
MapBuilder.builder<String, Any>()
|
|
26
|
+
.put(PresentEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPresent"))
|
|
27
|
+
.put(DismissEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDismiss"))
|
|
28
|
+
.put(SizeChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSizeChange"))
|
|
29
|
+
.build()
|
|
30
|
+
|
|
31
|
+
@ReactProp(name = "sizes")
|
|
32
|
+
fun setSizes(view: TrueSheetView, sizes: ReadableArray?) {
|
|
33
|
+
if (sizes != null) {
|
|
34
|
+
val result = ArrayList<Any>()
|
|
35
|
+
for (i in 0 until minOf(sizes.size(), 3)) {
|
|
36
|
+
when (sizes.getType(i)) {
|
|
37
|
+
ReadableType.Number -> result.add(sizes.getDouble(i))
|
|
38
|
+
ReadableType.String -> result.add(sizes.getString(i))
|
|
39
|
+
else -> Log.d(TAG, "Invalid type")
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
view.setSizes(result.toArray())
|
|
44
|
+
} else {
|
|
45
|
+
view.setSizes(arrayOf("medium", "large"))
|
|
46
|
+
}
|
|
14
47
|
}
|
|
15
48
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
view.setBackgroundColor(Color.parseColor(color))
|
|
49
|
+
companion object {
|
|
50
|
+
const val TAG = "TrueSheetView"
|
|
19
51
|
}
|
|
20
52
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package com.lodev09.truesheet
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.Promise
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod
|
|
8
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
9
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
10
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
11
|
+
import com.lodev09.truesheet.utils.withPromise
|
|
12
|
+
|
|
13
|
+
@ReactModule(name = TrueSheetViewModule.TAG)
|
|
14
|
+
class TrueSheetViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
15
|
+
override fun getName(): String = TAG
|
|
16
|
+
|
|
17
|
+
private fun withTrueSheetView(tag: Int, closure: (trueSheetView: TrueSheetView) -> Unit) {
|
|
18
|
+
UiThreadUtil.runOnUiThread {
|
|
19
|
+
try {
|
|
20
|
+
val manager = UIManagerHelper.getUIManagerForReactTag(reactApplicationContext, tag)
|
|
21
|
+
val view = manager?.resolveView(tag)
|
|
22
|
+
if (view == null) {
|
|
23
|
+
Log.d(TAG, "Tag $tag not found")
|
|
24
|
+
return@runOnUiThread
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (view is TrueSheetView) {
|
|
28
|
+
closure(view)
|
|
29
|
+
} else {
|
|
30
|
+
Log.d(TAG, "Tag $tag does not match")
|
|
31
|
+
}
|
|
32
|
+
} catch (e: Exception) {
|
|
33
|
+
e.printStackTrace()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@ReactMethod
|
|
39
|
+
fun present(tag: Int, index: Int, promise: Promise) {
|
|
40
|
+
withTrueSheetView(tag) {
|
|
41
|
+
it.present(index) {
|
|
42
|
+
withPromise(promise) {
|
|
43
|
+
return@withPromise null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@ReactMethod
|
|
50
|
+
fun dismiss(tag: Int, promise: Promise) {
|
|
51
|
+
withTrueSheetView(tag) {
|
|
52
|
+
it.dismiss {
|
|
53
|
+
withPromise(promise) {
|
|
54
|
+
return@withPromise null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
companion object {
|
|
61
|
+
const val TAG = "TrueSheetView"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
// onPresent
|
|
8
|
+
class PresentEvent(surfaceId: Int, viewId: Int) : Event<PresentEvent>(surfaceId, viewId) {
|
|
9
|
+
override fun getEventName() = EVENT_NAME
|
|
10
|
+
|
|
11
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
12
|
+
|
|
13
|
+
companion object {
|
|
14
|
+
const val EVENT_NAME = "present"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// onDismiss
|
|
19
|
+
class DismissEvent(surfaceId: Int, viewId: Int) : Event<PresentEvent>(surfaceId, viewId) {
|
|
20
|
+
override fun getEventName() = EVENT_NAME
|
|
21
|
+
|
|
22
|
+
override fun getEventData(): WritableMap = Arguments.createMap()
|
|
23
|
+
|
|
24
|
+
companion object {
|
|
25
|
+
const val EVENT_NAME = "dismiss"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class SizeChangeEvent(surfaceId: Int, viewId: Int, private val sizeInfo: SizeInfo) : Event<SizeChangeEvent>(surfaceId, viewId) {
|
|
30
|
+
override fun getEventName() = EVENT_NAME
|
|
31
|
+
|
|
32
|
+
override fun getEventData(): WritableMap {
|
|
33
|
+
val data = Arguments.createMap()
|
|
34
|
+
data.putInt("index", sizeInfo.index)
|
|
35
|
+
data.putDouble("value", sizeInfo.value.toDouble())
|
|
36
|
+
|
|
37
|
+
return data
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
companion object {
|
|
41
|
+
const val EVENT_NAME = "sizeChange"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package com.lodev09.truesheet.core
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.view.MotionEvent
|
|
6
|
+
import android.view.View
|
|
7
|
+
import com.facebook.react.bridge.GuardedRunnable
|
|
8
|
+
import com.facebook.react.config.ReactFeatureFlags
|
|
9
|
+
import com.facebook.react.uimanager.JSPointerDispatcher
|
|
10
|
+
import com.facebook.react.uimanager.JSTouchDispatcher
|
|
11
|
+
import com.facebook.react.uimanager.RootView
|
|
12
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
+
import com.facebook.react.uimanager.UIManagerModule
|
|
14
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
15
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* RootViewGroup is the ViewGroup which contains all the children of a Modal. It gets all
|
|
19
|
+
* child information forwarded from ReactModalHostView and uses that to create children. It is
|
|
20
|
+
* also responsible for acting as a RootView and handling touch events. It does this the same way
|
|
21
|
+
* as ReactRootView.
|
|
22
|
+
*
|
|
23
|
+
*
|
|
24
|
+
* To get layout to work properly, we need to layout all the elements within the Modal as if
|
|
25
|
+
* they can fill the entire window. To do that, we need to explicitly set the styleWidth and
|
|
26
|
+
* styleHeight on the LayoutShadowNode to be the window size. This is done through the
|
|
27
|
+
* UIManagerModule, and will then cause the children to layout as if they can fill the window.
|
|
28
|
+
*/
|
|
29
|
+
internal class RootViewGroup(context: Context?) :
|
|
30
|
+
ReactViewGroup(context),
|
|
31
|
+
RootView {
|
|
32
|
+
private var hasAdjustedSize = false
|
|
33
|
+
private var viewWidth = 0
|
|
34
|
+
private var viewHeight = 0
|
|
35
|
+
|
|
36
|
+
private val mJSTouchDispatcher = JSTouchDispatcher(this)
|
|
37
|
+
|
|
38
|
+
private var mJSPointerDispatcher: JSPointerDispatcher? = null
|
|
39
|
+
|
|
40
|
+
var eventDispatcher: EventDispatcher? = null
|
|
41
|
+
|
|
42
|
+
init {
|
|
43
|
+
if (ReactFeatureFlags.dispatchPointerEvents) {
|
|
44
|
+
mJSPointerDispatcher = JSPointerDispatcher(this)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
49
|
+
super.onSizeChanged(w, h, oldw, oldh)
|
|
50
|
+
|
|
51
|
+
viewWidth = w
|
|
52
|
+
viewHeight = h
|
|
53
|
+
updateFirstChildView()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private fun updateFirstChildView() {
|
|
57
|
+
if (childCount > 0) {
|
|
58
|
+
hasAdjustedSize = false
|
|
59
|
+
val viewTag = getChildAt(0).id
|
|
60
|
+
reactContext.runOnNativeModulesQueueThread(
|
|
61
|
+
object : GuardedRunnable(reactContext) {
|
|
62
|
+
override fun runGuarded() {
|
|
63
|
+
val uiManager: UIManagerModule =
|
|
64
|
+
reactContext
|
|
65
|
+
.reactApplicationContext
|
|
66
|
+
.getNativeModule(UIManagerModule::class.java) ?: return
|
|
67
|
+
|
|
68
|
+
uiManager.updateNodeSize(viewTag, viewWidth, viewHeight)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
} else {
|
|
73
|
+
hasAdjustedSize = true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun addView(child: View, index: Int, params: LayoutParams) {
|
|
78
|
+
super.addView(child, index, params)
|
|
79
|
+
if (hasAdjustedSize) {
|
|
80
|
+
updateFirstChildView()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override fun handleException(t: Throwable) {
|
|
85
|
+
reactContext.reactApplicationContext.handleException(RuntimeException(t))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private val reactContext: ThemedReactContext
|
|
89
|
+
get() = context as ThemedReactContext
|
|
90
|
+
|
|
91
|
+
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
92
|
+
mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher)
|
|
93
|
+
mJSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
|
|
94
|
+
return super.onInterceptTouchEvent(event)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
98
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
99
|
+
mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher)
|
|
100
|
+
mJSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
|
|
101
|
+
super.onTouchEvent(event)
|
|
102
|
+
|
|
103
|
+
// In case when there is no children interested in handling touch event, we return true from
|
|
104
|
+
// the root view in order to receive subsequent events related to that gesture
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
109
|
+
mJSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, true)
|
|
110
|
+
return super.onHoverEvent(event)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
114
|
+
mJSPointerDispatcher?.handleMotionEvent(event, eventDispatcher, false)
|
|
115
|
+
return super.onHoverEvent(event)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun onChildStartedNativeGesture(childView: View, ev: MotionEvent) {
|
|
119
|
+
mJSTouchDispatcher.onChildStartedNativeGesture(ev, eventDispatcher)
|
|
120
|
+
mJSPointerDispatcher?.onChildStartedNativeGesture(childView, ev, eventDispatcher)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
|
|
124
|
+
mJSTouchDispatcher.onChildEndedNativeGesture(ev, eventDispatcher)
|
|
125
|
+
mJSPointerDispatcher?.onChildEndedNativeGesture()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
129
|
+
// No-op - override in order to still receive events to onInterceptTouchEvent
|
|
130
|
+
// even when some other view disallow that
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
companion object {
|
|
134
|
+
const val TAG = "TrueSheetView"
|
|
135
|
+
}
|
|
136
|
+
}
|