@oxyhq/bloom 0.59.0 → 0.59.2
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 -2
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js +16 -17
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.js +1 -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/DialogContent.js +15 -4
- package/lib/commonjs/dialog/DialogContent.js.map +1 -1
- package/lib/commonjs/dialog/SheetShell.js +1 -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 +6 -4
- 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/module/bottom-sheet/BottomSheetBase.js +17 -18
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/dialog/Dialog.js +1 -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/DialogContent.js +16 -5
- package/lib/module/dialog/DialogContent.js.map +1 -1
- package/lib/module/dialog/SheetShell.js +1 -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 +6 -4
- 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/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/DialogContent.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/types.d.ts +5 -0
- package/lib/typescript/commonjs/dialog/types.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 +4 -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/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/DialogContent.d.ts.map +1 -1
- package/lib/typescript/module/dialog/SheetShell.d.ts.map +1 -1
- package/lib/typescript/module/dialog/types.d.ts +5 -0
- package/lib/typescript/module/dialog/types.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 +4 -2
- package/lib/typescript/module/overlay/index.d.ts.map +1 -1
- package/lib/typescript/module/provider/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/BottomSheet.test.tsx +46 -1
- package/src/__tests__/Dialog.test.tsx +33 -1
- package/src/bottom-sheet/BottomSheetBase.tsx +16 -17
- package/src/dialog/Dialog.tsx +1 -4
- package/src/dialog/DialogBottomSheet.tsx +0 -4
- package/src/dialog/DialogContent.tsx +16 -6
- package/src/dialog/SheetShell.tsx +1 -2
- package/src/dialog/types.ts +5 -0
- package/src/overlay/blur-target.tsx +45 -0
- package/src/overlay/index.tsx +9 -4
- package/src/provider/index.tsx +14 -11
|
@@ -25,7 +25,8 @@ import Animated, {
|
|
|
25
25
|
withTiming,
|
|
26
26
|
} from 'react-native-reanimated';
|
|
27
27
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
28
|
-
import { Backdrop } from '../overlay';
|
|
28
|
+
import { Backdrop, BACKDROP_DIM_OPACITY } from '../overlay';
|
|
29
|
+
import { useBloomBlurTarget } from '../overlay/blur-target';
|
|
29
30
|
import { useTheme } from '../theme/use-theme';
|
|
30
31
|
|
|
31
32
|
/** Hook that returns current screen dimensions and updates on rotation/resize. */
|
|
@@ -215,7 +216,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
215
216
|
onDismissAttempt,
|
|
216
217
|
detached = false,
|
|
217
218
|
showHandle = true,
|
|
218
|
-
backdropOpacity =
|
|
219
|
+
backdropOpacity = BACKDROP_DIM_OPACITY,
|
|
219
220
|
scrollable = true,
|
|
220
221
|
manualActivation = false,
|
|
221
222
|
dynamicBackdrop = false,
|
|
@@ -226,6 +227,7 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
226
227
|
} = props;
|
|
227
228
|
|
|
228
229
|
const insets = useSafeAreaInsets();
|
|
230
|
+
const blurTarget = useBloomBlurTarget();
|
|
229
231
|
const theme = useTheme();
|
|
230
232
|
const { colors } = theme;
|
|
231
233
|
const { height: screenHeight } = useScreenDimensions();
|
|
@@ -637,11 +639,11 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
637
639
|
// Native (with the plugin) auto-tracks and ignores the extra deps, so this
|
|
638
640
|
// is safe on both platforms. Do NOT strip the shared values from these deps.
|
|
639
641
|
//
|
|
640
|
-
// `opacity.value` drives the
|
|
641
|
-
//
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
//
|
|
642
|
+
// `opacity.value` drives the whole blur+dim layer in/out (0 -> 1).
|
|
643
|
+
// `backdropOpacity` belongs ONLY to Backdrop's black dim layer below; if it
|
|
644
|
+
// is multiplied into this group opacity instead, the opaque black child
|
|
645
|
+
// hides the BlurView and Android looks like a plain dark scrim.
|
|
646
|
+
// `dynamicBackdrop` fades the composed blur+dim proportionally with drag.
|
|
645
647
|
const backdropStyle = useAnimatedStyle(() => {
|
|
646
648
|
const dragFactor = dynamicBackdrop
|
|
647
649
|
? interpolate(
|
|
@@ -652,9 +654,9 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
652
654
|
)
|
|
653
655
|
: 1;
|
|
654
656
|
return {
|
|
655
|
-
opacity: opacity.value *
|
|
657
|
+
opacity: opacity.value * dragFactor,
|
|
656
658
|
};
|
|
657
|
-
}, [
|
|
659
|
+
}, [dynamicBackdrop, opacity, translateY, screenHeightSV]);
|
|
658
660
|
|
|
659
661
|
const sheetStyle = useAnimatedStyle(() => {
|
|
660
662
|
const scale = interpolate(translateY.value, [0, screenHeightSV.value], [1, 0.95]);
|
|
@@ -792,12 +794,12 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
792
794
|
</Animated.View>
|
|
793
795
|
) : (
|
|
794
796
|
// 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.
|
|
797
|
+
// `backdropStyle` drives the entrance/drag fade; the black
|
|
798
|
+
// dim stays translucent so the BlurView remains visible.
|
|
798
799
|
<AnimatedBackdrop
|
|
799
800
|
onPress={handleBackdropPress}
|
|
800
|
-
dimOpacity={
|
|
801
|
+
dimOpacity={backdropOpacity}
|
|
802
|
+
blurTarget={blurTarget ?? undefined}
|
|
801
803
|
style={[styles.backdrop, backdropStyle]}
|
|
802
804
|
/>
|
|
803
805
|
)}
|
|
@@ -827,10 +829,7 @@ BottomSheetBase.displayName = 'BottomSheetBase';
|
|
|
827
829
|
|
|
828
830
|
const styles = StyleSheet.create({
|
|
829
831
|
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
832
|
flex: 1,
|
|
833
|
-
backgroundColor: '#000',
|
|
834
833
|
},
|
|
835
834
|
backdropTouchable: {
|
|
836
835
|
flex: 1,
|
|
@@ -846,7 +845,7 @@ const styles = StyleSheet.create({
|
|
|
846
845
|
sheetDetached: {
|
|
847
846
|
left: 16,
|
|
848
847
|
right: 16,
|
|
849
|
-
borderRadius:
|
|
848
|
+
borderRadius: 28,
|
|
850
849
|
},
|
|
851
850
|
sheetNormal: {
|
|
852
851
|
left: 0,
|
package/src/dialog/Dialog.tsx
CHANGED
|
@@ -251,7 +251,7 @@ function CenteredOrSideDialog({
|
|
|
251
251
|
// All four corners rounded — bloom's BottomSheet defaults to top-only
|
|
252
252
|
// radius in flush mode, but we use `detached` so the whole card is
|
|
253
253
|
// floating and rounded uniformly.
|
|
254
|
-
borderRadius:
|
|
254
|
+
borderRadius: 28,
|
|
255
255
|
},
|
|
256
256
|
// Drives the card's `height` only while a morph is in flight; at rest it
|
|
257
257
|
// resolves to `height: 'auto'` — the card's normal content sizing.
|
|
@@ -344,9 +344,6 @@ function CenteredOrSideDialog({
|
|
|
344
344
|
/>
|
|
345
345
|
) : undefined
|
|
346
346
|
}
|
|
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
347
|
// Measures the floating card — the size a morph starts from.
|
|
351
348
|
onLayout={morphState.onPanelLayout}
|
|
352
349
|
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}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
ActivityIndicator,
|
|
3
4
|
Text,
|
|
4
5
|
TouchableOpacity,
|
|
5
6
|
View,
|
|
@@ -91,11 +92,13 @@ function ActionButton({ action }: { action: DialogAction }) {
|
|
|
91
92
|
const theme = useTheme();
|
|
92
93
|
const color: DialogActionColor = action.color ?? 'default';
|
|
93
94
|
const shouldCloseOnPress = action.shouldCloseOnPress ?? true;
|
|
95
|
+
const inactive = action.disabled === true || action.loading === true;
|
|
94
96
|
|
|
95
97
|
const { background, foreground } = getActionPalette(color, theme.colors);
|
|
96
98
|
|
|
97
99
|
const handlePress = useCallback(
|
|
98
100
|
(e: GestureResponderEvent) => {
|
|
101
|
+
if (inactive) return;
|
|
99
102
|
const onPress = action.onPress;
|
|
100
103
|
if (color === 'cancel') {
|
|
101
104
|
// Cancel always dismisses; consumer's onPress (rare) runs after.
|
|
@@ -108,7 +111,7 @@ function ActionButton({ action }: { action: DialogAction }) {
|
|
|
108
111
|
onPress?.(e);
|
|
109
112
|
}
|
|
110
113
|
},
|
|
111
|
-
[action.onPress, close, color, shouldCloseOnPress],
|
|
114
|
+
[action.onPress, close, color, inactive, shouldCloseOnPress],
|
|
112
115
|
);
|
|
113
116
|
|
|
114
117
|
return (
|
|
@@ -118,18 +121,25 @@ function ActionButton({ action }: { action: DialogAction }) {
|
|
|
118
121
|
alignItems: 'center',
|
|
119
122
|
justifyContent: 'center',
|
|
120
123
|
backgroundColor: background,
|
|
121
|
-
opacity:
|
|
124
|
+
opacity: inactive ? 0.5 : 1,
|
|
122
125
|
paddingVertical: 12,
|
|
123
126
|
paddingHorizontal: 24,
|
|
124
127
|
}}
|
|
125
128
|
onPress={handlePress}
|
|
126
|
-
disabled={
|
|
129
|
+
disabled={inactive}
|
|
130
|
+
accessibilityState={{ disabled: inactive, busy: action.loading === true }}
|
|
131
|
+
aria-disabled={inactive}
|
|
132
|
+
aria-busy={action.loading || undefined}
|
|
127
133
|
activeOpacity={0.7}
|
|
128
134
|
testID={action.testID}
|
|
129
135
|
>
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
|
|
136
|
+
{action.loading ? (
|
|
137
|
+
<ActivityIndicator color={foreground} size="small" />
|
|
138
|
+
) : (
|
|
139
|
+
<Text style={{ fontSize: 16, fontWeight: '500', color: foreground }}>
|
|
140
|
+
{action.label}
|
|
141
|
+
</Text>
|
|
142
|
+
)}
|
|
133
143
|
</TouchableOpacity>
|
|
134
144
|
);
|
|
135
145
|
}
|
|
@@ -87,7 +87,7 @@ export function SheetShell({
|
|
|
87
87
|
() => ({
|
|
88
88
|
maxWidth: 500,
|
|
89
89
|
backgroundColor: theme.colors.background,
|
|
90
|
-
borderRadius:
|
|
90
|
+
borderRadius: 28,
|
|
91
91
|
}),
|
|
92
92
|
[theme.colors.background],
|
|
93
93
|
);
|
|
@@ -98,7 +98,6 @@ export function SheetShell({
|
|
|
98
98
|
onDismiss={handleDismiss}
|
|
99
99
|
enablePanDownToClose
|
|
100
100
|
detached
|
|
101
|
-
backdropOpacity={0.7}
|
|
102
101
|
style={sheetStyle}
|
|
103
102
|
>
|
|
104
103
|
<Context.Provider value={context}>
|
package/src/dialog/types.ts
CHANGED
|
@@ -185,6 +185,11 @@ export type DialogAction = {
|
|
|
185
185
|
onPress?: (e: GestureResponderEvent) => void;
|
|
186
186
|
color?: DialogActionColor;
|
|
187
187
|
disabled?: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Replaces the label with a progress indicator and disables interaction while
|
|
190
|
+
* an async action is in flight.
|
|
191
|
+
*/
|
|
192
|
+
loading?: boolean;
|
|
188
193
|
/**
|
|
189
194
|
* Defaults to `true`. When `true`, the dialog closes (and runs the close
|
|
190
195
|
* animation) before `onPress` is invoked, which gives the surrounding
|
|
@@ -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,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 { forwardRef, memo, type ReactNode } from 'react';
|
|
42
|
+
import { forwardRef, memo, type ReactNode, type RefObject } from 'react';
|
|
43
43
|
import { BlurView } from 'expo-blur';
|
|
44
44
|
import {
|
|
45
45
|
Platform,
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
StyleSheet,
|
|
48
48
|
View,
|
|
49
49
|
type StyleProp,
|
|
50
|
+
type View as NativeView,
|
|
50
51
|
type ViewStyle,
|
|
51
52
|
} from 'react-native';
|
|
52
53
|
|
|
@@ -95,6 +96,8 @@ export interface BackdropProps {
|
|
|
95
96
|
dimColor?: string;
|
|
96
97
|
/** Dim opacity, 0–1. */
|
|
97
98
|
dimOpacity?: number;
|
|
99
|
+
/** Android view sampled by the native blur. Modal surfaces receive this from BloomProvider. */
|
|
100
|
+
blurTarget?: RefObject<NativeView | null>;
|
|
98
101
|
/** Extra style on the press target — animated opacity, insets, z-index. */
|
|
99
102
|
style?: StyleProp<ViewStyle>;
|
|
100
103
|
/** Rendered ON TOP of the dim, inside the press target (Dialog's panel does this). */
|
|
@@ -116,6 +119,7 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
116
119
|
blurTint = 'dark',
|
|
117
120
|
dimColor = '#000',
|
|
118
121
|
dimOpacity = BACKDROP_DIM_OPACITY,
|
|
122
|
+
blurTarget,
|
|
119
123
|
style,
|
|
120
124
|
children,
|
|
121
125
|
accessibilityLabel = 'Dismiss',
|
|
@@ -149,9 +153,10 @@ export const Backdrop = memo(forwardRef<View, BackdropProps>(function Backdrop(
|
|
|
149
153
|
<BlurView
|
|
150
154
|
intensity={blurIntensity}
|
|
151
155
|
tint={blurTint}
|
|
152
|
-
|
|
153
|
-
//
|
|
154
|
-
|
|
156
|
+
blurTarget={blurTarget}
|
|
157
|
+
// Expo disables Android blur without an explicit target. Sheets get
|
|
158
|
+
// one from BloomProvider; targetless surfaces degrade without warning.
|
|
159
|
+
blurMethod={Platform.OS === 'android' && blurTarget ? 'dimezisBlurViewSdk31Plus' : undefined}
|
|
155
160
|
pointerEvents="none"
|
|
156
161
|
style={StyleSheet.absoluteFill}
|
|
157
162
|
/>
|
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
|
|