@rific/auto-paper 0.11.0 → 0.12.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.
package/README.md CHANGED
@@ -201,6 +201,28 @@ 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
+
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.
207
+
208
+ ```tsx
209
+ // Theme.tsx
210
+ import { Provider as AutoPaperProvider, useThemeBridgeProps } from '@rific/auto-paper'
211
+ import { useCallback } from 'react'
212
+ import { shallowEqual, useDispatch, useSelector } from 'react-redux'
213
+
214
+ export function Theme({ children }: { children: React.ReactNode }) {
215
+ const settings = useSelector((state: RootState) => state.theme, shallowEqual)
216
+ const dispatch = useDispatch()
217
+ const onChange = useCallback((next: ThemeSettings) => dispatch(themeActions.initialize(next)), [dispatch])
218
+ const bridgeProps = useThemeBridgeProps({ initialValue: settings, onChange, onReady: SplashScreen.hideAsync })
219
+
220
+ return <AutoPaperProvider {...bridgeProps}>{children}</AutoPaperProvider>
221
+ }
222
+ ```
223
+
224
+ Keep the scope honest: this only replaces the final `<Provider initialValue={...} onChange={...} onReady={...}>` prop list with `<Provider {...bridgeProps}>`. It doesn't do the `useSelector`/`useDispatch` wiring above it — that's still yours to write, since `RootState`'s shape is app-local and this package has no opinion on it (or on state management being Redux at all; `useThemeBridgeProps` takes plain values and callbacks, not a `dispatch` function).
225
+
204
226
  ### With `useComputedTheme`
205
227
 
206
228
  Use the hook directly when you need to extend the theme before passing it to `PaperProvider`, for example to merge in a navigation theme or add custom color keys:
