@lodev09/react-native-true-sheet 3.5.1-beta.3 → 3.5.1-beta.5
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 +358 -271
- 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 +158 -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
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* Controls the presentation and behavior of a bottom sheet.
|
|
70
|
+
*
|
|
71
|
+
* Uses CoordinatorLayout with BottomSheetBehavior to manage the sheet within the activity window,
|
|
72
|
+
* enabling touch pass-through to underlying views. Handles detent configuration, drag interactions,
|
|
73
|
+
* keyboard avoidance, dimmed backgrounds, back button, and lifecycle events for stacked sheets.
|
|
71
74
|
*/
|
|
72
75
|
@SuppressLint("ClickableViewAccessibility", "ViewConstructor")
|
|
73
76
|
class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
74
77
|
ReactViewGroup(reactContext),
|
|
75
78
|
RootView,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
TrueSheetDetentCalculatorDelegate,
|
|
80
|
+
TrueSheetDimViewDelegate,
|
|
81
|
+
TrueSheetCoordinatorLayoutDelegate,
|
|
82
|
+
TrueSheetBottomSheetViewDelegate {
|
|
79
83
|
|
|
80
84
|
companion object {
|
|
81
85
|
const val TAG_NAME = "TrueSheet"
|
|
82
86
|
|
|
83
|
-
private const val FRAGMENT_TAG = "TrueSheetDialogFragment"
|
|
84
87
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
85
88
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
86
89
|
private const val TRANSLATE_ANIMATION_DURATION = 200L
|
|
90
|
+
private const val DISMISS_DURATION = 200L
|
|
91
|
+
private const val MODAL_FADE_DURATION = 150L
|
|
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,40 @@ 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
|
|
|
193
|
+
val isDimmedAtCurrentDetent: Boolean
|
|
194
|
+
get() = dimmed && currentDetentIndex >= dimmedDetentIndex
|
|
195
|
+
|
|
185
196
|
// =============================================================================
|
|
186
197
|
// MARK: - Computed Properties
|
|
187
198
|
// =============================================================================
|
|
188
199
|
|
|
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
|
|
200
|
+
// Behavior
|
|
201
|
+
private val behavior: BottomSheetBehavior<TrueSheetBottomSheetView>?
|
|
202
|
+
get() = sheetView?.behavior
|
|
201
203
|
|
|
202
204
|
private val containerView: TrueSheetContainerView?
|
|
203
205
|
get() = if (this.isNotEmpty()) getChildAt(0) as? TrueSheetContainerView else null
|
|
@@ -241,23 +243,23 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
241
243
|
private val edgeToEdgeEnabled: Boolean
|
|
242
244
|
get() {
|
|
243
245
|
val defaultEnabled = android.os.Build.VERSION.SDK_INT >= 36
|
|
244
|
-
return BuildConfig.EDGE_TO_EDGE_ENABLED ||
|
|
246
|
+
return BuildConfig.EDGE_TO_EDGE_ENABLED || defaultEnabled
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
// Sheet State
|
|
248
250
|
val isExpanded: Boolean
|
|
249
251
|
get() {
|
|
250
|
-
val sheetTop =
|
|
252
|
+
val sheetTop = sheetView?.top ?: return false
|
|
251
253
|
return sheetTop <= topInset
|
|
252
254
|
}
|
|
253
255
|
|
|
254
256
|
val currentTranslationY: Int
|
|
255
|
-
get() =
|
|
257
|
+
get() = sheetView?.translationY?.toInt() ?: 0
|
|
256
258
|
|
|
257
|
-
|
|
259
|
+
override val isTopmostSheet: Boolean
|
|
258
260
|
get() {
|
|
259
261
|
val hostView = delegate as? TrueSheetView ?: return true
|
|
260
|
-
return
|
|
262
|
+
return TrueSheetStackManager.isTopmostSheet(hostView)
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
private val dimViews: List<TrueSheetDimView>
|
|
@@ -268,120 +270,145 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
268
270
|
// =============================================================================
|
|
269
271
|
|
|
270
272
|
init {
|
|
271
|
-
|
|
273
|
+
jsPointerDispatcher = JSPointerDispatcher(this)
|
|
272
274
|
}
|
|
273
275
|
|
|
274
276
|
// =============================================================================
|
|
275
|
-
// MARK: -
|
|
277
|
+
// MARK: - Sheet Creation & Cleanup
|
|
276
278
|
// =============================================================================
|
|
277
279
|
|
|
278
|
-
fun
|
|
279
|
-
if (
|
|
280
|
+
fun createSheet() {
|
|
281
|
+
if (coordinatorLayout != null) return
|
|
280
282
|
|
|
281
|
-
|
|
283
|
+
// Create coordinator layout
|
|
284
|
+
coordinatorLayout = TrueSheetCoordinatorLayout(reactContext).apply {
|
|
282
285
|
delegate = this@TrueSheetViewController
|
|
283
|
-
contentView = this@TrueSheetViewController
|
|
284
|
-
syncFragmentProperties(this)
|
|
285
286
|
}
|
|
286
287
|
|
|
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
|
|
288
|
+
sheetView = TrueSheetBottomSheetView(reactContext).apply {
|
|
289
|
+
delegate = this@TrueSheetViewController
|
|
300
290
|
}
|
|
291
|
+
|
|
292
|
+
setupModalObserver()
|
|
301
293
|
}
|
|
302
294
|
|
|
303
|
-
private fun
|
|
295
|
+
private fun cleanupSheet() {
|
|
304
296
|
cleanupKeyboardObserver()
|
|
305
297
|
cleanupModalObserver()
|
|
306
|
-
|
|
298
|
+
cleanupBackCallback()
|
|
299
|
+
sheetView?.animate()?.cancel()
|
|
300
|
+
|
|
301
|
+
// Remove from activity
|
|
302
|
+
removeFromActivity()
|
|
303
|
+
|
|
304
|
+
// Cleanup dim views
|
|
307
305
|
dimView?.detach()
|
|
308
306
|
dimView = null
|
|
309
307
|
parentDimView?.detach()
|
|
310
308
|
parentDimView = null
|
|
311
|
-
sheetContainer?.removeView(this)
|
|
312
309
|
|
|
313
|
-
|
|
310
|
+
// Detach content from sheet
|
|
311
|
+
sheetView?.removeView(this)
|
|
312
|
+
|
|
313
|
+
coordinatorLayout = null
|
|
314
|
+
sheetView = null
|
|
315
|
+
|
|
314
316
|
interactionState = InteractionState.Idle
|
|
315
317
|
isDismissing = false
|
|
316
318
|
isPresented = false
|
|
317
|
-
|
|
319
|
+
isSheetVisible = false
|
|
318
320
|
wasHiddenByModal = false
|
|
321
|
+
isPresentAnimating = false
|
|
319
322
|
lastEmittedPositionPx = -1
|
|
320
323
|
shouldAnimatePresent = true
|
|
321
324
|
}
|
|
322
325
|
|
|
326
|
+
private fun removeFromActivity() {
|
|
327
|
+
val coordinator = coordinatorLayout ?: return
|
|
328
|
+
val contentView = reactContext.currentActivity?.findViewById<ViewGroup>(android.R.id.content)
|
|
329
|
+
contentView?.removeView(coordinator)
|
|
330
|
+
}
|
|
331
|
+
|
|
323
332
|
// =============================================================================
|
|
324
|
-
// MARK: -
|
|
333
|
+
// MARK: - Back Button Handling
|
|
325
334
|
// =============================================================================
|
|
326
335
|
|
|
327
|
-
|
|
328
|
-
|
|
336
|
+
private fun setupBackCallback() {
|
|
337
|
+
val activity = reactContext.currentActivity as? AppCompatActivity ?: return
|
|
329
338
|
|
|
330
|
-
|
|
331
|
-
|
|
339
|
+
backCallback = object : OnBackPressedCallback(true) {
|
|
340
|
+
override fun handleOnBackPressed() {
|
|
341
|
+
delegate?.viewControllerDidBackPress()
|
|
342
|
+
dismissOrCollapseToLowest()
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
activity.onBackPressedDispatcher.addCallback(backCallback!!)
|
|
332
347
|
}
|
|
333
348
|
|
|
334
|
-
|
|
335
|
-
|
|
349
|
+
private fun cleanupBackCallback() {
|
|
350
|
+
backCallback?.remove()
|
|
351
|
+
backCallback = null
|
|
352
|
+
}
|
|
336
353
|
|
|
337
|
-
|
|
338
|
-
|
|
354
|
+
// =============================================================================
|
|
355
|
+
// MARK: - TrueSheetCoordinatorLayout.Delegate
|
|
356
|
+
// =============================================================================
|
|
339
357
|
|
|
340
|
-
|
|
358
|
+
override fun coordinatorLayoutDidLayout(changed: Boolean) {
|
|
359
|
+
// Reposition footer when layout changes
|
|
360
|
+
if (isPresented && changed) {
|
|
361
|
+
positionFooter()
|
|
362
|
+
}
|
|
363
|
+
}
|
|
341
364
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
365
|
+
// =============================================================================
|
|
366
|
+
// MARK: - TrueSheetDimViewDelegate
|
|
367
|
+
// =============================================================================
|
|
345
368
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
post {
|
|
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
|
|
369
|
+
override fun dimViewDidTap() {
|
|
370
|
+
val hostView = delegate as? TrueSheetView ?: return
|
|
359
371
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
bottomSheetView?.y = toTop.toFloat()
|
|
372
|
+
val children = TrueSheetStackManager.getSheetsAbove(hostView)
|
|
373
|
+
val topmostChild = children.firstOrNull()?.viewController
|
|
363
374
|
|
|
364
|
-
|
|
365
|
-
|
|
375
|
+
// If topmost child is dimmed, only handle that child
|
|
376
|
+
if (topmostChild?.isDimmedAtCurrentDetent == true) {
|
|
377
|
+
if (topmostChild.dismissible) {
|
|
378
|
+
topmostChild.dismiss(animated = true)
|
|
366
379
|
}
|
|
380
|
+
return
|
|
367
381
|
}
|
|
368
|
-
}
|
|
369
382
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
383
|
+
// Pass through to parent - dismiss all if possible
|
|
384
|
+
val allDismissible = dismissible && children.all { it.viewController.dismissible }
|
|
385
|
+
if (allDismissible) {
|
|
386
|
+
children.forEach { it.viewController.dismiss(animated = true) }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
dismissOrCollapseToLowest()
|
|
373
390
|
}
|
|
374
391
|
|
|
375
|
-
|
|
376
|
-
|
|
392
|
+
// =============================================================================
|
|
393
|
+
// MARK: - BottomSheetCallback
|
|
394
|
+
// =============================================================================
|
|
395
|
+
|
|
396
|
+
private val sheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
|
|
397
|
+
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
398
|
+
handleStateChanged(sheetView, newState)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
402
|
+
handleSlide(sheetView, slideOffset)
|
|
403
|
+
}
|
|
377
404
|
}
|
|
378
405
|
|
|
379
|
-
|
|
406
|
+
private fun handleStateChanged(sheetView: View, newState: Int) {
|
|
380
407
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
381
408
|
if (isDismissing) return
|
|
382
409
|
isDismissing = true
|
|
383
410
|
emitWillDismissEvents()
|
|
384
|
-
|
|
411
|
+
finishDismiss()
|
|
385
412
|
return
|
|
386
413
|
}
|
|
387
414
|
|
|
@@ -398,21 +425,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
398
425
|
}
|
|
399
426
|
}
|
|
400
427
|
|
|
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
|
-
}
|
|
428
|
+
private fun handleSlide(sheetView: View, slideOffset: Float) {
|
|
429
|
+
// Skip during dismiss animation
|
|
430
|
+
if (isDismissing) return
|
|
416
431
|
|
|
417
432
|
val behavior = behavior ?: return
|
|
418
433
|
|
|
@@ -436,28 +451,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
436
451
|
}
|
|
437
452
|
}
|
|
438
453
|
|
|
439
|
-
override fun onBackPressed() {
|
|
440
|
-
delegate?.viewControllerDidBackPress()
|
|
441
|
-
if (dismissible) {
|
|
442
|
-
dismiss(animated = true)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
454
|
private fun handleStateSettled(sheetView: View, newState: Int) {
|
|
447
455
|
if (interactionState is InteractionState.Reconfiguring) return
|
|
448
456
|
|
|
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
457
|
val index = detentCalculator.getDetentIndexForState(newState) ?: return
|
|
458
458
|
val position = getPositionDpForView(sheetView)
|
|
459
459
|
val detentInfo = DetentInfo(index, position)
|
|
460
460
|
|
|
461
|
+
// Handle present animation completion
|
|
462
|
+
if (isPresentAnimating) {
|
|
463
|
+
isPresentAnimating = false
|
|
464
|
+
finishPresent()
|
|
465
|
+
return
|
|
466
|
+
}
|
|
467
|
+
|
|
461
468
|
when (interactionState) {
|
|
462
469
|
is InteractionState.Dragging -> {
|
|
463
470
|
val detent = detentCalculator.getDetentValueForIndex(detentInfo.index)
|
|
@@ -494,7 +501,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
494
501
|
rnScreensObserver = RNScreensFragmentObserver(
|
|
495
502
|
reactContext = reactContext,
|
|
496
503
|
onModalPresented = {
|
|
497
|
-
if (isPresented &&
|
|
504
|
+
if (isPresented && isSheetVisible && isTopmostSheet) {
|
|
498
505
|
hideForModal()
|
|
499
506
|
}
|
|
500
507
|
},
|
|
@@ -521,39 +528,46 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
521
528
|
rnScreensObserver = null
|
|
522
529
|
}
|
|
523
530
|
|
|
531
|
+
private fun setSheetVisibility(visible: Boolean) {
|
|
532
|
+
coordinatorLayout?.visibility = if (visible) VISIBLE else GONE
|
|
533
|
+
dimViews.forEach { it.visibility = if (visible) VISIBLE else INVISIBLE }
|
|
534
|
+
}
|
|
535
|
+
|
|
524
536
|
private fun hideForModal() {
|
|
525
|
-
|
|
537
|
+
isSheetVisible = false
|
|
526
538
|
wasHiddenByModal = true
|
|
527
539
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
dimViews.forEach { it.visibility = INVISIBLE }
|
|
540
|
+
val sheet = sheetView ?: run {
|
|
541
|
+
setSheetVisibility(false)
|
|
542
|
+
parentSheetView?.viewController?.hideForModal()
|
|
543
|
+
return
|
|
544
|
+
}
|
|
534
545
|
|
|
535
|
-
|
|
546
|
+
dimViews.forEach { it.animate().alpha(0f).setDuration(MODAL_FADE_DURATION).start() }
|
|
547
|
+
sheet.animate()
|
|
548
|
+
.alpha(0f)
|
|
549
|
+
.setDuration(MODAL_FADE_DURATION)
|
|
550
|
+
.withEndAction {
|
|
551
|
+
setSheetVisibility(false)
|
|
552
|
+
parentSheetView?.viewController?.hideForModal()
|
|
553
|
+
}
|
|
554
|
+
.start()
|
|
536
555
|
}
|
|
537
556
|
|
|
538
557
|
private fun showAfterModal() {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
dialog?.window?.decorView?.visibility = VISIBLE
|
|
543
|
-
dimViews.forEach { it.visibility = VISIBLE }
|
|
544
|
-
|
|
558
|
+
isSheetVisible = true
|
|
559
|
+
setSheetVisibility(true)
|
|
560
|
+
sheetView?.alpha = 1f
|
|
545
561
|
updateDimAmount(animated = true)
|
|
546
562
|
}
|
|
547
563
|
|
|
548
564
|
/**
|
|
549
565
|
* Re-applies hidden state after returning from background.
|
|
550
|
-
* Android may restore
|
|
566
|
+
* Android may restore visibility on activity resume, so we need to hide it again.
|
|
551
567
|
*/
|
|
552
568
|
fun reapplyHiddenState() {
|
|
553
569
|
if (!wasHiddenByModal) return
|
|
554
|
-
|
|
555
|
-
dialog?.window?.decorView?.visibility = GONE
|
|
556
|
-
dimViews.forEach { it.visibility = INVISIBLE }
|
|
570
|
+
setSheetVisibility(false)
|
|
557
571
|
}
|
|
558
572
|
|
|
559
573
|
// =============================================================================
|
|
@@ -561,13 +575,18 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
561
575
|
// =============================================================================
|
|
562
576
|
|
|
563
577
|
fun present(detentIndex: Int, animated: Boolean = true) {
|
|
564
|
-
val
|
|
565
|
-
RNLog.w(reactContext, "TrueSheet: No
|
|
578
|
+
val coordinator = this.coordinatorLayout ?: run {
|
|
579
|
+
RNLog.w(reactContext, "TrueSheet: No coordinator layout available. Ensure the sheet is mounted before presenting.")
|
|
580
|
+
return
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
val sheet = this.sheetView ?: run {
|
|
584
|
+
RNLog.w(reactContext, "TrueSheet: No sheet view available.")
|
|
566
585
|
return
|
|
567
586
|
}
|
|
568
587
|
|
|
569
|
-
val activity = reactContext.currentActivity
|
|
570
|
-
RNLog.w(reactContext, "TrueSheet: No
|
|
588
|
+
val activity = reactContext.currentActivity ?: run {
|
|
589
|
+
RNLog.w(reactContext, "TrueSheet: No activity available for presentation.")
|
|
571
590
|
return
|
|
572
591
|
}
|
|
573
592
|
|
|
@@ -579,16 +598,68 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
579
598
|
currentDetentIndex = detentIndex
|
|
580
599
|
interactionState = InteractionState.Idle
|
|
581
600
|
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
601
|
+
// Setup sheet in coordinator layout
|
|
602
|
+
setupSheetInCoordinator(coordinator, sheet)
|
|
603
|
+
|
|
604
|
+
// Add coordinator to activity
|
|
605
|
+
val contentView = activity.findViewById<ViewGroup>(android.R.id.content)
|
|
606
|
+
contentView?.addView(coordinator)
|
|
607
|
+
|
|
608
|
+
// Setup back button handling
|
|
609
|
+
setupBackCallback()
|
|
586
610
|
|
|
587
|
-
//
|
|
588
|
-
|
|
611
|
+
// Start presentation
|
|
612
|
+
onSheetShow()
|
|
589
613
|
}
|
|
590
614
|
}
|
|
591
615
|
|
|
616
|
+
private fun setupSheetInCoordinator(coordinator: TrueSheetCoordinatorLayout, sheet: TrueSheetBottomSheetView) {
|
|
617
|
+
// Add this controller as content to the sheet
|
|
618
|
+
(parent as? ViewGroup)?.removeView(this)
|
|
619
|
+
sheet.addView(this)
|
|
620
|
+
|
|
621
|
+
// Create layout params with behavior
|
|
622
|
+
val params = sheet.createLayoutParams()
|
|
623
|
+
val behavior = params.behavior as BottomSheetBehavior<TrueSheetBottomSheetView>
|
|
624
|
+
|
|
625
|
+
// Configure behavior
|
|
626
|
+
behavior.isHideable = true
|
|
627
|
+
behavior.isDraggable = draggable
|
|
628
|
+
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
|
629
|
+
behavior.addBottomSheetCallback(sheetCallback)
|
|
630
|
+
|
|
631
|
+
// Add sheet to coordinator
|
|
632
|
+
coordinator.addView(sheet, params)
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
private fun onSheetShow() {
|
|
636
|
+
val sheet = sheetView ?: run {
|
|
637
|
+
RNLog.e(reactContext, "TrueSheet: sheetView is null in onSheetShow")
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
emitWillPresentEvents()
|
|
642
|
+
|
|
643
|
+
setupSheetDetents()
|
|
644
|
+
setupDimmedBackground(currentDetentIndex)
|
|
645
|
+
setupKeyboardObserver()
|
|
646
|
+
sheet.setupBackground()
|
|
647
|
+
sheet.setupGrabber()
|
|
648
|
+
|
|
649
|
+
if (shouldAnimatePresent) {
|
|
650
|
+
isPresentAnimating = true
|
|
651
|
+
post { setStateForDetentIndex(currentDetentIndex) }
|
|
652
|
+
} else {
|
|
653
|
+
setStateForDetentIndex(currentDetentIndex)
|
|
654
|
+
emitChangePositionDelegate(detentCalculator.getSheetTopForDetentIndex(currentDetentIndex))
|
|
655
|
+
updateDimAmount()
|
|
656
|
+
finishPresent()
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
isPresented = true
|
|
660
|
+
isSheetVisible = true
|
|
661
|
+
}
|
|
662
|
+
|
|
592
663
|
fun dismiss(animated: Boolean = true) {
|
|
593
664
|
if (isDismissing) return
|
|
594
665
|
|
|
@@ -596,17 +667,40 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
596
667
|
emitWillDismissEvents()
|
|
597
668
|
|
|
598
669
|
if (animated) {
|
|
599
|
-
|
|
600
|
-
onUpdate = { effectiveTop -> updateSheetVisuals(effectiveTop) },
|
|
601
|
-
onEnd = { dialogFragment?.dismiss() }
|
|
602
|
-
)
|
|
670
|
+
animateDismiss()
|
|
603
671
|
} else {
|
|
604
672
|
emitChangePositionDelegate(realScreenHeight)
|
|
605
|
-
|
|
673
|
+
finishDismiss()
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
private fun dismissOrCollapseToLowest() {
|
|
678
|
+
if (dismissible) {
|
|
679
|
+
dismiss(animated = true)
|
|
680
|
+
} else if (parentSheetView == null && isDimmedAtCurrentDetent && dimmedDetentIndex > 0) {
|
|
681
|
+
setStateForDetentIndex(dimmedDetentIndex - 1)
|
|
606
682
|
}
|
|
607
683
|
}
|
|
608
684
|
|
|
685
|
+
private fun animateDismiss() {
|
|
686
|
+
val sheet = sheetView ?: run {
|
|
687
|
+
finishDismiss()
|
|
688
|
+
return
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
sheet.animate()
|
|
692
|
+
.y(realScreenHeight.toFloat())
|
|
693
|
+
.setDuration(DISMISS_DURATION)
|
|
694
|
+
.setInterpolator(android.view.animation.AccelerateInterpolator())
|
|
695
|
+
.setUpdateListener { updateSheetVisuals(sheet.y.toInt()) }
|
|
696
|
+
.withEndAction { finishDismiss() }
|
|
697
|
+
.start()
|
|
698
|
+
}
|
|
699
|
+
|
|
609
700
|
private fun finishPresent() {
|
|
701
|
+
// Restore isHideable to actual value after present animation
|
|
702
|
+
behavior?.isHideable = dismissible
|
|
703
|
+
|
|
610
704
|
val (index, position, detent) = getDetentInfoWithValue(currentDetentIndex)
|
|
611
705
|
delegate?.viewControllerDidPresent(index, position, detent)
|
|
612
706
|
parentSheetView?.viewControllerDidBlur()
|
|
@@ -616,40 +710,47 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
616
710
|
presentPromise = null
|
|
617
711
|
}
|
|
618
712
|
|
|
713
|
+
private fun finishDismiss() {
|
|
714
|
+
emitDidDismissEvents()
|
|
715
|
+
cleanupSheet()
|
|
716
|
+
}
|
|
717
|
+
|
|
619
718
|
// =============================================================================
|
|
620
719
|
// MARK: - Sheet Configuration
|
|
621
720
|
// =============================================================================
|
|
622
721
|
|
|
623
722
|
fun setupSheetDetents() {
|
|
624
|
-
val
|
|
625
|
-
|
|
723
|
+
val behavior = this.behavior ?: run {
|
|
724
|
+
RNLog.e(reactContext, "TrueSheet: behavior is null in setupSheetDetents")
|
|
725
|
+
return
|
|
726
|
+
}
|
|
626
727
|
|
|
627
728
|
interactionState = InteractionState.Reconfiguring
|
|
628
|
-
val edgeToEdgeTopInset: Int = if (!edgeToEdgeFullScreen) topInset else 0
|
|
629
729
|
|
|
630
730
|
behavior.isFitToContents = false
|
|
631
731
|
|
|
632
|
-
val maxAvailableHeight = realScreenHeight -
|
|
732
|
+
val maxAvailableHeight = realScreenHeight - topInset
|
|
633
733
|
|
|
634
|
-
val peekHeight = detentCalculator.getDetentHeight(detents[0])
|
|
734
|
+
val peekHeight = minOf(detentCalculator.getDetentHeight(detents[0]), maxAvailableHeight)
|
|
635
735
|
|
|
636
736
|
val halfExpandedDetentHeight = when (detents.size) {
|
|
637
737
|
1 -> peekHeight
|
|
638
738
|
else -> detentCalculator.getDetentHeight(detents[1])
|
|
639
739
|
}
|
|
640
740
|
|
|
641
|
-
val maxDetentHeight = detentCalculator.getDetentHeight(detents.last())
|
|
741
|
+
val maxDetentHeight = minOf(detentCalculator.getDetentHeight(detents.last()), maxAvailableHeight)
|
|
642
742
|
|
|
643
743
|
val adjustedHalfExpandedHeight = minOf(halfExpandedDetentHeight, maxAvailableHeight)
|
|
644
744
|
val halfExpandedRatio = (adjustedHalfExpandedHeight.toFloat() / realScreenHeight.toFloat())
|
|
645
745
|
.coerceIn(0f, 0.999f)
|
|
646
746
|
|
|
647
|
-
val expandedOffset =
|
|
747
|
+
val expandedOffset = realScreenHeight - maxDetentHeight
|
|
648
748
|
|
|
649
749
|
// fitToContents works better with <= 2 detents when no expanded offset
|
|
650
750
|
val fitToContents = detents.size < 3 && expandedOffset == 0
|
|
651
751
|
|
|
652
|
-
|
|
752
|
+
configureDetents(
|
|
753
|
+
behavior = behavior,
|
|
653
754
|
peekHeight = peekHeight,
|
|
654
755
|
halfExpandedRatio = halfExpandedRatio,
|
|
655
756
|
expandedOffset = expandedOffset,
|
|
@@ -674,87 +775,76 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
674
775
|
interactionState = InteractionState.Idle
|
|
675
776
|
}
|
|
676
777
|
|
|
778
|
+
private fun configureDetents(
|
|
779
|
+
behavior: BottomSheetBehavior<TrueSheetBottomSheetView>,
|
|
780
|
+
peekHeight: Int,
|
|
781
|
+
halfExpandedRatio: Float,
|
|
782
|
+
expandedOffset: Int,
|
|
783
|
+
fitToContents: Boolean,
|
|
784
|
+
animate: Boolean
|
|
785
|
+
) {
|
|
786
|
+
behavior.apply {
|
|
787
|
+
isFitToContents = fitToContents
|
|
788
|
+
skipCollapsed = false
|
|
789
|
+
setPeekHeight(peekHeight, animate)
|
|
790
|
+
this.halfExpandedRatio = halfExpandedRatio.coerceIn(0f, 0.999f)
|
|
791
|
+
this.expandedOffset = expandedOffset
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
677
795
|
fun setupSheetDetentsForSizeChange() {
|
|
678
796
|
setupSheetDetents()
|
|
679
797
|
positionFooter()
|
|
680
798
|
}
|
|
681
799
|
|
|
682
800
|
fun setStateForDetentIndex(index: Int) {
|
|
683
|
-
|
|
801
|
+
behavior?.state = detentCalculator.getStateForDetentIndex(index)
|
|
684
802
|
}
|
|
685
803
|
|
|
686
804
|
// =============================================================================
|
|
687
|
-
// MARK: -
|
|
805
|
+
// MARK: - Dimmed Background
|
|
688
806
|
// =============================================================================
|
|
689
807
|
|
|
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
808
|
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
|
|
809
|
+
val coordinator = this.coordinatorLayout ?: run {
|
|
810
|
+
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupDimmedBackground")
|
|
811
|
+
return
|
|
812
|
+
}
|
|
715
813
|
|
|
716
|
-
|
|
717
|
-
|
|
814
|
+
if (dimmed) {
|
|
815
|
+
val parentDimVisible = (parentSheetView?.viewController?.dimView?.alpha ?: 0f) > 0f
|
|
718
816
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (parentBottomSheet != null) {
|
|
723
|
-
if (parentDimView == null) parentDimView = TrueSheetDimView(reactContext)
|
|
724
|
-
parentDimView?.attach(parentBottomSheet, parentController.sheetCornerRadius)
|
|
817
|
+
if (dimView == null) {
|
|
818
|
+
dimView = TrueSheetDimView(reactContext).apply {
|
|
819
|
+
delegate = this@TrueSheetViewController
|
|
725
820
|
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
dimView
|
|
729
|
-
parentDimView?.detach()
|
|
730
|
-
parentDimView = null
|
|
821
|
+
}
|
|
822
|
+
if (!parentDimVisible) {
|
|
823
|
+
dimView?.attachToCoordinator(coordinator)
|
|
731
824
|
}
|
|
732
825
|
|
|
733
|
-
if
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
826
|
+
// Attach dim view to parent sheet if stacked
|
|
827
|
+
val parentController = parentSheetView?.viewController
|
|
828
|
+
val parentBottomSheet = parentController?.sheetView
|
|
829
|
+
if (parentBottomSheet != null) {
|
|
830
|
+
if (parentDimView == null) {
|
|
831
|
+
parentDimView = TrueSheetDimView(reactContext).apply {
|
|
832
|
+
delegate = this@TrueSheetViewController
|
|
737
833
|
}
|
|
738
|
-
true
|
|
739
834
|
}
|
|
740
|
-
|
|
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
|
-
}
|
|
750
|
-
dialog.setCanceledOnTouchOutside(false)
|
|
835
|
+
parentDimView?.attach(parentBottomSheet, parentController.sheetCornerRadius)
|
|
751
836
|
}
|
|
837
|
+
} else {
|
|
838
|
+
dimView?.detach()
|
|
839
|
+
dimView = null
|
|
840
|
+
parentDimView?.detach()
|
|
841
|
+
parentDimView = null
|
|
752
842
|
}
|
|
753
843
|
}
|
|
754
844
|
|
|
755
845
|
fun updateDimAmount(sheetTop: Int? = null, animated: Boolean = false) {
|
|
756
846
|
if (!dimmed) return
|
|
757
|
-
val top = (sheetTop ?:
|
|
847
|
+
val top = (sheetTop ?: sheetView?.top ?: return) + currentKeyboardInset
|
|
758
848
|
|
|
759
849
|
if (animated) {
|
|
760
850
|
val targetAlpha = dimView?.calculateAlpha(
|
|
@@ -775,11 +865,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
775
865
|
fun positionFooter(slideOffset: Float? = null) {
|
|
776
866
|
if (!isPresented) return
|
|
777
867
|
val footerView = containerView?.footerView ?: return
|
|
778
|
-
val
|
|
868
|
+
val sheet = sheetView ?: return
|
|
779
869
|
|
|
780
870
|
val footerHeight = footerView.height
|
|
781
|
-
val sheetHeight =
|
|
782
|
-
val sheetTop =
|
|
871
|
+
val sheetHeight = sheet.height
|
|
872
|
+
val sheetTop = sheet.top
|
|
783
873
|
|
|
784
874
|
var footerY = (sheetHeight - sheetTop - footerHeight - currentKeyboardInset).toFloat()
|
|
785
875
|
|
|
@@ -803,14 +893,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
803
893
|
}
|
|
804
894
|
|
|
805
895
|
fun setupKeyboardObserver() {
|
|
806
|
-
val
|
|
896
|
+
val coordinator = coordinatorLayout ?: run {
|
|
897
|
+
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupKeyboardObserver")
|
|
898
|
+
return
|
|
899
|
+
}
|
|
807
900
|
cleanupKeyboardObserver()
|
|
808
|
-
keyboardObserver = TrueSheetKeyboardObserver(
|
|
901
|
+
keyboardObserver = TrueSheetKeyboardObserver(coordinator, reactContext).apply {
|
|
809
902
|
delegate = object : TrueSheetKeyboardObserverDelegate {
|
|
810
903
|
override fun keyboardWillShow(height: Int) {
|
|
904
|
+
isKeyboardTransitioning = true
|
|
811
905
|
if (!shouldHandleKeyboard()) return
|
|
812
906
|
detentIndexBeforeKeyboard = currentDetentIndex
|
|
813
|
-
isKeyboardTransitioning = true
|
|
814
907
|
setupSheetDetents()
|
|
815
908
|
setStateForDetentIndex(detents.size - 1)
|
|
816
909
|
}
|
|
@@ -825,7 +918,6 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
825
918
|
}
|
|
826
919
|
|
|
827
920
|
override fun keyboardDidHide() {
|
|
828
|
-
if (!shouldHandleKeyboard()) return
|
|
829
921
|
isKeyboardTransitioning = false
|
|
830
922
|
}
|
|
831
923
|
|
|
@@ -919,19 +1011,14 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
919
1011
|
// MARK: - Detent Helpers
|
|
920
1012
|
// =============================================================================
|
|
921
1013
|
|
|
922
|
-
fun
|
|
923
|
-
|
|
924
|
-
return realScreenHeight - detentCalculator.getDetentHeight(detents[detentIndex])
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
fun translateDialog(translationY: Int) {
|
|
928
|
-
val bottomSheet = bottomSheetView ?: return
|
|
1014
|
+
fun translateSheet(translationY: Int) {
|
|
1015
|
+
val sheet = sheetView ?: return
|
|
929
1016
|
|
|
930
|
-
|
|
1017
|
+
sheet.animate()
|
|
931
1018
|
.translationY(translationY.toFloat())
|
|
932
1019
|
.setDuration(TRANSLATE_ANIMATION_DURATION)
|
|
933
1020
|
.setUpdateListener {
|
|
934
|
-
val effectiveTop =
|
|
1021
|
+
val effectiveTop = sheet.top + sheet.translationY.toInt()
|
|
935
1022
|
emitChangePositionDelegate(effectiveTop)
|
|
936
1023
|
}
|
|
937
1024
|
.start()
|
|
@@ -948,7 +1035,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
948
1035
|
private fun getPositionForDetentIndex(index: Int): Float {
|
|
949
1036
|
if (index < 0 || index >= detents.size) return screenHeight.pxToDp()
|
|
950
1037
|
|
|
951
|
-
|
|
1038
|
+
sheetView?.let {
|
|
952
1039
|
val visibleSheetHeight = detentCalculator.getVisibleSheetHeight(it.top)
|
|
953
1040
|
if (visibleSheetHeight in 1..<realScreenHeight) {
|
|
954
1041
|
return detentCalculator.getPositionDp(visibleSheetHeight)
|
|
@@ -980,7 +1067,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
980
1067
|
post {
|
|
981
1068
|
setupSheetDetents()
|
|
982
1069
|
positionFooter()
|
|
983
|
-
|
|
1070
|
+
sheetView?.let { emitChangePositionDelegate(it.top, realtime = false) }
|
|
984
1071
|
}
|
|
985
1072
|
}
|
|
986
1073
|
|
|
@@ -1021,41 +1108,41 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1021
1108
|
|
|
1022
1109
|
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
1023
1110
|
eventDispatcher?.let {
|
|
1024
|
-
|
|
1025
|
-
|
|
1111
|
+
jsTouchDispatcher.handleTouchEvent(event, it, reactContext)
|
|
1112
|
+
jsPointerDispatcher?.handleMotionEvent(event, it, true)
|
|
1026
1113
|
}
|
|
1027
1114
|
return super.onInterceptTouchEvent(event)
|
|
1028
1115
|
}
|
|
1029
1116
|
|
|
1030
1117
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
1031
1118
|
eventDispatcher?.let {
|
|
1032
|
-
|
|
1033
|
-
|
|
1119
|
+
jsTouchDispatcher.handleTouchEvent(event, it, reactContext)
|
|
1120
|
+
jsPointerDispatcher?.handleMotionEvent(event, it, false)
|
|
1034
1121
|
}
|
|
1035
1122
|
super.onTouchEvent(event)
|
|
1036
1123
|
return true
|
|
1037
1124
|
}
|
|
1038
1125
|
|
|
1039
1126
|
override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
|
|
1040
|
-
eventDispatcher?.let {
|
|
1127
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, true) }
|
|
1041
1128
|
return super.onHoverEvent(event)
|
|
1042
1129
|
}
|
|
1043
1130
|
|
|
1044
1131
|
override fun onHoverEvent(event: MotionEvent): Boolean {
|
|
1045
|
-
eventDispatcher?.let {
|
|
1132
|
+
eventDispatcher?.let { jsPointerDispatcher?.handleMotionEvent(event, it, false) }
|
|
1046
1133
|
return super.onHoverEvent(event)
|
|
1047
1134
|
}
|
|
1048
1135
|
|
|
1049
1136
|
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
|
|
1050
1137
|
eventDispatcher?.let {
|
|
1051
|
-
|
|
1052
|
-
|
|
1138
|
+
jsTouchDispatcher.onChildStartedNativeGesture(ev, it)
|
|
1139
|
+
jsPointerDispatcher?.onChildStartedNativeGesture(childView, ev, it)
|
|
1053
1140
|
}
|
|
1054
1141
|
}
|
|
1055
1142
|
|
|
1056
1143
|
override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
|
|
1057
|
-
eventDispatcher?.let {
|
|
1058
|
-
|
|
1144
|
+
eventDispatcher?.let { jsTouchDispatcher.onChildEndedNativeGesture(ev, it) }
|
|
1145
|
+
jsPointerDispatcher?.onChildEndedNativeGesture()
|
|
1059
1146
|
}
|
|
1060
1147
|
|
|
1061
1148
|
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|