@rific/auto-paper 0.12.0 → 0.12.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 CHANGED
@@ -201,6 +201,8 @@ dispatch(themeActions.setColor('#e91e63'))
201
201
  dispatch(themeActions.setAppearance('dark'))
202
202
  ```
203
203
 
204
+ Dispatching `themeActions` from anywhere in the app reaches the Provider because `initialValue` is live after mount: the Provider adopts any value that changes. That needs @rific/core 0.3.0 or later. With @rific/core 0.2.1 or older, `initialValue` is only read at mount, so write through the Provider's own setters instead.
205
+
204
206
  The `App.tsx` example above hand-builds the `initialValue`/`onReady` prop pair inline. Once you also need `onChange` wired back to `dispatch`, reach for `useThemeBridgeProps` instead of re-deriving that same three-field object by hand: it takes your already-resolved `settings`/`onChange`/`onReady` and reshapes them into the exact, memoized prop bag `Provider` expects.
205
207
 
206
208
  ```tsx
@@ -263,7 +265,7 @@ export default function App() {
263
265
  | `onChange` | `(settings: ThemeSettings) => void` | Called whenever settings change via `useThemeSettings().set()` |
264
266
  | `children` | `ReactNode` | |
265
267
  | `defaults` | `PaperDefaults` | Prop defaults for wrapper components (see below) |
266
- | `expoBlur` | `ExpoBlurModule` | Injects `expo-blur` (`import * as ExpoBlur from 'expo-blur'`) so `<BlurView>` renders the real frosted-glass effect. Omit to always render its solid fallback. |
268
+ | `expoBlur` | `ExpoBlurModule` | Injects `expo-blur` (`import * as ExpoBlur from 'expo-blur'`) so `<BlurView>` renders the real frosted-glass effect. Omit to always render its solid fallback. On Android, expo-blur only blurs when given a `blurTarget` and a `blurMethod` (`'dimezisBlurView'`, or `'dimezisBlurViewSdk31Plus'` on API 31+); without both, `<BlurView>` renders the solid fallback there too, rather than expo-blur's unblurred translucent tint. |
267
269
  | `fontFamily` | `string` | Applied to every Paper typography variant (`bodyLarge`, `headlineMedium`, `labelSmall`, ...) via `configureFonts`'s flat-config mode — each variant keeps its own MD3 fontSize/lineHeight/weight, only fontFamily changes. Omit to keep each platform's system font. |
268
270
  | `navigationBar` | `ExpoNavigationBarModule` | Injects `expo-navigation-bar` (`import * as ExpoNavigationBar from 'expo-navigation-bar'`) so the Android nav bar icon style auto-syncs with the theme while a `BottomNavigation` is mounted. Omit (and don't pass `onNavBarChange`) to skip nav bar syncing entirely. |
269
271
  | `onNavBarChange` | `(color: string, dark: boolean) => void` | Overrides the built-in nav bar sync: called on Android when the theme changes while a `BottomNavigation` is mounted, instead of the automatic `navigationBar`-driven icon-style sync |
@@ -272,6 +274,8 @@ export default function App() {
272
274
  | `statusBarProps` | `StatusBarProps` | Spread over the auto-derived `StatusBar` defaults |
273
275
  | `style` | `StyleProp<ViewStyle>` | Applied to the wrapper `View` |
274
276
 
277
+ `Provider` also renders its own `Portal.Host`, nested inside everything it injects, so anything rendered through `react-native-paper`'s `Portal` (this package's `Dialog`, Paper's `Menu` and `Tooltip`, or a `Modal`/`Snackbar`/anything else you wrap in `<Portal>` yourself) still sees `expoBlur`, `reanimated` and `defaults`. Paper's own host sits above all of those and its `Portal` only carries the theme across, so without this a portaled `Dialog` or `Menu` would silently render its solid fallback. Contexts your app provides *inside* `Provider` (a toast provider, say) are a separate matter: portal content can't see them unless you render another `<Portal.Host>` below them.
278
+
275
279
  `ThemeSettings` fields:
276
280
 
277
281
  | Field | Type | Default |
package/dist/index.js CHANGED
@@ -706,7 +706,7 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
706
706
  if (!theme) return null;
707
707
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native_paper4.Provider, { theme, children: [
708
708
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
709
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) })
709
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) })
710
710
  ] });
711
711
  }
