@rific/auto-paper 0.9.4 → 0.10.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/README.md +10 -0
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +15 -6
- package/dist/index.mjs +18 -9
- package/package.json +2 -1
- package/src/ThemeProvider.tsx +23 -5
- package/src/components/Dialog.tsx +7 -4
- package/src/useComputedTheme.ts +1 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Adaptive theming for [`react-native-paper`](https://callstack.github.io/react-na
|
|
|
10
10
|
- System/light/dark appearance with live updates via `Appearance` API
|
|
11
11
|
- Tinted surface, surfaceVariant, outline, and elevation levels derived from the seed
|
|
12
12
|
- Fixed `success`/`warning`/`danger` semantic color roles (each with `on*`/`*Container` variants) alongside MD3's built-in `error`
|
|
13
|
+
- App-wide `fontFamily` override, applied across every Paper typography variant in one prop
|
|
13
14
|
- Optional Redux slice for wiring appearance and color into your store
|
|
14
15
|
- Wrapper components (`Appbar`, `AppearancePicker`, `AutoAppearancePicker`, `AutoPalettePicker`, `BlurView`, `BottomNavigation`, `Button`, `Chip`, `ColorPicker`, `Dialog`, `FAB`, `HarmonyPicker`, `IconButton`, `Menu`, `PalettePicker`, `TextInput`) with prop defaults via context
|
|
15
16
|
- All color utilities exported for standalone use
|
|
@@ -61,6 +62,14 @@ export default function App() {
|
|
|
61
62
|
</AutoPaperProvider>
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
Pass `fontFamily` to reskin every Paper component's text to a custom typeface app-wide, instead of passing `fontFamily` to each individual `Text`/`TextInput`/`Button` yourself:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<AutoPaperProvider initialValue={{ appearance: 'system', color: '#6750a4' }} fontFamily='Menlo'>
|
|
69
|
+
{/* your app */}
|
|
70
|
+
</AutoPaperProvider>
|
|
71
|
+
```
|
|
72
|
+
|
|
64
73
|
Update theme settings from anywhere inside the tree using `useThemeSettings`:
|
|
65
74
|
|
|
66
75
|
```tsx
|
|
@@ -235,6 +244,7 @@ export default function App() {
|
|
|
235
244
|
| `children` | `ReactNode` | |
|
|
236
245
|
| `defaults` | `PaperDefaults` | Prop defaults for wrapper components (see below) |
|
|
237
246
|
| `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. |
|
|
247
|
+
| `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. |
|
|
238
248
|
| `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. |
|
|
239
249
|
| `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 |
|
|
240
250
|
| `onReady` | `() => void` | Called once when the theme first resolves |
|
package/dist/index.d.mts
CHANGED
|
@@ -305,6 +305,12 @@ type ProviderProps = {
|
|
|
305
305
|
defaults?: PaperDefaults;
|
|
306
306
|
/** Injects expo-blur for `<BlurView>`. Pass `import * as ExpoBlur from 'expo-blur'`; omit to always render BlurView's solid fallback. */
|
|
307
307
|
expoBlur?: ExpoBlurModule;
|
|
308
|
+
/** Applied to every Paper typography variant (bodyLarge, headlineMedium, labelSmall, ...) via
|
|
309
|
+
* `configureFonts`'s flat-config mode — each variant keeps its own MD3 fontSize/lineHeight/weight,
|
|
310
|
+
* only fontFamily changes. The one-line way to reskin an app's whole text to a custom typeface,
|
|
311
|
+
* instead of passing `fontFamily` to every individual Text/TextInput/Button across the app. Omit
|
|
312
|
+
* to keep each platform's system font (MD3's own default). */
|
|
313
|
+
fontFamily?: string;
|
|
308
314
|
initialValue?: Partial<ThemeSettings>;
|
|
309
315
|
/** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
|
|
310
316
|
navigationBar?: ExpoNavigationBarModule;
|
|
@@ -316,7 +322,7 @@ type ProviderProps = {
|
|
|
316
322
|
statusBarProps?: StatusBarProps;
|
|
317
323
|
style?: StyleProp<ViewStyle>;
|
|
318
324
|
};
|
|
319
|
-
declare function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element | null;
|
|
325
|
+
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element | null;
|
|
320
326
|
|
|
321
327
|
declare const useAutoPaperTheme: () => AutoPaperTheme;
|
|
322
328
|
|
package/dist/index.d.ts
CHANGED
|
@@ -305,6 +305,12 @@ type ProviderProps = {
|
|
|
305
305
|
defaults?: PaperDefaults;
|
|
306
306
|
/** Injects expo-blur for `<BlurView>`. Pass `import * as ExpoBlur from 'expo-blur'`; omit to always render BlurView's solid fallback. */
|
|
307
307
|
expoBlur?: ExpoBlurModule;
|
|
308
|
+
/** Applied to every Paper typography variant (bodyLarge, headlineMedium, labelSmall, ...) via
|
|
309
|
+
* `configureFonts`'s flat-config mode — each variant keeps its own MD3 fontSize/lineHeight/weight,
|
|
310
|
+
* only fontFamily changes. The one-line way to reskin an app's whole text to a custom typeface,
|
|
311
|
+
* instead of passing `fontFamily` to every individual Text/TextInput/Button across the app. Omit
|
|
312
|
+
* to keep each platform's system font (MD3's own default). */
|
|
313
|
+
fontFamily?: string;
|
|
308
314
|
initialValue?: Partial<ThemeSettings>;
|
|
309
315
|
/** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
|
|
310
316
|
navigationBar?: ExpoNavigationBarModule;
|
|
@@ -316,7 +322,7 @@ type ProviderProps = {
|
|
|
316
322
|
statusBarProps?: StatusBarProps;
|
|
317
323
|
style?: StyleProp<ViewStyle>;
|
|
318
324
|
};
|
|
319
|
-
declare function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element | null;
|
|
325
|
+
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element | null;
|
|
320
326
|
|
|
321
327
|
declare const useAutoPaperTheme: () => AutoPaperTheme;
|
|
322
328
|
|
package/dist/index.js
CHANGED
|
@@ -685,13 +685,15 @@ var BlurModuleContext = (0, import_react7.createContext)(void 0);
|
|
|
685
685
|
var useBlurModule = () => (0, import_react7.useContext)(BlurModuleContext);
|
|
686
686
|
var ReanimatedModuleContext = (0, import_react7.createContext)(void 0);
|
|
687
687
|
var useReanimatedModule = () => (0, import_react7.useContext)(ReanimatedModuleContext);
|
|
688
|
-
function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
688
|
+
function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
689
689
|
const [settings, setSettings] = (0, import_react7.useState)(() => ({ ...defaultThemeSettings, ...initialValue }));
|
|
690
690
|
const set = (0, import_react7.useCallback)((patch) => {
|
|
691
691
|
setSettings((prev) => ({ ...prev, ...patch }));
|
|
692
692
|
}, []);
|
|
693
693
|
const onChangeRef = (0, import_react7.useRef)(onChange);
|
|
694
|
-
|
|
694
|
+
(0, import_react7.useEffect)(() => {
|
|
695
|
+
onChangeRef.current = onChange;
|
|
696
|
+
});
|
|
695
697
|
const prevSettings = (0, import_react7.useRef)(settings);
|
|
696
698
|
(0, import_react7.useEffect)(() => {
|
|
697
699
|
if (prevSettings.current !== settings) {
|
|
@@ -699,7 +701,12 @@ function Provider({ children, defaults, expoBlur, initialValue, navigationBar, o
|
|
|
699
701
|
onChangeRef.current?.(settings);
|
|
700
702
|
}
|
|
701
703
|
}, [settings]);
|
|
702
|
-
const
|
|
704
|
+
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
|
|
705
|
+
const theme = (0, import_react7.useMemo)(() => {
|
|
706
|
+
if (!computedTheme) return null;
|
|
707
|
+
if (!fontFamily) return computedTheme;
|
|
708
|
+
return { ...computedTheme, fonts: (0, import_react_native_paper4.configureFonts)({ config: { fontFamily } }) };
|
|
709
|
+
}, [computedTheme, fontFamily]);
|
|
703
710
|
const called = (0, import_react7.useRef)(false);
|
|
704
711
|
(0, import_react7.useEffect)(() => {
|
|
705
712
|
if (theme && !called.current) {
|
|
@@ -756,18 +763,20 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
756
763
|
const { colors, isV3, roundness } = theme;
|
|
757
764
|
const { scale } = theme.animation;
|
|
758
765
|
const { bottom, left, right, top } = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
|
|
759
|
-
const opacity = (0, import_react9.
|
|
766
|
+
const [opacity] = (0, import_react9.useState)(() => new import_react_native5.Animated.Value(visible ? 1 : 0));
|
|
760
767
|
const [mounted, setMounted] = (0, import_react9.useState)(visible);
|
|
768
|
+
if (visible && !mounted) {
|
|
769
|
+
setMounted(true);
|
|
770
|
+
}
|
|
761
771
|
(0, import_react9.useEffect)(() => {
|
|
762
772
|
if (visible) {
|
|
763
|
-
setMounted(true);
|
|
764
773
|
import_react_native5.Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: import_react_native5.Easing.out(import_react_native5.Easing.cubic), toValue: 1, useNativeDriver: true }).start();
|
|
765
774
|
} else {
|
|
766
775
|
import_react_native5.Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: import_react_native5.Easing.out(import_react_native5.Easing.cubic), toValue: 0, useNativeDriver: true }).start(({ finished }) => {
|
|
767
776
|
if (finished) setMounted(false);
|
|
768
777
|
});
|
|
769
778
|
}
|
|
770
|
-
}, [visible]);
|
|
779
|
+
}, [visible, opacity, scale]);
|
|
771
780
|
(0, import_react9.useEffect)(() => {
|
|
772
781
|
if (!visible) return void 0;
|
|
773
782
|
const onHardwareBackPress = () => {
|
package/dist/index.mjs
CHANGED
|
@@ -417,15 +417,15 @@ import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "reac
|
|
|
417
417
|
import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper";
|
|
418
418
|
|
|
419
419
|
// src/components/Dialog.tsx
|
|
420
|
-
import React, { useEffect as useEffect3,
|
|
420
|
+
import React, { useEffect as useEffect3, useState as useState4 } from "react";
|
|
421
421
|
import { Animated, BackHandler, Easing, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
422
422
|
import { Dialog as PaperDialog, Portal, useTheme as useTheme3 } from "react-native-paper";
|
|
423
423
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
424
424
|
|
|
425
425
|
// src/ThemeProvider.tsx
|
|
426
|
-
import { createContext as createContext3, useCallback as useCallback2, useContext as useContext4, useEffect as useEffect2, useRef, useState as useState3 } from "react";
|
|
426
|
+
import { createContext as createContext3, useCallback as useCallback2, useContext as useContext4, useEffect as useEffect2, useMemo, useRef, useState as useState3 } from "react";
|
|
427
427
|
import { StatusBar as StatusBar2, StyleSheet, View } from "react-native";
|
|
428
|
-
import { Provider as PaperProvider } from "react-native-paper";
|
|
428
|
+
import { configureFonts, Provider as PaperProvider } from "react-native-paper";
|
|
429
429
|
|
|
430
430
|
// src/useComputedTheme.ts
|
|
431
431
|
import { useCallback, useEffect, useState as useState2 } from "react";
|
|
@@ -603,13 +603,15 @@ var BlurModuleContext = createContext3(void 0);
|
|
|
603
603
|
var useBlurModule = () => useContext4(BlurModuleContext);
|
|
604
604
|
var ReanimatedModuleContext = createContext3(void 0);
|
|
605
605
|
var useReanimatedModule = () => useContext4(ReanimatedModuleContext);
|
|
606
|
-
function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
606
|
+
function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
607
607
|
const [settings, setSettings] = useState3(() => ({ ...defaultThemeSettings, ...initialValue }));
|
|
608
608
|
const set = useCallback2((patch) => {
|
|
609
609
|
setSettings((prev) => ({ ...prev, ...patch }));
|
|
610
610
|
}, []);
|
|
611
611
|
const onChangeRef = useRef(onChange);
|
|
612
|
-
|
|
612
|
+
useEffect2(() => {
|
|
613
|
+
onChangeRef.current = onChange;
|
|
614
|
+
});
|
|
613
615
|
const prevSettings = useRef(settings);
|
|
614
616
|
useEffect2(() => {
|
|
615
617
|
if (prevSettings.current !== settings) {
|
|
@@ -617,7 +619,12 @@ function Provider({ children, defaults, expoBlur, initialValue, navigationBar, o
|
|
|
617
619
|
onChangeRef.current?.(settings);
|
|
618
620
|
}
|
|
619
621
|
}, [settings]);
|
|
620
|
-
const
|
|
622
|
+
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
|
|
623
|
+
const theme = useMemo(() => {
|
|
624
|
+
if (!computedTheme) return null;
|
|
625
|
+
if (!fontFamily) return computedTheme;
|
|
626
|
+
return { ...computedTheme, fonts: configureFonts({ config: { fontFamily } }) };
|
|
627
|
+
}, [computedTheme, fontFamily]);
|
|
621
628
|
const called = useRef(false);
|
|
622
629
|
useEffect2(() => {
|
|
623
630
|
if (theme && !called.current) {
|
|
@@ -674,18 +681,20 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
674
681
|
const { colors, isV3, roundness } = theme;
|
|
675
682
|
const { scale } = theme.animation;
|
|
676
683
|
const { bottom, left, right, top } = useSafeAreaInsets();
|
|
677
|
-
const opacity =
|
|
684
|
+
const [opacity] = useState4(() => new Animated.Value(visible ? 1 : 0));
|
|
678
685
|
const [mounted, setMounted] = useState4(visible);
|
|
686
|
+
if (visible && !mounted) {
|
|
687
|
+
setMounted(true);
|
|
688
|
+
}
|
|
679
689
|
useEffect3(() => {
|
|
680
690
|
if (visible) {
|
|
681
|
-
setMounted(true);
|
|
682
691
|
Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: Easing.out(Easing.cubic), toValue: 1, useNativeDriver: true }).start();
|
|
683
692
|
} else {
|
|
684
693
|
Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: Easing.out(Easing.cubic), toValue: 0, useNativeDriver: true }).start(({ finished }) => {
|
|
685
694
|
if (finished) setMounted(false);
|
|
686
695
|
});
|
|
687
696
|
}
|
|
688
|
-
}, [visible]);
|
|
697
|
+
}, [visible, opacity, scale]);
|
|
689
698
|
useEffect3(() => {
|
|
690
699
|
if (!visible) return void 0;
|
|
691
700
|
const onHardwareBackPress = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/auto-paper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"eslint-config-prettier": "^10.1.8",
|
|
77
77
|
"eslint-plugin-package-json": "^1.0.0",
|
|
78
78
|
"eslint-plugin-prettier": "^5.5.5",
|
|
79
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
79
80
|
"eslint-plugin-react-native": "^5.0.0",
|
|
80
81
|
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
81
82
|
"expo-blur": "^56.0.3",
|
package/src/ThemeProvider.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createContext, type ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react'
|
|
1
|
+
import { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { StatusBar, type StatusBarProps, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
3
|
-
import { Provider as PaperProvider } from 'react-native-paper'
|
|
3
|
+
import { configureFonts, 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'
|
|
@@ -29,6 +29,12 @@ export type ProviderProps = {
|
|
|
29
29
|
defaults?: PaperDefaults
|
|
30
30
|
/** Injects expo-blur for `<BlurView>`. Pass `import * as ExpoBlur from 'expo-blur'`; omit to always render BlurView's solid fallback. */
|
|
31
31
|
expoBlur?: ExpoBlurModule
|
|
32
|
+
/** Applied to every Paper typography variant (bodyLarge, headlineMedium, labelSmall, ...) via
|
|
33
|
+
* `configureFonts`'s flat-config mode — each variant keeps its own MD3 fontSize/lineHeight/weight,
|
|
34
|
+
* only fontFamily changes. The one-line way to reskin an app's whole text to a custom typeface,
|
|
35
|
+
* instead of passing `fontFamily` to every individual Text/TextInput/Button across the app. Omit
|
|
36
|
+
* to keep each platform's system font (MD3's own default). */
|
|
37
|
+
fontFamily?: string
|
|
32
38
|
initialValue?: Partial<ThemeSettings>
|
|
33
39
|
/** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
|
|
34
40
|
navigationBar?: ExpoNavigationBarModule
|
|
@@ -41,7 +47,7 @@ export type ProviderProps = {
|
|
|
41
47
|
style?: StyleProp<ViewStyle>
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
export function Provider({ children, defaults, expoBlur, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
|
|
50
|
+
export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
|
|
45
51
|
const [settings, setSettings] = useState<ThemeSettings>(() => ({ ...defaultThemeSettings, ...initialValue }))
|
|
46
52
|
|
|
47
53
|
const set = useCallback((patch: Partial<ThemeSettings>) => {
|
|
@@ -49,7 +55,9 @@ export function Provider({ children, defaults, expoBlur, initialValue, navigatio
|
|
|
49
55
|
}, [])
|
|
50
56
|
|
|
51
57
|
const onChangeRef = useRef(onChange)
|
|
52
|
-
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
onChangeRef.current = onChange
|
|
60
|
+
})
|
|
53
61
|
const prevSettings = useRef(settings)
|
|
54
62
|
useEffect(() => {
|
|
55
63
|
if (prevSettings.current !== settings) {
|
|
@@ -58,7 +66,17 @@ export function Provider({ children, defaults, expoBlur, initialValue, navigatio
|
|
|
58
66
|
}
|
|
59
67
|
}, [settings])
|
|
60
68
|
|
|
61
|
-
const
|
|
69
|
+
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony)
|
|
70
|
+
// Layered on top of useComputedTheme's own output rather than folded into it — that hook only
|
|
71
|
+
// ever recomputes for a color/appearance/harmony change, and fontFamily is a completely
|
|
72
|
+
// independent axis; merging it there would mean either re-running the whole color computation on
|
|
73
|
+
// every fontFamily change (wasteful) or threading fontFamily through as a fourth computeTheme
|
|
74
|
+
// dependency for a value it never actually uses.
|
|
75
|
+
const theme = useMemo(() => {
|
|
76
|
+
if (!computedTheme) return null
|
|
77
|
+
if (!fontFamily) return computedTheme
|
|
78
|
+
return { ...computedTheme, fonts: configureFonts({ config: { fontFamily } }) }
|
|
79
|
+
}, [computedTheme, fontFamily])
|
|
62
80
|
const called = useRef(false)
|
|
63
81
|
|
|
64
82
|
useEffect(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { type ComponentType, type ReactNode, useEffect,
|
|
1
|
+
import React, { type ComponentType, type ReactNode, useEffect, useState } from 'react'
|
|
2
2
|
import { Animated, BackHandler, Easing, Pressable, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
3
3
|
import { Dialog as PaperDialog, type DialogActionsProps, type DialogContentProps, type DialogProps as PaperDialogProps, type DialogScrollAreaProps, type DialogTitleProps, Portal, useTheme } from 'react-native-paper'
|
|
4
4
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
@@ -46,19 +46,22 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
46
46
|
const { colors, isV3, roundness } = theme
|
|
47
47
|
const { scale } = theme.animation
|
|
48
48
|
const { bottom, left, right, top } = useSafeAreaInsets()
|
|
49
|
-
const opacity =
|
|
49
|
+
const [opacity] = useState(() => new Animated.Value(visible ? 1 : 0))
|
|
50
50
|
const [mounted, setMounted] = useState(visible)
|
|
51
51
|
|
|
52
|
+
if (visible && !mounted) {
|
|
53
|
+
setMounted(true)
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
useEffect(() => {
|
|
53
57
|
if (visible) {
|
|
54
|
-
setMounted(true)
|
|
55
58
|
Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: Easing.out(Easing.cubic), toValue: 1, useNativeDriver: true }).start()
|
|
56
59
|
} else {
|
|
57
60
|
Animated.timing(opacity, { duration: scale * ANIMATION_DURATION, easing: Easing.out(Easing.cubic), toValue: 0, useNativeDriver: true }).start(({ finished }) => {
|
|
58
61
|
if (finished) setMounted(false)
|
|
59
62
|
})
|
|
60
63
|
}
|
|
61
|
-
}, [visible])
|
|
64
|
+
}, [visible, opacity, scale])
|
|
62
65
|
|
|
63
66
|
useEffect(() => {
|
|
64
67
|
if (!visible) return undefined
|
package/src/useComputedTheme.ts
CHANGED
|
@@ -147,6 +147,7 @@ export function useComputedTheme(appearance: ThemeAppearance, color: string | Tr
|
|
|
147
147
|
}, [appearance, primaryInput, secondaryInput, tertiaryInput, harmony])
|
|
148
148
|
|
|
149
149
|
useEffect(() => {
|
|
150
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: first render must stay theme-less (README's onReady/splash-screen contract) and Appearance must not be read during render (SSR/hydration risk)
|
|
150
151
|
computeTheme()
|
|
151
152
|
}, [computeTheme])
|
|
152
153
|
|