-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
ThemeProvider.tsx
101 lines (84 loc) 路 2.58 KB
/
ThemeProvider.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import React, { useContext, useMemo } from 'react';
import styledError from '../utils/error';
import isFunction from '../utils/isFunction';
/**
* Override DefaultTheme to get accurate typings for your project.
*
* ```
* // create styled-components.d.ts in your project source
* // if it isn't being picked up, check tsconfig compilerOptions.types
* import type { CSSProp } from "styled-components";
* import Theme from './theme';
*
* type ThemeType = typeof Theme;
*
* declare module "styled-components" {
* export interface DefaultTheme extends ThemeType {}
* }
*
* declare module "react" {
* interface DOMAttributes<T> {
* css?: CSSProp;
* }
* }
* ```
*/
export interface DefaultTheme {
[key: string]: any;
}
type ThemeFn = (outerTheme?: DefaultTheme | undefined) => DefaultTheme;
type ThemeArgument = DefaultTheme | ThemeFn;
type Props = {
children?: React.ReactNode;
theme: ThemeArgument;
};
export const ThemeContext = React.createContext<DefaultTheme | undefined>(undefined);
export const ThemeConsumer = ThemeContext.Consumer;
function mergeTheme(theme: ThemeArgument, outerTheme?: DefaultTheme | undefined): DefaultTheme {
if (!theme) {
throw styledError(14);
}
if (isFunction(theme)) {
const themeFn = theme as ThemeFn;
const mergedTheme = themeFn(outerTheme);
if (
process.env.NODE_ENV !== 'production' &&
(mergedTheme === null || Array.isArray(mergedTheme) || typeof mergedTheme !== 'object')
) {
throw styledError(7);
}
return mergedTheme;
}
if (Array.isArray(theme) || typeof theme !== 'object') {
throw styledError(8);
}
return outerTheme ? { ...outerTheme, ...theme } : theme;
}
/**
* Returns the current theme (as provided by the closest ancestor `ThemeProvider`.)
*
* If no `ThemeProvider` is found, the function will error. If you need access to the theme in an
* uncertain composition scenario, `React.useContext(ThemeContext)` will not emit an error if there
* is no `ThemeProvider` ancestor.
*/
export function useTheme(): DefaultTheme {
const theme = useContext(ThemeContext);
if (!theme) {
throw styledError(18);
}
return theme;
}
/**
* Provide a theme to an entire react component tree via context
*/
export default function ThemeProvider(props: Props): JSX.Element | null {
const outerTheme = React.useContext(ThemeContext);
const themeContext = useMemo(
() => mergeTheme(props.theme, outerTheme),
[props.theme, outerTheme]
);
if (!props.children) {
return null;
}
return <ThemeContext.Provider value={themeContext}>{props.children}</ThemeContext.Provider>;
}