712
712
  var styles = import_react_native3.StyleSheet.create({
@@ -718,6 +718,11 @@ var import_react6 = require("react");
718
718
  var import_react_native4 = require("react-native");
719
719
  var import_react_native_paper5 = require("react-native-paper");
720
720
  var import_jsx_runtime5 = require("react/jsx-runtime");
721
+ var blursOnAndroid = ({ blurMethod, blurTarget, experimentalBlurMethod }) => {
722
+ const method = blurMethod ?? experimentalBlurMethod ?? "none";
723
+ if (!blurTarget || method === "none") return false;
724
+ return method === "dimezisBlurView" || Number(import_react_native4.Platform.Version) >= 31;
725
+ };
721
726
  var ELEVATION_OPACITY = { 0: 0, 1: 0.05, 2: 0.08, 3: 0.11, 4: 0.12, 5: 0.14 };
722
727
  var VARIANT_TINT_OPACITY = { light: 0.12, dark: 0.18 };
723
728
  var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacity, variant = "surface", ...props }) => {
@@ -729,7 +734,7 @@ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacit
729
734
  const resolvedTintColor = tintColor ?? variantColor ?? (elevation !== void 0 ? colors.primary : void 0);
730
735
  const resolvedTintOpacity = tintOpacity ?? (tintColor ? dark ? 0.24 : 0.16 : variantColor ? dark ? VARIANT_TINT_OPACITY.dark : VARIANT_TINT_OPACITY.light : elevation !== void 0 ? ELEVATION_OPACITY[elevation] : 0);
731
736
  const tint = resolvedTintColor && resolvedTintOpacity > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [import_react_native4.StyleSheet.absoluteFill, { backgroundColor: resolvedTintColor, opacity: resolvedTintOpacity }] }) : null;
