@oxyhq/bloom 0.86.0 → 0.87.1

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.
@@ -225,6 +225,111 @@ export interface BackdropProps {
225
225
  testID?: string;
226
226
  }
227
227
 
228
+ interface BackdropLayerProps {
229
+ blurIntensity: number;
230
+ blurTint: 'light' | 'dark' | 'default';
231
+ dimColor: string;
232
+ dimOpacity: number;
233
+ /** Opacity the caller asked for, already lifted off the press target. */
234
+ staticOpacity: number;
235
+ layerStyle?: StyleProp<ViewStyle>;
236
+ }
237
+
238
+ /**
239
+ * Layers fading under a shared value. Reanimated owns the opacity on both, so
240
+ * the fade runs off the JS thread and the caller never has to animate an
241
+ * ancestor (which would erase the blur — see `BackdropProps['style']`).
242
+ */
243
+ function AnimatedBackdropLayers({
244
+ progress,
245
+ blurIntensity,
246
+ blurTint,
247
+ dimColor,
248
+ dimOpacity,
249
+ staticOpacity,
250
+ layerStyle,
251
+ }: BackdropLayerProps & { progress: SharedValue<number> }) {
252
+ // The fade lives on each LAYER, never on their shared ancestor.
253
+ const blurFade = useAnimatedStyle(
254
+ () => ({ opacity: progress.value * staticOpacity }),
255
+ [progress, staticOpacity],
256
+ );
257
+ const dimFade = useAnimatedStyle(
258
+ () => ({ opacity: progress.value * staticOpacity * dimOpacity }),
259
+ [progress, staticOpacity, dimOpacity],
260
+ );
261
+
262
+ return (
263
+ <>
264
+ {blurIntensity > 0 ? (
265
+ <AnimatedBlurView
266
+ intensity={blurIntensity}
267
+ tint={blurTint}
268
+ // Android's default blur is a no-op on many devices; this is the
269
+ // implementation that actually renders there.
270
+ experimentalBlurMethod="dimezisBlurView"
271
+ pointerEvents="none"
272
+ style={[StyleSheet.absoluteFill, layerStyle, blurFade]}
273
+ />
274
+ ) : null}
275
+ <Animated.View
276
+ pointerEvents="none"
277
+ style={[StyleSheet.absoluteFill, { backgroundColor: dimColor }, layerStyle, dimFade]}
278
+ />
279
+ </>
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Layers with no shared value driving them: the opacity is whatever the caller
285
+ * asked for, and a caller that still wants movement hands a CSS transition down
286
+ * through `layerStyle` — which is exactly what the web side sheet does, flipping
287
+ * its own `opacity` between 0 and 1 and letting the browser interpolate.
288
+ *
289
+ * These MUST stay plain, non-reanimated components. On web, an animated
290
+ * component whose style carries `transitionProperty` is routed through
291
+ * reanimated's CSS transitions manager, which assigns straight into
292
+ * `element.style` — where `element` is whatever ref the wrapped component
293
+ * exposes. expo-blur's web `BlurView` exposes a `useImperativeHandle` object
294
+ * carrying only `setNativeProps`, so there is no `.style` on it and opening the
295
+ * surface throws `Cannot set properties of undefined (setting
296
+ * 'transitionProperty')`. A plain layer puts the transition on the DOM node
297
+ * itself, which is both where it belongs and the only place it works.
298
+ */
299
+ function StaticBackdropLayers({
300
+ blurIntensity,
301
+ blurTint,
302
+ dimColor,
303
+ dimOpacity,
304
+ staticOpacity,
305
+ layerStyle,
306
+ }: BackdropLayerProps) {
307
+ return (
308
+ <>
309
+ {blurIntensity > 0 ? (
310
+ <BlurView
311
+ intensity={blurIntensity}
312
+ tint={blurTint}
313
+ experimentalBlurMethod="dimezisBlurView"
314
+ pointerEvents="none"
315
+ // Opacity last, as in the animated pair: the level this component
316
+ // resolved wins over anything `layerStyle` happens to carry.
317
+ style={[StyleSheet.absoluteFill, layerStyle, { opacity: staticOpacity }]}
318
+ />
319
+ ) : null}
320
+ <View
321
+ pointerEvents="none"
322
+ style={[
323
+ StyleSheet.absoluteFill,
324
+ { backgroundColor: dimColor },
325
+ layerStyle,
326
+ { opacity: staticOpacity * dimOpacity },
327
+ ]}
328
+ />
329
+ </>
330
+ );
331
+ }
332
+
228
333
  /**
229
334
  * Full-bleed blur + dim that dismisses the surface when pressed.
230
335
  *
@@ -271,15 +376,14 @@ export const Backdrop = memo(function Backdrop({
271
376
  const staticOpacity = typeof styleOpacity === 'number' ? styleOpacity : 1;
272
377
  const resolvedDimColor = typeof styleBackground === 'string' ? styleBackground : dimColor;
273
378
 
274
- // The fade lives on each LAYER, never on their shared ancestor.
275
- const blurFade = useAnimatedStyle(
276
- () => ({ opacity: (progress ? progress.value : 1) * staticOpacity }),
277
- [progress, staticOpacity],
278
- );
279
- const dimFade = useAnimatedStyle(
280
- () => ({ opacity: (progress ? progress.value : 1) * staticOpacity * dimOpacity }),
281
- [progress, staticOpacity, dimOpacity],
282
- );
379
+ const layerProps: BackdropLayerProps = {
380
+ blurIntensity,
381
+ blurTint,
382
+ dimColor: resolvedDimColor,
383
+ dimOpacity,
384
+ staticOpacity,
385
+ layerStyle,
386
+ };
283
387
 
284
388
  return (
285
389
  // `box-none` so this box never takes a press itself: the hit box below and
@@ -298,21 +402,16 @@ export const Backdrop = memo(function Backdrop({
298
402
  testID={testID}
299
403
  style={StyleSheet.absoluteFill}
300
404
  >
301
- {blurIntensity > 0 ? (
302
- <AnimatedBlurView
303
- intensity={blurIntensity}
304
- tint={blurTint}
305
- // Android's default blur is a no-op on many devices; this is the
306
- // implementation that actually renders there.
307
- experimentalBlurMethod="dimezisBlurView"
308
- pointerEvents="none"
309
- style={[StyleSheet.absoluteFill, layerStyle, blurFade]}
310
- />
311
- ) : null}
312
- <Animated.View
313
- pointerEvents="none"
314
- style={[StyleSheet.absoluteFill, { backgroundColor: resolvedDimColor }, layerStyle, dimFade]}
315
- />
405
+ {/* Split into two components rather than branching inside one, so the
406
+ reanimated hooks stay unconditional AND a caller without a shared
407
+ value gets layers reanimated never touches. Which branch a call site
408
+ takes is fixed by whether it passes `progress` at all, so this never
409
+ swaps mid-life. */}
410
+ {progress ? (
411
+ <AnimatedBackdropLayers progress={progress} {...layerProps} />
412
+ ) : (
413
+ <StaticBackdropLayers {...layerProps} />
414
+ )}
316
415
  </Pressable>
317
416
  {children}
318
417
  </View>