@oxyhq/bloom 0.61.0 → 0.63.0
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/lib/commonjs/bottom-sheet/BottomSheetBase.js +29 -12
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.js +8 -15
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +14 -15
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +0 -2
- package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/commonjs/dialog/placement.js +1 -4
- package/lib/commonjs/dialog/placement.js.map +1 -1
- package/lib/commonjs/overlay/index.js +42 -21
- package/lib/commonjs/overlay/index.js.map +1 -1
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js +2 -5
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- package/lib/module/bottom-sheet/BottomSheetBase.js +31 -14
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/dialog/Dialog.js +10 -17
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +15 -16
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +1 -3
- package/lib/module/dialog/DialogBottomSheet.js.map +1 -1
- package/lib/module/dialog/placement.js +0 -3
- package/lib/module/dialog/placement.js.map +1 -1
- package/lib/module/overlay/index.js +42 -23
- package/lib/module/overlay/index.js.map +1 -1
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js +2 -5
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- 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/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/placement.d.ts +0 -2
- package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -1
- package/lib/typescript/commonjs/overlay/index.d.ts +30 -5
- package/lib/typescript/commonjs/overlay/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- 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/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -1
- package/lib/typescript/module/dialog/placement.d.ts +0 -2
- package/lib/typescript/module/dialog/placement.d.ts.map +1 -1
- package/lib/typescript/module/overlay/index.d.ts +30 -5
- package/lib/typescript/module/overlay/index.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/animated-style-deps.test.ts +1 -1
- package/src/__tests__/overlay-pointer-events.test.tsx +17 -16
- package/src/bottom-sheet/BottomSheetBase.tsx +30 -13
- package/src/dialog/Dialog.tsx +9 -17
- package/src/dialog/Dialog.web.tsx +16 -25
- package/src/dialog/DialogBottomSheet.tsx +0 -2
- package/src/dialog/placement.ts +0 -2
- package/src/overlay/index.tsx +68 -32
- package/src/zoomable-image-gallery/ZoomableImageGallery.tsx +6 -5
|
@@ -20,12 +20,13 @@ 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';
|
|
29
30
|
import { useTheme } from '../theme/use-theme';
|
|
30
31
|
|
|
31
32
|
/** Hook that returns current screen dimensions and updates on rotation/resize. */
|
|
@@ -199,8 +200,6 @@ export interface BottomSheetBaseProps extends BottomSheetProps {
|
|
|
199
200
|
Shell: React.ComponentType<BottomSheetShellProps>;
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
const AnimatedBackdrop = Animated.createAnimatedComponent(Backdrop);
|
|
203
|
-
|
|
204
203
|
export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: React.ForwardedRef<BottomSheetRef>) => {
|
|
205
204
|
const {
|
|
206
205
|
Shell,
|
|
@@ -215,7 +214,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
215
214
|
onDismissAttempt,
|
|
216
215
|
detached = false,
|
|
217
216
|
showHandle = true,
|
|
218
|
-
backdropOpacity =
|
|
217
|
+
backdropOpacity = BACKDROP_DIM_OPACITY,
|
|
219
218
|
scrollable = true,
|
|
220
219
|
manualActivation = false,
|
|
221
220
|
dynamicBackdrop = false,
|
|
@@ -642,7 +641,11 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
642
641
|
// dim opacity applies smoothly across the animation. When
|
|
643
642
|
// `dynamicBackdrop` is enabled, the dim also fades proportionally with
|
|
644
643
|
// drag distance (iOS Photos style).
|
|
645
|
-
|
|
644
|
+
// A VALUE, not a style: `Backdrop` applies it to its blur and dim layers
|
|
645
|
+
// itself. Handing it over as an animated style would put the opacity on the
|
|
646
|
+
// layers' shared ancestor, which composites the group in isolation and
|
|
647
|
+
// leaves `backdrop-filter` with nothing behind it to blur.
|
|
648
|
+
const backdropProgress = useDerivedValue(() => {
|
|
646
649
|
const dragFactor = dynamicBackdrop
|
|
647
650
|
? interpolate(
|
|
648
651
|
translateY.value,
|
|
@@ -651,10 +654,16 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
651
654
|
'clamp',
|
|
652
655
|
)
|
|
653
656
|
: 1;
|
|
654
|
-
return
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
657
|
+
return opacity.value * dragFactor;
|
|
658
|
+
}, [dynamicBackdrop, opacity, translateY, screenHeightSV]);
|
|
659
|
+
|
|
660
|
+
// Only the consumer-supplied `backdropComponent` needs the progress as a
|
|
661
|
+
// style — it owns its own visuals, blur included, so the ancestor-opacity
|
|
662
|
+
// hazard is its call to make.
|
|
663
|
+
const customBackdropStyle = useAnimatedStyle(
|
|
664
|
+
() => ({ opacity: backdropProgress.value }),
|
|
665
|
+
[backdropProgress],
|
|
666
|
+
);
|
|
658
667
|
|
|
659
668
|
const sheetStyle = useAnimatedStyle(() => {
|
|
660
669
|
const scale = interpolate(translateY.value, [0, screenHeightSV.value], [1, 0.95]);
|
|
@@ -787,7 +796,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
787
796
|
<Shell visible={rendered} onRequestClose={dismiss} keyboardHeight={keyboardHeight}>
|
|
788
797
|
<View style={StyleSheet.absoluteFill}>
|
|
789
798
|
{backdropComponent ? (
|
|
790
|
-
<Animated.View style={[StyleSheet.absoluteFill,
|
|
799
|
+
<Animated.View style={[StyleSheet.absoluteFill, customBackdropStyle]}>
|
|
791
800
|
{backdropComponent({ onPress: handleBackdropPress })}
|
|
792
801
|
</Animated.View>
|
|
793
802
|
) : (
|
|
@@ -795,10 +804,18 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
795
804
|
// `backdropStyle` drives the fade; the dim level rides on
|
|
796
805
|
// `backdropOpacity`, so the shared component's own dim is
|
|
797
806
|
// switched off here to keep a single source of dimming.
|
|
798
|
-
<
|
|
807
|
+
<Backdrop
|
|
799
808
|
onPress={handleBackdropPress}
|
|
800
|
-
|
|
801
|
-
|
|
809
|
+
// `progress` is the FADE (0 → 1, folding the drag);
|
|
810
|
+
// how dark the backdrop gets is `backdropOpacity`,
|
|
811
|
+
// which defaults to the library-wide dim so a sheet
|
|
812
|
+
// matches every other surface. It goes through
|
|
813
|
+
// `progress` rather than an animated style because an
|
|
814
|
+
// opacity on the root would sit above the blur layer
|
|
815
|
+
// and neutralise it.
|
|
816
|
+
dimOpacity={backdropOpacity}
|
|
817
|
+
progress={backdropProgress}
|
|
818
|
+
style={styles.backdrop}
|
|
802
819
|
/>
|
|
803
820
|
)}
|
|
804
821
|
|
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 { Backdrop } from '../overlay';
|
|
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';
|
|
@@ -46,7 +47,6 @@ import {
|
|
|
46
47
|
DEFAULT_SIDE_WIDTH,
|
|
47
48
|
DIALOG_SHEET_BACKDROP_TESTID,
|
|
48
49
|
PANEL_RADIUS,
|
|
49
|
-
SHEET_BACKDROP_OPACITY,
|
|
50
50
|
SIDE_SHEET_MIN_GUTTER,
|
|
51
51
|
useResolvedPlacement,
|
|
52
52
|
} from './placement';
|
|
@@ -486,10 +486,6 @@ function SideSheet({
|
|
|
486
486
|
if (dismissOnBackdrop) onDismiss();
|
|
487
487
|
}, [dismissOnBackdrop, onDismiss]);
|
|
488
488
|
|
|
489
|
-
const backdropAnimatedStyle = useAnimatedStyle(() => ({
|
|
490
|
-
opacity: progress.value * SHEET_BACKDROP_OPACITY,
|
|
491
|
-
}), []);
|
|
492
|
-
|
|
493
489
|
const hiddenSign = side === 'left' ? -1 : 1;
|
|
494
490
|
const panelAnimatedStyle = useAnimatedStyle(() => {
|
|
495
491
|
const hidden = 1 - progress.value;
|
|
@@ -533,18 +529,14 @@ function SideSheet({
|
|
|
533
529
|
]}
|
|
534
530
|
{...(containerClassName ? ({ className: containerClassName } as Record<string, string>) : {})}
|
|
535
531
|
>
|
|
536
|
-
<
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
disabled={!dismissOnBackdrop}
|
|
545
|
-
style={StyleSheet.absoluteFill}
|
|
546
|
-
/>
|
|
547
|
-
</Animated.View>
|
|
532
|
+
<Backdrop
|
|
533
|
+
testID={testID ? `${testID}-backdrop` : DIALOG_SHEET_BACKDROP_TESTID}
|
|
534
|
+
accessibilityLabel={label ? `Dismiss ${label}` : 'Dismiss dialog'}
|
|
535
|
+
onPress={handleBackdropPress}
|
|
536
|
+
disabled={!dismissOnBackdrop}
|
|
537
|
+
progress={progress}
|
|
538
|
+
style={sideStyles.backdrop}
|
|
539
|
+
/>
|
|
548
540
|
|
|
549
541
|
<Animated.View
|
|
550
542
|
accessibilityViewIsModal
|
|
@@ -47,7 +47,6 @@ import {
|
|
|
47
47
|
DIALOG_SHEET_BACKDROP_TESTID,
|
|
48
48
|
EASE_OUT,
|
|
49
49
|
PANEL_RADIUS,
|
|
50
|
-
SHEET_BACKDROP_OPACITY,
|
|
51
50
|
SIDE_SHEET_MIN_GUTTER,
|
|
52
51
|
useResolvedPlacement,
|
|
53
52
|
} from './placement';
|
|
@@ -60,11 +59,6 @@ import type {
|
|
|
60
59
|
|
|
61
60
|
const FADE_OUT_DURATION = CENTER_FADE_OUT_DURATION;
|
|
62
61
|
|
|
63
|
-
/**
|
|
64
|
-
* The centered card's scrim reads darker than a sheet's because the card floats
|
|
65
|
-
* in the middle of it; the shared blur underneath is identical.
|
|
66
|
-
*/
|
|
67
|
-
const DIALOG_BACKDROP_DIM_OPACITY = 0.6;
|
|
68
62
|
|
|
69
63
|
/**
|
|
70
64
|
* The four CSS `animation` shorthands driving the centered card and its backdrop.
|
|
@@ -331,17 +325,17 @@ function CenterOrSideDialog({
|
|
|
331
325
|
onPress={() => close()}
|
|
332
326
|
disabled={!dismissOnBackdrop}
|
|
333
327
|
accessibilityLabel={label ? `Dismiss ${label}` : 'Dismiss dialog'}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
328
|
+
// The fade rides on the LAYERS, never on the press target: an
|
|
329
|
+
// opacity animation on the blur's ancestor composites the group in
|
|
330
|
+
// isolation and leaves `backdrop-filter` nothing to sample.
|
|
331
|
+
layerStyle={isClosing ? BACKDROP_FADE_OUT : BACKDROP_FADE_IN}
|
|
332
|
+
style={{
|
|
333
|
+
position: WEB_POSITION_FIXED,
|
|
334
|
+
zIndex: dialogZIndex.backdrop,
|
|
335
|
+
alignItems: 'center',
|
|
336
|
+
justifyContent: 'center',
|
|
337
|
+
paddingHorizontal: 20,
|
|
338
|
+
}}
|
|
345
339
|
>
|
|
346
340
|
<DialogPanel
|
|
347
341
|
testID={testID}
|
|
@@ -702,14 +696,11 @@ function SheetSurface({
|
|
|
702
696
|
accessibilityLabel={label ? `Dismiss ${label}` : 'Dismiss dialog'}
|
|
703
697
|
onPress={handleBackdropPress}
|
|
704
698
|
disabled={!dismissOnBackdrop}
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
dimOpacity
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
backdropTransition,
|
|
711
|
-
{ opacity: visible ? SHEET_BACKDROP_OPACITY : 0 },
|
|
712
|
-
]}
|
|
699
|
+
// Same reason as the centred dialog: the transition and the opacity it
|
|
700
|
+
// animates belong to the layers, not to the press target above them.
|
|
701
|
+
// No `dimOpacity` override — one darkness for every Bloom surface.
|
|
702
|
+
layerStyle={backdropTransition}
|
|
703
|
+
style={[sheetStyles.backdrop, { opacity: visible ? 1 : 0 }]}
|
|
713
704
|
/>
|
|
714
705
|
|
|
715
706
|
<View
|
|
@@ -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
|
|
|
@@ -286,7 +285,6 @@ export function DialogBottomSheet({
|
|
|
286
285
|
}
|
|
287
286
|
// Stronger dim than a lone sheet so an underlying sheet's handle/content
|
|
288
287
|
// doesn't bleed through when a Dialog is stacked over one.
|
|
289
|
-
backdropOpacity={SHEET_BACKDROP_OPACITY + 0.3}
|
|
290
288
|
// Measures the sheet card — the size a morph starts from.
|
|
291
289
|
onLayout={morphState.onPanelLayout}
|
|
292
290
|
style={sheetStyle}
|
package/src/dialog/placement.ts
CHANGED
|
@@ -83,8 +83,6 @@ export const CENTER_FADE_OUT_DURATION = 150;
|
|
|
83
83
|
/** CSS approximation of reanimated's `Easing.out(Easing.cubic)`. */
|
|
84
84
|
export const EASE_OUT = 'cubic-bezier(0.33, 1, 0.68, 1)';
|
|
85
85
|
|
|
86
|
-
/** Backdrop dim opacity once fully shown (matches `bg-black/40`). */
|
|
87
|
-
export const SHEET_BACKDROP_OPACITY = 0.4;
|
|
88
86
|
|
|
89
87
|
/** Stable testID for the dimmed backdrop of a side/bottom placement dialog. */
|
|
90
88
|
export const DIALOG_SHEET_BACKDROP_TESTID = 'bloom-dialog-sheet-backdrop';
|
package/src/overlay/index.tsx
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
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 {
|
|
42
|
+
import { memo, type ReactNode } from 'react';
|
|
43
43
|
import { BlurView } from 'expo-blur';
|
|
44
44
|
import {
|
|
45
45
|
Platform,
|
|
@@ -49,16 +49,26 @@ import {
|
|
|
49
49
|
type StyleProp,
|
|
50
50
|
type ViewStyle,
|
|
51
51
|
} from 'react-native';
|
|
52
|
+
import Animated, { useAnimatedStyle, type SharedValue } from 'react-native-reanimated';
|
|
52
53
|
|
|
53
54
|
import { WEB_POSITION_FIXED } from '../styles/web-view-style';
|
|
54
55
|
|
|
56
|
+
const AnimatedBlurView = Animated.createAnimatedComponent(BlurView);
|
|
57
|
+
|
|
55
58
|
/**
|
|
56
59
|
* One blur radius for every Bloom overlay. Surfaces differ in what they show,
|
|
57
60
|
* not in how the app behind them recedes.
|
|
58
61
|
*/
|
|
59
62
|
export const BACKDROP_BLUR_INTENSITY = 80;
|
|
60
|
-
/**
|
|
61
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Extra dim laid OVER the blur. The blur already paints its own tint wash
|
|
65
|
+
* (`rgba(25,25,25,~0.62)` at the intensity above), so this is what takes the
|
|
66
|
+
* total to roughly 0.72 — dark enough to read a panel against, still clearly
|
|
67
|
+
* see-through. Every surface uses this one value: with each of them picking its
|
|
68
|
+
* own, menus landed at ~0.83 and read as solid black next to the image viewer's
|
|
69
|
+
* ~0.72.
|
|
70
|
+
*/
|
|
71
|
+
export const BACKDROP_DIM_OPACITY = 0.28;
|
|
62
72
|
|
|
63
73
|
export interface OverlayRootProps {
|
|
64
74
|
children?: ReactNode;
|
|
@@ -85,6 +95,12 @@ OverlayRoot.displayName = 'OverlayRoot';
|
|
|
85
95
|
export interface BackdropProps {
|
|
86
96
|
/** Dismiss handler. Omit (or pass `disabled`) for a backdrop that only dims. */
|
|
87
97
|
onPress?: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Fade driver, 0 (hidden) → 1 (fully shown). The backdrop applies it to its
|
|
100
|
+
* OWN layers — see the note on `style` for why the caller must not animate
|
|
101
|
+
* opacity from the outside.
|
|
102
|
+
*/
|
|
103
|
+
progress?: SharedValue<number>;
|
|
88
104
|
/** `true` keeps the dim but makes it inert — a blocking dialog, a busy state. */
|
|
89
105
|
disabled?: boolean;
|
|
90
106
|
/** Blur radius behind the dim. `0` renders the dim alone. */
|
|
@@ -95,8 +111,19 @@ export interface BackdropProps {
|
|
|
95
111
|
dimColor?: string;
|
|
96
112
|
/** Dim opacity, 0–1. */
|
|
97
113
|
dimOpacity?: number;
|
|
98
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Geometry for the press target: insets, z-index, layout. NOT opacity —
|
|
116
|
+
* `backdrop-filter` samples nothing under an ancestor with `opacity < 1`
|
|
117
|
+
* (the group composites in isolation), so a fade applied here silently kills
|
|
118
|
+
* the blur. An `opacity` found in this style is redirected onto the layers;
|
|
119
|
+
* animate the fade through `progress` instead.
|
|
120
|
+
*/
|
|
99
121
|
style?: StyleProp<ViewStyle>;
|
|
122
|
+
/**
|
|
123
|
+
* Extra style for the blur + dim LAYERS, for fades a shared value can't
|
|
124
|
+
* express — the web dialog's CSS keyframes, for instance.
|
|
125
|
+
*/
|
|
126
|
+
layerStyle?: StyleProp<ViewStyle>;
|
|
100
127
|
/** Rendered ON TOP of the dim, inside the press target (Dialog's panel does this). */
|
|
101
128
|
children?: ReactNode;
|
|
102
129
|
accessibilityLabel?: string;
|
|
@@ -108,31 +135,40 @@ export interface BackdropProps {
|
|
|
108
135
|
* pointer events (that is its whole job), so anything that must stay pressable
|
|
109
136
|
* goes INSIDE it as `children`, never as a sibling rendered over it.
|
|
110
137
|
*/
|
|
111
|
-
export const Backdrop = memo(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
) {
|
|
138
|
+
export const Backdrop = memo(function Backdrop({
|
|
139
|
+
onPress,
|
|
140
|
+
progress,
|
|
141
|
+
disabled = false,
|
|
142
|
+
blurIntensity = BACKDROP_BLUR_INTENSITY,
|
|
143
|
+
blurTint = 'dark',
|
|
144
|
+
dimColor = '#000',
|
|
145
|
+
dimOpacity = BACKDROP_DIM_OPACITY,
|
|
146
|
+
style,
|
|
147
|
+
layerStyle,
|
|
148
|
+
children,
|
|
149
|
+
accessibilityLabel = 'Dismiss',
|
|
150
|
+
testID,
|
|
151
|
+
}: BackdropProps) {
|
|
126
152
|
const inert = disabled || !onPress;
|
|
153
|
+
|
|
154
|
+
// An `opacity` handed in through `style` would sit on the ancestor of the
|
|
155
|
+
// blur and kill it; hoist it onto the layers instead of honouring it there.
|
|
156
|
+
const flat: ViewStyle = StyleSheet.flatten(style) ?? {};
|
|
157
|
+
const { opacity: styleOpacity, ...rootStyle } = flat;
|
|
158
|
+
const staticOpacity = typeof styleOpacity === 'number' ? styleOpacity : 1;
|
|
159
|
+
|
|
160
|
+
// The fade lives on each LAYER, never on their shared ancestor.
|
|
161
|
+
const blurFade = useAnimatedStyle(
|
|
162
|
+
() => ({ opacity: (progress ? progress.value : 1) * staticOpacity }),
|
|
163
|
+
[progress, staticOpacity],
|
|
164
|
+
);
|
|
165
|
+
const dimFade = useAnimatedStyle(
|
|
166
|
+
() => ({ opacity: (progress ? progress.value : 1) * staticOpacity * dimOpacity }),
|
|
167
|
+
[progress, staticOpacity, dimOpacity],
|
|
168
|
+
);
|
|
169
|
+
|
|
127
170
|
return (
|
|
128
171
|
<Pressable
|
|
129
|
-
// MUST forward the ref: surfaces animate the backdrop through
|
|
130
|
-
// `Animated.createAnimatedComponent(Backdrop)`, and reanimated applies
|
|
131
|
-
// those updates by reaching the underlying host view. With the ref
|
|
132
|
-
// swallowed the animated style never lands, so a backdrop whose opacity
|
|
133
|
-
// is animated from 0 stays at 0 — fully transparent, with the surface
|
|
134
|
-
// floating over an undimmed app.
|
|
135
|
-
ref={ref}
|
|
136
172
|
pointerEvents="auto"
|
|
137
173
|
onPress={inert ? undefined : onPress}
|
|
138
174
|
disabled={inert}
|
|
@@ -143,27 +179,27 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
143
179
|
accessibilityRole={inert ? undefined : 'button'}
|
|
144
180
|
accessibilityLabel={inert ? undefined : accessibilityLabel}
|
|
145
181
|
testID={testID}
|
|
146
|
-
style={[StyleSheet.absoluteFill,
|
|
182
|
+
style={[StyleSheet.absoluteFill, rootStyle]}
|
|
147
183
|
>
|
|
148
184
|
{blurIntensity > 0 ? (
|
|
149
|
-
<
|
|
185
|
+
<AnimatedBlurView
|
|
150
186
|
intensity={blurIntensity}
|
|
151
187
|
tint={blurTint}
|
|
152
188
|
// Android's default blur is a no-op on many devices; this is the
|
|
153
189
|
// implementation that actually renders there.
|
|
154
190
|
experimentalBlurMethod="dimezisBlurView"
|
|
155
191
|
pointerEvents="none"
|
|
156
|
-
style={StyleSheet.absoluteFill}
|
|
192
|
+
style={[StyleSheet.absoluteFill, layerStyle, blurFade]}
|
|
157
193
|
/>
|
|
158
194
|
) : null}
|
|
159
|
-
<View
|
|
195
|
+
<Animated.View
|
|
160
196
|
pointerEvents="none"
|
|
161
|
-
style={[StyleSheet.absoluteFill, { backgroundColor: dimColor,
|
|
197
|
+
style={[StyleSheet.absoluteFill, { backgroundColor: dimColor }, layerStyle, dimFade]}
|
|
162
198
|
/>
|
|
163
199
|
{children}
|
|
164
200
|
</Pressable>
|
|
165
201
|
);
|
|
166
|
-
})
|
|
202
|
+
});
|
|
167
203
|
|
|
168
204
|
Backdrop.displayName = 'Backdrop';
|
|
169
205
|
|
|
@@ -74,8 +74,6 @@ import type {
|
|
|
74
74
|
} from './types';
|
|
75
75
|
|
|
76
76
|
const AnimatedImage = Animated.createAnimatedComponent(Image);
|
|
77
|
-
// The shared overlay backdrop, driven by the viewer's open/drag progress.
|
|
78
|
-
const AnimatedBackdrop = Animated.createAnimatedComponent(Backdrop);
|
|
79
77
|
|
|
80
78
|
// Web-only interaction hints (no-ops on native): the zoom surfaces are
|
|
81
79
|
// tap-to-dismiss (`cursor: pointer`) and must not select text or drag the
|
|
@@ -791,11 +789,14 @@ const ZoomableImageGalleryInner = React.forwardRef<ZoomableImageGalleryHandle, Z
|
|
|
791
789
|
did nothing on web while Escape still closed. `Backdrop` opts back
|
|
792
790
|
in via the `pointerEvents` PROP, the only form that reaches the DOM
|
|
793
791
|
(see `src/overlay`). */}
|
|
794
|
-
|
|
792
|
+
{/* `progress` rather than an animated style: an opacity on the
|
|
793
|
+
backdrop's root would sit ABOVE the blur layer and neutralise it —
|
|
794
|
+
`backdrop-filter` samples nothing under an ancestor that composites
|
|
795
|
+
in isolation. */}
|
|
796
|
+
<Backdrop
|
|
795
797
|
onPress={handleDismiss}
|
|
796
798
|
accessibilityLabel="Close image viewer"
|
|
797
|
-
|
|
798
|
-
style={backdropStyle}
|
|
799
|
+
progress={opacity}
|
|
799
800
|
/>
|
|
800
801
|
|
|
801
802
|
<GestureDetector gesture={panGesture}>
|