@lodev09/react-native-true-sheet 3.5.1-beta.3 → 3.5.1-beta.4
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/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +2 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +31 -28
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +336 -272
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +0 -5
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +7 -8
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +150 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +55 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +18 -12
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +91 -2
- package/android/src/main/java/com/lodev09/truesheet/core/{TrueSheetDialogObserver.kt → TrueSheetStackManager.kt} +7 -5
- package/ios/TrueSheetViewController.h +2 -3
- package/ios/TrueSheetViewController.mm +11 -4
- package/ios/core/TrueSheetDetentCalculator.h +2 -3
- package/ios/core/TrueSheetDetentCalculator.mm +7 -9
- package/lib/module/TrueSheet.js +0 -2
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +0 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +0 -8
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +0 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +1 -1
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.tsx +0 -2
- package/src/TrueSheet.types.ts +0 -9
- package/src/fabric/TrueSheetViewNativeComponent.ts +0 -1
- package/src/navigation/types.ts +0 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetAnimator.kt +0 -144
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogFragment.kt +0 -317
- package/android/src/main/res/anim/fast_fade_out.xml +0 -6
- package/android/src/main/res/values/styles.xml +0 -21
|
@@ -4,13 +4,13 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.os.Build
|
|
5
5
|
import android.view.MotionEvent
|
|
6
6
|
import android.view.View
|
|
7
|
-
import android.view.
|
|
7
|
+
import android.view.ViewGroup
|
|
8
8
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
9
|
-
import
|
|
9
|
+
import androidx.activity.OnBackPressedCallback
|
|
10
10
|
import androidx.appcompat.app.AppCompatActivity
|
|
11
|
+
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
11
12
|
import androidx.core.view.isNotEmpty
|
|
12
13
|
import androidx.core.view.isVisible
|
|
13
|
-
import androidx.core.view.postDelayed
|
|
14
14
|
import com.facebook.react.R
|
|
15
15
|
import com.facebook.react.uimanager.JSPointerDispatcher
|
|
16
16
|
import com.facebook.react.uimanager.JSTouchDispatcher
|
|
@@ -22,19 +22,19 @@ import com.facebook.react.uimanager.events.EventDispatcher
|
|
|
22
22
|
import com.facebook.react.util.RNLog
|
|
23
23
|
import com.facebook.react.views.view.ReactViewGroup
|
|
24
24
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
25
|
-
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
26
25
|
import com.lodev09.truesheet.core.GrabberOptions
|
|
27
26
|
import com.lodev09.truesheet.core.RNScreensFragmentObserver
|
|
28
|
-
import com.lodev09.truesheet.core.
|
|
29
|
-
import com.lodev09.truesheet.core.
|
|
27
|
+
import com.lodev09.truesheet.core.TrueSheetBottomSheetView
|
|
28
|
+
import com.lodev09.truesheet.core.TrueSheetBottomSheetViewDelegate
|
|
29
|
+
import com.lodev09.truesheet.core.TrueSheetCoordinatorLayout
|
|
30
|
+
import com.lodev09.truesheet.core.TrueSheetCoordinatorLayoutDelegate
|
|
30
31
|
import com.lodev09.truesheet.core.TrueSheetDetentCalculator
|
|
31
|
-
import com.lodev09.truesheet.core.
|
|
32
|
-
import com.lodev09.truesheet.core.TrueSheetDialogFragment
|
|
33
|
-
import com.lodev09.truesheet.core.TrueSheetDialogFragmentDelegate
|
|
34
|
-
import com.lodev09.truesheet.core.TrueSheetDialogObserver
|
|
32
|
+
import com.lodev09.truesheet.core.TrueSheetDetentCalculatorDelegate
|
|
35
33
|
import com.lodev09.truesheet.core.TrueSheetDimView
|
|
34
|
+
import com.lodev09.truesheet.core.TrueSheetDimViewDelegate
|
|
36
35
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserver
|
|
37
36
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserverDelegate
|
|
37
|
+
import com.lodev09.truesheet.core.TrueSheetStackManager
|
|
38
38
|
import com.lodev09.truesheet.utils.ScreenUtils
|
|
39
39
|
|
|
40
40
|
// =============================================================================
|
|
@@ -66,24 +66,29 @@ interface TrueSheetViewControllerDelegate {
|
|
|
66
66
|
// =============================================================================
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Manages the bottom sheet
|
|
70
|
-
*
|
|
69
|
+
* Manages the bottom sheet using CoordinatorLayout + BottomSheetBehavior.
|
|
70
|
+
*
|
|
71
|
+
* This approach keeps the sheet in the same activity window (no separate dialog window),
|
|
72
|
+
* which allows touch events to pass through to underlying views when the sheet is not
|
|
73
|
+
* covering them. This solves the touch lag issue when sheets are presented over
|
|
74
|
+
* interactive components like Maps.
|
|
71
75
|
*/
|
|
72
76
|
@SuppressLint("ClickableViewAccessibility", "ViewConstructor")
|
|
73
77
|
class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
74
78
|
ReactViewGroup(reactContext),
|
|
75
79
|
RootView,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
TrueSheetDetentCalculatorDelegate,
|
|
81
|
+
TrueSheetDimViewDelegate,
|
|
82
|
+
TrueSheetCoordinatorLayoutDelegate,
|
|
83
|
+
TrueSheetBottomSheetViewDelegate {
|
|
79
84
|
|
|
80
85
|
companion object {
|
|
81
86
|
const val TAG_NAME = "TrueSheet"
|
|
82
87
|
|
|
83
|
-
private const val FRAGMENT_TAG = "TrueSheetDialogFragment"
|
|
84
88
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
85
89
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
86
90
|
private const val TRANSLATE_ANIMATION_DURATION = 200L
|
|
91
|
+
private const val DISMISS_DURATION = 200L
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
// =============================================================================
|
|
@@ -102,16 +107,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
102
107
|
|
|
103
108
|
var delegate: TrueSheetViewControllerDelegate? = null
|
|
104
109
|
|
|
105
|
-
//
|
|
106
|
-
|
|
110
|
+
// CoordinatorLayout components (replaces DialogFragment)
|
|
111
|
+
internal var sheetView: TrueSheetBottomSheetView? = null
|
|
112
|
+
private var coordinatorLayout: TrueSheetCoordinatorLayout? = null
|
|
107
113
|
private var dimView: TrueSheetDimView? = null
|
|
108
114
|
private var parentDimView: TrueSheetDimView? = null
|
|
109
115
|
|
|
116
|
+
// Back button handling
|
|
117
|
+
private var backCallback: OnBackPressedCallback? = null
|
|
118
|
+
|
|
110
119
|
// Presentation State
|
|
111
120
|
var isPresented = false
|
|
112
121
|
private set
|
|
113
122
|
|
|
114
|
-
var
|
|
123
|
+
var isSheetVisible = false
|
|
115
124
|
private set
|
|
116
125
|
|
|
117
126
|
var currentDetentIndex: Int = -1
|
|
@@ -121,8 +130,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
121
130
|
private var isDismissing = false
|
|
122
131
|
private var wasHiddenByModal = false
|
|
123
132
|
private var shouldAnimatePresent = false
|
|
124
|
-
private var
|
|
125
|
-
private var isPresentingWithoutAnimation = false
|
|
133
|
+
private var isPresentAnimating = false
|
|
126
134
|
|
|
127
135
|
private var lastStateWidth: Int = 0
|
|
128
136
|
private var lastStateHeight: Int = 0
|
|
@@ -140,15 +148,16 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
140
148
|
var parentSheetView: TrueSheetView? = null
|
|
141
149
|
|
|
142
150
|
// Helper Objects
|
|
143
|
-
private val sheetAnimator = TrueSheetAnimator(this)
|
|
144
151
|
private var keyboardObserver: TrueSheetKeyboardObserver? = null
|
|
145
152
|
private var rnScreensObserver: RNScreensFragmentObserver? = null
|
|
146
|
-
|
|
153
|
+
internal val detentCalculator = TrueSheetDetentCalculator(reactContext).apply {
|
|
154
|
+
delegate = this@TrueSheetViewController
|
|
155
|
+
}
|
|
147
156
|
|
|
148
157
|
// Touch Dispatchers
|
|
149
158
|
internal var eventDispatcher: EventDispatcher? = null
|
|
150
|
-
private val
|
|
151
|
-
private var
|
|
159
|
+
private val jsTouchDispatcher = JSTouchDispatcher(this)
|
|
160
|
+
private var jsPointerDispatcher: JSPointerDispatcher? = null
|
|
152
161
|
|
|
153
162
|
// Detent Configuration
|
|
154
163
|
override var maxSheetHeight: Int? = null
|
|
@@ -157,47 +166,37 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
157
166
|
// Appearance Configuration
|
|
158
167
|
var dimmed = true
|
|
159
168
|
var dimmedDetentIndex = 0
|
|
160
|
-
var grabber: Boolean = true
|
|
161
|
-
var grabberOptions: GrabberOptions? = null
|
|
162
|
-
var sheetBackgroundColor: Int? = null
|
|
163
|
-
var edgeToEdgeFullScreen: Boolean = false
|
|
169
|
+
override var grabber: Boolean = true
|
|
170
|
+
override var grabberOptions: GrabberOptions? = null
|
|
171
|
+
override var sheetBackgroundColor: Int? = null
|
|
164
172
|
var insetAdjustment: String = "automatic"
|
|
165
173
|
|
|
166
|
-
var sheetCornerRadius: Float = DEFAULT_CORNER_RADIUS.dpToPx()
|
|
174
|
+
override var sheetCornerRadius: Float = DEFAULT_CORNER_RADIUS.dpToPx()
|
|
167
175
|
set(value) {
|
|
168
176
|
field = if (value < 0) DEFAULT_CORNER_RADIUS.dpToPx() else value
|
|
169
|
-
|
|
170
|
-
if (isPresented) dialogFragment?.setupBackground()
|
|
177
|
+
if (isPresented) sheetView?.setupBackground()
|
|
171
178
|
}
|
|
172
179
|
|
|
173
180
|
var dismissible: Boolean = true
|
|
174
181
|
set(value) {
|
|
175
182
|
field = value
|
|
176
|
-
|
|
183
|
+
behavior?.isHideable = value
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
var draggable: Boolean = true
|
|
180
187
|
set(value) {
|
|
181
188
|
field = value
|
|
182
|
-
|
|
189
|
+
behavior?.isDraggable = value
|
|
190
|
+
if (isPresented) sheetView?.setupGrabber()
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
// =============================================================================
|
|
186
194
|
// MARK: - Computed Properties
|
|
187
195
|
// =============================================================================
|
|
188
196
|
|
|
189
|
-
//
|
|
190
|
-
private val
|
|
191
|
-
get() =
|
|
192
|
-
|
|
193
|
-
private val behavior: BottomSheetBehavior<FrameLayout>?
|
|
194
|
-
get() = dialogFragment?.behavior
|
|
195
|
-
|
|
196
|
-
private val sheetContainer: FrameLayout?
|
|
197
|
-
get() = this.parent as? FrameLayout
|
|
198
|
-
|
|
199
|
-
override val bottomSheetView: FrameLayout?
|
|
200
|
-
get() = dialogFragment?.bottomSheetView
|
|
197
|
+
// Behavior
|
|
198
|
+
private val behavior: BottomSheetBehavior<TrueSheetBottomSheetView>?
|
|
199
|
+
get() = sheetView?.behavior
|
|
201
200
|
|
|
202
201
|
private val containerView: TrueSheetContainerView?
|
|
203
202
|
get() = if (this.isNotEmpty()) getChildAt(0) as? TrueSheetContainerView else null
|
|
@@ -241,23 +240,23 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
241
240
|
private val edgeToEdgeEnabled: Boolean
|
|
242
241
|
get() {
|
|
243
242
|
val defaultEnabled = android.os.Build.VERSION.SDK_INT >= 36
|
|
244
|
-
return BuildConfig.EDGE_TO_EDGE_ENABLED ||
|
|
243
|
+
return BuildConfig.EDGE_TO_EDGE_ENABLED || defaultEnabled
|
|
245
244
|
}
|
|
246
245
|
|
|
247
246
|
// Sheet State
|
|
248
247
|
val isExpanded: Boolean
|
|
249
248
|
get() {
|
|
250
|
-
val sheetTop =
|
|
249
|
+
val sheetTop = sheetView?.top ?: return false
|
|
251
250
|
return sheetTop <= topInset
|
|
252
251
|
}
|
|
253
252
|
|
|
254
253
|
val currentTranslationY: Int
|
|
255
|
-
get() =
|
|
254
|
+
get() = sheetView?.translationY?.toInt() ?: 0
|
|
256
255
|
|
|
257
|
-
|
|
256
|
+
override val isTopmostSheet: Boolean
|
|
258
257
|
get() {
|
|
259
258
|
val hostView = delegate as? TrueSheetView ?: return true
|
|
260
|
-
return
|
|
259
|
+
return TrueSheetStackManager.isTopmostSheet(hostView)
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
private val dimViews: List<TrueSheetDimView>
|
|
@@ -268,120 +267,147 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
268
267
|
// =============================================================================
|
|
269
268
|
|
|
270
269
|
init {
|
|
271
|
-
|
|
270
|
+
jsPointerDispatcher = JSPointerDispatcher(this)
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
// =============================================================================
|
|
275
|
-
// MARK: -
|
|
274
|
+
// MARK: - Sheet Creation & Cleanup
|
|
276
275
|
// =============================================================================
|
|
277
276
|
|
|
278
|
-
fun
|
|
279
|
-
if (
|
|
277
|
+
fun createSheet() {
|
|
278
|
+
if (coordinatorLayout != null) return
|
|
280
279
|
|
|
281
|
-
|
|
280
|
+
// Create coordinator layout
|
|
281
|
+
coordinatorLayout = TrueSheetCoordinatorLayout(reactContext).apply {
|
|
282
282
|
delegate = this@TrueSheetViewController
|
|
283
|
-
contentView = this@TrueSheetViewController
|
|
284
|
-
syncFragmentProperties(this)
|
|
285
283
|
}
|
|
286
284
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
private fun syncFragmentProperties(fragment: TrueSheetDialogFragment) {
|
|
291
|
-
fragment.apply {
|
|
292
|
-
reactContext = this@TrueSheetViewController.reactContext
|
|
293
|
-
sheetCornerRadius = this@TrueSheetViewController.sheetCornerRadius
|
|
294
|
-
sheetBackgroundColor = this@TrueSheetViewController.sheetBackgroundColor
|
|
295
|
-
edgeToEdgeFullScreen = this@TrueSheetViewController.edgeToEdgeFullScreen
|
|
296
|
-
grabberEnabled = this@TrueSheetViewController.grabber
|
|
297
|
-
grabberOptions = this@TrueSheetViewController.grabberOptions
|
|
298
|
-
dismissible = this@TrueSheetViewController.dismissible
|
|
299
|
-
draggable = this@TrueSheetViewController.draggable
|
|
285
|
+
sheetView = TrueSheetBottomSheetView(reactContext).apply {
|
|
286
|
+
delegate = this@TrueSheetViewController
|
|
300
287
|
}
|
|
288
|
+
|
|
289
|
+
setupModalObserver()
|
|
301
290
|
}
|
|
302
291
|
|
|
303
|
-
private fun
|
|
292
|
+
private fun cleanupSheet() {
|
|
304
293
|
cleanupKeyboardObserver()
|
|
305
294
|
cleanupModalObserver()
|
|
306
|
-
|
|
295
|
+
cleanupBackCallback()
|
|
296
|
+
sheetView?.animate()?.cancel()
|
|
297
|
+
|
|
298
|
+
// Remove from activity
|
|
299
|
+
removeFromActivity()
|
|
300
|
+
|
|
301
|
+
// Cleanup dim views
|
|
307
302
|
dimView?.detach()
|
|
308
303
|
dimView = null
|
|
309
304
|
parentDimView?.detach()
|
|
310
305
|
parentDimView = null
|
|
311
|
-
sheetContainer?.removeView(this)
|
|
312
306
|
|
|
313
|
-
|
|
307
|
+
// Detach content from sheet
|
|
308
|
+
sheetView?.removeView(this)
|
|
309
|
+
|
|
310
|
+
coordinatorLayout = null
|
|
311
|
+
sheetView = null
|
|
312
|
+
|
|
314
313
|
interactionState = InteractionState.Idle
|
|
315
314
|
isDismissing = false
|
|
316
315
|
isPresented = false
|
|
317
|
-
|
|
316
|
+
isSheetVisible = false
|
|
318
317
|
wasHiddenByModal = false
|
|
318
|
+
isPresentAnimating = false
|
|
319
319
|
lastEmittedPositionPx = -1
|
|
320
320
|
shouldAnimatePresent = true
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
private fun removeFromActivity() {
|
|
324
|
+
val coordinator = coordinatorLayout ?: return
|
|
325
|
+
val contentView = reactContext.currentActivity?.findViewById<ViewGroup>(android.R.id.content)
|
|
326
|
+
contentView?.removeView(coordinator)
|
|
327
|
+
}
|
|
328
|
+
|
|
323
329
|
// =============================================================================
|
|
324
|
-
// MARK: -
|
|
330
|
+
// MARK: - Back Button Handling
|
|
325
331
|
// =============================================================================
|
|
326
332
|
|
|
327
|
-
|
|
328
|
-
|
|
333
|
+
private fun setupBackCallback() {
|
|
334
|
+
val activity = reactContext.currentActivity as? AppCompatActivity ?: return
|
|
329
335
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
336
|
+
backCallback = object : OnBackPressedCallback(true) {
|
|
337
|
+
override fun handleOnBackPressed() {
|
|
338
|
+
delegate?.viewControllerDidBackPress()
|
|
339
|
+
if (dismissible) {
|
|
340
|
+
dismiss(animated = true)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
333
344
|
|
|
334
|
-
|
|
335
|
-
|
|
345
|
+
activity.onBackPressedDispatcher.addCallback(backCallback!!)
|
|
346
|
+
}
|
|
336
347
|
|
|
337
|
-
|
|
338
|
-
|
|
348
|
+
private fun cleanupBackCallback() {
|
|
349
|
+
backCallback?.remove()
|
|
350
|
+
backCallback = null
|
|
351
|
+
}
|
|
339
352
|
|
|
340
|
-
|
|
353
|
+
// =============================================================================
|
|
354
|
+
// MARK: - TrueSheetCoordinatorLayout.Delegate
|
|
355
|
+
// =============================================================================
|
|
341
356
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
357
|
+
override fun coordinatorLayoutDidLayout(changed: Boolean) {
|
|
358
|
+
// Reposition footer when layout changes
|
|
359
|
+
if (isPresented && changed) {
|
|
360
|
+
positionFooter()
|
|
361
|
+
}
|
|
362
|
+
}
|
|
345
363
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
val toTop = getExpectedSheetTop(currentDetentIndex)
|
|
350
|
-
sheetAnimator.animatePresent(
|
|
351
|
-
toTop = toTop,
|
|
352
|
-
onUpdate = { effectiveTop -> updateSheetVisuals(effectiveTop) },
|
|
353
|
-
onStart = { wasPresentingWithAnimation = false },
|
|
354
|
-
onEnd = { finishPresent() }
|
|
355
|
-
)
|
|
356
|
-
}
|
|
357
|
-
} else {
|
|
358
|
-
isPresentingWithoutAnimation = true
|
|
364
|
+
// =============================================================================
|
|
365
|
+
// MARK: - TrueSheetDimViewDelegate
|
|
366
|
+
// =============================================================================
|
|
359
367
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
368
|
+
override fun dimViewDidTap() {
|
|
369
|
+
val hostView = delegate as? TrueSheetView
|
|
370
|
+
if (hostView == null) {
|
|
371
|
+
RNLog.e(reactContext, "TrueSheet: Expected delegate to be TrueSheetView")
|
|
372
|
+
return
|
|
373
|
+
}
|
|
363
374
|
|
|
364
|
-
|
|
365
|
-
|
|
375
|
+
// If there's a child sheet on top, handle it instead
|
|
376
|
+
val topmostChild = TrueSheetStackManager.getSheetsAbove(hostView).firstOrNull()
|
|
377
|
+
if (topmostChild != null) {
|
|
378
|
+
if (topmostChild.viewController.dismissible) {
|
|
379
|
+
topmostChild.viewController.dismiss(animated = true)
|
|
366
380
|
}
|
|
381
|
+
return
|
|
367
382
|
}
|
|
368
|
-
}
|
|
369
383
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
384
|
+
if (dismissible) {
|
|
385
|
+
dismiss(animated = true)
|
|
386
|
+
} else if (parentSheetView == null && currentDetentIndex > 0) {
|
|
387
|
+
setStateForDetentIndex(0)
|
|
388
|
+
}
|
|
373
389
|
}
|
|
374
390
|
|
|
375
|
-
|
|
376
|
-
|
|
391
|
+
// =============================================================================
|
|
392
|
+
// MARK: - BottomSheetCallback
|
|
393
|
+
// =============================================================================
|
|
394
|
+
|
|
395
|
+
private val sheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
|
|
396
|
+
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
397
|
+
handleStateChanged(sheetView, newState)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
401
|
+
handleSlide(sheetView, slideOffset)
|
|
402
|
+
}
|
|
377
403
|
}
|
|
378
404
|
|
|
379
|
-
|
|
405
|
+
private fun handleStateChanged(sheetView: View, newState: Int) {
|
|
380
406
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
381
407
|
if (isDismissing) return
|
|
382
408
|
isDismissing = true
|
|
383
409
|
emitWillDismissEvents()
|
|
384
|
-
|
|
410
|
+
finishDismiss()
|
|
385
411
|
return
|
|
386
412
|
}
|
|
387
413
|
|
|
@@ -398,21 +424,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
398
424
|
}
|
|
399
425
|
}
|
|
400
426
|
|
|
401
|
-
|
|
402
|
-
// Skip
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
// Keep it off screen to prevent flicker
|
|
406
|
-
if (wasPresentingWithAnimation) {
|
|
407
|
-
sheetView.y = realScreenHeight.toFloat()
|
|
408
|
-
return
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// When presenting without animation, keep the sheet at the target position
|
|
412
|
-
if (isPresentingWithoutAnimation) {
|
|
413
|
-
sheetView.y = getExpectedSheetTop(currentDetentIndex).toFloat()
|
|
414
|
-
return
|
|
415
|
-
}
|
|
427
|
+
private fun handleSlide(sheetView: View, slideOffset: Float) {
|
|
428
|
+
// Skip during dismiss animation
|
|
429
|
+
if (isDismissing) return
|
|
416
430
|
|
|
417
431
|
val behavior = behavior ?: return
|
|
418
432
|
|
|
@@ -436,28 +450,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
436
450
|
}
|
|
437
451
|
}
|
|
438
452
|
|
|
439
|
-
override fun onBackPressed() {
|
|
440
|
-
delegate?.viewControllerDidBackPress()
|
|
441
|
-
if (dismissible) {
|
|
442
|
-
dismiss(animated = true)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
453
|
private fun handleStateSettled(sheetView: View, newState: Int) {
|
|
447
454
|
if (interactionState is InteractionState.Reconfiguring) return
|
|
448
455
|
|
|
449
|
-
// Reset non-animated presentation flag once behavior has settled at the target
|
|
450
|
-
if (isPresentingWithoutAnimation) {
|
|
451
|
-
val targetTop = getExpectedSheetTop(currentDetentIndex)
|
|
452
|
-
sheetView.y = targetTop.toFloat()
|
|
453
|
-
|
|
454
|
-
isPresentingWithoutAnimation = false
|
|
455
|
-
}
|
|
456
|
-
|
|
457
456
|
val index = detentCalculator.getDetentIndexForState(newState) ?: return
|
|
458
457
|
val position = getPositionDpForView(sheetView)
|
|
459
458
|
val detentInfo = DetentInfo(index, position)
|
|
460
459
|
|
|
460
|
+
// Handle present animation completion
|
|
461
|
+
if (isPresentAnimating) {
|
|
462
|
+
isPresentAnimating = false
|
|
463
|
+
finishPresent()
|
|
464
|
+
return
|
|
465
|
+
}
|
|
466
|
+
|
|
461
467
|
when (interactionState) {
|
|
462
468
|
is InteractionState.Dragging -> {
|
|
463
469
|
val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
|
|
@@ -494,7 +500,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
494
500
|
rnScreensObserver = RNScreensFragmentObserver(
|
|
495
501
|
reactContext = reactContext,
|
|
496
502
|
onModalPresented = {
|
|
497
|
-
if (isPresented &&
|
|
503
|
+
if (isPresented && isSheetVisible && isTopmostSheet) {
|
|
498
504
|
hideForModal()
|
|
499
505
|
}
|
|
500
506
|
},
|
|
@@ -521,39 +527,32 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
521
527
|
rnScreensObserver = null
|
|
522
528
|
}
|
|
523
529
|
|
|
530
|
+
private fun setSheetVisibility(visible: Boolean) {
|
|
531
|
+
coordinatorLayout?.visibility = if (visible) VISIBLE else GONE
|
|
532
|
+
dimViews.forEach { it.visibility = if (visible) VISIBLE else INVISIBLE }
|
|
533
|
+
}
|
|
534
|
+
|
|
524
535
|
private fun hideForModal() {
|
|
525
|
-
|
|
536
|
+
isSheetVisible = false
|
|
526
537
|
wasHiddenByModal = true
|
|
527
|
-
|
|
528
|
-
// Prepare for fast fade out
|
|
529
538
|
dimViews.forEach { it.alpha = 0f }
|
|
530
|
-
|
|
531
|
-
dialog?.window?.setWindowAnimations(com.lodev09.truesheet.R.style.TrueSheetFastFadeOut)
|
|
532
|
-
dialog?.window?.decorView?.visibility = GONE
|
|
533
|
-
dimViews.forEach { it.visibility = INVISIBLE }
|
|
534
|
-
|
|
539
|
+
setSheetVisibility(false)
|
|
535
540
|
parentSheetView?.viewController?.hideForModal()
|
|
536
541
|
}
|
|
537
542
|
|
|
538
543
|
private fun showAfterModal() {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
dialog?.window?.setWindowAnimations(0)
|
|
542
|
-
dialog?.window?.decorView?.visibility = VISIBLE
|
|
543
|
-
dimViews.forEach { it.visibility = VISIBLE }
|
|
544
|
-
|
|
544
|
+
isSheetVisible = true
|
|
545
|
+
setSheetVisibility(true)
|
|
545
546
|
updateDimAmount(animated = true)
|
|
546
547
|
}
|
|
547
548
|
|
|
548
549
|
/**
|
|
549
550
|
* Re-applies hidden state after returning from background.
|
|
550
|
-
* Android may restore
|
|
551
|
+
* Android may restore visibility on activity resume, so we need to hide it again.
|
|
551
552
|
*/
|
|
552
553
|
fun reapplyHiddenState() {
|
|
553
554
|
if (!wasHiddenByModal) return
|
|
554
|
-
|
|
555
|
-
dialog?.window?.decorView?.visibility = GONE
|
|
556
|
-
dimViews.forEach { it.visibility = INVISIBLE }
|
|
555
|
+
setSheetVisibility(false)
|
|
557
556
|
}
|
|
558
557
|
|
|
559
558
|
// =============================================================================
|
|
@@ -561,13 +560,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
561
560
|
// =============================================================================
|
|
562
561
|
|
|
563
562
|
fun present(detentIndex: Int, animated: Boolean = true) {
|
|
564
|
-
val
|
|
565
|
-
RNLog.w(reactContext, "TrueSheet: No
|
|
563
|
+
val coordinator = this.coordinatorLayout ?: run {
|
|
564
|
+
RNLog.w(reactContext, "TrueSheet: No coordinator layout available. Ensure the sheet is mounted before presenting.")
|
|
565
|
+
return
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
val sheet = this.sheetView ?: run {
|
|
569
|
+
RNLog.w(reactContext, "TrueSheet: No sheet view available.")
|
|
566
570
|
return
|
|
567
571
|
}
|
|
568
572
|
|
|
569
|
-
val activity = reactContext.currentActivity
|
|
570
|
-
RNLog.w(reactContext, "TrueSheet: No
|
|
573
|
+
val activity = reactContext.currentActivity ?: run {
|
|
574
|
+
RNLog.w(reactContext, "TrueSheet: No activity available for presentation.")
|
|
571
575
|
return
|
|
572
576
|
}
|
|
573
577
|
|
|
@@ -579,14 +583,66 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
579
583
|
currentDetentIndex = detentIndex
|
|
580
584
|
interactionState = InteractionState.Idle
|
|
581
585
|
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
+
// Setup sheet in coordinator layout
|
|
587
|
+
setupSheetInCoordinator(coordinator, sheet)
|
|
588
|
+
|
|
589
|
+
// Add coordinator to activity
|
|
590
|
+
val contentView = activity.findViewById<ViewGroup>(android.R.id.content)
|
|
591
|
+
contentView?.addView(coordinator)
|
|
592
|
+
|
|
593
|
+
// Setup back button handling
|
|
594
|
+
setupBackCallback()
|
|
595
|
+
|
|
596
|
+
// Start presentation
|
|
597
|
+
onSheetShow()
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private fun setupSheetInCoordinator(coordinator: TrueSheetCoordinatorLayout, sheet: TrueSheetBottomSheetView) {
|
|
602
|
+
// Add this controller as content to the sheet
|
|
603
|
+
(parent as? ViewGroup)?.removeView(this)
|
|
604
|
+
sheet.addView(this)
|
|
605
|
+
|
|
606
|
+
// Create layout params with behavior
|
|
607
|
+
val params = sheet.createLayoutParams()
|
|
608
|
+
val behavior = params.behavior as BottomSheetBehavior<TrueSheetBottomSheetView>
|
|
586
609
|
|
|
587
|
-
|
|
588
|
-
|
|
610
|
+
// Configure behavior
|
|
611
|
+
behavior.isHideable = true
|
|
612
|
+
behavior.isDraggable = draggable
|
|
613
|
+
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
|
614
|
+
behavior.addBottomSheetCallback(sheetCallback)
|
|
615
|
+
|
|
616
|
+
// Add sheet to coordinator
|
|
617
|
+
coordinator.addView(sheet, params)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
private fun onSheetShow() {
|
|
621
|
+
val sheet = sheetView ?: run {
|
|
622
|
+
RNLog.e(reactContext, "TrueSheet: sheetView is null in onSheetShow")
|
|
623
|
+
return
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
emitWillPresentEvents()
|
|
627
|
+
|
|
628
|
+
setupSheetDetents()
|
|
629
|
+
setupDimmedBackground(currentDetentIndex)
|
|
630
|
+
setupKeyboardObserver()
|
|
631
|
+
sheet.setupBackground()
|
|
632
|
+
sheet.setupGrabber()
|
|
633
|
+
|
|
634
|
+
if (shouldAnimatePresent) {
|
|
635
|
+
isPresentAnimating = true
|
|
636
|
+
post { setStateForDetentIndex(currentDetentIndex) }
|
|
637
|
+
} else {
|
|
638
|
+
setStateForDetentIndex(currentDetentIndex)
|
|
639
|
+
emitChangePositionDelegate(detentCalculator.getSheetTopForDetentIndex(currentDetentIndex))
|
|
640
|
+
updateDimAmount()
|
|
641
|
+
finishPresent()
|
|
589
642
|
}
|
|
643
|
+
|
|
644
|
+
isPresented = true
|
|
645
|
+
isSheetVisible = true
|
|
590
646
|
}
|
|
591
647
|
|
|
592
648
|
fun dismiss(animated: Boolean = true) {
|
|
@@ -596,17 +652,32 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
596
652
|
emitWillDismissEvents()
|
|
597
653
|
|
|
598
654
|
if (animated) {
|
|
599
|
-
|
|
600
|
-
onUpdate = { effectiveTop -> updateSheetVisuals(effectiveTop) },
|
|
601
|
-
onEnd = { dialogFragment?.dismiss() }
|
|
602
|
-
)
|
|
655
|
+
animateDismiss()
|
|
603
656
|
} else {
|
|
604
657
|
emitChangePositionDelegate(realScreenHeight)
|
|
605
|
-
|
|
658
|
+
finishDismiss()
|
|
606
659
|
}
|
|
607
660
|
}
|
|
608
661
|
|
|
662
|
+
private fun animateDismiss() {
|
|
663
|
+
val sheet = sheetView ?: run {
|
|
664
|
+
finishDismiss()
|
|
665
|
+
return
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
sheet.animate()
|
|
669
|
+
.y(realScreenHeight.toFloat())
|
|
670
|
+
.setDuration(DISMISS_DURATION)
|
|
671
|
+
.setInterpolator(android.view.animation.AccelerateInterpolator())
|
|
672
|
+
.setUpdateListener { updateSheetVisuals(sheet.y.toInt()) }
|
|
673
|
+
.withEndAction { finishDismiss() }
|
|
674
|
+
.start()
|
|
675
|
+
}
|
|
676
|
+
|
|
609
677
|
private fun finishPresent() {
|
|
678
|
+
// Restore isHideable to actual value after present animation
|
|
679
|
+
behavior?.isHideable = dismissible
|
|
680
|
+
|
|
610
681
|
val (index, position, detent) = getDetentInfoWithValue(currentDetentIndex)
|
|
611
682
|
delegate?.viewControllerDidPresent(index, position, detent)
|
|
612
683
|
parentSheetView?.viewControllerDidBlur()
|
|
@@ -616,40 +687,47 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
616
687
|
presentPromise = null
|
|
617
688
|
}
|
|
618
689
|
|
|
690
|
+
private fun finishDismiss() {
|
|
691
|
+
emitDidDismissEvents()
|
|
692
|
+
cleanupSheet()
|
|
693
|
+
}
|
|
694
|
+
|
|
619
695
|
// =============================================================================
|
|
620
696
|
// MARK: - Sheet Configuration
|
|
621
697
|
// =============================================================================
|
|
622
698
|
|
|
623
699
|
fun setupSheetDetents() {
|
|
624
|
-
val
|
|
625
|
-
|
|
700
|
+
val behavior = this.behavior ?: run {
|
|
701
|
+
RNLog.e(reactContext, "TrueSheet: behavior is null in setupSheetDetents")
|
|
702
|
+
return
|
|
703
|
+
}
|
|
626
704
|
|
|
627
705
|
interactionState = InteractionState.Reconfiguring
|
|
628
|
-
val edgeToEdgeTopInset: Int = if (!edgeToEdgeFullScreen) topInset else 0
|
|
629
706
|
|
|
630
707
|
behavior.isFitToContents = false
|
|
631
708
|
|
|
632
|
-
val maxAvailableHeight = realScreenHeight -
|
|
709
|
+
val maxAvailableHeight = realScreenHeight - topInset
|
|
633
710
|
|
|
634
|
-
val peekHeight = detentCalculator.getDetentHeight(detents[0])
|
|
711
|
+
val peekHeight = minOf(detentCalculator.getDetentHeight(detents[0]), maxAvailableHeight)
|
|
635
712
|
|
|
636
713
|
val halfExpandedDetentHeight = when (detents.size) {
|
|
637
714
|
1 -> peekHeight
|
|
638
715
|
else -> detentCalculator.getDetentHeight(detents[1])
|
|
639
716
|
}
|
|
640
717
|
|
|
641
|
-
val maxDetentHeight = detentCalculator.getDetentHeight(detents.last())
|
|
718
|
+
val maxDetentHeight = minOf(detentCalculator.getDetentHeight(detents.last()), maxAvailableHeight)
|
|
642
719
|
|
|
643
720
|
val adjustedHalfExpandedHeight = minOf(halfExpandedDetentHeight, maxAvailableHeight)
|
|
644
721
|
val halfExpandedRatio = (adjustedHalfExpandedHeight.toFloat() / realScreenHeight.toFloat())
|
|
645
722
|
.coerceIn(0f, 0.999f)
|
|
646
723
|
|
|
647
|
-
val expandedOffset =
|
|
724
|
+
val expandedOffset = realScreenHeight - maxDetentHeight
|
|
648
725
|
|
|
649
726
|
// fitToContents works better with <= 2 detents when no expanded offset
|
|
650
727
|
val fitToContents = detents.size < 3 && expandedOffset == 0
|
|
651
728
|
|
|
652
|
-
|
|
729
|
+
configureDetents(
|
|
730
|
+
behavior = behavior,
|
|
653
731
|
peekHeight = peekHeight,
|
|
654
732
|
halfExpandedRatio = halfExpandedRatio,
|
|
655
733
|
expandedOffset = expandedOffset,
|
|
@@ -674,87 +752,76 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
674
752
|
interactionState = InteractionState.Idle
|
|
675
753
|
}
|
|
676
754
|
|
|
755
|
+
private fun configureDetents(
|
|
756
|
+
behavior: BottomSheetBehavior<TrueSheetBottomSheetView>,
|
|
757
|
+
peekHeight: Int,
|
|
758
|
+
halfExpandedRatio: Float,
|
|
759
|
+
expandedOffset: Int,
|
|
760
|
+
fitToContents: Boolean,
|
|
761
|
+
animate: Boolean
|
|
762
|
+
) {
|
|
763
|
+
behavior.apply {
|
|
764
|
+
isFitToContents = fitToContents
|
|
765
|
+
skipCollapsed = false
|
|
766
|
+
setPeekHeight(peekHeight, animate)
|
|
767
|
+
this.halfExpandedRatio = halfExpandedRatio.coerceIn(0f, 0.999f)
|
|
768
|
+
this.expandedOffset = expandedOffset
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
677
772
|
fun setupSheetDetentsForSizeChange() {
|
|
678
773
|
setupSheetDetents()
|
|
679
774
|
positionFooter()
|
|
680
775
|
}
|
|
681
776
|
|
|
682
777
|
fun setStateForDetentIndex(index: Int) {
|
|
683
|
-
|
|
778
|
+
behavior?.state = detentCalculator.getStateForDetentIndex(index)
|
|
684
779
|
}
|
|
685
780
|
|
|
686
781
|
// =============================================================================
|
|
687
|
-
// MARK: -
|
|
782
|
+
// MARK: - Dimmed Background
|
|
688
783
|
// =============================================================================
|
|
689
784
|
|
|
690
|
-
fun setupGrabber() {
|
|
691
|
-
dialogFragment?.let {
|
|
692
|
-
syncFragmentProperties(it)
|
|
693
|
-
it.setupGrabber()
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
fun setupBackground() {
|
|
698
|
-
dialogFragment?.let {
|
|
699
|
-
syncFragmentProperties(it)
|
|
700
|
-
it.setupBackground()
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
|
|
704
785
|
fun setupDimmedBackground(detentIndex: Int) {
|
|
705
|
-
val
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
|
710
|
-
|
|
711
|
-
val shouldDimAtDetent = dimmed && detentIndex >= dimmedDetentIndex
|
|
712
|
-
|
|
713
|
-
if (dimmed) {
|
|
714
|
-
val parentDimVisible = (parentSheetView?.viewController?.dimView?.alpha ?: 0f) > 0f
|
|
786
|
+
val coordinator = this.coordinatorLayout ?: run {
|
|
787
|
+
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupDimmedBackground")
|
|
788
|
+
return
|
|
789
|
+
}
|
|
715
790
|
|
|
716
|
-
|
|
717
|
-
|
|
791
|
+
if (dimmed) {
|
|
792
|
+
val parentDimVisible = (parentSheetView?.viewController?.dimView?.alpha ?: 0f) > 0f
|
|
718
793
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (parentBottomSheet != null) {
|
|
723
|
-
if (parentDimView == null) parentDimView = TrueSheetDimView(reactContext)
|
|
724
|
-
parentDimView?.attach(parentBottomSheet, parentController.sheetCornerRadius)
|
|
794
|
+
if (dimView == null) {
|
|
795
|
+
dimView = TrueSheetDimView(reactContext).apply {
|
|
796
|
+
delegate = this@TrueSheetViewController
|
|
725
797
|
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
dimView
|
|
729
|
-
parentDimView?.detach()
|
|
730
|
-
parentDimView = null
|
|
798
|
+
}
|
|
799
|
+
if (!parentDimVisible) {
|
|
800
|
+
dimView?.attachToCoordinator(coordinator)
|
|
731
801
|
}
|
|
732
802
|
|
|
733
|
-
if
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
803
|
+
// Attach dim view to parent sheet if stacked
|
|
804
|
+
val parentController = parentSheetView?.viewController
|
|
805
|
+
val parentBottomSheet = parentController?.sheetView
|
|
806
|
+
if (parentBottomSheet != null) {
|
|
807
|
+
if (parentDimView == null) {
|
|
808
|
+
parentDimView = TrueSheetDimView(reactContext).apply {
|
|
809
|
+
delegate = this@TrueSheetViewController
|
|
737
810
|
}
|
|
738
|
-
true
|
|
739
|
-
}
|
|
740
|
-
} else {
|
|
741
|
-
// Pass through touches to parent or activity when not dimmed
|
|
742
|
-
touchOutside.setOnTouchListener { v, event ->
|
|
743
|
-
event.setLocation(event.rawX - v.x, event.rawY - v.y)
|
|
744
|
-
(
|
|
745
|
-
parentSheetView?.viewController?.dialog?.window?.decorView
|
|
746
|
-
?: reactContext.currentActivity?.window?.decorView
|
|
747
|
-
)?.dispatchTouchEvent(event)
|
|
748
|
-
false
|
|
749
811
|
}
|
|
750
|
-
|
|
812
|
+
parentDimView?.attach(parentBottomSheet, parentController.sheetCornerRadius)
|
|
751
813
|
}
|
|
814
|
+
} else {
|
|
815
|
+
dimView?.detach()
|
|
816
|
+
dimView = null
|
|
817
|
+
parentDimView?.detach()
|
|
818
|
+
parentDimView = null
|
|
752
819
|
}
|
|
753
820
|
}
|
|
754
821
|
|
|
755
822
|
fun updateDimAmount(sheetTop: Int? = null, animated: Boolean = false) {
|
|
756
823
|
if (!dimmed) return
|
|
757
|
-
val top = (sheetTop ?:
|
|
824
|
+
val top = (sheetTop ?: sheetView?.top ?: return) + currentKeyboardInset
|
|
758
825
|
|
|
759
826
|
if (animated) {
|
|
760
827
|
val targetAlpha = dimView?.calculateAlpha(
|
|
@@ -775,11 +842,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
775
842
|
fun positionFooter(slideOffset: Float? = null) {
|
|
776
843
|
if (!isPresented) return
|
|
777
844
|
val footerView = containerView?.footerView ?: return
|
|
778
|
-
val
|
|
845
|
+
val sheet = sheetView ?: return
|
|
779
846
|
|
|
780
847
|
val footerHeight = footerView.height
|
|
781
|
-
val sheetHeight =
|
|
782
|
-
val sheetTop =
|
|
848
|
+
val sheetHeight = sheet.height
|
|
849
|
+
val sheetTop = sheet.top
|
|
783
850
|
|
|
784
851
|
var footerY = (sheetHeight - sheetTop - footerHeight - currentKeyboardInset).toFloat()
|
|
785
852
|
|
|
@@ -803,14 +870,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
803
870
|
}
|
|
804
871
|
|
|
805
872
|
fun setupKeyboardObserver() {
|
|
806
|
-
val
|
|
873
|
+
val coordinator = coordinatorLayout ?: run {
|
|
874
|
+
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupKeyboardObserver")
|
|
875
|
+
return
|
|
876
|
+
}
|
|
807
877
|
cleanupKeyboardObserver()
|
|
808
|
-
keyboardObserver = TrueSheetKeyboardObserver(
|
|
878
|
+
keyboardObserver = TrueSheetKeyboardObserver(coordinator, reactContext).apply {
|
|
809
879
|
delegate = object : TrueSheetKeyboardObserverDelegate {
|
|
810
880
|
override fun keyboardWillShow(height: Int) {
|
|
881
|
+
isKeyboardTransitioning = true
|
|
811
882
|
if (!shouldHandleKeyboard()) return
|
|
812
883
|
detentIndexBeforeKeyboard = currentDetentIndex
|
|
813
|
-
isKeyboardTransitioning = true
|
|
814
884
|
setupSheetDetents()
|
|
815
885
|
setStateForDetentIndex(detents.size - 1)
|
|
816
886
|
}
|
|
@@ -825,7 +895,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
825
895
|
}
|
|
826
896
|
|
|
827
897
|
override fun keyboardDidHide() {
|
|
828
|
-
if (!shouldHandleKeyboard()) return
|
|
829
898
|
isKeyboardTransitioning = false
|
|
830
899
|
}
|
|
831
900
|
|
|
@@ -919,19 +988,14 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
919
988
|
// MARK: - Detent Helpers
|
|
920
989
|
// =============================================================================
|
|
921
990
|
|
|
922
|
-
fun
|
|
923
|
-
|
|
924
|
-
return realScreenHeight - detentCalculator.getDetentHeight(detents[detentIndex])
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
fun translateDialog(translationY: Int) {
|
|
928
|
-
val bottomSheet = bottomSheetView ?: return
|
|
991
|
+
fun translateSheet(translationY: Int) {
|
|
992
|
+
val sheet = sheetView ?: return
|
|
929
993
|
|
|
930
|
-
|
|
994
|
+
sheet.animate()
|
|
931
995
|
.translationY(translationY.toFloat())
|
|
932
996
|
.setDuration(TRANSLATE_ANIMATION_DURATION)
|
|
933
997
|
.setUpdateListener {
|
|
934
|
-
val effectiveTop =
|
|
998
|
+
val effectiveTop = sheet.top + sheet.translationY.toInt()
|
|
935
999
|
emitChangePositionDelegate(effectiveTop)
|
|
936
1000
|
}
|
|
937
1001
|
.start()
|
|
@@ -948,7 +1012,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
948
1012
|
private fun getPositionForDetentIndex(index: Int): Float {
|
|
949
1013
|
if (index < 0 || index >= detents.size) return screenHeight.pxToDp()
|
|
950
1014
|
|
|
951
|
-
|
|
1015
|
+
sheetView?.let {
|
|
952
1016
|
val visibleSheetHeight = detentCalculator.getVisibleSheetHeight(it.top)
|
|
953
1017
|
if (visibleSheetHeight in 1..<realScreenHeight) {
|
|
954
1018
|
return detentCalculator.getPositionDp(visibleSheetHeight)
|
|
@@ -980,7 +1044,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
980
1044
|
post {
|
|
981
1045
|
setupSheetDetents()
|
|
982
1046
|
positionFooter()
|
|
983
|
-
|
|
1047
|
+
sheetView?.let { emitChangePositionDelegate(it.top, realtime = false) }
|
|
984
1048
|
}
|
|
985
1049
|
}
|
|
986
1050
|
|
|
@@ -1021,41 +1085,41 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1021
1085
|
|
|
1022
1086
|
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
1023
1087
|
eventDispatcher?.let {
|
|
1024
|
-
|
|
1025
|
-
|
|
1088
|
+
jsTouchDispatcher.handleTouchEvent(event, it, reactContext)
|
|
1089
|
+
jsPointerDispatcher?.handleMotionEvent(event, it, true)
|
|
1026
1090
|
}
|
|
1027
1091
|
return super.onInterceptTouchEvent(event)
|
|
1028
1092
|
}
|
|
1029
1093
|
|
|
1030
1094
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
1031
1095
|
eventDispatcher?.let {
|
|
1032
|
-
|
|
1033
|
-
|
|
1096
|
+
jsTouchDispatcher.handleTouchEvent(event, it, reactContext)
|
|
1097
|
+
jsPointerDispatcher?.handleMotionEvent(event, it, false)
|
|
1034
1098
|
}
|
|
1035
1099
|
super.onTouchEvent(event)
|
|
1036
1100
|
return true
|
|
1037
1101
|
}
|
|
1038
1102
|
|
|
1039
1103
|
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
1040
|
-
eventDispatcher?.let {
|
|
1104
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
|
|
1041
1105
|
return super.onHoverEvent(event)
|
|
1042
1106
|
}
|
|
1043
1107
|
|
|
1044
1108
|
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
1045
|
-
eventDispatcher?.let {
|
|
1109
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
|
|
1046
1110
|
return super.onHoverEvent(event)
|
|
1047
1111
|
}
|
|
1048
1112
|
|
|
1049
1113
|
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
|
|
1050
1114
|
eventDispatcher?.let {
|
|
1051
|
-
|
|
1052
|
-
|
|
1115
|
+
jsTouchDispatcher.onChildStartedNativeGesture(ev, it)
|
|
1116
|
+
jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, it)
|
|
1053
1117
|
}
|
|
1054
1118
|
}
|
|
1055
1119
|
|
|
1056
1120
|
override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
|
|
1057
|
-
eventDispatcher?.let {
|
|
1058
|
-
|
|
1121
|
+
eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
|
|
1122
|
+
jsPointerDispatcher?.onChildEndedNativeGesture()
|
|
1059
1123
|
}
|
|
1060
1124
|
|
|
1061
1125
|
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|