@lodev09/react-native-true-sheet 4.0.0-beta.24 → 4.0.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +12 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +120 -19
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +103 -27
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +18 -10
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +9 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +3 -16
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +14 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +11 -17
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +29 -38
- package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +6 -2
- package/ios/TrueSheetContainerView.h +1 -0
- package/ios/TrueSheetContainerView.mm +6 -0
- package/ios/TrueSheetContentView.h +1 -0
- package/ios/TrueSheetContentView.mm +2 -0
- package/ios/TrueSheetFooterView.mm +5 -0
- package/ios/TrueSheetModule.mm +11 -2
- package/ios/TrueSheetView.h +2 -0
- package/ios/TrueSheetView.mm +62 -9
- package/ios/TrueSheetViewController.h +14 -0
- package/ios/TrueSheetViewController.mm +490 -33
- package/ios/core/RNScreensEventObserver.mm +18 -1
- package/ios/core/TrueSheetDetentCalculator.h +11 -0
- package/ios/core/TrueSheetDetentCalculator.mm +17 -5
- package/ios/core/TrueSheetDimView.h +27 -0
- package/ios/core/TrueSheetDimView.mm +41 -0
- package/ios/core/TrueSheetKeyboardObserver.mm +98 -15
- package/lib/module/TrueSheet.js +88 -17
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +150 -37
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetProvider.web.js +13 -9
- package/lib/module/TrueSheetProvider.web.js.map +1 -1
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +8 -0
- package/lib/module/scrollable.js +28 -0
- package/lib/module/scrollable.js.map +1 -0
- package/lib/module/web/keyboard.js +69 -0
- package/lib/module/web/keyboard.js.map +1 -0
- package/lib/module/web/layout.js +1 -1
- package/lib/module/web/layout.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/index.js +18 -8
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +15 -10
- package/lib/module/web/vaul/use-snap-points.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +14 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +53 -2
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +7 -0
- 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/lib/typescript/src/scrollable.d.ts +2 -0
- package/lib/typescript/src/scrollable.d.ts.map +1 -0
- package/lib/typescript/src/web/keyboard.d.ts +15 -0
- package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
- package/lib/typescript/src/web/layout.d.ts +1 -0
- package/lib/typescript/src/web/layout.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +2 -1
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +8 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/types.d.ts +9 -0
- package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts +3 -2
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.tsx +91 -14
- package/src/TrueSheet.types.ts +60 -2
- package/src/TrueSheet.web.tsx +170 -41
- package/src/TrueSheetProvider.web.tsx +22 -13
- package/src/fabric/TrueSheetViewNativeComponent.ts +8 -0
- package/src/navigation/types.ts +3 -0
- package/src/scrollable.ts +25 -0
- package/src/web/keyboard.ts +79 -0
- package/src/web/layout.ts +1 -1
- package/src/web/vaul/context.ts +2 -1
- package/src/web/vaul/index.tsx +20 -5
- package/src/web/vaul/types.ts +10 -0
- package/src/web/vaul/use-snap-points.ts +19 -9
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.view.WindowManager
|
|
4
|
-
import com.facebook.react.bridge.ColorPropConverter
|
|
5
4
|
import com.facebook.react.bridge.ReadableArray
|
|
6
5
|
import com.facebook.react.bridge.ReadableMap
|
|
7
|
-
import com.facebook.react.bridge.ReadableType
|
|
8
6
|
import com.facebook.react.module.annotations.ReactModule
|
|
9
7
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
10
8
|
import com.facebook.react.uimanager.PointerEvents
|
|
@@ -20,6 +18,7 @@ import com.facebook.react.viewmanagers.TrueSheetViewManagerInterface
|
|
|
20
18
|
import com.lodev09.truesheet.core.AccessibilityOptions
|
|
21
19
|
import com.lodev09.truesheet.core.GrabberOptions
|
|
22
20
|
import com.lodev09.truesheet.events.*
|
|
21
|
+
import com.lodev09.truesheet.utils.getColorOrNull
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
24
|
* ViewManager for TrueSheetView - Fabric architecture
|
|
@@ -120,13 +119,7 @@ class TrueSheetViewManager :
|
|
|
120
119
|
override fun setDetentBackgrounds(view: TrueSheetView, value: ReadableArray?) {
|
|
121
120
|
view.setDetentBackgrounds(
|
|
122
121
|
(0 until (value?.size() ?: 0)).map { index ->
|
|
123
|
-
|
|
124
|
-
if (entry != null && entry.hasKey("color") && !entry.isNull("color")) {
|
|
125
|
-
val color = if (entry.getType("color") == ReadableType.Map) entry.getMap("color") else entry.getDouble("color")
|
|
126
|
-
ColorPropConverter.getColor(color, view.context)
|
|
127
|
-
} else {
|
|
128
|
-
null
|
|
129
|
-
}
|
|
122
|
+
value?.getMap(index)?.getColorOrNull("color", view.context)
|
|
130
123
|
}
|
|
131
124
|
)
|
|
132
125
|
}
|
|
@@ -164,7 +157,7 @@ class TrueSheetViewManager :
|
|
|
164
157
|
} else {
|
|
165
158
|
null
|
|
166
159
|
},
|
|
167
|
-
color =
|
|
160
|
+
color = options.getColorOrNull("color", view.context),
|
|
168
161
|
adaptive = if (options.hasKey("adaptive")) options.getBoolean("adaptive") else true
|
|
169
162
|
)
|
|
170
163
|
view.setGrabberOptions(grabberOptions)
|
|
@@ -209,6 +202,16 @@ class TrueSheetViewManager :
|
|
|
209
202
|
view.setDimmed(dimmed)
|
|
210
203
|
}
|
|
211
204
|
|
|
205
|
+
@ReactProp(name = "dimColor", customType = "Color")
|
|
206
|
+
override fun setDimColor(view: TrueSheetView, color: Int?) {
|
|
207
|
+
view.setDimColor(color)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@ReactProp(name = "dimOpacity", defaultDouble = -1.0)
|
|
211
|
+
override fun setDimOpacity(view: TrueSheetView, opacity: Double) {
|
|
212
|
+
view.setDimOpacity(opacity.toFloat())
|
|
213
|
+
}
|
|
214
|
+
|
|
212
215
|
@ReactProp(name = "dimmedDetentIndex", defaultInt = 0)
|
|
213
216
|
override fun setDimmedDetentIndex(view: TrueSheetView, index: Int) {
|
|
214
217
|
view.setDimmedDetentIndex(index)
|
|
@@ -250,6 +253,11 @@ class TrueSheetViewManager :
|
|
|
250
253
|
// iOS-specific prop - no-op on Android
|
|
251
254
|
}
|
|
252
255
|
|
|
256
|
+
@ReactProp(name = "foldOptions")
|
|
257
|
+
override fun setFoldOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
258
|
+
// iOS-specific prop - no-op on Android
|
|
259
|
+
}
|
|
260
|
+
|
|
253
261
|
@ReactProp(name = "insetAdjustment")
|
|
254
262
|
override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
|
|
255
263
|
view.setInsetAdjustment(insetAdjustment ?: "automatic")
|
|
@@ -22,6 +22,15 @@ class RNScreensEventObserver : EventDispatcherListener {
|
|
|
22
22
|
private var eventDispatcher: EventDispatcher? = null
|
|
23
23
|
var presenterScreenTag: Int = 0
|
|
24
24
|
|
|
25
|
+
// Screens enclosing the presenter. react-native-screens only dispatches lifecycle
|
|
26
|
+
// events for stack screens, so a presenter inside a plain screen container (e.g. a
|
|
27
|
+
// bottom tab) never receives them and an enclosing stack screen has to stand in.
|
|
28
|
+
var ancestorScreenTags: Set<Int> = emptySet()
|
|
29
|
+
|
|
30
|
+
// Only the screen that hid the sheet brings it back. An enclosing screen reappearing
|
|
31
|
+
// must not reveal a sheet whose own screen is still covered inside it.
|
|
32
|
+
private var disappearedScreenTag: Int = 0
|
|
33
|
+
|
|
25
34
|
fun startObserving(dispatcher: EventDispatcher?) {
|
|
26
35
|
if (eventDispatcher != null || dispatcher == null) return
|
|
27
36
|
|
|
@@ -35,29 +44,37 @@ class RNScreensEventObserver : EventDispatcherListener {
|
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
fun capturePresenterScreenFromView(view: View?) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
while (current != null) {
|
|
42
|
-
if (isScreenView(current)) {
|
|
43
|
-
presenterScreenTag = current.id
|
|
44
|
-
break
|
|
45
|
-
}
|
|
46
|
-
current = (current.parent as? View)
|
|
47
|
-
}
|
|
47
|
+
val tags = screenTags(view).toList()
|
|
48
|
+
presenterScreenTag = tags.firstOrNull() ?: 0
|
|
49
|
+
ancestorScreenTags = tags.drop(1).toSet()
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
override fun onEventDispatch(event: Event<*>) {
|
|
51
|
-
// Only process events for the presenter screen
|
|
52
|
-
if (presenterScreenTag == 0
|
|
53
|
+
// Only process events for the presenter screen and the screens enclosing it
|
|
54
|
+
if (presenterScreenTag == 0) return
|
|
55
|
+
if (event.viewTag != presenterScreenTag && event.viewTag !in ancestorScreenTags) return
|
|
53
56
|
|
|
54
57
|
when (event.eventName) {
|
|
55
|
-
"topWillDisappear" ->
|
|
56
|
-
|
|
58
|
+
"topWillDisappear" -> {
|
|
59
|
+
if (disappearedScreenTag != 0) return
|
|
60
|
+
disappearedScreenTag = event.viewTag
|
|
61
|
+
delegate?.presenterScreenWillDisappear()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
"topWillAppear" -> {
|
|
65
|
+
if (event.viewTag != disappearedScreenTag) return
|
|
66
|
+
disappearedScreenTag = 0
|
|
67
|
+
delegate?.presenterScreenWillAppear()
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
companion object {
|
|
61
73
|
private fun isScreenView(view: View): Boolean = view.javaClass.name == RN_SCREENS_VIEW_CLASS
|
|
74
|
+
|
|
75
|
+
// Tags of the screens enclosing the view, innermost first
|
|
76
|
+
private fun screenTags(view: View?): Sequence<Int> = generateSequence(view) { it.parent as? View }.filter(::isScreenView).map { it.id }
|
|
77
|
+
|
|
78
|
+
fun findPresenterScreenTag(view: View?): Int = screenTags(view).firstOrNull() ?: 0
|
|
62
79
|
}
|
|
63
80
|
}
|
|
@@ -40,6 +40,15 @@ class TrueSheetBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() {
|
|
|
40
40
|
hideFriction = HIDE_FRICTION
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// Material's first layout registers an IME callback that rides the sheet's translationY
|
|
44
|
+
// with the keyboard. TrueSheet positions sheets for the keyboard itself, and the callback
|
|
45
|
+
// overwrites a parent's stack translation when the keyboard rises mid-translation.
|
|
46
|
+
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
|
|
47
|
+
val handled = super.onLayoutChild(parent, child, layoutDirection)
|
|
48
|
+
ViewCompat.setWindowInsetsAnimationCallback(child, null)
|
|
49
|
+
return handled
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
// With the short threshold, settle at or below the lowest detent like
|
|
44
53
|
// Compose Material3's sheet instead of Material's half-height rule.
|
|
45
54
|
private fun releaseState(child: V, dragged: Boolean, velocityY: Float): Int? {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package com.lodev09.truesheet.core
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import android.content.res.Configuration
|
|
5
4
|
import android.graphics.Color
|
|
6
5
|
import android.graphics.Outline
|
|
7
6
|
import android.graphics.Rect
|
|
@@ -20,6 +19,7 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
20
19
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
21
20
|
import com.lodev09.truesheet.TrueSheetPlacement
|
|
22
21
|
import com.lodev09.truesheet.utils.ScreenUtils
|
|
22
|
+
import com.lodev09.truesheet.utils.isDarkMode
|
|
23
23
|
|
|
24
24
|
interface TrueSheetBottomSheetViewDelegate {
|
|
25
25
|
val isTopmostSheet: Boolean
|
|
@@ -81,14 +81,6 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
|
|
|
81
81
|
clipToPadding = false
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
override fun setTranslationY(translationY: Float) {
|
|
85
|
-
// This prevents keyboard inset animations from resetting parent sheet translation
|
|
86
|
-
if (translationY == 0f && this.translationY != 0f) {
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
super.setTranslationY(translationY)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
84
|
// =============================================================================
|
|
93
85
|
// MARK: - Layout
|
|
94
86
|
// =============================================================================
|
|
@@ -186,13 +178,8 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
|
|
|
186
178
|
}
|
|
187
179
|
}
|
|
188
180
|
|
|
189
|
-
fun getDefaultBackgroundColor(): Int
|
|
190
|
-
|
|
191
|
-
reactContext.resources.configuration.uiMode and
|
|
192
|
-
Configuration.UI_MODE_NIGHT_MASK
|
|
193
|
-
) == Configuration.UI_MODE_NIGHT_YES
|
|
194
|
-
return if (isNight) COLOR_SURFACE_CONTAINER_LOW_DARK else COLOR_SURFACE_CONTAINER_LOW_LIGHT
|
|
195
|
-
}
|
|
181
|
+
fun getDefaultBackgroundColor(): Int =
|
|
182
|
+
if (reactContext.isDarkMode) COLOR_SURFACE_CONTAINER_LOW_DARK else COLOR_SURFACE_CONTAINER_LOW_LIGHT
|
|
196
183
|
|
|
197
184
|
fun setupElevation() {
|
|
198
185
|
val value = delegate?.sheetElevation ?: DEFAULT_ELEVATION
|
|
@@ -28,6 +28,9 @@ interface TrueSheetCoordinatorLayoutDelegate {
|
|
|
28
28
|
* didn't follow, `keyboardWasVisible` whether the keyboard was up when it began.
|
|
29
29
|
*/
|
|
30
30
|
fun coordinatorLayoutDidPullDown(overdragPx: Int, keyboardWasVisible: Boolean)
|
|
31
|
+
|
|
32
|
+
// A touch that began inside the sheet ended, after the behavior handled the release
|
|
33
|
+
fun coordinatorLayoutDidEndTouch()
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -86,6 +89,10 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
86
89
|
private var pullOverdrag = 0f
|
|
87
90
|
private var pullKeyboardVisible = false
|
|
88
91
|
|
|
92
|
+
// A touch that began inside the sheet is still down
|
|
93
|
+
val isTouchingSheet: Boolean
|
|
94
|
+
get() = pullTracking
|
|
95
|
+
|
|
89
96
|
init {
|
|
90
97
|
layoutParams = LayoutParams(
|
|
91
98
|
LayoutParams.MATCH_PARENT,
|
|
@@ -150,10 +157,16 @@ class TrueSheetCoordinatorLayout(context: Context) :
|
|
|
150
157
|
if (dy > dx && pullOverdrag > PULL_DOWN_THRESHOLD_DP.dpToPx()) {
|
|
151
158
|
delegate?.coordinatorLayoutDidPullDown(pullOverdrag.toInt(), pullKeyboardVisible)
|
|
152
159
|
}
|
|
160
|
+
delegate?.coordinatorLayoutDidEndTouch()
|
|
153
161
|
return handled
|
|
154
162
|
}
|
|
155
163
|
|
|
156
|
-
MotionEvent.ACTION_CANCEL -> pullTracking
|
|
164
|
+
MotionEvent.ACTION_CANCEL -> if (pullTracking) {
|
|
165
|
+
pullTracking = false
|
|
166
|
+
val handled = super.dispatchTouchEvent(ev)
|
|
167
|
+
delegate?.coordinatorLayoutDidEndTouch()
|
|
168
|
+
return handled
|
|
169
|
+
}
|
|
157
170
|
}
|
|
158
171
|
return super.dispatchTouchEvent(ev)
|
|
159
172
|
}
|
|
@@ -34,10 +34,6 @@ class TrueSheetDimView(private val reactContext: ThemedReactContext) :
|
|
|
34
34
|
View(reactContext),
|
|
35
35
|
ReactPointerEventsView {
|
|
36
36
|
|
|
37
|
-
companion object {
|
|
38
|
-
private const val MAX_ALPHA = 0.5f
|
|
39
|
-
}
|
|
40
|
-
|
|
41
37
|
var delegate: TrueSheetDimViewDelegate? = null
|
|
42
38
|
|
|
43
39
|
private var targetView: ViewGroup? = null
|
|
@@ -48,6 +44,10 @@ class TrueSheetDimView(private val reactContext: ThemedReactContext) :
|
|
|
48
44
|
private val blockGestures: Boolean
|
|
49
45
|
get() = alpha > 0f
|
|
50
46
|
|
|
47
|
+
fun setDimColor(color: Int) {
|
|
48
|
+
setBackgroundColor(color)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
51
|
init {
|
|
52
52
|
layoutParams = ViewGroup.LayoutParams(
|
|
53
53
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
@@ -109,30 +109,24 @@ class TrueSheetDimView(private val reactContext: ThemedReactContext) :
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// =============================================================================
|
|
112
|
-
// MARK: -
|
|
112
|
+
// MARK: - Progress Calculation
|
|
113
113
|
// =============================================================================
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
/**
|
|
116
|
+
* How far the sheet is into its dimmed range, from 0 (undimmed) to 1 (fully dimmed).
|
|
117
|
+
*/
|
|
118
|
+
fun calculateProgress(sheetTop: Int, dimmedDetentIndex: Int, getSheetTopForDetentIndex: (Int) -> Int): Float {
|
|
116
119
|
val realHeight = ScreenUtils.getRealScreenHeight(reactContext)
|
|
117
120
|
val dimmedDetentTop = getSheetTopForDetentIndex(dimmedDetentIndex)
|
|
118
121
|
val belowDimmedTop = if (dimmedDetentIndex > 0) getSheetTopForDetentIndex(dimmedDetentIndex - 1) else realHeight
|
|
119
122
|
|
|
120
123
|
return when {
|
|
121
|
-
sheetTop <= dimmedDetentTop ->
|
|
122
|
-
|
|
124
|
+
sheetTop <= dimmedDetentTop -> 1f
|
|
123
125
|
sheetTop >= belowDimmedTop -> 0f
|
|
124
|
-
|
|
125
|
-
else -> {
|
|
126
|
-
val progress = 1f - (sheetTop - dimmedDetentTop).toFloat() / (belowDimmedTop - dimmedDetentTop)
|
|
127
|
-
(progress * MAX_ALPHA).coerceIn(0f, MAX_ALPHA)
|
|
128
|
-
}
|
|
126
|
+
else -> (1f - (sheetTop - dimmedDetentTop).toFloat() / (belowDimmedTop - dimmedDetentTop)).coerceIn(0f, 1f)
|
|
129
127
|
}
|
|
130
128
|
}
|
|
131
129
|
|
|
132
|
-
fun interpolateAlpha(sheetTop: Int, dimmedDetentIndex: Int, getSheetTopForDetentIndex: (Int) -> Int) {
|
|
133
|
-
alpha = calculateAlpha(sheetTop, dimmedDetentIndex, getSheetTopForDetentIndex)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
130
|
// =============================================================================
|
|
137
131
|
// MARK: - Touch Handling
|
|
138
132
|
// =============================================================================
|
|
@@ -2,7 +2,6 @@ package com.lodev09.truesheet.core
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.content.res.Configuration
|
|
6
5
|
import android.graphics.Color
|
|
7
6
|
import android.graphics.drawable.GradientDrawable
|
|
8
7
|
import android.os.Bundle
|
|
@@ -13,6 +12,7 @@ import android.widget.FrameLayout
|
|
|
13
12
|
import androidx.core.graphics.ColorUtils
|
|
14
13
|
import androidx.core.view.ViewCompat
|
|
15
14
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
15
|
+
import com.lodev09.truesheet.utils.isDarkMode
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Options for customizing the grabber appearance.
|
|
@@ -165,9 +165,7 @@ class TrueSheetGrabberView(
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
private fun getAdaptiveColor(baseColor: Int? = null): Int {
|
|
168
|
-
val
|
|
169
|
-
val isDarkMode = nightMode == Configuration.UI_MODE_NIGHT_YES
|
|
170
|
-
val modeColor = if (isDarkMode) Color.WHITE else Color.BLACK
|
|
168
|
+
val modeColor = if (context.isDarkMode) Color.WHITE else Color.BLACK
|
|
171
169
|
|
|
172
170
|
return if (baseColor != null) {
|
|
173
171
|
// Blend user color with mode color for adaptive effect
|
|
@@ -12,16 +12,6 @@ object TrueSheetStackManager {
|
|
|
12
12
|
|
|
13
13
|
private val presentedSheetStack = mutableListOf<TrueSheetView>()
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* Gets the parent sheet at the given index, if any.
|
|
17
|
-
* Only returns a parent if it's in the same container.
|
|
18
|
-
* Must be called within synchronized block.
|
|
19
|
-
*/
|
|
20
|
-
private fun getParentSheetAt(index: Int, rootContainer: ViewGroup?): TrueSheetView? {
|
|
21
|
-
if (index <= 0) return null
|
|
22
|
-
return presentedSheetStack[index - 1].takeIf { it.rootContainerView == rootContainer }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
15
|
/**
|
|
26
16
|
* Returns the topmost presented and visible sheet, skipping sheets that are dismissing.
|
|
27
17
|
* A dismissing sheet can't parent a new one: its dim would suppress the child's own dim
|
|
@@ -33,6 +23,22 @@ object TrueSheetStackManager {
|
|
|
33
23
|
it.viewController.run { isPresented && isSheetVisible && !isBeingDismissed }
|
|
34
24
|
}
|
|
35
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Returns the topmost sheet hidden by screen navigation that was presented from the same
|
|
28
|
+
* screen as the given sheet. Sheets presented on the covering screen must not parent to it,
|
|
29
|
+
* or the hidden sheet would receive focus and blur events while it can't be seen.
|
|
30
|
+
* Must be called within synchronized block.
|
|
31
|
+
*/
|
|
32
|
+
private fun findScreenHiddenSheet(sheetView: TrueSheetView): TrueSheetView? {
|
|
33
|
+
val screenTag = RNScreensEventObserver.findPresenterScreenTag(sheetView)
|
|
34
|
+
if (screenTag == 0) return null
|
|
35
|
+
|
|
36
|
+
return presentedSheetStack.lastOrNull {
|
|
37
|
+
it.viewController.run { isPresented && wasHiddenByScreen && !isBeingDismissed } &&
|
|
38
|
+
it.screensEventObserver?.presenterScreenTag == screenTag
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* Registers a sheet in the stack and returns its parent sheet if any.
|
|
38
44
|
* Only returns a parent if it's in the same container (e.g., same Screen).
|
|
@@ -40,7 +46,8 @@ object TrueSheetStackManager {
|
|
|
40
46
|
@JvmStatic
|
|
41
47
|
fun registerSheet(sheetView: TrueSheetView): TrueSheetView? {
|
|
42
48
|
synchronized(presentedSheetStack) {
|
|
43
|
-
val parentSheet = findTopmostSheet()
|
|
49
|
+
val parentSheet = (findTopmostSheet() ?: findScreenHiddenSheet(sheetView))
|
|
50
|
+
?.takeIf { it.rootContainerView == sheetView.rootContainerView }
|
|
44
51
|
|
|
45
52
|
if (!presentedSheetStack.contains(sheetView)) {
|
|
46
53
|
presentedSheetStack.add(sheetView)
|
|
@@ -62,24 +69,20 @@ object TrueSheetStackManager {
|
|
|
62
69
|
|
|
63
70
|
/**
|
|
64
71
|
* Updates parent sheet translation based on the child sheet's position.
|
|
65
|
-
* Only affects parent sheets in the same container.
|
|
66
72
|
*/
|
|
67
73
|
@JvmStatic
|
|
68
74
|
fun updateParentTranslation(sheetView: TrueSheetView) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
sheetView.viewController.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
val childSheetTop = maxOf(childMinSheetTop, childCurrentSheetTop)
|
|
81
|
-
parentSheet.updateTranslationForChild(childSheetTop)
|
|
82
|
-
}
|
|
75
|
+
val parentSheet = sheetView.viewController.parentSheetView ?: return
|
|
76
|
+
|
|
77
|
+
// Post to ensure layout is complete before reading position
|
|
78
|
+
sheetView.viewController.post {
|
|
79
|
+
val childMinSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(0)
|
|
80
|
+
val childCurrentSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(
|
|
81
|
+
sheetView.viewController.currentDetentIndex
|
|
82
|
+
)
|
|
83
|
+
// Cap to minimum detent position
|
|
84
|
+
val childSheetTop = maxOf(childMinSheetTop, childCurrentSheetTop)
|
|
85
|
+
parentSheet.updateTranslationForChild(childSheetTop)
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -113,18 +116,6 @@ object TrueSheetStackManager {
|
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
/**
|
|
117
|
-
* Gets the parent sheet of the given sheet, if any.
|
|
118
|
-
* Only returns a parent if it's in the same container.
|
|
119
|
-
*/
|
|
120
|
-
@JvmStatic
|
|
121
|
-
fun getParentSheet(sheetView: TrueSheetView): TrueSheetView? {
|
|
122
|
-
synchronized(presentedSheetStack) {
|
|
123
|
-
val index = presentedSheetStack.indexOf(sheetView)
|
|
124
|
-
return getParentSheetAt(index, sheetView.rootContainerView)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
119
|
/**
|
|
129
120
|
* Returns true if the given sheet is the topmost presented sheet in its container.
|
|
130
121
|
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package com.lodev09.truesheet.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
5
|
+
import com.facebook.react.bridge.ColorPropConverter
|
|
6
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.bridge.ReadableType
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves a processed color (number or `PlatformColor` map) at [key], or null when missing.
|
|
11
|
+
*/
|
|
12
|
+
fun ReadableMap.getColorOrNull(key: String, context: Context): Int? {
|
|
13
|
+
if (!hasKey(key) || isNull(key)) return null
|
|
14
|
+
val value = if (getType(key) == ReadableType.Map) getMap(key) else getDouble(key)
|
|
15
|
+
return ColorPropConverter.getColor(value, context)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
val Context.isDarkMode: Boolean
|
|
19
|
+
get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#include "TrueSheetContentViewShadowNode.h"
|
|
2
2
|
|
|
3
|
-
#include <folly/ScopeGuard.h>
|
|
4
3
|
#include <react/renderer/components/view/conversions.h>
|
|
5
4
|
#include <react/renderer/core/ComponentDescriptor.h>
|
|
6
5
|
#include <react/renderer/core/LayoutConstraints.h>
|
|
@@ -95,7 +94,12 @@ void TrueSheetContentViewShadowNode::updateNaturalHeightIfNeeded(const LayoutCon
|
|
|
95
94
|
if (contentChanged_ || !measurement_ || !(measurement_->constraints == constraints) ||
|
|
96
95
|
!(measurement_->context == measurementContext) || measurement_->errata != errata) {
|
|
97
96
|
gIsMeasuringNaturalHeight = true;
|
|
98
|
-
|
|
97
|
+
// Not folly::makeGuard: its terminate() isn't linked into RN < 0.86 on Android.
|
|
98
|
+
struct RestoreMeasurement {
|
|
99
|
+
~RestoreMeasurement() {
|
|
100
|
+
gIsMeasuringNaturalHeight = false;
|
|
101
|
+
}
|
|
102
|
+
} restoreMeasurement;
|
|
99
103
|
auto measurementNode =
|
|
100
104
|
measurement_ ? cloneForMeasurement(*this, *measurementSourceChildren_, measurement_->root->getChildren())
|
|
101
105
|
: cloneForMeasurement(*this, {}, {});
|
|
@@ -287,6 +287,12 @@ using namespace facebook::react;
|
|
|
287
287
|
[self.delegate containerViewScrollViewDidChange];
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
// Footer controls can mount, unmount, or hide deep in its subtree without
|
|
291
|
+
// passing through its own mount hooks or changing its size.
|
|
292
|
+
- (void)contentViewDidMountTransaction {
|
|
293
|
+
[self.delegate containerViewDidMountTransaction];
|
|
294
|
+
}
|
|
295
|
+
|
|
290
296
|
#pragma mark - TrueSheetHeaderViewDelegate
|
|
291
297
|
|
|
292
298
|
- (void)headerViewDidChangeSize:(CGSize)newSize {
|
|
@@ -181,6 +181,8 @@ using namespace facebook::react;
|
|
|
181
181
|
// Layout has settled for the whole tree — the footer overlap (sheet height,
|
|
182
182
|
// scroll view frame) and the keyboard inset are current here
|
|
183
183
|
[self updateBottomInset];
|
|
184
|
+
|
|
185
|
+
[self.delegate contentViewDidMountTransaction];
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
#pragma mark - Scrollable
|
|
@@ -123,6 +123,11 @@ using namespace facebook::react;
|
|
|
123
123
|
|
|
124
124
|
- (void)collectAccessibilityElementsFromView:(UIView *)view into:(NSMutableArray *)elements {
|
|
125
125
|
for (UIView *subview in view.subviews) {
|
|
126
|
+
// Fabric hides `display: none` views instead of unmounting them
|
|
127
|
+
if (subview.hidden) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
126
131
|
if (subview.isAccessibilityElement || subview.accessibilityLabel || subview.accessibilityIdentifier) {
|
|
127
132
|
[elements addObject:subview];
|
|
128
133
|
} else if (subview.accessibilityElements.count > 0) {
|
package/ios/TrueSheetModule.mm
CHANGED
|
@@ -149,11 +149,17 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
149
149
|
- (void)dismissAll:(BOOL)animated resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
150
150
|
RCTExecuteOnMainQueue(^{
|
|
151
151
|
@synchronized(viewRegistry) {
|
|
152
|
+
// Drop presents still waiting on another sheet's dismissal
|
|
153
|
+
for (TrueSheetView *view in viewRegistry.objectEnumerator) {
|
|
154
|
+
[view cancelPendingPresent];
|
|
155
|
+
}
|
|
156
|
+
|
|
152
157
|
// Find the root presented sheet (one without a parent TrueSheet)
|
|
153
158
|
TrueSheetView *rootSheet = nil;
|
|
154
159
|
|
|
155
160
|
for (TrueSheetView *view in viewRegistry.objectEnumerator) {
|
|
156
|
-
|
|
161
|
+
// A sheet still animating in counts too — dismissAnimated waits for it to land
|
|
162
|
+
if (!view.viewController.isPresented && !view.viewController.isBeingPresented) {
|
|
157
163
|
continue;
|
|
158
164
|
}
|
|
159
165
|
|
|
@@ -171,7 +177,10 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
171
177
|
return;
|
|
172
178
|
}
|
|
173
179
|
|
|
174
|
-
|
|
180
|
+
// Mid-present, the deferred dismiss tracks its own position like a regular dismiss
|
|
181
|
+
if (rootSheet.viewController.isPresented) {
|
|
182
|
+
[rootSheet emitDismissedPosition];
|
|
183
|
+
}
|
|
175
184
|
[rootSheet dismissAnimated:animated
|
|
176
185
|
completion:^(BOOL success, NSError *_Nullable error) {
|
|
177
186
|
if (success) {
|
package/ios/TrueSheetView.h
CHANGED
|
@@ -34,6 +34,8 @@ typedef void (^TrueSheetCompletionBlock)(BOOL success, NSError *_Nullable error)
|
|
|
34
34
|
|
|
35
35
|
- (void)emitDismissedPosition;
|
|
36
36
|
|
|
37
|
+
- (BOOL)cancelPendingPresent;
|
|
38
|
+
|
|
37
39
|
- (void)resizeToIndex:(NSInteger)index completion:(nullable TrueSheetCompletionBlock)completion;
|
|
38
40
|
|
|
39
41
|
- (void)dismissStackAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion;
|