@lodev09/react-native-true-sheet 4.0.0-beta.19 → 4.0.0-beta.20
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 +6 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +43 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +17 -10
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +8 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +16 -0
- package/ios/TrueSheetView.mm +20 -7
- package/ios/TrueSheetViewController.h +1 -3
- package/ios/TrueSheetViewController.mm +98 -22
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +1 -0
- package/ios/core/TrueSheetDetentCalculator.mm +18 -0
- package/lib/module/TrueSheet.js +16 -5
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +39 -6
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +6 -9
- package/lib/module/mocks/index.js +8 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +30 -39
- 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/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +4 -6
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.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 +13 -4
- package/src/TrueSheet.types.ts +30 -41
- package/src/TrueSheet.web.tsx +42 -6
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +6 -9
- package/src/mocks/index.ts +10 -2
- package/src/navigation/types.ts +3 -2
|
@@ -204,6 +204,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
204
204
|
setupScrollable()
|
|
205
205
|
|
|
206
206
|
if (viewController.isPresented) {
|
|
207
|
+
viewController.setupBackground()
|
|
207
208
|
viewController.sheetView?.setupBackground()
|
|
208
209
|
viewController.sheetView?.setupGrabber()
|
|
209
210
|
viewController.sheetView?.updateGravity()
|
|
@@ -260,9 +261,12 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
260
261
|
viewController.sheetCornerRadius = radius
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
fun setDetentBackgrounds(colors: List<Int?>) {
|
|
265
|
+
viewController.detentBackgrounds = colors
|
|
266
|
+
}
|
|
267
|
+
|
|
263
268
|
fun setSheetBackgroundColor(color: Int?) {
|
|
264
|
-
|
|
265
|
-
viewController.sheetBackgroundColor = color
|
|
269
|
+
viewController.backgroundColor = color
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
fun setDismissible(dismissible: Boolean) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
|
+
import android.animation.ValueAnimator
|
|
3
4
|
import android.annotation.SuppressLint
|
|
4
5
|
import android.graphics.Canvas
|
|
5
6
|
import android.os.Build
|
|
@@ -227,7 +228,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
227
228
|
override var grabber: Boolean = true
|
|
228
229
|
override var grabberOptions: GrabberOptions? = null
|
|
229
230
|
override var accessibilityOptions: AccessibilityOptions? = null
|
|
231
|
+
var backgroundColor: Int? = null
|
|
232
|
+
var detentBackgrounds: List<Int?> = emptyList()
|
|
233
|
+
private var displayedBackgroundIndex = 0
|
|
234
|
+
private var backgroundAnimator: ValueAnimator? = null
|
|
235
|
+
private var targetBackgroundColor: Int? = null
|
|
230
236
|
override var sheetBackgroundColor: Int? = null
|
|
237
|
+
private set
|
|
231
238
|
var insetAdjustment: TrueSheetInsetAdjustment = TrueSheetInsetAdjustment.AUTOMATIC
|
|
232
239
|
|
|
233
240
|
var scrollableOptions: ScrollableOptions? = null
|
|
@@ -425,6 +432,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
425
432
|
|
|
426
433
|
private fun cleanupSheet() {
|
|
427
434
|
cleanupKeyboardObserver()
|
|
435
|
+
backgroundAnimator?.cancel()
|
|
436
|
+
backgroundAnimator = null
|
|
437
|
+
targetBackgroundColor = null
|
|
428
438
|
sheetView?.animate()?.cancel()
|
|
429
439
|
|
|
430
440
|
// Cleanup dim views
|
|
@@ -846,6 +856,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
846
856
|
setupDimmedBackground()
|
|
847
857
|
setupKeyboardObserver()
|
|
848
858
|
|
|
859
|
+
displayedBackgroundIndex = detentIndex
|
|
860
|
+
setupBackground(animated = false)
|
|
849
861
|
sheet.setupBackground()
|
|
850
862
|
sheet.setupElevation()
|
|
851
863
|
sheet.setupGrabber()
|
|
@@ -1099,6 +1111,30 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1099
1111
|
// MARK: - Dimmed Background
|
|
1100
1112
|
// =============================================================================
|
|
1101
1113
|
|
|
1114
|
+
fun setupBackground(animated: Boolean = true) {
|
|
1115
|
+
val sheet = sheetView ?: return
|
|
1116
|
+
val color = detentBackgrounds.getOrNull(displayedBackgroundIndex)
|
|
1117
|
+
?: backgroundColor ?: sheet.getDefaultBackgroundColor()
|
|
1118
|
+
if (animated && color == targetBackgroundColor) return
|
|
1119
|
+
|
|
1120
|
+
backgroundAnimator?.cancel()
|
|
1121
|
+
targetBackgroundColor = color
|
|
1122
|
+
val startColor = sheetBackgroundColor ?: color
|
|
1123
|
+
if (!animated || !isPresented || startColor == color) {
|
|
1124
|
+
sheetBackgroundColor = color
|
|
1125
|
+
sheet.updateBackgroundColor(color)
|
|
1126
|
+
return
|
|
1127
|
+
}
|
|
1128
|
+
backgroundAnimator = ValueAnimator.ofArgb(startColor, color).apply {
|
|
1129
|
+
duration = 200L
|
|
1130
|
+
addUpdateListener {
|
|
1131
|
+
sheetBackgroundColor = it.animatedValue as Int
|
|
1132
|
+
sheet.updateBackgroundColor(it.animatedValue as Int)
|
|
1133
|
+
}
|
|
1134
|
+
start()
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1102
1138
|
fun setupDimmedBackground() {
|
|
1103
1139
|
val coordinator = this.coordinatorLayout ?: run {
|
|
1104
1140
|
RNLog.e(reactContext, "TrueSheet: coordinatorLayout is null in setupDimmedBackground")
|
|
@@ -1427,6 +1463,13 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
1427
1463
|
}
|
|
1428
1464
|
|
|
1429
1465
|
private fun emitChangePositionDelegate(currentTop: Int, realtime: Boolean = true) {
|
|
1466
|
+
if (isPresented && !isPresentAnimating && !isBeingDismissed) {
|
|
1467
|
+
val index = detentCalculator.getBackgroundIndex(currentTop, displayedBackgroundIndex)
|
|
1468
|
+
if (index != displayedBackgroundIndex) {
|
|
1469
|
+
displayedBackgroundIndex = index
|
|
1470
|
+
setupBackground()
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1430
1473
|
// Dedupe emissions for same position
|
|
1431
1474
|
if (currentTop == lastEmittedPositionPx) return
|
|
1432
1475
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.view.WindowManager
|
|
4
|
+
import com.facebook.react.bridge.ColorPropConverter
|
|
4
5
|
import com.facebook.react.bridge.ReadableArray
|
|
5
6
|
import com.facebook.react.bridge.ReadableMap
|
|
7
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
7
9
|
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
8
10
|
import com.facebook.react.uimanager.PointerEvents
|
|
@@ -114,6 +116,21 @@ class TrueSheetViewManager :
|
|
|
114
116
|
view.setDetents(detents)
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
@ReactProp(name = "detentBackgrounds")
|
|
120
|
+
override fun setDetentBackgrounds(view: TrueSheetView, value: ReadableArray?) {
|
|
121
|
+
view.setDetentBackgrounds(
|
|
122
|
+
(0 until (value?.size() ?: 0)).map { index ->
|
|
123
|
+
val entry = value?.getMap(index)
|
|
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
|
+
}
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
117
134
|
@ReactProp(name = "backgroundColor", customType = "Color")
|
|
118
135
|
override fun setBackgroundColor(view: TrueSheetView, color: Int?) {
|
|
119
136
|
view.setSheetBackgroundColor(color)
|
|
@@ -228,16 +245,6 @@ class TrueSheetViewManager :
|
|
|
228
245
|
// iOS-specific prop - no-op on Android
|
|
229
246
|
}
|
|
230
247
|
|
|
231
|
-
@ReactProp(name = "blurOptions")
|
|
232
|
-
override fun setBlurOptions(view: TrueSheetView, options: ReadableMap?) {
|
|
233
|
-
// iOS-specific prop - no-op on Android
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
@ReactProp(name = "glass", defaultBoolean = true)
|
|
237
|
-
override fun setGlass(view: TrueSheetView, glass: Boolean) {
|
|
238
|
-
// iOS-specific prop - no-op on Android
|
|
239
|
-
}
|
|
240
|
-
|
|
241
248
|
@ReactProp(name = "insetAdjustment")
|
|
242
249
|
override fun setInsetAdjustment(view: TrueSheetView, insetAdjustment: String?) {
|
|
243
250
|
view.setInsetAdjustment(insetAdjustment ?: "automatic")
|
|
@@ -179,7 +179,14 @@ class TrueSheetBottomSheetView(private val reactContext: ThemedReactContext) : F
|
|
|
179
179
|
clipToOutline = true
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
fun updateBackgroundColor(color: Int) {
|
|
183
|
+
(background as? ShapeDrawable)?.let {
|
|
184
|
+
it.paint.color = color
|
|
185
|
+
it.invalidateSelf()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fun getDefaultBackgroundColor(): Int {
|
|
183
190
|
val isNight = (
|
|
184
191
|
reactContext.resources.configuration.uiMode and
|
|
185
192
|
Configuration.UI_MODE_NIGHT_MASK
|
|
@@ -137,6 +137,22 @@ class TrueSheetDetentCalculator(private val reactContext: ThemedReactContext) {
|
|
|
137
137
|
return maxContentHeight?.let { minOf(height, it, maxAllowedHeight) } ?: minOf(height, maxAllowedHeight)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
fun getBackgroundIndex(sheetTop: Int, displayedIndex: Int): Int {
|
|
141
|
+
if (detents.isEmpty()) return -1
|
|
142
|
+
|
|
143
|
+
val height = getVisibleSheetHeight(sheetTop)
|
|
144
|
+
var nextIndex = displayedIndex.coerceIn(0, detents.size - 1)
|
|
145
|
+
var distance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(nextIndex)))
|
|
146
|
+
for (index in detents.indices) {
|
|
147
|
+
val nextDistance = kotlin.math.abs(height - getVisibleSheetHeight(getSheetTopForDetentIndex(index)))
|
|
148
|
+
if (nextDistance < distance) {
|
|
149
|
+
nextIndex = index
|
|
150
|
+
distance = nextDistance
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return nextIndex
|
|
154
|
+
}
|
|
155
|
+
|
|
140
156
|
/**
|
|
141
157
|
* Get the expected sheet top position for a detent index.
|
|
142
158
|
*/
|
package/ios/TrueSheetView.mm
CHANGED
|
@@ -236,16 +236,29 @@ using namespace facebook::react;
|
|
|
236
236
|
// Background color
|
|
237
237
|
_controller.backgroundColor = RCTUIColorFromSharedColor(newProps.backgroundColor);
|
|
238
238
|
|
|
239
|
+
NSMutableArray<NSDictionary *> *detentBackgrounds = [NSMutableArray new];
|
|
240
|
+
ContextContainer contextContainer;
|
|
241
|
+
PropsParserContext parserContext{-1, contextContainer};
|
|
242
|
+
for (const auto &entry : newProps.detentBackgrounds) {
|
|
243
|
+
NSMutableDictionary *background = [NSMutableDictionary new];
|
|
244
|
+
UIColor *color = RCTUIColorFromSharedColor(entry.color);
|
|
245
|
+
if (color) {
|
|
246
|
+
background[@"color"] = color;
|
|
247
|
+
}
|
|
248
|
+
if (!entry.blur.empty()) {
|
|
249
|
+
auto blur = TrueSheetViewBackgroundBlur::None;
|
|
250
|
+
fromRawValue(parserContext, RawValue(entry.blur), blur);
|
|
251
|
+
background[@"blur"] = @(static_cast<int>(blur));
|
|
252
|
+
}
|
|
253
|
+
[detentBackgrounds addObject:background];
|
|
254
|
+
}
|
|
255
|
+
if (![_controller.detentBackgrounds isEqualToArray:detentBackgrounds]) {
|
|
256
|
+
_controller.detentBackgrounds = detentBackgrounds;
|
|
257
|
+
}
|
|
258
|
+
|
|
239
259
|
// Blur tint
|
|
240
260
|
_controller.backgroundBlur = newProps.backgroundBlur;
|
|
241
261
|
|
|
242
|
-
// Blur options
|
|
243
|
-
const auto &blurOpts = newProps.blurOptions;
|
|
244
|
-
_controller.blurIntensity = blurOpts.intensity >= 0 ? @(blurOpts.intensity) : nil;
|
|
245
|
-
_controller.blurInteraction = blurOpts.interaction;
|
|
246
|
-
|
|
247
|
-
_controller.glass = newProps.glass;
|
|
248
|
-
|
|
249
262
|
// Corner radius
|
|
250
263
|
_controller.cornerRadius = newProps.cornerRadius < 0 ? nil : @(newProps.cornerRadius);
|
|
251
264
|
|
|
@@ -55,6 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
55
55
|
|
|
56
56
|
@property (nonatomic, weak, nullable) id<TrueSheetViewControllerDelegate> delegate;
|
|
57
57
|
@property (nonatomic, strong) NSArray<NSNumber *> *detents;
|
|
58
|
+
@property (nonatomic, copy) NSArray<NSDictionary *> *detentBackgrounds;
|
|
58
59
|
@property (nonatomic, strong, nullable) NSNumber *maxContentHeight;
|
|
59
60
|
@property (nonatomic, strong, nullable) NSNumber *maxContentWidth;
|
|
60
61
|
@property (nonatomic, strong, nullable) NSNumber *contentHeight;
|
|
@@ -79,9 +80,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
79
80
|
@property (nonatomic, assign) BOOL dimmed;
|
|
80
81
|
@property (nonatomic, strong, nullable) NSNumber *dimmedDetentIndex;
|
|
81
82
|
@property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
|
|
82
|
-
@property (nonatomic, strong, nullable) NSNumber *blurIntensity;
|
|
83
|
-
@property (nonatomic, assign) BOOL blurInteraction;
|
|
84
|
-
@property (nonatomic, assign) BOOL glass;
|
|
85
83
|
@property (nonatomic, assign) facebook::react::TrueSheetViewPresentation presentation;
|
|
86
84
|
@property (nonatomic, assign) facebook::react::TrueSheetViewPlacement placement;
|
|
87
85
|
@property (nonatomic, assign) facebook::react::TrueSheetViewInsetAdjustment insetAdjustment;
|
|
@@ -74,6 +74,8 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
|
|
|
74
74
|
CGFloat _autoDetentHeight;
|
|
75
75
|
CGFloat _peekDetentHeight;
|
|
76
76
|
NSInteger _pendingDetentIndex;
|
|
77
|
+
NSInteger _displayedBackgroundIndex;
|
|
78
|
+
BOOL _usesBackgroundEffects;
|
|
77
79
|
|
|
78
80
|
// Present/dismiss tracking: a display link scoped to the transition
|
|
79
81
|
// coordinator — started alongside the transition, stopped in its completion.
|
|
@@ -112,6 +114,8 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
|
|
|
112
114
|
- (instancetype)init {
|
|
113
115
|
if (self = [super initWithNibName:nil bundle:nil]) {
|
|
114
116
|
_detents = @[ @0.5, @1 ];
|
|
117
|
+
_detentBackgrounds = @[];
|
|
118
|
+
_displayedBackgroundIndex = 0;
|
|
115
119
|
_contentHeight = @(0);
|
|
116
120
|
_headerHeight = @(0);
|
|
117
121
|
_footerHeight = @(0);
|
|
@@ -134,7 +138,6 @@ static char TrueSheetAccessibilityWindowPreviousElementsKey;
|
|
|
134
138
|
_activeDetentIndex = -1;
|
|
135
139
|
_pendingDetentIndex = -1;
|
|
136
140
|
|
|
137
|
-
_blurInteraction = YES;
|
|
138
141
|
_insetAdjustment = TrueSheetViewInsetAdjustment::Automatic;
|
|
139
142
|
_detentCalculator = [[TrueSheetDetentCalculator alloc] init];
|
|
140
143
|
_detentCalculator.delegate = self;
|
|
@@ -370,6 +373,9 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
370
373
|
}
|
|
371
374
|
|
|
372
375
|
- (BOOL)isDesignCompatibilityMode {
|
|
376
|
+
if (@available(iOS 27.0, *)) {
|
|
377
|
+
return NO;
|
|
378
|
+
}
|
|
373
379
|
if (@available(iOS 26.0, *)) {
|
|
374
380
|
NSNumber *value = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
|
|
375
381
|
return value.boolValue;
|
|
@@ -1119,6 +1125,20 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
1119
1125
|
}
|
|
1120
1126
|
}
|
|
1121
1127
|
|
|
1128
|
+
if (self.isPresented && !self.isBeingDismissed && !_isWillDismissEmitted && !_usesBackgroundEffects) {
|
|
1129
|
+
NSInteger index = [_detentCalculator backgroundIndexForPosition:position displayedIndex:_displayedBackgroundIndex];
|
|
1130
|
+
if (index != _displayedBackgroundIndex) {
|
|
1131
|
+
_displayedBackgroundIndex = index;
|
|
1132
|
+
[UIView animateWithDuration:0.2
|
|
1133
|
+
delay:0
|
|
1134
|
+
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction
|
|
1135
|
+
animations:^{
|
|
1136
|
+
[self setupBackground];
|
|
1137
|
+
}
|
|
1138
|
+
completion:nil];
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1122
1142
|
TrueSheetPositionState state = {
|
|
1123
1143
|
.position = position,
|
|
1124
1144
|
.detent = [self interpolatedDetentForPosition:position],
|
|
@@ -1227,6 +1247,20 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
1227
1247
|
}
|
|
1228
1248
|
|
|
1229
1249
|
[_detentCalculator setDetentCount:self.detents.count];
|
|
1250
|
+
_usesBackgroundEffects = [self canUseBackgroundEffects:detents];
|
|
1251
|
+
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1) && !TARGET_OS_MACCATALYST
|
|
1252
|
+
if (@available(iOS 26.1, *)) {
|
|
1253
|
+
if (_usesBackgroundEffects) {
|
|
1254
|
+
for (NSInteger index = 0; index < detents.count; index++) {
|
|
1255
|
+
NSDictionary *entry = [self backgroundForIndex:index];
|
|
1256
|
+
auto blur = entry[@"blur"] ? static_cast<TrueSheetViewBackgroundBlur>([entry[@"blur"] intValue])
|
|
1257
|
+
: TrueSheetViewBackgroundBlur::None;
|
|
1258
|
+
detents[index].backgroundEffect = [self backgroundEffectForColor:entry[@"color"] blur:blur];
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
#endif
|
|
1263
|
+
|
|
1230
1264
|
sheet.detents = detents;
|
|
1231
1265
|
|
|
1232
1266
|
if (self.dimmed && [self.dimmedDetentIndex integerValue] == 0) {
|
|
@@ -1394,6 +1428,8 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
1394
1428
|
|
|
1395
1429
|
- (void)setupActiveDetentWithIndex:(NSInteger)index {
|
|
1396
1430
|
_activeDetentIndex = index;
|
|
1431
|
+
_displayedBackgroundIndex = index;
|
|
1432
|
+
[self setupBackground];
|
|
1397
1433
|
[self applyActiveDetent];
|
|
1398
1434
|
}
|
|
1399
1435
|
|
|
@@ -1407,34 +1443,72 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
1407
1443
|
[self applyActiveDetent];
|
|
1408
1444
|
}
|
|
1409
1445
|
|
|
1410
|
-
- (
|
|
1411
|
-
|
|
1412
|
-
if (@available(iOS 26.
|
|
1413
|
-
//
|
|
1414
|
-
|
|
1415
|
-
|
|
1446
|
+
- (BOOL)canUseBackgroundEffects:(NSArray<UISheetPresentationControllerDetent *> *)detents {
|
|
1447
|
+
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1) && !TARGET_OS_MACCATALYST
|
|
1448
|
+
if (@available(iOS 26.1, *)) {
|
|
1449
|
+
// Some 26.1 builds omit setters declared in the SDK.
|
|
1450
|
+
if (self.isDesignCompatibilityMode || ![self.sheet respondsToSelector:@selector(setBackgroundEffect:)]) {
|
|
1451
|
+
return NO;
|
|
1452
|
+
}
|
|
1453
|
+
for (UISheetPresentationControllerDetent *detent in detents) {
|
|
1454
|
+
if (![detent respondsToSelector:@selector(setBackgroundEffect:)]) {
|
|
1455
|
+
return NO;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
return YES;
|
|
1416
1459
|
}
|
|
1460
|
+
#endif
|
|
1461
|
+
return NO;
|
|
1462
|
+
}
|
|
1417
1463
|
|
|
1418
|
-
|
|
1464
|
+
- (NSDictionary *)backgroundForIndex:(NSInteger)index {
|
|
1465
|
+
return index >= 0 && index < self.detentBackgrounds.count ? self.detentBackgrounds[index] : nil;
|
|
1466
|
+
}
|
|
1419
1467
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1468
|
+
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1) && !TARGET_OS_MACCATALYST
|
|
1469
|
+
- (UIVisualEffect *)backgroundEffectForColor:(UIColor *)color
|
|
1470
|
+
blur:(TrueSheetViewBackgroundBlur)blur API_AVAILABLE(ios(26.1)) {
|
|
1471
|
+
if (blur != TrueSheetViewBackgroundBlur::None) {
|
|
1472
|
+
return [UIBlurEffect effectWithStyle:[BlurUtil blurEffectStyleFromEnum:blur]];
|
|
1473
|
+
}
|
|
1474
|
+
return color ? [UIColorEffect effectWithColor:color] : nil;
|
|
1475
|
+
}
|
|
1476
|
+
#endif
|
|
1424
1477
|
|
|
1478
|
+
- (void)setupBackground {
|
|
1425
1479
|
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1) && !TARGET_OS_MACCATALYST
|
|
1426
1480
|
if (@available(iOS 26.1, *)) {
|
|
1427
|
-
if (
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1481
|
+
if (_usesBackgroundEffects) {
|
|
1482
|
+
self.sheet.backgroundEffect = [self backgroundEffectForColor:self.backgroundColor blur:self.backgroundBlur];
|
|
1483
|
+
self.view.backgroundColor = nil;
|
|
1484
|
+
_blurView.hidden = YES;
|
|
1485
|
+
_blurView.backgroundBlur = TrueSheetViewBackgroundBlur::None;
|
|
1486
|
+
[_blurView applyBlurEffect];
|
|
1487
|
+
return;
|
|
1433
1488
|
}
|
|
1434
1489
|
}
|
|
1435
1490
|
#endif
|
|
1436
1491
|
|
|
1437
|
-
|
|
1492
|
+
NSDictionary *entry = [self backgroundForIndex:_displayedBackgroundIndex];
|
|
1493
|
+
BOOL hasEntry = entry[@"color"] || entry[@"blur"];
|
|
1494
|
+
UIColor *color = hasEntry ? entry[@"color"] : self.backgroundColor;
|
|
1495
|
+
auto effectiveBackgroundBlur =
|
|
1496
|
+
hasEntry ? (entry[@"blur"] ? static_cast<TrueSheetViewBackgroundBlur>([entry[@"blur"] intValue])
|
|
1497
|
+
: TrueSheetViewBackgroundBlur::None)
|
|
1498
|
+
: self.backgroundBlur;
|
|
1499
|
+
if (@available(iOS 26.0, *)) {
|
|
1500
|
+
// iOS 26+ has default liquid glass effect
|
|
1501
|
+
} else if (effectiveBackgroundBlur == TrueSheetViewBackgroundBlur::None && !color) {
|
|
1502
|
+
effectiveBackgroundBlur = TrueSheetViewBackgroundBlur::SystemMaterial;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
BOOL hasBlur = effectiveBackgroundBlur != TrueSheetViewBackgroundBlur::None;
|
|
1506
|
+
|
|
1507
|
+
_blurView.hidden = NO;
|
|
1508
|
+
_blurView.backgroundBlur = effectiveBackgroundBlur;
|
|
1509
|
+
[_blurView applyBlurEffect];
|
|
1510
|
+
|
|
1511
|
+
self.view.backgroundColor = hasBlur ? nil : color;
|
|
1438
1512
|
}
|
|
1439
1513
|
|
|
1440
1514
|
- (void)setupGrabber {
|
|
@@ -1554,9 +1628,11 @@ static BOOL TrueSheetIsPhoneIdiom(void) {
|
|
|
1554
1628
|
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(27_0)
|
|
1555
1629
|
// iOS 27 exposes native placement. `sourceView` must stay nil since it overrides `preferredPlacement`.
|
|
1556
1630
|
if (@available(iOS 27.0, *)) {
|
|
1557
|
-
self.sheetPresentationController
|
|
1558
|
-
|
|
1559
|
-
|
|
1631
|
+
if ([self.sheetPresentationController respondsToSelector:@selector(setPreferredPlacement:)]) {
|
|
1632
|
+
self.sheetPresentationController.sourceView = nil;
|
|
1633
|
+
self.sheetPresentationController.preferredPlacement = [self preferredPlacement];
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1560
1636
|
}
|
|
1561
1637
|
#endif
|
|
1562
1638
|
|
|
@@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
14
14
|
@interface TrueSheetBlurView : UIVisualEffectView
|
|
15
15
|
|
|
16
16
|
@property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
|
|
17
|
-
@property (nonatomic, strong, nullable) NSNumber *blurIntensity;
|
|
18
|
-
@property (nonatomic, assign) BOOL blurInteraction;
|
|
19
17
|
|
|
20
18
|
/// Adds the blur view to a parent view with proper constraints
|
|
21
19
|
- (void)addToView:(UIView *)parentView;
|
|
@@ -13,15 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
using namespace facebook::react;
|
|
15
15
|
|
|
16
|
-
@implementation TrueSheetBlurView
|
|
17
|
-
UIViewPropertyAnimator *_blurAnimator;
|
|
18
|
-
}
|
|
16
|
+
@implementation TrueSheetBlurView
|
|
19
17
|
|
|
20
18
|
#pragma mark - Initialization
|
|
21
19
|
|
|
22
20
|
- (instancetype)init {
|
|
23
21
|
if (self = [super init]) {
|
|
24
|
-
|
|
22
|
+
self.userInteractionEnabled = NO;
|
|
25
23
|
}
|
|
26
24
|
return self;
|
|
27
25
|
}
|
|
@@ -38,45 +36,14 @@ using namespace facebook::react;
|
|
|
38
36
|
[parentView insertSubview:self atIndex:0];
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
- (void)clearAnimator {
|
|
42
|
-
if (_blurAnimator) {
|
|
43
|
-
[_blurAnimator stopAnimation:YES];
|
|
44
|
-
_blurAnimator = nil;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
39
|
- (void)applyBlurEffect {
|
|
49
|
-
self.userInteractionEnabled = self.blurInteraction;
|
|
50
|
-
|
|
51
40
|
if (self.backgroundBlur == TrueSheetViewBackgroundBlur::None) {
|
|
52
|
-
[self clearAnimator];
|
|
53
41
|
self.effect = nil;
|
|
54
42
|
return;
|
|
55
43
|
}
|
|
56
44
|
|
|
57
45
|
UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromEnum:self.backgroundBlur];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
CGFloat intensity =
|
|
61
|
-
(self.blurIntensity && [self.blurIntensity floatValue] >= 0) ? [self.blurIntensity floatValue] / 100.0 : 1.0;
|
|
62
|
-
|
|
63
|
-
if (intensity >= 1.0) {
|
|
64
|
-
[self clearAnimator];
|
|
65
|
-
self.effect = blurEffect;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!_blurAnimator) {
|
|
70
|
-
__weak __typeof(self) weakSelf = self;
|
|
71
|
-
_blurAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:1.0
|
|
72
|
-
curve:UIViewAnimationCurveLinear
|
|
73
|
-
animations:^{
|
|
74
|
-
weakSelf.effect = blurEffect;
|
|
75
|
-
}];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_blurAnimator.pausesOnCompletion = YES;
|
|
79
|
-
_blurAnimator.fractionComplete = intensity;
|
|
46
|
+
self.effect = [UIBlurEffect effectWithStyle:style];
|
|
80
47
|
}
|
|
81
48
|
|
|
82
49
|
@end
|
|
@@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
46
46
|
For peek (-2) detents, calculates based on header + footer height.
|
|
47
47
|
*/
|
|
48
48
|
- (CGFloat)detentValueForIndex:(NSInteger)index;
|
|
49
|
+
- (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex;
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
Returns the height for auto (-1) detents: content + header + footer height.
|
|
@@ -98,6 +98,24 @@
|
|
|
98
98
|
return 0;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
- (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex {
|
|
102
|
+
NSInteger count = self.delegate.detents.count;
|
|
103
|
+
if (count == 0)
|
|
104
|
+
return -1;
|
|
105
|
+
|
|
106
|
+
CGFloat height = self.delegate.screenHeight - position;
|
|
107
|
+
NSInteger nextIndex = MAX(0, MIN(displayedIndex, count - 1));
|
|
108
|
+
CGFloat distance = fabs(height - [self resolvedHeightForIndex:nextIndex]);
|
|
109
|
+
for (NSInteger index = 0; index < count; index++) {
|
|
110
|
+
CGFloat nextDistance = fabs(height - [self resolvedHeightForIndex:index]);
|
|
111
|
+
if (nextDistance < distance) {
|
|
112
|
+
nextIndex = index;
|
|
113
|
+
distance = nextDistance;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return nextIndex;
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
- (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
|
|
102
120
|
NSArray<NSNumber *> *detents = self.delegate.detents;
|
|
103
121
|
NSInteger count = detents.count;
|
package/lib/module/TrueSheet.js
CHANGED
|
@@ -7,6 +7,8 @@ import TrueSheetContentViewNativeComponent from './fabric/TrueSheetContentViewNa
|
|
|
7
7
|
import TrueSheetHeaderViewNativeComponent from './fabric/TrueSheetHeaderViewNativeComponent';
|
|
8
8
|
import TrueSheetFooterViewNativeComponent from './fabric/TrueSheetFooterViewNativeComponent';
|
|
9
9
|
import TrueSheetModule from "./specs/NativeTrueSheetModule.js";
|
|
10
|
+
import { TrueSheetBackground } from "./TrueSheetBackground.js";
|
|
11
|
+
import { normalizeDetentBackground } from "./detentBackgrounds.js";
|
|
10
12
|
import { Platform, StyleSheet, BackHandler, RootTagContext, findNodeHandle, processColor } from 'react-native';
|
|
11
13
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
14
|
// RN has no public API for hosting the element inspector inside a presented view;
|
|
@@ -439,6 +441,9 @@ export class TrueSheet extends PureComponent {
|
|
|
439
441
|
const {
|
|
440
442
|
detents = [0.5, 1],
|
|
441
443
|
backgroundColor,
|
|
444
|
+
detentBackgrounds,
|
|
445
|
+
background,
|
|
446
|
+
backgroundStyle,
|
|
442
447
|
dismissible = true,
|
|
443
448
|
draggable = true,
|
|
444
449
|
grabber = true,
|
|
@@ -449,8 +454,6 @@ export class TrueSheet extends PureComponent {
|
|
|
449
454
|
initialDetentAnimated = true,
|
|
450
455
|
dimmedDetentIndex,
|
|
451
456
|
backgroundBlur,
|
|
452
|
-
blurOptions,
|
|
453
|
-
glass = true,
|
|
454
457
|
cornerRadius,
|
|
455
458
|
maxContentHeight,
|
|
456
459
|
maxContentWidth,
|
|
@@ -497,7 +500,10 @@ export class TrueSheet extends PureComponent {
|
|
|
497
500
|
};
|
|
498
501
|
}
|
|
499
502
|
const sheetContent = /*#__PURE__*/_jsxs(_Fragment, {
|
|
500
|
-
children: [
|
|
503
|
+
children: [/*#__PURE__*/_jsx(TrueSheetBackground, {
|
|
504
|
+
background: background,
|
|
505
|
+
backgroundStyle: backgroundStyle
|
|
506
|
+
}), header && /*#__PURE__*/_jsx(TrueSheetHeaderViewNativeComponent, {
|
|
501
507
|
style: [styles.header, headerOptions?.position === 'absolute' && styles.absoluteHeader, headerStyle],
|
|
502
508
|
children: /*#__PURE__*/isValidElement(header) ? header : /*#__PURE__*/createElement(header)
|
|
503
509
|
}), /*#__PURE__*/_jsx(TrueSheetContentViewNativeComponent, {
|
|
@@ -517,9 +523,14 @@ export class TrueSheet extends PureComponent {
|
|
|
517
523
|
style: styles.sheetView,
|
|
518
524
|
detents: resolvedDetents,
|
|
519
525
|
backgroundBlur: backgroundBlur,
|
|
520
|
-
blurOptions: blurOptions,
|
|
521
|
-
glass: glass,
|
|
522
526
|
backgroundColor: backgroundColor,
|
|
527
|
+
detentBackgrounds: detentBackgrounds && Array.from(detentBackgrounds.slice(0, 3), entry => {
|
|
528
|
+
const normalized = normalizeDetentBackground(entry);
|
|
529
|
+
return normalized ? {
|
|
530
|
+
...normalized,
|
|
531
|
+
color: processColor(normalized.color)
|
|
532
|
+
} : {};
|
|
533
|
+
}),
|
|
523
534
|
cornerRadius: cornerRadius,
|
|
524
535
|
grabber: grabber,
|
|
525
536
|
grabberOptions: this.resolvedGrabberOptions,
|