@oxyhq/bloom 0.59.1 → 0.59.3
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 +4 -3
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js +38 -23
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.js +2 -5
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +0 -4
- package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/commonjs/dialog/SheetShell.js +2 -2
- package/lib/commonjs/dialog/SheetShell.js.map +1 -1
- package/lib/commonjs/overlay/blur-target.js +53 -0
- package/lib/commonjs/overlay/blur-target.js.map +1 -0
- package/lib/commonjs/overlay/index.js +15 -5
- package/lib/commonjs/overlay/index.js.map +1 -1
- package/lib/commonjs/provider/index.js +4 -6
- package/lib/commonjs/provider/index.js.map +1 -1
- package/lib/commonjs/styles/radii.js +9 -0
- package/lib/commonjs/styles/radii.js.map +1 -0
- package/lib/module/bottom-sheet/BottomSheetBase.js +40 -25
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/dialog/Dialog.js +2 -5
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +1 -5
- package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/module/dialog/SheetShell.js +2 -2
- package/lib/module/dialog/SheetShell.js.map +1 -1
- package/lib/module/overlay/blur-target.js +48 -0
- package/lib/module/overlay/blur-target.js.map +1 -0
- package/lib/module/overlay/index.js +14 -5
- package/lib/module/overlay/index.js.map +1 -1
- package/lib/module/provider/index.js +4 -6
- package/lib/module/provider/index.js.map +1 -1
- package/lib/module/styles/radii.js +5 -0
- package/lib/module/styles/radii.js.map +1 -0
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts +4 -5
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/commonjs/overlay/blur-target.d.ts +21 -0
- package/lib/typescript/commonjs/overlay/blur-target.d.ts.map +1 -0
- package/lib/typescript/commonjs/overlay/index.d.ts +7 -2
- package/lib/typescript/commonjs/overlay/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/provider/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/radii.d.ts +3 -0
- package/lib/typescript/commonjs/styles/radii.d.ts.map +1 -0
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts +4 -5
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -1
- package/lib/typescript/module/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/module/overlay/blur-target.d.ts +21 -0
- package/lib/typescript/module/overlay/blur-target.d.ts.map +1 -0
- package/lib/typescript/module/overlay/index.d.ts +7 -2
- package/lib/typescript/module/overlay/index.d.ts.map +1 -1
- package/lib/typescript/module/provider/index.d.ts.map +1 -1
- package/lib/typescript/module/styles/radii.d.ts +3 -0
- package/lib/typescript/module/styles/radii.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/BottomSheet.test.tsx +46 -1
- package/src/__tests__/BottomSheetGesture.test.tsx +21 -3
- package/src/bottom-sheet/BottomSheetBase.tsx +46 -29
- package/src/dialog/Dialog.tsx +2 -4
- package/src/dialog/DialogBottomSheet.tsx +0 -4
- package/src/dialog/SheetShell.tsx +2 -2
- package/src/overlay/blur-target.tsx +45 -0
- package/src/overlay/index.tsx +21 -5
- package/src/provider/index.tsx +14 -11
- package/src/styles/radii.ts +2 -0
|
@@ -20,12 +20,15 @@ import Animated, {
|
|
|
20
20
|
type SharedValue,
|
|
21
21
|
useAnimatedScrollHandler,
|
|
22
22
|
useAnimatedStyle,
|
|
23
|
+
useDerivedValue,
|
|
23
24
|
useSharedValue,
|
|
24
25
|
withSpring,
|
|
25
26
|
withTiming,
|
|
26
27
|
} from 'react-native-reanimated';
|
|
27
28
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
28
|
-
import { Backdrop } from '../overlay';
|
|
29
|
+
import { Backdrop, BACKDROP_DIM_OPACITY } from '../overlay';
|
|
30
|
+
import { useBloomBlurTarget } from '../overlay/blur-target';
|
|
31
|
+
import { DETACHED_SHEET_RADIUS } from '../styles/radii';
|
|
29
32
|
import { useTheme } from '../theme/use-theme';
|
|
30
33
|
|
|
31
34
|
/** Hook that returns current screen dimensions and updates on rotation/resize. */
|
|
@@ -125,12 +128,11 @@ export interface BottomSheetProps {
|
|
|
125
128
|
*/
|
|
126
129
|
manualActivation?: boolean;
|
|
127
130
|
/**
|
|
128
|
-
* When `true`, the backdrop
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* `backdropOpacity` still controls the resting dim level.
|
|
131
|
+
* When `true`, the backdrop and native blur fade proportionally with drag
|
|
132
|
+
* distance — fully visible at rest and fully gone once the sheet leaves the
|
|
133
|
+
* viewport. The base `backdropOpacity` still controls the resting dim level.
|
|
132
134
|
*
|
|
133
|
-
* Defaults to `false`
|
|
135
|
+
* Defaults to `true`. Set to `false` only for a deliberately fixed backdrop.
|
|
134
136
|
*/
|
|
135
137
|
dynamicBackdrop?: boolean;
|
|
136
138
|
/**
|
|
@@ -215,10 +217,10 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
215
217
|
onDismissAttempt,
|
|
216
218
|
detached = false,
|
|
217
219
|
showHandle = true,
|
|
218
|
-
backdropOpacity =
|
|
220
|
+
backdropOpacity = BACKDROP_DIM_OPACITY,
|
|
219
221
|
scrollable = true,
|
|
220
222
|
manualActivation = false,
|
|
221
|
-
dynamicBackdrop =
|
|
223
|
+
dynamicBackdrop = true,
|
|
222
224
|
handleComponent,
|
|
223
225
|
scrollY: externalScrollY,
|
|
224
226
|
headerOverlay,
|
|
@@ -226,6 +228,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
226
228
|
} = props;
|
|
227
229
|
|
|
228
230
|
const insets = useSafeAreaInsets();
|
|
231
|
+
const blurTarget = useBloomBlurTarget();
|
|
229
232
|
const theme = useTheme();
|
|
230
233
|
const { colors } = theme;
|
|
231
234
|
const { height: screenHeight } = useScreenDimensions();
|
|
@@ -255,6 +258,10 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
255
258
|
}, [screenHeight, screenHeightSV]);
|
|
256
259
|
|
|
257
260
|
const translateY = useSharedValue(screenHeight);
|
|
261
|
+
// The blur must reach zero when the CARD itself has moved fully below the
|
|
262
|
+
// viewport. A short detached dialog can be far smaller than the screen, so
|
|
263
|
+
// screen height is not a valid denominator for this interaction.
|
|
264
|
+
const sheetHeight = useSharedValue(screenHeight);
|
|
258
265
|
const opacity = useSharedValue(0);
|
|
259
266
|
const scrollOffsetY = useSharedValue(0);
|
|
260
267
|
const isScrollAtTop = useSharedValue(true);
|
|
@@ -637,24 +644,30 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
637
644
|
// Native (with the plugin) auto-tracks and ignores the extra deps, so this
|
|
638
645
|
// is safe on both platforms. Do NOT strip the shared values from these deps.
|
|
639
646
|
//
|
|
640
|
-
// `opacity.value` drives the
|
|
641
|
-
//
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
//
|
|
645
|
-
|
|
647
|
+
// `opacity.value` drives the whole blur+dim layer in/out (0 -> 1).
|
|
648
|
+
// `backdropOpacity` belongs ONLY to Backdrop's black dim layer below; if it
|
|
649
|
+
// is multiplied into this group opacity instead, the opaque black child
|
|
650
|
+
// hides the BlurView and Android looks like a plain dark scrim.
|
|
651
|
+
// `dynamicBackdrop` fades the composed blur+dim proportionally with drag.
|
|
652
|
+
// The SAME progress drives BlurView's native `intensity` prop below:
|
|
653
|
+
// Android's RenderEffect can remain visibly blurred when only an ancestor's
|
|
654
|
+
// alpha changes, so reducing the radius itself is required for the content
|
|
655
|
+
// behind the sheet to become genuinely sharp during the gesture.
|
|
656
|
+
const backdropProgress = useDerivedValue(() => {
|
|
646
657
|
const dragFactor = dynamicBackdrop
|
|
647
658
|
? interpolate(
|
|
648
659
|
translateY.value,
|
|
649
|
-
[0,
|
|
650
|
-
[1, 0
|
|
660
|
+
[0, sheetHeight.value],
|
|
661
|
+
[1, 0],
|
|
651
662
|
'clamp',
|
|
652
663
|
)
|
|
653
664
|
: 1;
|
|
654
|
-
return
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
665
|
+
return opacity.value * dragFactor;
|
|
666
|
+
}, [dynamicBackdrop, opacity, translateY, sheetHeight]);
|
|
667
|
+
|
|
668
|
+
const backdropStyle = useAnimatedStyle(() => ({
|
|
669
|
+
opacity: backdropProgress.value,
|
|
670
|
+
}), [backdropProgress]);
|
|
658
671
|
|
|
659
672
|
const sheetStyle = useAnimatedStyle(() => {
|
|
660
673
|
const scale = interpolate(translateY.value, [0, screenHeightSV.value], [1, 0.95]);
|
|
@@ -690,6 +703,12 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
690
703
|
dismiss();
|
|
691
704
|
}, [onDismissAttempt, dismiss]);
|
|
692
705
|
|
|
706
|
+
const handleSheetLayout = useCallback((event: LayoutChangeEvent) => {
|
|
707
|
+
const measuredHeight = event.nativeEvent.layout.height;
|
|
708
|
+
if (measuredHeight > 0) sheetHeight.value = measuredHeight;
|
|
709
|
+
onLayout?.(event);
|
|
710
|
+
}, [onLayout, sheetHeight]);
|
|
711
|
+
|
|
693
712
|
const scrollHandler = useAnimatedScrollHandler({
|
|
694
713
|
onScroll: (event) => {
|
|
695
714
|
scrollOffsetY.value = event.contentOffset.y;
|
|
@@ -792,19 +811,20 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
792
811
|
</Animated.View>
|
|
793
812
|
) : (
|
|
794
813
|
// The ONE Bloom backdrop (blur + dim + press-to-dismiss).
|
|
795
|
-
// `backdropStyle` drives the fade; the
|
|
796
|
-
//
|
|
797
|
-
// switched off here to keep a single source of dimming.
|
|
814
|
+
// `backdropStyle` drives the entrance/drag fade; the black
|
|
815
|
+
// dim stays translucent so the BlurView remains visible.
|
|
798
816
|
<AnimatedBackdrop
|
|
799
817
|
onPress={handleBackdropPress}
|
|
800
|
-
dimOpacity={
|
|
818
|
+
dimOpacity={backdropOpacity}
|
|
819
|
+
blurTarget={blurTarget ?? undefined}
|
|
820
|
+
blurProgress={backdropProgress}
|
|
801
821
|
style={[styles.backdrop, backdropStyle]}
|
|
802
822
|
/>
|
|
803
823
|
)}
|
|
804
824
|
|
|
805
825
|
<GestureDetector gesture={panGesture}>
|
|
806
826
|
<Animated.View
|
|
807
|
-
onLayout={
|
|
827
|
+
onLayout={handleSheetLayout}
|
|
808
828
|
style={[dynamicStyles.sheet, sheetMarginStyle, sheetStyle, sheetHeightStyle, style]}
|
|
809
829
|
>
|
|
810
830
|
{backgroundComponent?.({ style: styles.background })}
|
|
@@ -827,10 +847,7 @@ BottomSheetBase.displayName = 'BottomSheetBase';
|
|
|
827
847
|
|
|
828
848
|
const styles = StyleSheet.create({
|
|
829
849
|
backdrop: {
|
|
830
|
-
// Color is solid black; the dim level is driven by `backdropOpacity` via
|
|
831
|
-
// the animated `opacity` style on the wrapping <Animated.View>.
|
|
832
850
|
flex: 1,
|
|
833
|
-
backgroundColor: '#000',
|
|
834
851
|
},
|
|
835
852
|
backdropTouchable: {
|
|
836
853
|
flex: 1,
|
|
@@ -846,7 +863,7 @@ const styles = StyleSheet.create({
|
|
|
846
863
|
sheetDetached: {
|
|
847
864
|
left: 16,
|
|
848
865
|
right: 16,
|
|
849
|
-
borderRadius:
|
|
866
|
+
borderRadius: DETACHED_SHEET_RADIUS,
|
|
850
867
|
},
|
|
851
868
|
sheetNormal: {
|
|
852
869
|
left: 0,
|
package/src/dialog/Dialog.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import Animated, {
|
|
|
26
26
|
} from 'react-native-reanimated';
|
|
27
27
|
|
|
28
28
|
import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
|
|
29
|
+
import { DETACHED_SHEET_RADIUS } from '../styles/radii';
|
|
29
30
|
import { Z_INDEX, Z_INDEX_LAYER_STEP } from '../styles/z-index';
|
|
30
31
|
import { useTheme } from '../theme/use-theme';
|
|
31
32
|
import { Context, useDialogControl } from './context';
|
|
@@ -251,7 +252,7 @@ function CenteredOrSideDialog({
|
|
|
251
252
|
// All four corners rounded — bloom's BottomSheet defaults to top-only
|
|
252
253
|
// radius in flush mode, but we use `detached` so the whole card is
|
|
253
254
|
// floating and rounded uniformly.
|
|
254
|
-
borderRadius:
|
|
255
|
+
borderRadius: DETACHED_SHEET_RADIUS,
|
|
255
256
|
},
|
|
256
257
|
// Drives the card's `height` only while a morph is in flight; at rest it
|
|
257
258
|
// resolves to `height: 'auto'` — the card's normal content sizing.
|
|
@@ -344,9 +345,6 @@ function CenteredOrSideDialog({
|
|
|
344
345
|
/>
|
|
345
346
|
) : undefined
|
|
346
347
|
}
|
|
347
|
-
// Stronger dim when a Dialog is stacked over another sheet so the
|
|
348
|
-
// underlying sheet's handle/content doesn't bleed through.
|
|
349
|
-
backdropOpacity={0.7}
|
|
350
348
|
// Measures the floating card — the size a morph starts from.
|
|
351
349
|
onLayout={morphState.onPanelLayout}
|
|
352
350
|
style={sheetStyle}
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
DEFAULT_DIALOG_CONTENT_PADDING,
|
|
31
31
|
DEFAULT_MAX_HEIGHT_RATIO,
|
|
32
32
|
PANEL_RADIUS,
|
|
33
|
-
SHEET_BACKDROP_OPACITY,
|
|
34
33
|
} from './placement';
|
|
35
34
|
import type { DialogControlProps, DialogProps } from './types';
|
|
36
35
|
|
|
@@ -284,9 +283,6 @@ export function DialogBottomSheet({
|
|
|
284
283
|
/>
|
|
285
284
|
) : undefined
|
|
286
285
|
}
|
|
287
|
-
// Stronger dim than a lone sheet so an underlying sheet's handle/content
|
|
288
|
-
// doesn't bleed through when a Dialog is stacked over one.
|
|
289
|
-
backdropOpacity={SHEET_BACKDROP_OPACITY + 0.3}
|
|
290
286
|
// Measures the sheet card — the size a morph starts from.
|
|
291
287
|
onLayout={morphState.onPanelLayout}
|
|
292
288
|
style={sheetStyle}
|
|
@@ -14,6 +14,7 @@ import React, { useCallback, useImperativeHandle, useMemo, useRef } from 'react'
|
|
|
14
14
|
import { Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
|
15
15
|
|
|
16
16
|
import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
|
|
17
|
+
import { DETACHED_SHEET_RADIUS } from '../styles/radii';
|
|
17
18
|
import { Z_INDEX } from '../styles/z-index';
|
|
18
19
|
import { useTheme } from '../theme/use-theme';
|
|
19
20
|
import { Context } from './context';
|
|
@@ -87,7 +88,7 @@ export function SheetShell({
|
|
|
87
88
|
() => ({
|
|
88
89
|
maxWidth: 500,
|
|
89
90
|
backgroundColor: theme.colors.background,
|
|
90
|
-
borderRadius:
|
|
91
|
+
borderRadius: DETACHED_SHEET_RADIUS,
|
|
91
92
|
}),
|
|
92
93
|
[theme.colors.background],
|
|
93
94
|
);
|
|
@@ -98,7 +99,6 @@ export function SheetShell({
|
|
|
98
99
|
onDismiss={handleDismiss}
|
|
99
100
|
enablePanDownToClose
|
|
100
101
|
detached
|
|
101
|
-
backdropOpacity={0.7}
|
|
102
102
|
style={sheetStyle}
|
|
103
103
|
>
|
|
104
104
|
<Context.Provider value={context}>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createContext, useContext, useRef, type ReactNode, type RefObject } from 'react';
|
|
2
|
+
import { BlurTargetView } from 'expo-blur';
|
|
3
|
+
import { Platform, StyleSheet, type View } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type BlurTargetRef = RefObject<View | null>;
|
|
6
|
+
|
|
7
|
+
const BlurTargetContext = createContext<BlurTargetRef | null>(null);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gives Android overlay blurs a real view to sample.
|
|
11
|
+
*
|
|
12
|
+
* Expo Blur 56+ intentionally disables the Dimezis implementation unless a
|
|
13
|
+
* `BlurTargetView` is supplied. Bloom's sheets render their backdrop inside a
|
|
14
|
+
* native `Modal`, so the target must live around the app content in the
|
|
15
|
+
* underlying window and travel to the modal through React context.
|
|
16
|
+
*
|
|
17
|
+
* Only modal backdrops consume this target. Ordinary in-tree BlurViews never
|
|
18
|
+
* target their own ancestor, avoiding the recursive capture/crash that causes.
|
|
19
|
+
*/
|
|
20
|
+
export function BloomBlurTargetProvider({ children }: { children: ReactNode }) {
|
|
21
|
+
const targetRef = useRef<View>(null);
|
|
22
|
+
|
|
23
|
+
if (Platform.OS !== 'android') {
|
|
24
|
+
return <>{children}</>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<BlurTargetContext.Provider value={targetRef}>
|
|
29
|
+
<BlurTargetView ref={targetRef} style={styles.target}>
|
|
30
|
+
{children}
|
|
31
|
+
</BlurTargetView>
|
|
32
|
+
</BlurTargetContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Internal target used only by Bloom's native modal backdrops. */
|
|
37
|
+
export function useBloomBlurTarget(): BlurTargetRef | null {
|
|
38
|
+
return useContext(BlurTargetContext);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const styles = StyleSheet.create({
|
|
42
|
+
target: {
|
|
43
|
+
flex: 1,
|
|
44
|
+
},
|
|
45
|
+
});
|
package/src/overlay/index.tsx
CHANGED
|
@@ -39,14 +39,16 @@
|
|
|
39
39
|
* Use `<OverlayRoot>` for the surface's outermost node and `<Backdrop>` for its
|
|
40
40
|
* dimming layer; do not re-implement either with raw `View`s.
|
|
41
41
|
*/
|
|
42
|
-
import { forwardRef, memo, type ReactNode } from 'react';
|
|
42
|
+
import { forwardRef, memo, type ReactNode, type RefObject } from 'react';
|
|
43
43
|
import { BlurView } from 'expo-blur';
|
|
44
|
+
import Animated, { type SharedValue, useAnimatedProps } from 'react-native-reanimated';
|
|
44
45
|
import {
|
|
45
46
|
Platform,
|
|
46
47
|
Pressable,
|
|
47
48
|
StyleSheet,
|
|
48
49
|
View,
|
|
49
50
|
type StyleProp,
|
|
51
|
+
type View as NativeView,
|
|
50
52
|
type ViewStyle,
|
|
51
53
|
} from 'react-native';
|
|
52
54
|
|
|
@@ -60,6 +62,8 @@ export const BACKDROP_BLUR_INTENSITY = 40;
|
|
|
60
62
|
/** Dim laid over the blur — the blur alone does not give enough contrast. */
|
|
61
63
|
export const BACKDROP_DIM_OPACITY = 0.45;
|
|
62
64
|
|
|
65
|
+
const AnimatedBlurView = Animated.createAnimatedComponent(BlurView);
|
|
66
|
+
|
|
63
67
|
export interface OverlayRootProps {
|
|
64
68
|
children?: ReactNode;
|
|
65
69
|
style?: StyleProp<ViewStyle>;
|
|
@@ -95,6 +99,10 @@ export interface BackdropProps {
|
|
|
95
99
|
dimColor?: string;
|
|
96
100
|
/** Dim opacity, 0–1. */
|
|
97
101
|
dimOpacity?: number;
|
|
102
|
+
/** Android view sampled by the native blur. Modal surfaces receive this from BloomProvider. */
|
|
103
|
+
blurTarget?: RefObject<NativeView | null>;
|
|
104
|
+
/** Live visibility progress used to reduce native blur while a surface leaves. */
|
|
105
|
+
blurProgress?: SharedValue<number>;
|
|
98
106
|
/** Extra style on the press target — animated opacity, insets, z-index. */
|
|
99
107
|
style?: StyleProp<ViewStyle>;
|
|
100
108
|
/** Rendered ON TOP of the dim, inside the press target (Dialog's panel does this). */
|
|
@@ -116,6 +124,8 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
116
124
|
blurTint = 'dark',
|
|
117
125
|
dimColor = '#000',
|
|
118
126
|
dimOpacity = BACKDROP_DIM_OPACITY,
|
|
127
|
+
blurTarget,
|
|
128
|
+
blurProgress,
|
|
119
129
|
style,
|
|
120
130
|
children,
|
|
121
131
|
accessibilityLabel = 'Dismiss',
|
|
@@ -124,6 +134,10 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
124
134
|
ref,
|
|
125
135
|
) {
|
|
126
136
|
const inert = disabled || !onPress;
|
|
137
|
+
const animatedBlurProps = useAnimatedProps(() => ({
|
|
138
|
+
intensity: Math.max(0, Math.min(100, blurIntensity * (blurProgress?.value ?? 1))),
|
|
139
|
+
}), [blurIntensity, blurProgress]);
|
|
140
|
+
|
|
127
141
|
return (
|
|
128
142
|
<Pressable
|
|
129
143
|
// MUST forward the ref: surfaces animate the backdrop through
|
|
@@ -146,12 +160,14 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
146
160
|
style={[StyleSheet.absoluteFill, style]}
|
|
147
161
|
>
|
|
148
162
|
{blurIntensity > 0 ? (
|
|
149
|
-
<
|
|
163
|
+
<AnimatedBlurView
|
|
150
164
|
intensity={blurIntensity}
|
|
165
|
+
animatedProps={animatedBlurProps}
|
|
151
166
|
tint={blurTint}
|
|
152
|
-
|
|
153
|
-
//
|
|
154
|
-
|
|
167
|
+
blurTarget={blurTarget}
|
|
168
|
+
// Expo disables Android blur without an explicit target. Sheets get
|
|
169
|
+
// one from BloomProvider; targetless surfaces degrade without warning.
|
|
170
|
+
blurMethod={Platform.OS === 'android' && blurTarget ? 'dimezisBlurViewSdk31Plus' : undefined}
|
|
155
171
|
pointerEvents="none"
|
|
156
172
|
style={StyleSheet.absoluteFill}
|
|
157
173
|
/>
|
package/src/provider/index.tsx
CHANGED
|
@@ -34,6 +34,7 @@ import type { ReactNode } from 'react';
|
|
|
34
34
|
|
|
35
35
|
import { ImageResolverProvider, type ImageResolver } from '../image-resolver';
|
|
36
36
|
import { BloomHapticsProvider } from '../hooks/useHaptics';
|
|
37
|
+
import { BloomBlurTargetProvider } from '../overlay/blur-target';
|
|
37
38
|
import { TabBarMinimizeProvider } from '../tab-bar/minimize-context';
|
|
38
39
|
import { BloomThemeProvider, type BloomThemeProviderProps } from '../theme';
|
|
39
40
|
import { ScrollRestorationProvider } from './scroll-provider';
|
|
@@ -57,17 +58,19 @@ export function BloomProvider({
|
|
|
57
58
|
...themeProps
|
|
58
59
|
}: BloomProviderProps) {
|
|
59
60
|
return (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
<BloomBlurTargetProvider>
|
|
62
|
+
{/* `value` is passed unconditionally (null when unset) so toggling a resolver
|
|
63
|
+
never changes the tree shape and remounts everything below it. */}
|
|
64
|
+
<ImageResolverProvider value={imageResolver ?? null}>
|
|
65
|
+
<BloomThemeProvider {...themeProps}>
|
|
66
|
+
<ScrollRestorationProvider>
|
|
67
|
+
<BloomHapticsProvider enabled={haptics}>
|
|
68
|
+
<TabBarMinimizeProvider>{children}</TabBarMinimizeProvider>
|
|
69
|
+
</BloomHapticsProvider>
|
|
70
|
+
</ScrollRestorationProvider>
|
|
71
|
+
</BloomThemeProvider>
|
|
72
|
+
</ImageResolverProvider>
|
|
73
|
+
</BloomBlurTargetProvider>
|
|
71
74
|
);
|
|
72
75
|
}
|
|
73
76
|
|