import React, { useLayoutEffect, useState } from 'react' import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming' import { CacheProvider } from '@emotion/core' import createCache from '@emotion/cache' import merge from 'deepmerge' import defaultTheme from '../system/default-theme' import { flattenPalette } from '../system' import { Theme } from '../index' import { objectToCSSProperties, shallowEqual } from '../utils' import { setRootStyles, styleReset } from './styles' const STORAGE_KEY = 'ui-kit-theme' export const cache = createCache({ key: 'uikit', prefix: (key) => { switch (key) { case 'appearance': case 'user-select': case 'position': case ':placeholder': return true default: return false } }, }) export const themeStorage = { get: () => window.localStorage.getItem(STORAGE_KEY), set: (value) => window.localStorage.setItem(STORAGE_KEY, value), } export const themeInitializerScript = ` try { var theme = window.localStorage.getItem('${STORAGE_KEY}') if (theme) { document.body.dataset.theme = theme } } catch (e) {} ` const emptyTheme = {} function mergeThemes(a: Theme = {}, b: Theme = {}) { return { ...a, ...b, button: { ...a.button, ...b.button, themes: merge(a.button?.themes || {}, b.button?.themes || {}), }, colors: merge( flattenPalette(a.colors || {}), flattenPalette(b.colors || {}) ), spinner: { ...a.spinner, ...b.spinner, sizes: merge(a.spinner?.sizes || {}, b.spinner?.sizes || {}), thickness: merge(a.spinner?.thickness || {}, b.spinner?.thickness || {}), }, } } export function ThemeProvider({ theme = emptyTheme, children, }: { theme?: Theme children: React.ReactNode }) { const [mergedTheme, setMergedTheme] = useState(() => mergeThemes(defaultTheme, theme) ) useLayoutEffect(() => { const nextTheme = mergeThemes(defaultTheme, theme) setRootStyles(objectToCSSProperties('colors', nextTheme.colors)) if (!shallowEqual(mergedTheme, nextTheme)) { setMergedTheme(nextTheme) } }, [theme]) return ( {children} ) } ThemeProvider.cache = cache ThemeProvider.setRootStyles = setRootStyles ThemeProvider.styleReset = styleReset ThemeProvider.themeInitializerScript = themeInitializerScript ThemeProvider.themeStorage = themeStorage export { setRootStyles, styleReset }