@@ -262,6 +284,21 @@ export default function App() {
262
284
  | `blur` | `boolean` | `true` |
263
285
  | `blurTint` | `number` | `0.2` |
264
286
 
287
+ ### `useThemeBridgeProps({ initialValue, onChange, onReady })`
288
+
289
+ Reshapes already-resolved values into the exact `Pick<ProviderProps, 'initialValue' | 'onChange' | 'onReady'>` shape `Provider` expects, memoized on those three fields so the returned object is referentially stable across re-renders until an input actually changes. Meant for a consuming app's own Redux-backed "Theme" component — see "With Redux" above — but takes plain values and callbacks rather than a `dispatch` function or anything Redux-specific, so it works with any state management, or none.
290
+
291
+ It only replaces the final prop list handed to `Provider`; it does not do any `useSelector`/`dispatch` wiring itself, and `onReady` is threaded through unchanged.
292
+
293
+ ```ts
294
+ import { useThemeBridgeProps } from '@rific/auto-paper'
295
+
296
+ const bridgeProps = useThemeBridgeProps({ initialValue: settings, onChange, onReady })
297
+ // → { initialValue, onChange, onReady }
298
+
299
+ <Provider {...bridgeProps}>{children}</Provider>
300
+ ```
301
+
265
302
  ### `useThemeSettings()`
266
303
 
267
304
  Returns `{ settings: ThemeSettings, set: (patch: Partial<ThemeSettings>) => void }`. Use this hook from any component inside `Provider` to read or update the current theme settings.
package/dist/index.d.mts CHANGED
@@ -318,6 +318,47 @@ declare function Provider({ children, defaults, expoBlur, fontFamily, initialVal
318
318
 
319
319
  declare const useAutoPaperTheme: () => AutoPaperTheme;
320
320
 
321
+ /**
322
+ * The subset of ProviderProps a consuming app's own Redux-backed "Theme" component is responsible
323
+ * for supplying. Every field here has an identically-named counterpart on ProviderProps itself
324
+ * (see ThemeProvider.tsx) - deliberately not renamed, so wiring this up needs no new vocabulary
325
+ * beyond the provider's own.
326
+ */
327
+ type ThemeBridgeProps = Pick<ProviderProps, 'initialValue' | 'onChange' | 'onReady'>;
328
+ /**
329
+ * Reshapes an app's own Redux-sourced theme state and callbacks into the exact prop object
330
+ * `<Provider>` expects, so a consuming app's own "Theme" component - every game in the fleet
331
+ * defines one, reading `state.theme` from its own store via `useSelector(..., shallowEqual)` and
332
+ * dispatching `themeActions.initialize` on change - can hand its own `useSelector`/`dispatch`
333
+ * results straight to this hook instead of re-deriving the same three-prop shape by hand in every
334
+ * app:
335
+ *
336
+ * ```tsx
337
+ * export function Theme({ children }: Props) {
338
+ * const settings = useSelector((state: RootState) => state.theme, shallowEqual)
339
+ * const dispatch = useDispatch()
340
+ * const onChange = useCallback((next: ThemeSettings) => dispatch(themeActions.initialize(next)), [dispatch])
341
+ * const onReady = useCallback(() => markSplashReady('theme'), [])
342
+ * const bridgeProps = useThemeBridgeProps({ initialValue: settings, onChange, onReady })
343
+ * return <Provider {...bridgeProps}>{children}</Provider>
344
+ * }
345
+ * ```
346
+ *
347
+ * Deliberately takes plain values and callbacks rather than a `dispatch` function or any
348
+ * Redux-specific type: this package has no opinion on any app's own RootState shape, or even that
349
+ * state management is Redux at all - the app does its own `useSelector`/`dispatch` wiring and
350
+ * passes just the results in.
351
+ *
352
+ * `onReady` is threaded through completely unchanged, never recomputed or wrapped here - it fires
353
+ * exactly once, the first time `<Provider>` itself reports its computed theme is ready, which is
354
+ * the timing a consuming app's own splash-gate mark depends on. A wrong `onReady` callback (e.g.
355
+ * one that marks the gate ready before `<Provider>` actually is) is a bug in the app's own
356
+ * callback, not something this hook can catch or fix - see BoxHockey's and LightCycles' own history
357
+ * of hand-fixing exactly that "marked ready before Provider was actually ready" bug in their own
358
+ * Theme.tsx before this hook existed.
359
+ */
360
+ declare function useThemeBridgeProps(props: ThemeBridgeProps): ThemeBridgeProps;
361
+
321
362
  declare const useThemeSettings: () => _rific_core.SettingsContextValue<ThemeSettings>;
322
363
 
323
364
  declare const getBlendedColor: (foreground: string, background: string, alpha: number) => string;
@@ -352,4 +393,4 @@ declare const isDarkColor: (value: string) => boolean;
352
393
 
353
394
  declare const resolveSeedColor: (color: string | TriadicPalette) => string;
354
395
 
355
- export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeSettings };
396
+ export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
package/dist/index.d.ts CHANGED
@@ -318,6 +318,47 @@ declare function Provider({ children, defaults, expoBlur, fontFamily, initialVal
318
318
 
319
319
  declare const useAutoPaperTheme: () => AutoPaperTheme;
320
320
 
321
+ /**
322
+ * The subset of ProviderProps a consuming app's own Redux-backed "Theme" component is responsible
323
+ * for supplying. Every field here has an identically-named counterpart on ProviderProps itself
324
+ * (see ThemeProvider.tsx) - deliberately not renamed, so wiring this up needs no new vocabulary
325
+ * beyond the provider's own.
326
+ */
327
+ type ThemeBridgeProps = Pick<ProviderProps, 'initialValue' | 'onChange' | 'onReady'>;
328
+ /**
329
+ * Reshapes an app's own Redux-sourced theme state and callbacks into the exact prop object
330
+ * `<Provider>` expects, so a consuming app's own "Theme" component - every game in the fleet
331
+ * defines one, reading `state.theme` from its own store via `useSelector(..., shallowEqual)` and
332
+ * dispatching `themeActions.initialize` on change - can hand its own `useSelector`/`dispatch`
333
+ * results straight to this hook instead of re-deriving the same three-prop shape by hand in every
334
+ * app:
335
+ *
336
+ * ```tsx
337
+ * export function Theme({ children }: Props) {
338
+ * const settings = useSelector((state: RootState) => state.theme, shallowEqual)
339
+ * const dispatch = useDispatch()
340
+ * const onChange = useCallback((next: ThemeSettings) => dispatch(themeActions.initialize(next)), [dispatch])
341
+ * const onReady = useCallback(() => markSplashReady('theme'), [])
342
+ * const bridgeProps = useThemeBridgeProps({ initialValue: settings, onChange, onReady })
343
+ * return <Provider {...bridgeProps}>{children}</Provider>
344
+ * }
345
+ * ```
346
+ *
347
+ * Deliberately takes plain values and callbacks rather than a `dispatch` function or any
348
+ * Redux-specific type: this package has no opinion on any app's own RootState shape, or even that
349
+ * state management is Redux at all - the app does its own `useSelector`/`dispatch` wiring and
350
+ * passes just the results in.
351
+ *
352
+ * `onReady` is threaded through completely unchanged, never recomputed or wrapped here - it fires
353
+ * exactly once, the first time `<Provider>` itself reports its computed theme is ready, which is
354
+ * the timing a consuming app's own splash-gate mark depends on. A wrong `onReady` callback (e.g.
355
+ * one that marks the gate ready before `<Provider>` actually is) is a bug in the app's own
356
+ * callback, not something this hook can catch or fix - see BoxHockey's and LightCycles' own history
357
+ * of hand-fixing exactly that "marked ready before Provider was actually ready" bug in their own
358
+ * Theme.tsx before this hook existed.
359
+ */
360
+ declare function useThemeBridgeProps(props: ThemeBridgeProps): ThemeBridgeProps;
361
+
321
362
  declare const useThemeSettings: () => _rific_core.SettingsContextValue<ThemeSettings>;
322
363
 
323
364
  declare const getBlendedColor: (foreground: string, background: string, alpha: number) => string;
@@ -352,4 +393,4 @@ declare const isDarkColor: (value: string) => boolean;
352
393
 
353
394
  declare const resolveSeedColor: (color: string | TriadicPalette) => string;
354
395
 
355
- export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeSettings };
396
+ export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
package/dist/index.js CHANGED
@@ -76,6 +76,7 @@ __export(index_exports, {
76
76
  useNavBarContext: () => useNavBarContext,
77
77
  usePaperDefaults: () => usePaperDefaults,
78
78
  useReanimatedModule: () => useReanimatedModule,
79
+ useThemeBridgeProps: () => useThemeBridgeProps,
79
80
  useThemeSettings: () => useThemeSettings
80
81
  });
81
82
  module.exports = __toCommonJS(index_exports);
@@ -939,8 +940,8 @@ var styles4 = import_react_native7.StyleSheet.create({
939
940
  harmonyRow: { marginBottom: 4 },
940
941
  innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
941
942
  pie: { overflow: "hidden" },
942
- rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
943
943
  ring: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
944
+ rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
944
945
  swatch: { alignItems: "center", justifyContent: "center" },
945
946
  swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
946
947
  trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE2 / 2, height: SIZE2, justifyContent: "center", width: SIZE2 },
@@ -1078,6 +1079,13 @@ var selectThemeHarmony = themeSlice.selectors.selectHarmony;
1078
1079
  var import_react_native_paper17 = require("react-native-paper");
1079
1080
  var useAutoPaperTheme = () => (0, import_react_native_paper17.useTheme)();
1080
1081
 
1082
+ // src/useThemeBridgeProps.ts
1083
+ var import_react12 = require("react");
1084
+ function useThemeBridgeProps(props) {
1085
+ const { initialValue, onChange, onReady } = props;
1086
+ return (0, import_react12.useMemo)(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady]);
1087
+ }
1088
+
1081
1089
  // src/utils/getThirdColor.ts
1082
1090
  function rgbToHsl(r, g, b) {
1083
1091
  r /= 255;
@@ -1207,5 +1215,6 @@ var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
1207
1215
  useNavBarContext,
1208
1216
  usePaperDefaults,
1209
1217
  useReanimatedModule,
1218
+ useThemeBridgeProps,
1210
1219
  useThemeSettings
1211
1220
  });
package/dist/index.mjs CHANGED
@@ -857,8 +857,8 @@ var styles4 = StyleSheet5.create({
857
857
  harmonyRow: { marginBottom: 4 },
858
858
  innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
859
859
  pie: { overflow: "hidden" },
860
- rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
861
860
  ring: { alignItems: "center", bottom: 0, justifyContent: "center", left: 0, position: "absolute", right: 0, top: 0 },
861
+ rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
862
862
  swatch: { alignItems: "center", justifyContent: "center" },
863
863
  swatches: { flexDirection: "row", flexWrap: "wrap", gap: SWATCH_GRID_GAP, paddingTop: 12 },
864
864
  trigger: { alignItems: "center", alignSelf: "flex-start", borderRadius: SIZE2 / 2, height: SIZE2, justifyContent: "center", width: SIZE2 },
@@ -996,6 +996,13 @@ var selectThemeHarmony = themeSlice.selectors.selectHarmony;
996
996
  import { useTheme as usePaperTheme } from "react-native-paper";
997
997
  var useAutoPaperTheme = () => usePaperTheme();
998
998
 
999
+ // src/useThemeBridgeProps.ts
1000
+ import { useMemo as useMemo2 } from "react";
1001
+ function useThemeBridgeProps(props) {
1002
+ const { initialValue, onChange, onReady } = props;
1003
+ return useMemo2(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady]);
1004
+ }
1005
+
999
1006
  // src/utils/getThirdColor.ts
1000
1007
  function rgbToHsl(r, g, b) {
1001
1008
  r /= 255;
@@ -1124,5 +1131,6 @@ export {
1124
1131
  useNavBarContext,
1125
1132
  usePaperDefaults,
1126
1133
  useReanimatedModule,
1134
+ useThemeBridgeProps,
1127
1135
  useThemeSettings
1128
1136
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
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",
@@ -68,11 +68,11 @@
68
68
  "esbuild": "0.28.1"
69
69
  },
70
70
  "devDependencies": {
71
- "@infinitetoken/eslint-config": "^0.1.8",
72
- "@infinitetoken/jest-config": "^0.2.0",
73
- "@infinitetoken/tsconfig": "^0.3.0",
71
+ "@infinitetoken/eslint-config": "0.2.0",
72
+ "@infinitetoken/jest-config": "^0.4.0",
73
+ "@infinitetoken/tsconfig": "^0.5.0",
74
74
  "@reduxjs/toolkit": "^2.0.0",
75
- "@rific/core": "^0.1.1",
75
+ "@rific/core": "^0.2.0",
76
76
  "@testing-library/dom": "^10.4.1",
77
77
  "@testing-library/react": "^16.0.0",
78
78
  "@types/jest": "^30.0.0",
@@ -119,8 +119,8 @@ const styles = StyleSheet.create({
119
119
  harmonyRow: { marginBottom: 4 },
120
120
  innerRotator: { height: '100%', left: '-100%', overflow: 'hidden', position: 'absolute', top: 0, width: '200%' },
121
121
  pie: { overflow: 'hidden' },
122
- rotator: { height: '100%', left: 0, overflow: 'hidden', position: 'absolute', top: 0, width: '100%' },
123
122
  ring: { alignItems: 'center', bottom: 0, justifyContent: 'center', left: 0, position: 'absolute', right: 0, top: 0 },
123
+ rotator: { height: '100%', left: 0, overflow: 'hidden', position: 'absolute', top: 0, width: '100%' },
124
124
  swatch: { alignItems: 'center', justifyContent: 'center' },
125
125
  swatches: { flexDirection: 'row', flexWrap: 'wrap', gap: SWATCH_GRID_GAP, paddingTop: 12 },
126
126
  trigger: { alignItems: 'center', alignSelf: 'flex-start', borderRadius: SIZE / 2, height: SIZE, justifyContent: 'center', width: SIZE },
package/src/index.ts CHANGED
@@ -35,6 +35,8 @@ export { defaultThemeSettings, ThemeSettingsContext } from './ThemeSettingsConte
35
35
  export type { AutoPaperTheme, SemanticColorRoles, ThemeAppearance } from './useComputedTheme'
36
36
  export { SEMANTIC_BASE_COLORS, useComputedTheme } from './useComputedTheme'
37
37
  export { useAutoPaperTheme } from './useTheme'
38
+ export type { ThemeBridgeProps } from './useThemeBridgeProps'
39
+ export { useThemeBridgeProps } from './useThemeBridgeProps'
38
40
  export { useThemeSettings } from './useThemeSettings'
39
41
  export { getBlendedColor } from './utils/getBlendedColor'
40
42
  export type { ColorRoles } from './utils/getColorRoles'
@@ -0,0 +1,48 @@
1
+ import { useMemo } from 'react'
2
+
3
+ import type { ProviderProps } from './ThemeProvider'
4
+
5
+ /**
6
+ * The subset of ProviderProps a consuming app's own Redux-backed "Theme" component is responsible
7
+ * for supplying. Every field here has an identically-named counterpart on ProviderProps itself
8
+ * (see ThemeProvider.tsx) - deliberately not renamed, so wiring this up needs no new vocabulary
9
+ * beyond the provider's own.
10
+ */
11
+ export type ThemeBridgeProps = Pick<ProviderProps, 'initialValue' | 'onChange' | 'onReady'>
12
+
13
+ /**
14
+ * Reshapes an app's own Redux-sourced theme state and callbacks into the exact prop object
15
+ * `<Provider>` expects, so a consuming app's own "Theme" component - every game in the fleet
16
+ * defines one, reading `state.theme` from its own store via `useSelector(..., shallowEqual)` and
17
+ * dispatching `themeActions.initialize` on change - can hand its own `useSelector`/`dispatch`
18
+ * results straight to this hook instead of re-deriving the same three-prop shape by hand in every
19
+ * app:
20
+ *
21
+ * ```tsx
22
+ * export function Theme({ children }: Props) {
23
+ * const settings = useSelector((state: RootState) => state.theme, shallowEqual)
24
+ * const dispatch = useDispatch()
25
+ * const onChange = useCallback((next: ThemeSettings) => dispatch(themeActions.initialize(next)), [dispatch])
26
+ * const onReady = useCallback(() => markSplashReady('theme'), [])
27
+ * const bridgeProps = useThemeBridgeProps({ initialValue: settings, onChange, onReady })
28
+ * return <Provider {...bridgeProps}>{children}</Provider>
29
+ * }
30
+ * ```
31
+ *
32
+ * Deliberately takes plain values and callbacks rather than a `dispatch` function or any
33
+ * Redux-specific type: this package has no opinion on any app's own RootState shape, or even that
34
+ * state management is Redux at all - the app does its own `useSelector`/`dispatch` wiring and
35
+ * passes just the results in.
36
+ *
37
+ * `onReady` is threaded through completely unchanged, never recomputed or wrapped here - it fires
38
+ * exactly once, the first time `<Provider>` itself reports its computed theme is ready, which is
39
+ * the timing a consuming app's own splash-gate mark depends on. A wrong `onReady` callback (e.g.
40
+ * one that marks the gate ready before `<Provider>` actually is) is a bug in the app's own
41
+ * callback, not something this hook can catch or fix - see BoxHockey's and LightCycles' own history
42
+ * of hand-fixing exactly that "marked ready before Provider was actually ready" bug in their own
43
+ * Theme.tsx before this hook existed.
44
+ */
45
+ export function useThemeBridgeProps(props: ThemeBridgeProps): ThemeBridgeProps {
46
+ const { initialValue, onChange, onReady } = props
47
+ return useMemo(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady])
48
+ }