react-native-screens 4.0.0-beta.1 → 4.0.0-beta.10
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/fabric/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +61 -0
- package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +14 -0
- package/android/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt +2 -4
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +46 -7
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +24 -9
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +37 -17
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +1 -2
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigShadowNode.kt +25 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +18 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +6 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/ext/ViewExt.kt +17 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/PaddingBundle.kt +8 -0
- package/android/src/main/jni/rnscreens.h +2 -0
- package/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledHeaderConfigViewGroup.kt +39 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigComponentDescriptor.h +44 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigShadowNode.cpp +8 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigShadowNode.h +32 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigState.cpp +23 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderConfigState.h +50 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewComponentDescriptor.h +27 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewShadowNode.cpp +8 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewShadowNode.h +32 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewState.cpp +15 -0
- package/common/cpp/react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewState.h +40 -0
- package/ios/RNSConvert.mm +0 -1
- package/ios/RNSScreen.h +9 -0
- package/ios/RNSScreen.mm +37 -8
- package/ios/RNSScreenStack.mm +79 -1
- package/ios/RNSScreenStackHeaderConfig.h +63 -0
- package/ios/RNSScreenStackHeaderConfig.mm +126 -9
- package/ios/RNSScreenStackHeaderSubview.mm +26 -0
- package/ios/utils/UINavigationBar+RNSUtility.h +37 -0
- package/ios/utils/UINavigationBar+RNSUtility.mm +44 -0
- package/ios/utils/UIView+RNSUtility.mm +0 -1
- package/lib/commonjs/components/Screen.js +58 -7
- package/lib/commonjs/components/Screen.js.map +1 -1
- package/lib/commonjs/components/ScreenContentWrapper.web.js +11 -0
- package/lib/commonjs/components/ScreenContentWrapper.web.js.map +1 -0
- package/lib/commonjs/components/ScreenFooter.web.js +11 -0
- package/lib/commonjs/components/ScreenFooter.web.js.map +1 -0
- package/lib/commonjs/components/ScreenStackHeaderConfig.js +53 -17
- package/lib/commonjs/components/ScreenStackHeaderConfig.js.map +1 -1
- package/lib/commonjs/components/helpers/usePrevious.js +15 -0
- package/lib/commonjs/components/helpers/usePrevious.js.map +1 -0
- package/lib/commonjs/fabric/ModalScreenNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/ScreenNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js +3 -1
- package/lib/commonjs/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js +3 -1
- package/lib/commonjs/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
- package/lib/commonjs/native-stack/views/NativeStackView.js +4 -10
- package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/module/components/Screen.js +58 -7
- package/lib/module/components/Screen.js.map +1 -1
- package/lib/module/components/ScreenContentWrapper.web.js +5 -0
- package/lib/module/components/ScreenContentWrapper.web.js.map +1 -0
- package/lib/module/components/ScreenFooter.web.js +5 -0
- package/lib/module/components/ScreenFooter.web.js.map +1 -0
- package/lib/module/components/ScreenStackHeaderConfig.js +50 -16
- package/lib/module/components/ScreenStackHeaderConfig.js.map +1 -1
- package/lib/module/components/helpers/usePrevious.js +9 -0
- package/lib/module/components/helpers/usePrevious.js.map +1 -0
- package/lib/module/fabric/ModalScreenNativeComponent.js.map +1 -1
- package/lib/module/fabric/ScreenNativeComponent.js.map +1 -1
- package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js +3 -1
- package/lib/module/fabric/ScreenStackHeaderConfigNativeComponent.js.map +1 -1
- package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js +3 -1
- package/lib/module/fabric/ScreenStackHeaderSubviewNativeComponent.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.js +4 -10
- package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/typescript/components/Screen.d.ts +5 -1
- package/lib/typescript/components/Screen.d.ts.map +1 -1
- package/lib/typescript/components/ScreenContentWrapper.web.d.ts +5 -0
- package/lib/typescript/components/ScreenContentWrapper.web.d.ts.map +1 -0
- package/lib/typescript/components/ScreenFooter.web.d.ts +5 -0
- package/lib/typescript/components/ScreenFooter.web.d.ts.map +1 -0
- package/lib/typescript/components/ScreenStackHeaderConfig.d.ts +1 -1
- package/lib/typescript/components/ScreenStackHeaderConfig.d.ts.map +1 -1
- package/lib/typescript/components/helpers/usePrevious.d.ts +2 -0
- package/lib/typescript/components/helpers/usePrevious.d.ts.map +1 -0
- package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts +1 -0
- package/lib/typescript/fabric/ModalScreenNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/ScreenNativeComponent.d.ts +1 -1
- package/lib/typescript/fabric/ScreenNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/native-stack/types.d.ts +2 -3
- package/lib/typescript/native-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +26 -9
- package/lib/typescript/types.d.ts.map +1 -1
- package/native-stack/README.md +3 -2
- package/package.json +1 -1
- package/src/components/Screen.tsx +97 -12
- package/src/components/ScreenContentWrapper.web.tsx +6 -0
- package/src/components/ScreenFooter.web.tsx +6 -0
- package/src/components/ScreenStackHeaderConfig.tsx +59 -26
- package/src/components/helpers/usePrevious.tsx +11 -0
- package/src/fabric/ModalScreenNativeComponent.ts +1 -0
- package/src/fabric/ScreenNativeComponent.ts +0 -1
- package/src/fabric/ScreenStackHeaderConfigNativeComponent.ts +3 -1
- package/src/fabric/ScreenStackHeaderSubviewNativeComponent.ts +3 -1
- package/src/native-stack/types.tsx +2 -3
- package/src/native-stack/views/NativeStackView.tsx +4 -10
- package/src/types.tsx +29 -13
- package/windows/RNScreens/Screen.h +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import androidx.annotation.UiThread
|
|
6
|
+
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import com.facebook.react.bridge.WritableNativeMap
|
|
8
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
9
|
+
import com.facebook.react.uimanager.StateWrapper
|
|
10
|
+
import kotlin.math.abs
|
|
11
|
+
|
|
12
|
+
abstract class FabricEnabledHeaderConfigViewGroup(
|
|
13
|
+
context: Context?,
|
|
14
|
+
) : ViewGroup(context) {
|
|
15
|
+
private var mStateWrapper: StateWrapper? = null
|
|
16
|
+
|
|
17
|
+
private var lastPaddingStart = 0f
|
|
18
|
+
private var lastPaddingEnd = 0f
|
|
19
|
+
|
|
20
|
+
fun setStateWrapper(wrapper: StateWrapper?) {
|
|
21
|
+
mStateWrapper = wrapper
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fun updatePaddingsFabric(
|
|
25
|
+
paddingStart: Int,
|
|
26
|
+
paddingEnd: Int,
|
|
27
|
+
) {
|
|
28
|
+
updateState(paddingStart, paddingEnd)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@UiThread
|
|
32
|
+
fun updateState(
|
|
33
|
+
paddingStart: Int,
|
|
34
|
+
paddingEnd: Int,
|
|
35
|
+
) {
|
|
36
|
+
val paddingStartDip: Float = PixelUtil.toDIPFromPixel(paddingStart.toFloat())
|
|
37
|
+
val paddingEndDip: Float = PixelUtil.toDIPFromPixel(paddingEnd.toFloat())
|
|
38
|
+
|
|
39
|
+
// Check incoming state values. If they're already the correct value, return early to prevent
|
|
40
|
+
// infinite UpdateState/SetState loop.
|
|
41
|
+
if (abs(lastPaddingStart - paddingStart) < DELTA &&
|
|
42
|
+
abs(lastPaddingEnd - paddingEnd) < DELTA
|
|
43
|
+
) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
lastPaddingStart = paddingStartDip
|
|
48
|
+
lastPaddingEnd = paddingEndDip
|
|
49
|
+
|
|
50
|
+
val map: WritableMap =
|
|
51
|
+
WritableNativeMap().apply {
|
|
52
|
+
putDouble("paddingStart", paddingStartDip.toDouble())
|
|
53
|
+
putDouble("paddingEnd", paddingEndDip.toDouble())
|
|
54
|
+
}
|
|
55
|
+
mStateWrapper?.updateState(map)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
companion object {
|
|
59
|
+
private const val DELTA = 0.9f
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -48,4 +48,18 @@ open class CustomToolbar(
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
override fun onLayout(
|
|
53
|
+
changed: Boolean,
|
|
54
|
+
l: Int,
|
|
55
|
+
t: Int,
|
|
56
|
+
r: Int,
|
|
57
|
+
b: Int,
|
|
58
|
+
) {
|
|
59
|
+
super.onLayout(changed, l, t, r, b)
|
|
60
|
+
|
|
61
|
+
// our children are already laid out
|
|
62
|
+
val contentInsetStart = if (navigationIcon != null) contentInsetStartWithNavigation else contentInsetStart
|
|
63
|
+
config.updatePaddingsFabric(contentInsetStart, contentInsetEnd)
|
|
64
|
+
}
|
|
51
65
|
}
|
|
@@ -57,10 +57,8 @@ object InsetsObserverProxy : OnApplyWindowInsetsListener {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
fun
|
|
61
|
-
|
|
62
|
-
ViewCompat.setOnApplyWindowInsetsListener(it, null)
|
|
63
|
-
}
|
|
60
|
+
fun unregisterOnView(view: View) {
|
|
61
|
+
ViewCompat.setOnApplyWindowInsetsListener(view, null)
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
private fun getObservedView(): View? = eventSourceView.get()
|
|
@@ -16,13 +16,18 @@ import androidx.fragment.app.Fragment
|
|
|
16
16
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
17
17
|
import com.facebook.react.bridge.GuardedRunnable
|
|
18
18
|
import com.facebook.react.bridge.ReactContext
|
|
19
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
19
20
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
20
21
|
import com.facebook.react.uimanager.UIManagerModule
|
|
21
22
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
22
|
-
import com.facebook.react.views.scroll.ReactScrollView
|
|
23
23
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
24
|
+
import com.google.android.material.shape.CornerFamily
|
|
25
|
+
import com.google.android.material.shape.MaterialShapeDrawable
|
|
26
|
+
import com.google.android.material.shape.ShapeAppearanceModel
|
|
24
27
|
import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
|
|
25
28
|
import com.swmansion.rnscreens.events.SheetDetentChangedEvent
|
|
29
|
+
import com.swmansion.rnscreens.ext.isInsideScrollViewWithRemoveClippedSubviews
|
|
30
|
+
import java.lang.ref.WeakReference
|
|
26
31
|
|
|
27
32
|
@SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
|
|
28
33
|
class Screen(
|
|
@@ -32,6 +37,8 @@ class Screen(
|
|
|
32
37
|
val fragment: Fragment?
|
|
33
38
|
get() = fragmentWrapper?.fragment
|
|
34
39
|
|
|
40
|
+
var contentWrapper = WeakReference<ScreenContentWrapper>(null)
|
|
41
|
+
|
|
35
42
|
val sheetBehavior: BottomSheetBehavior<Screen>?
|
|
36
43
|
get() = (layoutParams as? CoordinatorLayout.LayoutParams)?.behavior as? BottomSheetBehavior<Screen>
|
|
37
44
|
|
|
@@ -54,10 +61,16 @@ class Screen(
|
|
|
54
61
|
|
|
55
62
|
// Props for controlling modal presentation
|
|
56
63
|
var isSheetGrabberVisible: Boolean = false
|
|
64
|
+
|
|
65
|
+
// corner radius must be updated after all props prop updates from a single transaction
|
|
66
|
+
// have been applied, because it depends on the presentation type.
|
|
67
|
+
private var shouldUpdateSheetCornerRadius = false
|
|
57
68
|
var sheetCornerRadius: Float = 0F
|
|
58
69
|
set(value) {
|
|
59
|
-
field
|
|
60
|
-
|
|
70
|
+
if (field != value) {
|
|
71
|
+
field = value
|
|
72
|
+
shouldUpdateSheetCornerRadius = true
|
|
73
|
+
}
|
|
61
74
|
}
|
|
62
75
|
var sheetExpandsWhenScrolledToEdge: Boolean = true
|
|
63
76
|
|
|
@@ -118,6 +131,7 @@ class Screen(
|
|
|
118
131
|
|
|
119
132
|
fun registerLayoutCallbackForWrapper(wrapper: ScreenContentWrapper) {
|
|
120
133
|
wrapper.delegate = this
|
|
134
|
+
this.contentWrapper = WeakReference(wrapper)
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
|
@@ -224,6 +238,9 @@ class Screen(
|
|
|
224
238
|
if (activityState == this.activityState) {
|
|
225
239
|
return
|
|
226
240
|
}
|
|
241
|
+
if (container is ScreenStack && this.activityState != null && activityState < this.activityState!!) {
|
|
242
|
+
throw IllegalStateException("[RNScreens] activityState can only progress in NativeStack")
|
|
243
|
+
}
|
|
227
244
|
this.activityState = activityState
|
|
228
245
|
container?.notifyChildUpdate()
|
|
229
246
|
}
|
|
@@ -383,10 +400,10 @@ class Screen(
|
|
|
383
400
|
}
|
|
384
401
|
if (child is ViewGroup) {
|
|
385
402
|
// The children are miscounted when there's a FlatList with
|
|
386
|
-
//
|
|
403
|
+
// removeClippedSubviews set to true (default).
|
|
387
404
|
// We add a simple view for each item in the list to make it work as expected.
|
|
388
|
-
// See https://github.com/software-mansion/react-native-screens/
|
|
389
|
-
if (
|
|
405
|
+
// See https://github.com/software-mansion/react-native-screens/pull/2383
|
|
406
|
+
if (child.isInsideScrollViewWithRemoveClippedSubviews()) {
|
|
390
407
|
for (j in 0 until child.childCount) {
|
|
391
408
|
child.addView(View(context))
|
|
392
409
|
}
|
|
@@ -420,6 +437,29 @@ class Screen(
|
|
|
420
437
|
)
|
|
421
438
|
}
|
|
422
439
|
|
|
440
|
+
internal fun onFinalizePropsUpdate() {
|
|
441
|
+
if (shouldUpdateSheetCornerRadius) {
|
|
442
|
+
shouldUpdateSheetCornerRadius = false
|
|
443
|
+
onSheetCornerRadiusChange()
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
internal fun onSheetCornerRadiusChange() {
|
|
448
|
+
if (stackPresentation !== StackPresentation.FORM_SHEET || background == null) {
|
|
449
|
+
return
|
|
450
|
+
}
|
|
451
|
+
(background as? MaterialShapeDrawable?)?.let {
|
|
452
|
+
val resolvedCornerRadius = PixelUtil.toDIPFromPixel(sheetCornerRadius)
|
|
453
|
+
it.shapeAppearanceModel =
|
|
454
|
+
ShapeAppearanceModel
|
|
455
|
+
.Builder()
|
|
456
|
+
.apply {
|
|
457
|
+
setTopLeftCorner(CornerFamily.ROUNDED, resolvedCornerRadius)
|
|
458
|
+
setTopRightCorner(CornerFamily.ROUNDED, resolvedCornerRadius)
|
|
459
|
+
}.build()
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
423
463
|
enum class StackPresentation {
|
|
424
464
|
PUSH,
|
|
425
465
|
MODAL,
|
|
@@ -435,7 +475,6 @@ class Screen(
|
|
|
435
475
|
SLIDE_FROM_RIGHT,
|
|
436
476
|
SLIDE_FROM_LEFT,
|
|
437
477
|
FADE_FROM_BOTTOM,
|
|
438
|
-
IOS,
|
|
439
478
|
IOS_FROM_RIGHT,
|
|
440
479
|
IOS_FROM_LEFT,
|
|
441
480
|
}
|
|
@@ -104,7 +104,7 @@ class ScreenStack(
|
|
|
104
104
|
|
|
105
105
|
for (i in screenWrappers.indices.reversed()) {
|
|
106
106
|
val screenWrapper = getScreenFragmentWrapperAt(i)
|
|
107
|
-
if (!dismissedWrappers.contains(screenWrapper)) {
|
|
107
|
+
if (!dismissedWrappers.contains(screenWrapper) && screenWrapper.screen.activityState !== Screen.ActivityState.INACTIVE) {
|
|
108
108
|
if (newTop == null) {
|
|
109
109
|
newTop = screenWrapper
|
|
110
110
|
} else {
|
|
@@ -182,9 +182,16 @@ class ScreenStack(
|
|
|
182
182
|
R.anim.rns_no_animation_medium,
|
|
183
183
|
)
|
|
184
184
|
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350)
|
|
185
|
-
StackAnimation.
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
StackAnimation.IOS_FROM_RIGHT ->
|
|
186
|
+
it.setCustomAnimations(
|
|
187
|
+
R.anim.rns_ios_from_right_foreground_open,
|
|
188
|
+
R.anim.rns_ios_from_right_background_open,
|
|
189
|
+
)
|
|
190
|
+
StackAnimation.IOS_FROM_LEFT ->
|
|
191
|
+
it.setCustomAnimations(
|
|
192
|
+
R.anim.rns_ios_from_left_foreground_open,
|
|
193
|
+
R.anim.rns_ios_from_left_background_open,
|
|
194
|
+
)
|
|
188
195
|
}
|
|
189
196
|
} else {
|
|
190
197
|
when (stackAnimation) {
|
|
@@ -222,9 +229,16 @@ class ScreenStack(
|
|
|
222
229
|
R.anim.rns_slide_out_to_bottom,
|
|
223
230
|
)
|
|
224
231
|
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
|
|
225
|
-
StackAnimation.
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
StackAnimation.IOS_FROM_RIGHT ->
|
|
233
|
+
it.setCustomAnimations(
|
|
234
|
+
R.anim.rns_ios_from_right_background_close,
|
|
235
|
+
R.anim.rns_ios_from_right_foreground_close,
|
|
236
|
+
)
|
|
237
|
+
StackAnimation.IOS_FROM_LEFT ->
|
|
238
|
+
it.setCustomAnimations(
|
|
239
|
+
R.anim.rns_ios_from_left_background_close,
|
|
240
|
+
R.anim.rns_ios_from_left_foreground_close,
|
|
241
|
+
)
|
|
228
242
|
}
|
|
229
243
|
}
|
|
230
244
|
}
|
|
@@ -260,7 +274,9 @@ class ScreenStack(
|
|
|
260
274
|
break
|
|
261
275
|
}
|
|
262
276
|
// detach all screens that should not be visible
|
|
263
|
-
if (fragmentWrapper !== newTop && !dismissedWrappers.contains(fragmentWrapper))
|
|
277
|
+
if ((fragmentWrapper !== newTop && !dismissedWrappers.contains(fragmentWrapper)) ||
|
|
278
|
+
fragmentWrapper.screen.activityState === Screen.ActivityState.INACTIVE
|
|
279
|
+
) {
|
|
264
280
|
it.remove(fragmentWrapper.fragment)
|
|
265
281
|
}
|
|
266
282
|
}
|
|
@@ -417,7 +433,6 @@ class ScreenStack(
|
|
|
417
433
|
Build.VERSION.SDK_INT >= 33 ||
|
|
418
434
|
fragmentWrapper.screen.stackAnimation === StackAnimation.SLIDE_FROM_BOTTOM ||
|
|
419
435
|
fragmentWrapper.screen.stackAnimation === StackAnimation.FADE_FROM_BOTTOM ||
|
|
420
|
-
fragmentWrapper.screen.stackAnimation === StackAnimation.IOS ||
|
|
421
436
|
fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_RIGHT ||
|
|
422
437
|
fragmentWrapper.screen.stackAnimation === StackAnimation.IOS_FROM_LEFT
|
|
423
438
|
}
|
|
@@ -142,7 +142,11 @@ class ScreenStackFragment :
|
|
|
142
142
|
// once it is hidden by user gesture.
|
|
143
143
|
private val bottomSheetStateCallback =
|
|
144
144
|
object : BottomSheetCallback() {
|
|
145
|
-
private var lastStableState: Int =
|
|
145
|
+
private var lastStableState: Int =
|
|
146
|
+
SheetUtils.sheetStateFromDetentIndex(
|
|
147
|
+
screen.sheetInitialDetentIndex,
|
|
148
|
+
screen.sheetDetents.count(),
|
|
149
|
+
)
|
|
146
150
|
|
|
147
151
|
override fun onStateChanged(
|
|
148
152
|
bottomSheet: View,
|
|
@@ -150,9 +154,20 @@ class ScreenStackFragment :
|
|
|
150
154
|
) {
|
|
151
155
|
if (SheetUtils.isStateStable(newState)) {
|
|
152
156
|
lastStableState = newState
|
|
153
|
-
screen.notifySheetDetentChange(
|
|
157
|
+
screen.notifySheetDetentChange(
|
|
158
|
+
SheetUtils.detentIndexFromSheetState(
|
|
159
|
+
lastStableState,
|
|
160
|
+
screen.sheetDetents.count()
|
|
161
|
+
), true
|
|
162
|
+
)
|
|
154
163
|
} else if (newState == BottomSheetBehavior.STATE_DRAGGING) {
|
|
155
|
-
screen.notifySheetDetentChange(
|
|
164
|
+
screen.notifySheetDetentChange(
|
|
165
|
+
SheetUtils.detentIndexFromSheetState(
|
|
166
|
+
lastStableState,
|
|
167
|
+
screen.sheetDetents.count()
|
|
168
|
+
),
|
|
169
|
+
false,
|
|
170
|
+
)
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
@@ -191,13 +206,7 @@ class ScreenStackFragment :
|
|
|
191
206
|
}
|
|
192
207
|
|
|
193
208
|
internal fun onSheetCornerRadiusChange() {
|
|
194
|
-
|
|
195
|
-
ShapeAppearanceModel
|
|
196
|
-
.Builder()
|
|
197
|
-
.apply {
|
|
198
|
-
setTopLeftCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius)
|
|
199
|
-
setTopRightCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius)
|
|
200
|
-
}.build()
|
|
209
|
+
screen.onSheetCornerRadiusChange()
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
override fun onCreateView(
|
|
@@ -232,9 +241,7 @@ class ScreenStackFragment :
|
|
|
232
241
|
|
|
233
242
|
coordinatorLayout.addView(screen.recycle())
|
|
234
243
|
|
|
235
|
-
if (screen.stackPresentation != Screen.StackPresentation.
|
|
236
|
-
screen.stackPresentation != Screen.StackPresentation.FORM_SHEET
|
|
237
|
-
) {
|
|
244
|
+
if (screen.stackPresentation != Screen.StackPresentation.FORM_SHEET) {
|
|
238
245
|
appBarLayout =
|
|
239
246
|
context?.let { AppBarLayout(it) }?.apply {
|
|
240
247
|
// By default AppBarLayout will have a background color set but since we cover the whole layout
|
|
@@ -341,13 +348,23 @@ class ScreenStackFragment :
|
|
|
341
348
|
return when (keyboardState) {
|
|
342
349
|
is KeyboardNotVisible -> {
|
|
343
350
|
when (screen.sheetDetents.count()) {
|
|
344
|
-
1 ->
|
|
351
|
+
1 -> if (screen.sheetDetents.first() == Screen.SHEET_FIT_TO_CONTENTS) {
|
|
352
|
+
behavior.apply {
|
|
353
|
+
state = BottomSheetBehavior.STATE_EXPANDED
|
|
354
|
+
screen.contentWrapper.get()?.let {
|
|
355
|
+
maxHeight = it.height
|
|
356
|
+
}
|
|
357
|
+
skipCollapsed = true
|
|
358
|
+
isFitToContents = true
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
345
361
|
behavior.apply {
|
|
346
362
|
state = BottomSheetBehavior.STATE_EXPANDED
|
|
347
363
|
skipCollapsed = true
|
|
348
364
|
isFitToContents = true
|
|
349
365
|
maxHeight = (screen.sheetDetents.first() * containerHeight).toInt()
|
|
350
366
|
}
|
|
367
|
+
}
|
|
351
368
|
|
|
352
369
|
2 ->
|
|
353
370
|
behavior.apply {
|
|
@@ -372,7 +389,8 @@ class ScreenStackFragment :
|
|
|
372
389
|
skipCollapsed = false
|
|
373
390
|
isFitToContents = false
|
|
374
391
|
peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
|
|
375
|
-
expandedOffset =
|
|
392
|
+
expandedOffset =
|
|
393
|
+
((1 - screen.sheetDetents[2]) * containerHeight).toInt()
|
|
376
394
|
halfExpandedRatio =
|
|
377
395
|
(screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat()
|
|
378
396
|
}
|
|
@@ -451,7 +469,8 @@ class ScreenStackFragment :
|
|
|
451
469
|
skipCollapsed = false
|
|
452
470
|
isFitToContents = false
|
|
453
471
|
peekHeight = (screen.sheetDetents[0] * containerHeight).toInt()
|
|
454
|
-
expandedOffset =
|
|
472
|
+
expandedOffset =
|
|
473
|
+
((1 - screen.sheetDetents[2]) * containerHeight).toInt()
|
|
455
474
|
halfExpandedRatio =
|
|
456
475
|
(screen.sheetDetents[1] / screen.sheetDetents[2]).toFloat()
|
|
457
476
|
}
|
|
@@ -576,7 +595,8 @@ class ScreenStackFragment :
|
|
|
576
595
|
// ) : CoordinatorLayout(context), ReactCompoundViewGroup, ReactHitSlopView {
|
|
577
596
|
) : CoordinatorLayout(context),
|
|
578
597
|
ReactPointerEventsView {
|
|
579
|
-
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets =
|
|
598
|
+
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets =
|
|
599
|
+
super.onApplyWindowInsets(insets)
|
|
580
600
|
|
|
581
601
|
private val animationListener: Animation.AnimationListener =
|
|
582
602
|
object : Animation.AnimationListener {
|
|
@@ -8,7 +8,6 @@ import android.text.TextUtils
|
|
|
8
8
|
import android.util.TypedValue
|
|
9
9
|
import android.view.Gravity
|
|
10
10
|
import android.view.View.OnClickListener
|
|
11
|
-
import android.view.ViewGroup
|
|
12
11
|
import android.view.WindowInsets
|
|
13
12
|
import android.widget.ImageView
|
|
14
13
|
import android.widget.TextView
|
|
@@ -25,7 +24,7 @@ import com.swmansion.rnscreens.events.HeaderDetachedEvent
|
|
|
25
24
|
|
|
26
25
|
class ScreenStackHeaderConfig(
|
|
27
26
|
context: Context,
|
|
28
|
-
) :
|
|
27
|
+
) : FabricEnabledHeaderConfigViewGroup(context) {
|
|
29
28
|
private val configSubviews = ArrayList<ScreenStackHeaderSubview>(3)
|
|
30
29
|
val toolbar: CustomToolbar
|
|
31
30
|
var isHeaderHidden = false // named this way to avoid conflict with platform's isHidden
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactContext
|
|
4
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
|
5
|
+
import com.facebook.react.uimanager.Spacing
|
|
6
|
+
import com.swmansion.rnscreens.utils.PaddingBundle
|
|
7
|
+
|
|
8
|
+
internal class ScreenStackHeaderConfigShadowNode(
|
|
9
|
+
private var context: ReactContext,
|
|
10
|
+
) : LayoutShadowNode() {
|
|
11
|
+
var paddingStart: Float = 0f
|
|
12
|
+
var paddingEnd: Float = 0f
|
|
13
|
+
|
|
14
|
+
override fun setLocalData(data: Any?) {
|
|
15
|
+
if (data is PaddingBundle) {
|
|
16
|
+
paddingStart = data.paddingStart
|
|
17
|
+
paddingEnd = data.paddingEnd
|
|
18
|
+
|
|
19
|
+
setPadding(Spacing.START, paddingStart)
|
|
20
|
+
setPadding(Spacing.END, paddingEnd)
|
|
21
|
+
} else {
|
|
22
|
+
super.setLocalData(data)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -3,8 +3,12 @@ package com.swmansion.rnscreens
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import android.view.View
|
|
5
5
|
import com.facebook.react.bridge.JSApplicationCausedNativeException
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
7
|
import com.facebook.react.common.MapBuilder
|
|
7
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
9
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
|
10
|
+
import com.facebook.react.uimanager.ReactStylesDiffMap
|
|
11
|
+
import com.facebook.react.uimanager.StateWrapper
|
|
8
12
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
9
13
|
import com.facebook.react.uimanager.ViewGroupManager
|
|
10
14
|
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
@@ -29,6 +33,9 @@ class ScreenStackHeaderConfigViewManager :
|
|
|
29
33
|
|
|
30
34
|
override fun createViewInstance(reactContext: ThemedReactContext) = ScreenStackHeaderConfig(reactContext)
|
|
31
35
|
|
|
36
|
+
// This works only on Paper. On Fabric the shadow node is implemented in C++ layer.
|
|
37
|
+
override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreenStackHeaderConfigShadowNode(context)
|
|
38
|
+
|
|
32
39
|
override fun addView(
|
|
33
40
|
parent: ScreenStackHeaderConfig,
|
|
34
41
|
child: View,
|
|
@@ -42,6 +49,17 @@ class ScreenStackHeaderConfigViewManager :
|
|
|
42
49
|
parent.addConfigSubview(child, index)
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
override fun updateState(
|
|
53
|
+
view: ScreenStackHeaderConfig,
|
|
54
|
+
props: ReactStylesDiffMap?,
|
|
55
|
+
stateWrapper: StateWrapper?,
|
|
56
|
+
): Any? {
|
|
57
|
+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
58
|
+
view.setStateWrapper(stateWrapper)
|
|
59
|
+
}
|
|
60
|
+
return super.updateState(view, props, stateWrapper)
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
override fun onDropViewInstance(
|
|
46
64
|
@Nonnull view: ScreenStackHeaderConfig,
|
|
47
65
|
) {
|
|
@@ -92,6 +92,12 @@ open class ScreenViewManager :
|
|
|
92
92
|
return super.updateState(view, props, stateWrapper)
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// Called after all props are updated for given view
|
|
96
|
+
override fun onAfterUpdateTransaction(view: Screen) {
|
|
97
|
+
super.onAfterUpdateTransaction(view)
|
|
98
|
+
view.onFinalizePropsUpdate()
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
@ReactProp(name = "activityState")
|
|
96
102
|
fun setActivityState(
|
|
97
103
|
view: Screen,
|
|
@@ -142,7 +148,6 @@ open class ScreenViewManager :
|
|
|
142
148
|
"slide_from_left" -> Screen.StackAnimation.SLIDE_FROM_LEFT
|
|
143
149
|
"slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM
|
|
144
150
|
"fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM
|
|
145
|
-
"ios" -> Screen.StackAnimation.IOS
|
|
146
151
|
"ios_from_right" -> Screen.StackAnimation.IOS_FROM_RIGHT
|
|
147
152
|
"ios_from_left" -> Screen.StackAnimation.IOS_FROM_LEFT
|
|
148
153
|
else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation")
|
|
@@ -163,6 +163,7 @@ object ScreenWindowTraits {
|
|
|
163
163
|
InsetsObserverProxy.registerOnView(decorView)
|
|
164
164
|
InsetsObserverProxy.addOnApplyWindowInsetsListener(windowInsetsListener)
|
|
165
165
|
} else {
|
|
166
|
+
InsetsObserverProxy.unregisterOnView(decorView)
|
|
166
167
|
InsetsObserverProxy.removeOnApplyWindowInsetsListener(windowInsetsListener)
|
|
167
168
|
}
|
|
168
169
|
ViewCompat.requestApplyInsets(decorView)
|
|
@@ -3,6 +3,9 @@ package com.swmansion.rnscreens.ext
|
|
|
3
3
|
import android.graphics.drawable.ColorDrawable
|
|
4
4
|
import android.view.View
|
|
5
5
|
import android.view.ViewGroup
|
|
6
|
+
import com.facebook.react.views.scroll.ReactHorizontalScrollView
|
|
7
|
+
import com.facebook.react.views.scroll.ReactScrollView
|
|
8
|
+
import com.swmansion.rnscreens.ScreenStack
|
|
6
9
|
|
|
7
10
|
internal fun View.parentAsView() = this.parent as? View
|
|
8
11
|
|
|
@@ -30,3 +33,17 @@ internal fun View.maybeBgColor(): Int? {
|
|
|
30
33
|
}
|
|
31
34
|
return null
|
|
32
35
|
}
|
|
36
|
+
|
|
37
|
+
internal fun View.isInsideScrollViewWithRemoveClippedSubviews(): Boolean {
|
|
38
|
+
if (this is ReactHorizontalScrollView || this is ReactScrollView) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
var parentView = this.parent
|
|
42
|
+
while (parentView is ViewGroup && parentView !is ScreenStack) {
|
|
43
|
+
if (parentView is ReactScrollView) {
|
|
44
|
+
return parentView.removeClippedSubviews
|
|
45
|
+
}
|
|
46
|
+
parentView = parentView.parent
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
#include <react/renderer/components/rnscreens/RNSScreenComponentDescriptor.h>
|
|
20
20
|
#include <react/renderer/components/rnscreens/RNSModalScreenComponentDescriptor.h>
|
|
21
|
+
#include <react/renderer/components/rnscreens/RNSScreenStackHeaderSubviewComponentDescriptor.h>
|
|
22
|
+
#include <react/renderer/components/rnscreens/RNSScreenStackHeaderConfigComponentDescriptor.h>
|
|
21
23
|
|
|
22
24
|
namespace facebook {
|
|
23
25
|
namespace react {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import com.facebook.react.bridge.ReactContext
|
|
6
|
+
import com.facebook.react.uimanager.StateWrapper
|
|
7
|
+
import com.facebook.react.uimanager.UIManagerModule
|
|
8
|
+
import com.swmansion.rnscreens.utils.PaddingBundle
|
|
9
|
+
import kotlin.math.abs
|
|
10
|
+
|
|
11
|
+
abstract class FabricEnabledHeaderConfigViewGroup(
|
|
12
|
+
context: Context,
|
|
13
|
+
) : ViewGroup(context) {
|
|
14
|
+
private var lastPaddingStart = 0
|
|
15
|
+
private var lastPaddingEnd = 0
|
|
16
|
+
|
|
17
|
+
fun setStateWrapper(wrapper: StateWrapper?) = Unit
|
|
18
|
+
|
|
19
|
+
fun updatePaddingsFabric(
|
|
20
|
+
paddingStart: Int,
|
|
21
|
+
paddingEnd: Int,
|
|
22
|
+
) {
|
|
23
|
+
// Note that on Paper we do not convert these props from px to dip. This is done internally by RN.
|
|
24
|
+
if (abs(lastPaddingStart - paddingStart) < DELTA && abs(lastPaddingEnd - paddingEnd) < DELTA) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
lastPaddingStart = paddingStart
|
|
29
|
+
lastPaddingEnd = paddingEnd
|
|
30
|
+
|
|
31
|
+
val reactContext = context as? ReactContext
|
|
32
|
+
val uiManagerModule = reactContext?.getNativeModule(UIManagerModule::class.java)
|
|
33
|
+
uiManagerModule?.setViewLocalData(this.id, PaddingBundle(paddingStart.toFloat(), paddingEnd.toFloat()))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
private const val DELTA = 0.9
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef ANDROID
|
|
4
|
+
#include <fbjni/fbjni.h>
|
|
5
|
+
#endif
|
|
6
|
+
#include <react/debug/react_native_assert.h>
|
|
7
|
+
#include <react/renderer/components/rnscreens/Props.h>
|
|
8
|
+
#include <react/renderer/components/rnscreens/utils/RectUtil.h>
|
|
9
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
10
|
+
#include "RNSScreenStackHeaderConfigShadowNode.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
using namespace rnscreens;
|
|
15
|
+
|
|
16
|
+
class RNSScreenStackHeaderConfigComponentDescriptor final
|
|
17
|
+
: public ConcreteComponentDescriptor<RNSScreenStackHeaderConfigShadowNode> {
|
|
18
|
+
public:
|
|
19
|
+
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
20
|
+
|
|
21
|
+
void adopt(ShadowNode &shadowNode) const override {
|
|
22
|
+
react_native_assert(
|
|
23
|
+
dynamic_cast<RNSScreenStackHeaderConfigShadowNode *>(&shadowNode));
|
|
24
|
+
auto &configShadowNode =
|
|
25
|
+
static_cast<RNSScreenStackHeaderConfigShadowNode &>(shadowNode);
|
|
26
|
+
|
|
27
|
+
react_native_assert(
|
|
28
|
+
dynamic_cast<YogaLayoutableShadowNode *>(&configShadowNode));
|
|
29
|
+
auto &layoutableShadowNode =
|
|
30
|
+
dynamic_cast<YogaLayoutableShadowNode &>(configShadowNode);
|
|
31
|
+
|
|
32
|
+
auto state = std::static_pointer_cast<
|
|
33
|
+
const RNSScreenStackHeaderConfigShadowNode::ConcreteState>(
|
|
34
|
+
shadowNode.getState());
|
|
35
|
+
auto stateData = state->getData();
|
|
36
|
+
|
|
37
|
+
layoutableShadowNode.setPadding(
|
|
38
|
+
{stateData.getPaddingStart(), 0, stateData.getPaddingEnd(), 0});
|
|
39
|
+
|
|
40
|
+
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
} // namespace facebook::react
|