import React, { createContext, PropsWithChildren, useCallback, useEffect, useState, } from 'react'; import { Appearance, Dimensions, StyleSheet, View, useWindowDimensions, } from 'react-native'; import { fonts, fontSizes, titleFontSizes } from './fonts'; import { colorsDark, colorsLight, zIndices, sizes, borderRadius, } from './defaultValues'; import { FontTypes, ThemeContextProps, ThemeContextValue, ThemeProps, } from './types'; import { ColorName, ColorSchema } from './color.type'; import { getColorValue } from './utils'; import { useSetThemeBody } from '../../hooks/useSetThemeBody'; import { ScreenLoadingProvider } from '../screen-loading'; const dimensions = Dimensions.get('screen'); const colorScheme = Appearance.getColorScheme(); const initialColorFunction = ( colors: ColorSchema ): ThemeContextValue['colors'] => { return { ...colors, get: (color: ColorName) => { return getColorValue(color, colors); }, }; }; type InternalStateTheme = ThemeProps; const initialValue: InternalStateTheme = { theme: colorScheme === 'dark' ? 'dark' : 'light', isDark: colorScheme === 'dark', fonts: fonts as FontTypes, fontSizes, width: dimensions.width, height: dimensions.height, titleFontSizes, activeOpacity: 0.6, zIndices, sizes, spacing: 8, borderWidth: 1, darkColors: colorsDark, lightColors: colorsLight, borderRadius, colors: colorScheme === 'dark' ? initialColorFunction(colorsDark) : initialColorFunction(colorsLight), }; const themeContextDefaultValue: ThemeContextValue = { ...initialValue, maxHeight: dimensions.height, maxWidth: dimensions.width, setContextTheme: () => {}, setTheme: () => {}, }; export const ThemeContext = createContext( themeContextDefaultValue ); export const resolveSize = ( maxValue: number, value?: ThemeContextProps['width'] ) => { if (typeof value === 'function') { return value(maxValue); } return value || undefined; }; export const ThemeProvider: React.FC> = React.memo(({ children, onChangeTheme, theme, ...props }) => { const { width, height } = useWindowDimensions(); const [internalTheme, setInternalTheme] = useState({ ...initialValue, }); const setContextTheme = React.useCallback( (_theme: ThemeContextProps['theme']) => { setInternalTheme((prevTheme) => { const themeScheme = _theme?.theme ?? prevTheme?.theme; const darkColors = Object.assign( prevTheme.darkColors, _theme?.colors?.dark ); const lightColors = Object.assign( prevTheme.lightColors, _theme?.colors?.light ); const colors = initialColorFunction( Object.assign( themeScheme === 'dark' ? darkColors : lightColors, _theme?.colors?.[themeScheme] ) ); const _borderRadius = Object.assign( prevTheme?.borderRadius, _theme?.borderRadius ); // createColorName(colors); return { colors, theme: themeScheme, darkColors, lightColors, width: _theme?.width ?? width, height: _theme?.height ?? height, borderRadius: _borderRadius, isDark: themeScheme === 'dark', borderWidth: _theme?.borderWidth ?? prevTheme.borderWidth, activeOpacity: _theme?.activeOpacity ?? prevTheme.activeOpacity, spacing: _theme?.spacing ?? prevTheme.spacing, fonts: Object.assign(prevTheme?.fonts, _theme?.fonts), sizes: Object.assign(prevTheme?.sizes, _theme?.sizes), fontSizes: Object.assign(prevTheme?.fontSizes, _theme?.fontSizes), titleFontSizes: Object.assign( prevTheme?.titleFontSizes, _theme?.titleFontSizes ), zIndices: Object.assign(prevTheme?.zIndices, _theme?.zIndices), }; }); }, [height, width] ); const setTheme = useCallback((_theme: 'dark' | 'light') => { setInternalTheme((prevTheme) => { return { ...prevTheme, theme: _theme, isDark: _theme === 'dark', colors: initialColorFunction( _theme === 'dark' ? prevTheme.darkColors : prevTheme.lightColors ), }; }); }, []); useEffect(() => { setContextTheme(theme || {}); }, [theme, setContextTheme]); useEffect(() => { onChangeTheme?.(internalTheme?.theme); }, [internalTheme, onChangeTheme]); // set the body theme in web useSetThemeBody(internalTheme.theme, internalTheme.colors); const maxWidth = resolveSize(internalTheme.width, props?.width); const maxHeight = resolveSize(internalTheme.height, props?.height); return ( { return { colors: internalTheme.colors, theme: internalTheme.theme, darkColors: internalTheme.darkColors, lightColors: internalTheme.lightColors, isDark: internalTheme.isDark, zIndices: internalTheme.zIndices, sizes: internalTheme.sizes, spacing: internalTheme.spacing, fonts: internalTheme.fonts, borderRadius: internalTheme.borderRadius, fontSizes: internalTheme.fontSizes, titleFontSizes: internalTheme.titleFontSizes, activeOpacity: internalTheme.activeOpacity, borderWidth: internalTheme.borderWidth, setContextTheme: setContextTheme, setTheme, width: internalTheme.width, height: internalTheme.height, maxHeight, maxWidth, } as ThemeContextValue; // eslint-disable-next-line react-hooks/exhaustive-deps }, [internalTheme, setTheme, setContextTheme])} > {children} ); }); const styles = StyleSheet.create({ wrapper: { flex: 1, }, });