@lodev09/react-native-true-sheet 3.5.0 → 3.5.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +150 -136
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +173 -217
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetAnimator.kt +22 -11
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogFragment.kt +320 -0
- package/android/src/main/res/values/styles.xml +2 -8
- package/package.json +1 -1
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import android.
|
|
5
|
-
import android.graphics.drawable.ShapeDrawable
|
|
6
|
-
import android.graphics.drawable.shapes.RoundRectShape
|
|
7
|
-
import android.util.TypedValue
|
|
4
|
+
import android.util.Log
|
|
8
5
|
import android.view.MotionEvent
|
|
9
6
|
import android.view.View
|
|
10
7
|
import android.view.WindowManager
|
|
11
8
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
12
9
|
import android.widget.FrameLayout
|
|
10
|
+
import androidx.appcompat.app.AppCompatActivity
|
|
13
11
|
import androidx.core.view.isNotEmpty
|
|
14
12
|
import androidx.core.view.isVisible
|
|
15
13
|
import com.facebook.react.R
|
|
@@ -30,9 +28,10 @@ import com.lodev09.truesheet.core.TrueSheetAnimator
|
|
|
30
28
|
import com.lodev09.truesheet.core.TrueSheetAnimatorProvider
|
|
31
29
|
import com.lodev09.truesheet.core.TrueSheetDetentCalculator
|
|
32
30
|
import com.lodev09.truesheet.core.TrueSheetDetentMeasurements
|
|
31
|
+
import com.lodev09.truesheet.core.TrueSheetDialogFragment
|
|
32
|
+
import com.lodev09.truesheet.core.TrueSheetDialogFragmentDelegate
|
|
33
33
|
import com.lodev09.truesheet.core.TrueSheetDialogObserver
|
|
34
34
|
import com.lodev09.truesheet.core.TrueSheetDimView
|
|
35
|
-
import com.lodev09.truesheet.core.TrueSheetGrabberView
|
|
36
35
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserver
|
|
37
36
|
import com.lodev09.truesheet.core.TrueSheetKeyboardObserverDelegate
|
|
38
37
|
import com.lodev09.truesheet.utils.ScreenUtils
|
|
@@ -66,7 +65,7 @@ interface TrueSheetViewControllerDelegate {
|
|
|
66
65
|
// =============================================================================
|
|
67
66
|
|
|
68
67
|
/**
|
|
69
|
-
* Manages the bottom sheet dialog and its presentation lifecycle.
|
|
68
|
+
* Manages the bottom sheet dialog fragment and its presentation lifecycle.
|
|
70
69
|
* Acts as a RootView to properly dispatch touch events to React Native.
|
|
71
70
|
*/
|
|
72
71
|
@SuppressLint("ClickableViewAccessibility", "ViewConstructor")
|
|
@@ -74,14 +73,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
74
73
|
ReactViewGroup(reactContext),
|
|
75
74
|
RootView,
|
|
76
75
|
TrueSheetDetentMeasurements,
|
|
77
|
-
TrueSheetAnimatorProvider
|
|
76
|
+
TrueSheetAnimatorProvider,
|
|
77
|
+
TrueSheetDialogFragmentDelegate {
|
|
78
78
|
|
|
79
79
|
companion object {
|
|
80
80
|
const val TAG_NAME = "TrueSheet"
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
private const val MAX_HALF_EXPANDED_RATIO = 0.999f
|
|
84
|
-
private const val GRABBER_TAG = "TrueSheetGrabber"
|
|
82
|
+
private const val FRAGMENT_TAG = "TrueSheetDialogFragment"
|
|
85
83
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
86
84
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
87
85
|
private const val TRANSLATE_ANIMATION_DURATION = 200L
|
|
@@ -103,8 +101,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
103
101
|
|
|
104
102
|
var delegate: TrueSheetViewControllerDelegate? = null
|
|
105
103
|
|
|
106
|
-
// Dialog
|
|
107
|
-
private var
|
|
104
|
+
// Dialog Fragment
|
|
105
|
+
private var dialogFragment: TrueSheetDialogFragment? = null
|
|
108
106
|
private var dimView: TrueSheetDimView? = null
|
|
109
107
|
private var parentDimView: TrueSheetDimView? = null
|
|
110
108
|
|
|
@@ -165,23 +163,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
165
163
|
var sheetCornerRadius: Float = DEFAULT_CORNER_RADIUS.dpToPx()
|
|
166
164
|
set(value) {
|
|
167
165
|
field = if (value < 0) DEFAULT_CORNER_RADIUS.dpToPx() else value
|
|
168
|
-
|
|
166
|
+
dialogFragment?.sheetCornerRadius = field
|
|
167
|
+
if (isPresented) dialogFragment?.setupBackground()
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
var dismissible: Boolean = true
|
|
172
171
|
set(value) {
|
|
173
172
|
field = value
|
|
174
|
-
|
|
175
|
-
setCanceledOnTouchOutside(value)
|
|
176
|
-
setCancelable(value)
|
|
177
|
-
behavior.isHideable = value
|
|
178
|
-
}
|
|
173
|
+
dialogFragment?.dismissible = value
|
|
179
174
|
}
|
|
180
175
|
|
|
181
176
|
var draggable: Boolean = true
|
|
182
177
|
set(value) {
|
|
183
178
|
field = value
|
|
184
|
-
|
|
179
|
+
dialogFragment?.updateDraggable(value)
|
|
185
180
|
}
|
|
186
181
|
|
|
187
182
|
// =============================================================================
|
|
@@ -189,14 +184,17 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
189
184
|
// =============================================================================
|
|
190
185
|
|
|
191
186
|
// Dialog
|
|
187
|
+
private val dialog: BottomSheetDialog?
|
|
188
|
+
get() = dialogFragment?.bottomSheetDialog
|
|
189
|
+
|
|
192
190
|
private val behavior: BottomSheetBehavior<FrameLayout>?
|
|
193
|
-
get() =
|
|
191
|
+
get() = dialogFragment?.behavior
|
|
194
192
|
|
|
195
193
|
private val sheetContainer: FrameLayout?
|
|
196
194
|
get() = this.parent as? FrameLayout
|
|
197
195
|
|
|
198
196
|
override val bottomSheetView: FrameLayout?
|
|
199
|
-
get() =
|
|
197
|
+
get() = dialogFragment?.bottomSheetView
|
|
200
198
|
|
|
201
199
|
private val containerView: TrueSheetContainerView?
|
|
202
200
|
get() = if (this.isNotEmpty()) getChildAt(0) as? TrueSheetContainerView else null
|
|
@@ -268,53 +266,29 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
268
266
|
}
|
|
269
267
|
|
|
270
268
|
// =============================================================================
|
|
271
|
-
// MARK: -
|
|
269
|
+
// MARK: - Fragment Creation & Cleanup
|
|
272
270
|
// =============================================================================
|
|
273
271
|
|
|
274
272
|
fun createDialog() {
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
if (dialogFragment != null) return
|
|
274
|
+
|
|
275
|
+
dialogFragment = TrueSheetDialogFragment.newInstance().apply {
|
|
276
|
+
this.delegate = this@TrueSheetViewController
|
|
277
|
+
this.contentView = this@TrueSheetViewController
|
|
278
|
+
this.reactContext = this@TrueSheetViewController.reactContext
|
|
279
|
+
this.sheetCornerRadius = this@TrueSheetViewController.sheetCornerRadius
|
|
280
|
+
this.sheetBackgroundColor = this@TrueSheetViewController.sheetBackgroundColor
|
|
281
|
+
this.edgeToEdgeFullScreen = this@TrueSheetViewController.edgeToEdgeFullScreen
|
|
282
|
+
this.grabberEnabled = this@TrueSheetViewController.grabber
|
|
283
|
+
this.grabberOptions = this@TrueSheetViewController.grabberOptions
|
|
284
|
+
this.dismissible = this@TrueSheetViewController.dismissible
|
|
285
|
+
this.draggable = this@TrueSheetViewController.draggable
|
|
281
286
|
}
|
|
282
287
|
|
|
283
|
-
|
|
284
|
-
setContentView(this@TrueSheetViewController)
|
|
285
|
-
|
|
286
|
-
window?.apply {
|
|
287
|
-
setWindowAnimations(0)
|
|
288
|
-
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
setupModalObserver()
|
|
292
|
-
setupDialogListeners(this)
|
|
293
|
-
setupBottomSheetBehavior(this)
|
|
294
|
-
|
|
295
|
-
setCanceledOnTouchOutside(dismissible)
|
|
296
|
-
setCancelable(dismissible)
|
|
297
|
-
behavior.isHideable = dismissible
|
|
298
|
-
behavior.isDraggable = draggable
|
|
299
|
-
|
|
300
|
-
onBackPressedDispatcher.addCallback(object : androidx.activity.OnBackPressedCallback(true) {
|
|
301
|
-
override fun handleOnBackPressed() {
|
|
302
|
-
this@TrueSheetViewController.delegate?.viewControllerDidBackPress()
|
|
303
|
-
if (dismissible) {
|
|
304
|
-
dismiss()
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
})
|
|
308
|
-
}
|
|
288
|
+
setupModalObserver()
|
|
309
289
|
}
|
|
310
290
|
|
|
311
291
|
private fun cleanupDialog() {
|
|
312
|
-
dialog?.apply {
|
|
313
|
-
setOnShowListener(null)
|
|
314
|
-
setOnCancelListener(null)
|
|
315
|
-
setOnDismissListener(null)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
292
|
cleanupKeyboardObserver()
|
|
319
293
|
cleanupModalObserver()
|
|
320
294
|
sheetAnimator.cancel()
|
|
@@ -324,7 +298,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
324
298
|
parentDimView = null
|
|
325
299
|
sheetContainer?.removeView(this)
|
|
326
300
|
|
|
327
|
-
|
|
301
|
+
dialogFragment = null
|
|
328
302
|
interactionState = InteractionState.Idle
|
|
329
303
|
isDismissing = false
|
|
330
304
|
isPresented = false
|
|
@@ -335,86 +309,94 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
335
309
|
}
|
|
336
310
|
|
|
337
311
|
// =============================================================================
|
|
338
|
-
// MARK: -
|
|
312
|
+
// MARK: - TrueSheetDialogFragmentDelegate
|
|
339
313
|
// =============================================================================
|
|
340
314
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
toTop = toTop,
|
|
353
|
-
onUpdate = { effectiveTop ->
|
|
354
|
-
emitChangePositionDelegate(effectiveTop)
|
|
355
|
-
positionFooter()
|
|
356
|
-
updateDimAmount(effectiveTop)
|
|
357
|
-
},
|
|
358
|
-
onEnd = { finishPresent() }
|
|
359
|
-
)
|
|
360
|
-
} else {
|
|
361
|
-
val toTop = getExpectedSheetTop(currentDetentIndex)
|
|
362
|
-
emitChangePositionDelegate(toTop)
|
|
363
|
-
positionFooter()
|
|
364
|
-
finishPresent()
|
|
365
|
-
}
|
|
366
|
-
}
|
|
315
|
+
override fun onDialogShow() {
|
|
316
|
+
bottomSheetView?.visibility = VISIBLE
|
|
317
|
+
|
|
318
|
+
isPresented = true
|
|
319
|
+
isDialogVisible = true
|
|
320
|
+
|
|
321
|
+
emitWillPresentEvents()
|
|
322
|
+
|
|
323
|
+
setupSheetDetents()
|
|
324
|
+
setupDimmedBackground(currentDetentIndex)
|
|
325
|
+
setupKeyboardObserver()
|
|
367
326
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
327
|
+
if (shouldAnimatePresent) {
|
|
328
|
+
val toTop = getExpectedSheetTop(currentDetentIndex)
|
|
329
|
+
sheetAnimator.animatePresent(
|
|
330
|
+
toTop = toTop,
|
|
331
|
+
onUpdate = { effectiveTop ->
|
|
332
|
+
emitChangePositionDelegate(effectiveTop)
|
|
333
|
+
positionFooter()
|
|
334
|
+
updateDimAmount(effectiveTop)
|
|
335
|
+
},
|
|
336
|
+
onEnd = { finishPresent() }
|
|
337
|
+
)
|
|
338
|
+
} else {
|
|
339
|
+
val toTop = getExpectedSheetTop(currentDetentIndex)
|
|
340
|
+
emitChangePositionDelegate(toTop)
|
|
341
|
+
positionFooter()
|
|
342
|
+
finishPresent()
|
|
371
343
|
}
|
|
372
344
|
}
|
|
373
345
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
val behavior = behavior ?: return
|
|
346
|
+
override fun onDialogDismiss() {
|
|
347
|
+
emitDidDismissEvents()
|
|
348
|
+
cleanupDialog()
|
|
349
|
+
}
|
|
379
350
|
|
|
380
|
-
|
|
351
|
+
override fun onDialogCancel() {
|
|
352
|
+
// Cancel is called before dismiss for user-initiated cancellation
|
|
353
|
+
}
|
|
381
354
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
355
|
+
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
356
|
+
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
357
|
+
if (isDismissing) return
|
|
358
|
+
isDismissing = true
|
|
359
|
+
emitWillDismissEvents()
|
|
360
|
+
dialogFragment?.dismiss()
|
|
361
|
+
return
|
|
362
|
+
}
|
|
385
363
|
|
|
386
|
-
|
|
387
|
-
}
|
|
364
|
+
if (!isPresented) return
|
|
388
365
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
updateDimAmount(sheetView.top)
|
|
392
|
-
}
|
|
393
|
-
}
|
|
366
|
+
when (newState) {
|
|
367
|
+
BottomSheetBehavior.STATE_DRAGGING -> handleDragBegin(sheetView)
|
|
394
368
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
isDismissing = true
|
|
399
|
-
emitWillDismissEvents()
|
|
400
|
-
dialog.dismiss()
|
|
401
|
-
return
|
|
402
|
-
}
|
|
369
|
+
BottomSheetBehavior.STATE_EXPANDED,
|
|
370
|
+
BottomSheetBehavior.STATE_COLLAPSED,
|
|
371
|
+
BottomSheetBehavior.STATE_HALF_EXPANDED -> handleStateSettled(sheetView, newState)
|
|
403
372
|
|
|
404
|
-
|
|
373
|
+
else -> {}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
405
376
|
|
|
406
|
-
|
|
407
|
-
|
|
377
|
+
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
378
|
+
// Skip if our custom animator is handling the animation
|
|
379
|
+
if (sheetAnimator.isAnimating) return
|
|
408
380
|
|
|
409
|
-
|
|
410
|
-
BottomSheetBehavior.STATE_COLLAPSED,
|
|
411
|
-
BottomSheetBehavior.STATE_HALF_EXPANDED -> handleStateSettled(sheetView, newState)
|
|
381
|
+
val behavior = behavior ?: return
|
|
412
382
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
383
|
+
when (behavior.state) {
|
|
384
|
+
BottomSheetBehavior.STATE_DRAGGING,
|
|
385
|
+
BottomSheetBehavior.STATE_SETTLING -> handleDragChange(sheetView)
|
|
386
|
+
|
|
387
|
+
else -> { }
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
emitChangePositionDelegate(sheetView.top)
|
|
391
|
+
|
|
392
|
+
if (!isKeyboardTransitioning) {
|
|
393
|
+
positionFooter(slideOffset)
|
|
394
|
+
updateDimAmount(sheetView.top)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
override fun onBackPressed() {
|
|
399
|
+
delegate?.viewControllerDidBackPress()
|
|
418
400
|
}
|
|
419
401
|
|
|
420
402
|
private fun handleStateSettled(sheetView: View, newState: Int) {
|
|
@@ -519,31 +501,33 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
519
501
|
// =============================================================================
|
|
520
502
|
|
|
521
503
|
fun present(detentIndex: Int, animated: Boolean = true) {
|
|
522
|
-
val
|
|
523
|
-
RNLog.w(reactContext, "TrueSheet: No dialog available. Ensure the sheet is mounted before presenting.")
|
|
504
|
+
val fragment = this.dialogFragment ?: run {
|
|
505
|
+
RNLog.w(reactContext, "TrueSheet: No dialog fragment available. Ensure the sheet is mounted before presenting.")
|
|
524
506
|
return
|
|
525
507
|
}
|
|
526
508
|
|
|
527
|
-
|
|
509
|
+
val activity = reactContext.currentActivity as? AppCompatActivity ?: run {
|
|
510
|
+
RNLog.w(reactContext, "TrueSheet: No AppCompatActivity available for fragment transaction.")
|
|
511
|
+
return
|
|
512
|
+
}
|
|
528
513
|
|
|
529
514
|
if (isPresented) {
|
|
515
|
+
setupDimmedBackground(detentIndex)
|
|
530
516
|
setStateForDetentIndex(detentIndex)
|
|
531
517
|
} else {
|
|
532
518
|
shouldAnimatePresent = animated
|
|
533
519
|
currentDetentIndex = detentIndex
|
|
534
520
|
interactionState = InteractionState.Idle
|
|
535
521
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
setupBackground()
|
|
541
|
-
setupGrabber()
|
|
522
|
+
// Show the fragment - detents are configured in onDialogShow
|
|
523
|
+
if (!fragment.isAdded) {
|
|
524
|
+
fragment.show(activity.supportFragmentManager, FRAGMENT_TAG)
|
|
525
|
+
}
|
|
542
526
|
|
|
543
|
-
//
|
|
527
|
+
// Execute pending transactions to ensure fragment is added
|
|
528
|
+
activity.supportFragmentManager.executePendingTransactions()
|
|
529
|
+
bottomSheetView?.translationY = realScreenHeight.toFloat()
|
|
544
530
|
bottomSheetView?.visibility = INVISIBLE
|
|
545
|
-
|
|
546
|
-
dialog.show()
|
|
547
531
|
}
|
|
548
532
|
}
|
|
549
533
|
|
|
@@ -560,11 +544,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
560
544
|
positionFooter()
|
|
561
545
|
updateDimAmount(effectiveTop)
|
|
562
546
|
},
|
|
563
|
-
onEnd = {
|
|
547
|
+
onEnd = { dialogFragment?.dismiss() }
|
|
564
548
|
)
|
|
565
549
|
} else {
|
|
566
550
|
emitChangePositionDelegate(realScreenHeight)
|
|
567
|
-
|
|
551
|
+
dialogFragment?.dismiss()
|
|
568
552
|
}
|
|
569
553
|
}
|
|
570
554
|
|
|
@@ -583,53 +567,57 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
583
567
|
// =============================================================================
|
|
584
568
|
|
|
585
569
|
fun setupSheetDetents() {
|
|
570
|
+
val fragment = this.dialogFragment ?: return
|
|
586
571
|
val behavior = this.behavior ?: return
|
|
587
572
|
|
|
588
573
|
interactionState = InteractionState.Reconfiguring
|
|
589
574
|
val edgeToEdgeTopInset: Int = if (!edgeToEdgeFullScreen) topInset else 0
|
|
590
575
|
|
|
591
|
-
behavior.
|
|
592
|
-
isFitToContents = false
|
|
593
|
-
maxWidth = DEFAULT_MAX_WIDTH.dpToPx().toInt()
|
|
576
|
+
behavior.isFitToContents = false
|
|
594
577
|
|
|
595
|
-
|
|
578
|
+
val maxAvailableHeight = realScreenHeight - edgeToEdgeTopInset
|
|
596
579
|
|
|
597
|
-
|
|
580
|
+
val peekHeight = detentCalculator.getDetentHeight(detents[0])
|
|
598
581
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
582
|
+
val halfExpandedDetentHeight = when (detents.size) {
|
|
583
|
+
1 -> peekHeight
|
|
584
|
+
else -> detentCalculator.getDetentHeight(detents[1])
|
|
585
|
+
}
|
|
603
586
|
|
|
604
|
-
|
|
587
|
+
val maxDetentHeight = detentCalculator.getDetentHeight(detents.last())
|
|
605
588
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
MAX_HALF_EXPANDED_RATIO
|
|
610
|
-
)
|
|
589
|
+
val adjustedHalfExpandedHeight = minOf(halfExpandedDetentHeight, maxAvailableHeight)
|
|
590
|
+
val halfExpandedRatio = (adjustedHalfExpandedHeight.toFloat() / realScreenHeight.toFloat())
|
|
591
|
+
.coerceIn(0f, 0.999f)
|
|
611
592
|
|
|
612
|
-
|
|
593
|
+
val expandedOffset = maxOf(edgeToEdgeTopInset, realScreenHeight - maxDetentHeight)
|
|
613
594
|
|
|
614
|
-
|
|
615
|
-
|
|
595
|
+
// fitToContents works better with <= 2 detents when no expanded offset
|
|
596
|
+
val fitToContents = detents.size < 3 && expandedOffset == 0
|
|
616
597
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
598
|
+
fragment.configureDetents(
|
|
599
|
+
peekHeight = peekHeight,
|
|
600
|
+
halfExpandedRatio = halfExpandedRatio,
|
|
601
|
+
expandedOffset = expandedOffset,
|
|
602
|
+
fitToContents = fitToContents,
|
|
603
|
+
animate = isPresented
|
|
604
|
+
)
|
|
620
605
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
delegate?.viewControllerDidChangeSize(newWidth, newHeight)
|
|
625
|
-
}
|
|
606
|
+
val offset = if (expandedOffset == 0) topInset else 0
|
|
607
|
+
val newHeight = realScreenHeight - expandedOffset - offset
|
|
608
|
+
val newWidth = minOf(screenWidth, DEFAULT_MAX_WIDTH.dpToPx().toInt())
|
|
626
609
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
610
|
+
if (lastStateWidth != newWidth || lastStateHeight != newHeight) {
|
|
611
|
+
lastStateWidth = newWidth
|
|
612
|
+
lastStateHeight = newHeight
|
|
613
|
+
delegate?.viewControllerDidChangeSize(newWidth, newHeight)
|
|
614
|
+
}
|
|
630
615
|
|
|
631
|
-
|
|
616
|
+
if (isPresented) {
|
|
617
|
+
setStateForDetentIndex(currentDetentIndex)
|
|
632
618
|
}
|
|
619
|
+
|
|
620
|
+
interactionState = InteractionState.Idle
|
|
633
621
|
}
|
|
634
622
|
|
|
635
623
|
fun setupSheetDetentsForSizeChange() {
|
|
@@ -638,7 +626,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
638
626
|
}
|
|
639
627
|
|
|
640
628
|
fun setStateForDetentIndex(index: Int) {
|
|
641
|
-
|
|
629
|
+
dialogFragment?.setState(detentCalculator.getStateForDetentIndex(index))
|
|
642
630
|
}
|
|
643
631
|
|
|
644
632
|
// =============================================================================
|
|
@@ -646,19 +634,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
646
634
|
// =============================================================================
|
|
647
635
|
|
|
648
636
|
fun setupGrabber() {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (!grabber || !draggable) return
|
|
656
|
-
|
|
657
|
-
val grabberView = TrueSheetGrabberView(reactContext, grabberOptions).apply {
|
|
658
|
-
tag = GRABBER_TAG
|
|
637
|
+
dialogFragment?.apply {
|
|
638
|
+
grabberEnabled = this@TrueSheetViewController.grabber
|
|
639
|
+
grabberOptions = this@TrueSheetViewController.grabberOptions
|
|
640
|
+
setupGrabber()
|
|
659
641
|
}
|
|
660
|
-
|
|
661
|
-
bottomSheet.addView(grabberView)
|
|
662
642
|
}
|
|
663
643
|
|
|
664
644
|
// =============================================================================
|
|
@@ -666,25 +646,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
666
646
|
// =============================================================================
|
|
667
647
|
|
|
668
648
|
fun setupBackground() {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
sheetCornerRadius,
|
|
674
|
-
sheetCornerRadius,
|
|
675
|
-
sheetCornerRadius,
|
|
676
|
-
sheetCornerRadius,
|
|
677
|
-
0f,
|
|
678
|
-
0f,
|
|
679
|
-
0f,
|
|
680
|
-
0f
|
|
681
|
-
)
|
|
682
|
-
val backgroundColor = sheetBackgroundColor ?: getDefaultBackgroundColor()
|
|
683
|
-
|
|
684
|
-
bottomSheet.background = ShapeDrawable(RoundRectShape(outerRadii, null, null)).apply {
|
|
685
|
-
paint.color = backgroundColor
|
|
649
|
+
dialogFragment?.apply {
|
|
650
|
+
sheetCornerRadius = this@TrueSheetViewController.sheetCornerRadius
|
|
651
|
+
sheetBackgroundColor = this@TrueSheetViewController.sheetBackgroundColor
|
|
652
|
+
setupBackground()
|
|
686
653
|
}
|
|
687
|
-
bottomSheet.clipToOutline = true
|
|
688
654
|
}
|
|
689
655
|
|
|
690
656
|
fun setupDimmedBackground(detentIndex: Int) {
|
|
@@ -756,25 +722,12 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
756
722
|
}
|
|
757
723
|
}
|
|
758
724
|
|
|
759
|
-
fun getDefaultBackgroundColor(): Int {
|
|
760
|
-
val typedValue = TypedValue()
|
|
761
|
-
return if (reactContext.theme.resolveAttribute(
|
|
762
|
-
com.google.android.material.R.attr.colorSurfaceContainerLow,
|
|
763
|
-
typedValue,
|
|
764
|
-
true
|
|
765
|
-
)
|
|
766
|
-
) {
|
|
767
|
-
typedValue.data
|
|
768
|
-
} else {
|
|
769
|
-
Color.WHITE
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
725
|
// =============================================================================
|
|
774
726
|
// MARK: - Footer Positioning
|
|
775
727
|
// =============================================================================
|
|
776
728
|
|
|
777
729
|
fun positionFooter(slideOffset: Float? = null) {
|
|
730
|
+
if (!isPresented) return
|
|
778
731
|
val footerView = containerView?.footerView ?: return
|
|
779
732
|
val bottomSheet = bottomSheetView ?: return
|
|
780
733
|
|
|
@@ -829,7 +782,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
829
782
|
isKeyboardTransitioning = false
|
|
830
783
|
}
|
|
831
784
|
|
|
832
|
-
override fun keyboardDidChangeHeight(height: Int) {
|
|
785
|
+
override fun keyboardDidChangeHeight(height: Int) {
|
|
786
|
+
if (!shouldHandleKeyboard()) return
|
|
787
|
+
positionFooter()
|
|
788
|
+
}
|
|
833
789
|
}
|
|
834
790
|
start()
|
|
835
791
|
}
|
|
@@ -961,7 +917,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
961
917
|
// Skip reconfiguration if expanded and only height changed (e.g., keyboard)
|
|
962
918
|
if (h + topInset >= screenHeight && isExpanded && oldw == w) return
|
|
963
919
|
|
|
964
|
-
|
|
920
|
+
post {
|
|
965
921
|
setupSheetDetents()
|
|
966
922
|
positionFooter()
|
|
967
923
|
bottomSheetView?.let { emitChangePositionDelegate(it.top, realtime = false) }
|