732
- if (blur && expoBlur)
737
+ if (blur && expoBlur && (import_react_native4.Platform.OS !== "android" || blursOnAndroid(props)))
733
738
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(expoBlur.BlurView, { ...props, tint: dark ? "dark" : "light", style, children: [
734
739
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: [import_react_native4.StyleSheet.absoluteFill, { backgroundColor: surfaceColor, opacity: settings.blurTint }] }),
735
740
  tint,
package/dist/index.mjs CHANGED
@@ -417,13 +417,13 @@ import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper
417
417
  // src/components/Dialog.tsx
418
418
  import React, { useEffect as useEffect3, useState as useState3 } from "react";
419
419
  import { Animated, BackHandler, Easing, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
420
- import { Dialog as PaperDialog, Portal, useTheme as useTheme3 } from "react-native-paper";
420
+ import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
421
421
  import { useSafeAreaInsets } from "react-native-safe-area-context";
422
422
 
423
423
  // src/ThemeProvider.tsx
424
424
  import { createContext as createContext2, useContext as useContext3, useEffect as useEffect2, useMemo, useRef } from "react";
425
425
  import { StatusBar as StatusBar2, StyleSheet, View } from "react-native";
426
- import { configureFonts, Provider as PaperProvider } from "react-native-paper";
426
+ import { configureFonts, Portal, Provider as PaperProvider } from "react-native-paper";
427
427
 
428
428
  // src/useComputedTheme.ts
429
429
  import { useCallback, useEffect, useState as useState2 } from "react";
@@ -623,7 +623,7 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
623
623
  if (!theme) return null;
624
624
  return /* @__PURE__ */ jsxs2(PaperProvider, { theme, children: [
625
625
  /* @__PURE__ */ jsx4(StatusBar2, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
626
- /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx4(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx4(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx4(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) })
626
+ /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx4(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx4(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx4(Portal.Host, { children: /* @__PURE__ */ jsx4(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) })
627
627
  ] });
628
628
  }
629
629
  var styles = StyleSheet.create({
@@ -632,9 +632,14 @@ var styles = StyleSheet.create({
632
632
 
633
633
  // src/components/BlurView.tsx
634
634
  import { useContext as useContext4 } from "react";
635
- import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
635
+ import { Platform, StyleSheet as StyleSheet2, View as View2 } from "react-native";
636
636
  import { useTheme as useTheme2 } from "react-native-paper";
637
637
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
638
+ var blursOnAndroid = ({ blurMethod, blurTarget, experimentalBlurMethod }) => {
639
+ const method = blurMethod ?? experimentalBlurMethod ?? "none";
640
+ if (!blurTarget || method === "none") return false;
641
+ return method === "dimezisBlurView" || Number(Platform.Version) >= 31;
642
+ };
638
643
  var ELEVATION_OPACITY = { 0: 0, 1: 0.05, 2: 0.08, 3: 0.11, 4: 0.12, 5: 0.14 };
639
644
  var VARIANT_TINT_OPACITY = { light: 0.12, dark: 0.18 };
640
645
  var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacity, variant = "surface", ...props }) => {
@@ -646,7 +651,7 @@ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacit
646
651
  const resolvedTintColor = tintColor ?? variantColor ?? (elevation !== void 0 ? colors.primary : void 0);
647
652
  const resolvedTintOpacity = tintOpacity ?? (tintColor ? dark ? 0.24 : 0.16 : variantColor ? dark ? VARIANT_TINT_OPACITY.dark : VARIANT_TINT_OPACITY.light : elevation !== void 0 ? ELEVATION_OPACITY[elevation] : 0);
648
653
  const tint = resolvedTintColor && resolvedTintOpacity > 0 ? /* @__PURE__ */ jsx5(View2, { style: [StyleSheet2.absoluteFill, { backgroundColor: resolvedTintColor, opacity: resolvedTintOpacity }] }) : null;
649
- if (blur && expoBlur)
654
+ if (blur && expoBlur && (Platform.OS !== "android" || blursOnAndroid(props)))
650
655
  return /* @__PURE__ */ jsxs3(expoBlur.BlurView, { ...props, tint: dark ? "dark" : "light", style, children: [
651
656
  /* @__PURE__ */ jsx5(View2, { style: [StyleSheet2.absoluteFill, { backgroundColor: surfaceColor, opacity: settings.blurTint }] }),
652
657
  tint,
@@ -705,7 +710,7 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
705
710
  return child;
706
711
  });
707
712
  const borderRadius = (isV3 ? 7 : 1) * roundness;
708
- return /* @__PURE__ */ jsx6(Portal, { children: /* @__PURE__ */ jsxs4(Animated.View, { accessibilityLiveRegion: "polite", accessibilityViewIsModal: true, pointerEvents: visible ? "box-none" : "none", style: [StyleSheet3.absoluteFill, { opacity }], testID, children: [
713
+ return /* @__PURE__ */ jsx6(Portal2, { children: /* @__PURE__ */ jsxs4(Animated.View, { accessibilityLiveRegion: "polite", accessibilityViewIsModal: true, pointerEvents: visible ? "box-none" : "none", style: [StyleSheet3.absoluteFill, { opacity }], testID, children: [
709
714
  /* @__PURE__ */ jsx6(Pressable, { accessibilityLabel: "Close modal", accessibilityRole: "button", disabled: !dismissable, onPress: dismissable ? onDismiss : void 0, style: [StyleSheet3.absoluteFill, { backgroundColor: colors.backdrop }], testID: `${testID}-backdrop` }),
710
715
  /* @__PURE__ */ jsx6(View3, { pointerEvents: "box-none", style: [styles2.wrapper, { marginBottom: bottom, marginTop: top, paddingHorizontal: Math.max(left, right, DIALOG_MARGIN) }], testID: `${testID}-wrapper`, children: reanimated && animatedStyle ? /* @__PURE__ */ jsx6(reanimated.View, { style: animatedStyle, testID: `${testID}-animated-wrapper`, children: /* @__PURE__ */ jsx6(BlurView, { blur, style: [styles2.card, { borderRadius }, style], testID: `${testID}-surface`, children: content }) }) : /* @__PURE__ */ jsx6(BlurView, { blur, style: [styles2.card, { borderRadius }, style], testID: `${testID}-surface`, children: content }) })
711
716
  ] }) });
@@ -876,7 +881,7 @@ var AutoPalettePicker = (props) => {
876
881
 
877
882
  // src/components/BottomNavigation.tsx
878
883
  import { useEffect as useEffect4 } from "react";
879
- import { Platform } from "react-native";
884
+ import { Platform as Platform2 } from "react-native";
880
885
  import { BottomNavigation as PaperBottomNavigation, useTheme as useTheme6 } from "react-native-paper";
881
886
 
882
887
  // src/navigation-bar.ts
@@ -891,7 +896,7 @@ function useNavigationBarSync() {
891
896
  const theme = useTheme6();
892
897
  const { navigationBar, onNavBarChange } = useNavBarContext();
893
898
  useEffect4(() => {
894
- if (Platform.OS !== "android") return;
899
+ if (Platform2.OS !== "android") return;
895
900
  if (onNavBarChange) onNavBarChange(theme.colors.surface, theme.dark);
896
901
  else if (navigationBar) setNavigationBarStyle(navigationBar, theme.dark);
897
902
  }, [theme, onNavBarChange, navigationBar]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Adaptive react-native-paper theming: triadic palette from a seed color + system/light/dark appearance",
5
5
  "keywords": [
6
6
  "react-native",
@@ -1,6 +1,6 @@
1
1
  import { createContext, type ReactNode, useContext, useEffect, useMemo, useRef } from 'react'
2
2
  import { StatusBar, type StatusBarProps, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
3
- import { configureFonts, Provider as PaperProvider } from 'react-native-paper'
3
+ import { configureFonts, Portal, Provider as PaperProvider } from 'react-native-paper'
4
4
 
5
5
  import type { ExpoBlurModule } from './components/BlurView'
6
6
  import type { ReanimatedModule } from './components/Dialog'
@@ -99,7 +99,17 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
99
99
  <NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
100
100
  <BlurModuleContext.Provider value={expoBlur}>
101
101
  <ReanimatedModuleContext.Provider value={reanimated}>
102
- <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
102
+ {/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
103
+ Portal only carries its theme and settings across, so anything rendered through a
104
+ Portal (this package's Dialog, Paper's Menu and Tooltip, or anything an app wraps
105
+ in <Portal> itself) would otherwise lose expoBlur, reanimated, defaults and the nav
106
+ bar hooks: Dialog/Menu's blur silently fell back to solid. This nested host is the
107
+ nearest one for every Portal under Provider, so their content mounts inside all of
108
+ these contexts instead. It wraps the styled View, not the other way round, so
109
+ portals still cover the full area regardless of `style`. */}
110
+ <Portal.Host>
111
+ <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
112
+ </Portal.Host>
103
113
  </ReanimatedModuleContext.Provider>
104
114
  </BlurModuleContext.Provider>
105
115
  </NavBarContext.Provider>
@@ -1,5 +1,5 @@
1
1
  import { type ComponentType, type ReactNode, type RefObject, useContext } from 'react'
2
- import { type StyleProp, StyleSheet, View, type ViewProps, type ViewStyle } from 'react-native'
2
+ import { Platform, type StyleProp, StyleSheet, View, type ViewProps, type ViewStyle } from 'react-native'
3
3
  import { useTheme } from 'react-native-paper'
4
4
 
5
5
  import { useBlurModule } from '../ThemeProvider'
@@ -33,6 +33,18 @@ type ExpoBlurViewProps = {
33
33
  tint?: ExpoBlurTint
34
34
  } & ViewProps
35
35
 
36
+ // On Android, expo-blur only really blurs when it has both a blurTarget and a blur method
37
+ // ('dimezisBlurView', or 'dimezisBlurViewSdk31Plus' on API 31+); blurMethod defaults to 'none'.
38
+ // Otherwise it paints its tint as a flat translucent fill (roughly 0.35-0.39 alpha at the default
39
+ // intensity) with the screen showing through unblurred, so BlurView renders its solid look instead.
40
+ // Checks the blurTarget ref object, not its .current, same as expo-blur's own JS: the target view
41
+ // usually commits after this render, so .current is still null here even when wired correctly.
42
+ const blursOnAndroid = ({ blurMethod, blurTarget, experimentalBlurMethod }: ExpoBlurViewProps) => {
43
+ const method = blurMethod ?? experimentalBlurMethod ?? 'none'
44
+ if (!blurTarget || method === 'none') return false
45
+ return method === 'dimezisBlurView' || Number(Platform.Version) >= 31
46
+ }
47
+
36
48
  const ELEVATION_OPACITY: Record<number, number> = { 0: 0, 1: 0.05, 2: 0.08, 3: 0.11, 4: 0.12, 5: 0.14 }
37
49
  const VARIANT_TINT_OPACITY = { light: 0.12, dark: 0.18 }
38
50
 
@@ -55,7 +67,7 @@ export const BlurView = ({ blur = true, children, elevation, style, tintColor, t
55
67
  const resolvedTintColor = tintColor ?? variantColor ?? (elevation !== undefined ? colors.primary : undefined)
56
68
  const resolvedTintOpacity = tintOpacity ?? (tintColor ? (dark ? 0.24 : 0.16) : variantColor ? (dark ? VARIANT_TINT_OPACITY.dark : VARIANT_TINT_OPACITY.light) : elevation !== undefined ? ELEVATION_OPACITY[elevation] : 0)
57
69
  const tint = resolvedTintColor && resolvedTintOpacity > 0 ? <View style={[StyleSheet.absoluteFill, { backgroundColor: resolvedTintColor, opacity: resolvedTintOpacity }]} /> : null
58
- if (blur && expoBlur)
70
+ if (blur && expoBlur && (Platform.OS !== 'android' || blursOnAndroid(props)))
59
71
  return (
60
72
  <expoBlur.BlurView {...props} tint={dark ? 'dark' : 'light'} style={style}>
61
73
  <View style={[StyleSheet.absoluteFill, { backgroundColor: surfaceColor, opacity: settings.blurTint }]} />