@rific/auto-paper 0.9.3 → 0.9.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
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",
@@ -49,7 +49,9 @@ export function Provider({ children, defaults, expoBlur, initialValue, navigatio
49
49
  }, [])
50
50
 
51
51
  const onChangeRef = useRef(onChange)
52
- onChangeRef.current = onChange
52
+ useEffect(() => {
53
+ onChangeRef.current = onChange
54
+ })
53
55
  const prevSettings = useRef(settings)
54
56
  useEffect(() => {
55
57
  if (prevSettings.current !== settings) {
@@ -0,0 +1,17 @@
1
+ import { useThemeSettings } from '../useThemeSettings'
2
+ import { type AppearanceIcons, AppearancePicker } from './AppearancePicker'
3
+
4
+ type Props = {
5
+ showLabels?: boolean
6
+ icons?: AppearanceIcons
7
+ }
8
+
9
+ // AppearancePicker wired straight to the Provider's own ThemeSettings, for the common case where
10
+ // the appearance being edited IS the whole app's own theme. Takes neither `value` nor `onChange`
11
+ // — reads/writes useThemeSettings() itself, so there's nothing for a consumer to wire up. Reach
12
+ // for the plain AppearancePicker instead when it needs to be controlled by something other than
13
+ // the global theme (a local preview before committing, one-off UI unrelated to Provider).
14
+ export const AutoAppearancePicker = (props: Props) => {
15
+ const { settings, set } = useThemeSettings()
16
+ return <AppearancePicker {...props} value={settings.appearance} onChange={(appearance) => set({ appearance })} />
17
+ }
@@ -0,0 +1,21 @@
1
+ import { useThemeSettings } from '../useThemeSettings'
2
+ import { resolveSeedColor } from '../utils/resolveSeedColor'
3
+ import type { SeedColor } from './ColorPicker'
4
+ import { PalettePicker, type PaletteWeights } from './PalettePicker'
5
+
6
+ type Props = {
7
+ weights?: PaletteWeights
8
+ colors?: SeedColor[]
9
+ blur?: boolean
10
+ }
11
+
12
+ // PalettePicker wired straight to the Provider's own ThemeSettings — both color AND harmony —
13
+ // for the common case where the palette being edited IS the whole app's own theme. Takes none of
14
+ // `value`/`onChange`/`harmony`/`onHarmonyChange` — reads/writes useThemeSettings() itself
15
+ // (resolveSeedColor narrows ThemeSettings.color's `string | TriadicPalette` down to the plain
16
+ // string PalettePicker's own `value` wants). Reach for the plain PalettePicker instead when it
17
+ // needs to be controlled by something other than the global theme.
18
+ export const AutoPalettePicker = (props: Props) => {
19
+ const { settings, set } = useThemeSettings()
20
+ return <PalettePicker {...props} value={resolveSeedColor(settings.color)} onChange={(color) => set({ color })} harmony={settings.harmony} onHarmonyChange={(harmony) => set({ harmony })} />
21
+ }
@@ -1,4 +1,4 @@
1
- import React, { type ComponentType, type ReactNode, useEffect, useRef, useState } from 'react'
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 = useRef(new Animated.Value(visible ? 1 : 0)).current
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/index.ts CHANGED
@@ -2,6 +2,8 @@ export { useBlur } from './BlurContext'
2
2
  export { Appbar } from './components/Appbar'
3
3
  export type { AppearanceIcons } from './components/AppearancePicker'
4
4
  export { AppearancePicker } from './components/AppearancePicker'
5
+ export { AutoAppearancePicker } from './components/AutoAppearancePicker'
6
+ export { AutoPalettePicker } from './components/AutoPalettePicker'
5
7
  export type { BlurViewProps, ExpoBlurModule } from './components/BlurView'
6
8
  export { BlurView } from './components/BlurView'
7
9
  export { BottomNavigation } from './components/BottomNavigation'
@@ -47,6 +49,7 @@ export { getTonalColor } from './utils/getTonalColor'
47
49
  export type { ColorHarmony, TriadicPalette } from './utils/getTriadicPalette'
48
50
  export { getTriadicPalette } from './utils/getTriadicPalette'
49
51
  export { isDarkColor } from './utils/isDarkColor'
52
+ export { resolveSeedColor } from './utils/resolveSeedColor'
50
53
  export type { AppbarHeaderProps } from 'react-native-paper'
51
54
  export type { BottomNavigationProps } from 'react-native-paper'
52
55
  export type { BottomNavigationRoute } from 'react-native-paper'
@@ -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
 
@@ -0,0 +1,8 @@
1
+ import type { TriadicPalette } from './getTriadicPalette'
2
+
3
+ // ThemeSettings.color is `string | TriadicPalette` — a plain seed hex normally, or an explicit
4
+ // triad when a consumer bypassed harmony math entirely (see the README's own "Explicit triad"
5
+ // section). Anything that needs a single representative color from it — a picker's own `value`,
6
+ // a card's accent — narrows via this: `.primary` is that triad's own seed, so it's the
7
+ // representative color on the rare path where this branch is actually hit.
8
+ export const resolveSeedColor = (color: string | TriadicPalette): string => (typeof color === 'string' ? color : color.primary)