@draftbit/theme 49.5.1-312cdf.2
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/LICENSE +21 -0
- package/README.md +1 -0
- package/lib/commonjs/DefaultTheme.js +1 -0
- package/lib/commonjs/Provider.js +1 -0
- package/lib/commonjs/createTheme.js +1 -0
- package/lib/commonjs/createThemeValuesProxy.js +1 -0
- package/lib/commonjs/index.js +1 -0
- package/lib/commonjs/types.js +0 -0
- package/lib/commonjs/validators.js +1 -0
- package/lib/typescript/src/DefaultTheme.d.ts +2 -0
- package/lib/typescript/src/DefaultTheme.js +146 -0
- package/lib/typescript/src/DefaultTheme.js.map +1 -0
- package/lib/typescript/src/Provider.d.ts +14 -0
- package/lib/typescript/src/Provider.js +55 -0
- package/lib/typescript/src/Provider.js.map +1 -0
- package/lib/typescript/src/createTheme.d.ts +7 -0
- package/lib/typescript/src/createTheme.js +17 -0
- package/lib/typescript/src/createTheme.js.map +1 -0
- package/lib/typescript/src/createThemeValuesProxy.d.ts +10 -0
- package/lib/typescript/src/createThemeValuesProxy.js +96 -0
- package/lib/typescript/src/createThemeValuesProxy.js.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.js +4 -0
- package/lib/typescript/src/index.js.map +1 -0
- package/lib/typescript/src/types.d.ts +37 -0
- package/lib/typescript/src/types.js +2 -0
- package/lib/typescript/src/types.js.map +1 -0
- package/lib/typescript/src/validators.d.ts +4 -0
- package/lib/typescript/src/validators.js +52 -0
- package/lib/typescript/src/validators.js.map +1 -0
- package/lib/typescript/tsconfig.tsbuildinfo +1 -0
- package/package.json +67 -0
- package/src/DefaultTheme.js +146 -0
- package/src/DefaultTheme.js.map +1 -0
- package/src/DefaultTheme.ts +149 -0
- package/src/Provider.js +55 -0
- package/src/Provider.js.map +1 -0
- package/src/Provider.tsx +124 -0
- package/src/createTheme.js +17 -0
- package/src/createTheme.js.map +1 -0
- package/src/createTheme.ts +40 -0
- package/src/createThemeValuesProxy.js +96 -0
- package/src/createThemeValuesProxy.js.map +1 -0
- package/src/createThemeValuesProxy.ts +156 -0
- package/src/index.js +4 -0
- package/src/index.js.map +1 -0
- package/src/index.ts +15 -0
- package/src/types.js +2 -0
- package/src/types.js.map +1 -0
- package/src/types.ts +41 -0
- package/src/validators.js +52 -0
- package/src/validators.js.map +1 -0
- package/src/validators.ts +59 -0
package/src/Provider.tsx
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Dimensions, Platform } from "react-native";
|
|
3
|
+
import createThemeValuesProxy from "./createThemeValuesProxy";
|
|
4
|
+
import DefaultTheme from "./DefaultTheme";
|
|
5
|
+
import { Breakpoints, ReadTheme, ValidatedTheme } from "./types";
|
|
6
|
+
|
|
7
|
+
type ThemeContextType = {
|
|
8
|
+
theme: ReadTheme;
|
|
9
|
+
changeTheme: (themeName: string) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const ThemeContext = React.createContext<ThemeContextType>({
|
|
13
|
+
theme: DefaultTheme,
|
|
14
|
+
changeTheme: () => {},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
type ProviderProps = {
|
|
18
|
+
themes: ValidatedTheme[];
|
|
19
|
+
breakpoints: Breakpoints;
|
|
20
|
+
initialThemeName: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
|
|
24
|
+
themes,
|
|
25
|
+
breakpoints,
|
|
26
|
+
initialThemeName,
|
|
27
|
+
children,
|
|
28
|
+
}) => {
|
|
29
|
+
const initialTheme =
|
|
30
|
+
themes.find((theme) => theme.name === initialThemeName) ?? DefaultTheme;
|
|
31
|
+
|
|
32
|
+
const [currentTheme, setCurrentTheme] = React.useState(initialTheme);
|
|
33
|
+
const [deviceWidth, setDeviceWidth] = React.useState(
|
|
34
|
+
Dimensions.get("window").width
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
const listener = Dimensions.addEventListener("change", ({ window }) =>
|
|
39
|
+
setDeviceWidth(window.width)
|
|
40
|
+
);
|
|
41
|
+
return () => {
|
|
42
|
+
listener.remove();
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const changeTheme = (themeName: string) => {
|
|
47
|
+
const theme = themes.find((theme) => theme.name === themeName);
|
|
48
|
+
if (!theme) {
|
|
49
|
+
console.warn(
|
|
50
|
+
"Theme with name",
|
|
51
|
+
themeName,
|
|
52
|
+
"not found. Make sure it's passed into the top level ThemeProvider"
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
setCurrentTheme(theme);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const proxiedTheme: ReadTheme = React.useMemo(
|
|
60
|
+
() => ({
|
|
61
|
+
...currentTheme,
|
|
62
|
+
colors: {
|
|
63
|
+
branding: createThemeValuesProxy(
|
|
64
|
+
currentTheme.colors.branding,
|
|
65
|
+
breakpoints,
|
|
66
|
+
deviceWidth,
|
|
67
|
+
Platform.OS
|
|
68
|
+
),
|
|
69
|
+
text: createThemeValuesProxy(
|
|
70
|
+
currentTheme.colors.text,
|
|
71
|
+
breakpoints,
|
|
72
|
+
deviceWidth,
|
|
73
|
+
Platform.OS
|
|
74
|
+
),
|
|
75
|
+
background: createThemeValuesProxy(
|
|
76
|
+
currentTheme.colors.background,
|
|
77
|
+
breakpoints,
|
|
78
|
+
deviceWidth,
|
|
79
|
+
Platform.OS
|
|
80
|
+
),
|
|
81
|
+
foreground: createThemeValuesProxy(
|
|
82
|
+
currentTheme.colors.foreground,
|
|
83
|
+
breakpoints,
|
|
84
|
+
deviceWidth,
|
|
85
|
+
Platform.OS
|
|
86
|
+
),
|
|
87
|
+
border: createThemeValuesProxy(
|
|
88
|
+
currentTheme.colors.border,
|
|
89
|
+
breakpoints,
|
|
90
|
+
deviceWidth,
|
|
91
|
+
Platform.OS
|
|
92
|
+
),
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
[currentTheme, deviceWidth, breakpoints]
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<ThemeContext.Provider value={{ theme: proxiedTheme, changeTheme }}>
|
|
100
|
+
{children}
|
|
101
|
+
</ThemeContext.Provider>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const useTheme = (): ReadTheme => {
|
|
106
|
+
const { theme } = React.useContext(ThemeContext);
|
|
107
|
+
return theme;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const useChangeTheme = (): ((themeName: string) => void) => {
|
|
111
|
+
const { changeTheme } = React.useContext(ThemeContext);
|
|
112
|
+
return changeTheme;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const withTheme = <Props extends { theme: ReadTheme }>(
|
|
116
|
+
Component: React.ComponentType<Props>
|
|
117
|
+
): React.ComponentType<Omit<Props, "theme">> => {
|
|
118
|
+
return (props: Omit<Props, "theme">) => {
|
|
119
|
+
const { theme } = React.useContext(ThemeContext);
|
|
120
|
+
return <Component {...(props as Props)} theme={theme} />;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export { Provider, useTheme, useChangeTheme, withTheme };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import merge from "deepmerge";
|
|
2
|
+
import { validateBreakpoints, validatePalettes, validateTheme, } from "./validators";
|
|
3
|
+
export default function createTheme({ breakpoints, palettes, theme, baseTheme, }) {
|
|
4
|
+
validateBreakpoints(breakpoints);
|
|
5
|
+
validatePalettes(palettes);
|
|
6
|
+
validateTheme(theme);
|
|
7
|
+
let resultTheme = theme;
|
|
8
|
+
if (baseTheme) {
|
|
9
|
+
validateTheme(baseTheme);
|
|
10
|
+
resultTheme = merge(baseTheme, theme);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
...resultTheme,
|
|
14
|
+
validated: true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=createTheme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createTheme.js","sourceRoot":"","sources":["createTheme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,WAAW,CAAC;AAO9B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAW,EACX,QAAQ,EACR,KAAK,EACL,SAAS,GAMV;IACC,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACjC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,aAAa,CAAC,KAAK,CAAC,CAAC;IAErB,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,IAAI,SAAS,EAAE;QACb,aAAa,CAAC,SAAS,CAAC,CAAC;QACzB,WAAW,GAAG,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;KACvC;IAED,OAAO;QACL,GAAG,WAAW;QACd,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import merge from "deepmerge";
|
|
2
|
+
import type {
|
|
3
|
+
Breakpoints,
|
|
4
|
+
Theme,
|
|
5
|
+
ValidatedTheme,
|
|
6
|
+
ColorPalettes,
|
|
7
|
+
} from "./types";
|
|
8
|
+
import {
|
|
9
|
+
validateBreakpoints,
|
|
10
|
+
validatePalettes,
|
|
11
|
+
validateTheme,
|
|
12
|
+
} from "./validators";
|
|
13
|
+
|
|
14
|
+
export default function createTheme({
|
|
15
|
+
breakpoints,
|
|
16
|
+
palettes,
|
|
17
|
+
theme,
|
|
18
|
+
baseTheme,
|
|
19
|
+
}: {
|
|
20
|
+
breakpoints: Breakpoints;
|
|
21
|
+
palettes: ColorPalettes;
|
|
22
|
+
theme: Theme;
|
|
23
|
+
baseTheme?: Theme;
|
|
24
|
+
}): ValidatedTheme {
|
|
25
|
+
validateBreakpoints(breakpoints);
|
|
26
|
+
validatePalettes(palettes);
|
|
27
|
+
validateTheme(theme);
|
|
28
|
+
|
|
29
|
+
let resultTheme = theme;
|
|
30
|
+
|
|
31
|
+
if (baseTheme) {
|
|
32
|
+
validateTheme(baseTheme);
|
|
33
|
+
resultTheme = merge(baseTheme, theme);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
...resultTheme,
|
|
38
|
+
validated: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isObject } from "lodash";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a proxy for theme value objects to select a value whenever
|
|
4
|
+
* multiple values are provided for different platforms and/or breakpoints
|
|
5
|
+
*
|
|
6
|
+
* Ex: {color: {ios: "blue", android: "red"}}
|
|
7
|
+
* -> theme.color returns "blue" when the platform is ios and "red" when android
|
|
8
|
+
*/
|
|
9
|
+
export default function createThemeValuesProxy(value, breakpoints, deviceWidth, devicePlatform) {
|
|
10
|
+
if (value === undefined || value === null) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return new Proxy(value, {
|
|
14
|
+
get: (target, key) => {
|
|
15
|
+
const currentValue = target[key];
|
|
16
|
+
if (!isObject(currentValue)) {
|
|
17
|
+
return currentValue;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const platformKeys = ["ios", "android", "web", "macos", "windows"];
|
|
21
|
+
const breakpointKeys = Object.keys(breakpoints);
|
|
22
|
+
const keysType = getKeysType(currentValue, platformKeys, breakpointKeys);
|
|
23
|
+
if (keysType === "default") {
|
|
24
|
+
return createThemeValuesProxy(currentValue, breakpoints, deviceWidth, devicePlatform);
|
|
25
|
+
}
|
|
26
|
+
else if (keysType === "platform") {
|
|
27
|
+
return getPlatformValue(currentValue, breakpoints, deviceWidth, devicePlatform);
|
|
28
|
+
}
|
|
29
|
+
else if (keysType === "breakpoint") {
|
|
30
|
+
return getBreakpointValue(currentValue, breakpoints, deviceWidth, devicePlatform);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
set: () => {
|
|
38
|
+
throw new Error("Theme is read only, cannot be modified at runtime");
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function getKeysType(value, platformKeys, breakpointKeys) {
|
|
43
|
+
const hasPlatformKeys = platformKeys.some((key) => value[key] !== undefined);
|
|
44
|
+
const hasBreakpointKeys = breakpointKeys.some((key) => value[key] !== undefined);
|
|
45
|
+
const hasUserDefinedKeys = Object.keys(value).some((key) => !platformKeys.includes(key) &&
|
|
46
|
+
!breakpointKeys.includes(key) &&
|
|
47
|
+
key !== "default");
|
|
48
|
+
if (!onlyOneTrue(hasPlatformKeys, hasBreakpointKeys, hasUserDefinedKeys) &&
|
|
49
|
+
!allFalse(hasPlatformKeys, hasBreakpointKeys, hasUserDefinedKeys)) {
|
|
50
|
+
throw new Error("Cannot mix usage of platform keys, breakpoint keys, and custom defined keys on the same level");
|
|
51
|
+
}
|
|
52
|
+
else if (hasPlatformKeys) {
|
|
53
|
+
return "platform";
|
|
54
|
+
}
|
|
55
|
+
else if (hasBreakpointKeys) {
|
|
56
|
+
return "breakpoint";
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
return "default";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function onlyOneTrue(a, b, c) {
|
|
63
|
+
return [a, b, c].filter((x) => x).length === 1;
|
|
64
|
+
}
|
|
65
|
+
function allFalse(a, b, c) {
|
|
66
|
+
return [a, b, c].filter((x) => x).length === 0;
|
|
67
|
+
}
|
|
68
|
+
function getPlatformValue(value, breakpoints, deviceWidth, devicePlatform) {
|
|
69
|
+
var _a;
|
|
70
|
+
const currentPlatformValue = (_a = value[devicePlatform]) !== null && _a !== void 0 ? _a : value.default;
|
|
71
|
+
if (!isObject(currentPlatformValue)) {
|
|
72
|
+
return currentPlatformValue;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return createThemeValuesProxy(currentPlatformValue, breakpoints, deviceWidth, devicePlatform);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function getBreakpointValue(value, breakpoints, deviceWidth, devicePlatform) {
|
|
79
|
+
var _a;
|
|
80
|
+
const keysToBreakpointValue = Object.keys(value).map((key) => [key, breakpoints[key]]);
|
|
81
|
+
const orderedBreakpoints = keysToBreakpointValue.sort(([_, val]) => val);
|
|
82
|
+
let currentBreakpointKey = "";
|
|
83
|
+
for (const [breakpointKey, breakpointValue] of orderedBreakpoints) {
|
|
84
|
+
if (deviceWidth >= breakpointValue) {
|
|
85
|
+
currentBreakpointKey = breakpointKey;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const currentBreakpointValue = (_a = value[currentBreakpointKey]) !== null && _a !== void 0 ? _a : value.default;
|
|
89
|
+
if (!isObject(currentBreakpointValue)) {
|
|
90
|
+
return currentBreakpointValue;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return createThemeValuesProxy(currentBreakpointValue, breakpoints, deviceWidth, devicePlatform);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=createThemeValuesProxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createThemeValuesProxy.js","sourceRoot":"","sources":["createThemeValuesProxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ThemeValues, Breakpoints } from "./types";
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import { isObject } from "lodash";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a proxy for theme value objects to select a value whenever
|
|
7
|
+
* multiple values are provided for different platforms and/or breakpoints
|
|
8
|
+
*
|
|
9
|
+
* Ex: {color: {ios: "blue", android: "red"}}
|
|
10
|
+
* -> theme.color returns "blue" when the platform is ios and "red" when android
|
|
11
|
+
*/
|
|
12
|
+
export default function createThemeValuesProxy(
|
|
13
|
+
value: ThemeValues | undefined,
|
|
14
|
+
breakpoints: Breakpoints,
|
|
15
|
+
deviceWidth: number,
|
|
16
|
+
devicePlatform: typeof Platform.OS
|
|
17
|
+
): any /* return type 'any' to allow arbitrary object references (object.example.another.there) */ {
|
|
18
|
+
if (value === undefined || value === null) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return new Proxy(value, {
|
|
22
|
+
get: (
|
|
23
|
+
target: ThemeValues,
|
|
24
|
+
key: string
|
|
25
|
+
): string | number | ThemeValues | undefined => {
|
|
26
|
+
const currentValue = target[key];
|
|
27
|
+
if (!isObject(currentValue)) {
|
|
28
|
+
return currentValue;
|
|
29
|
+
} else {
|
|
30
|
+
const platformKeys = ["ios", "android", "web", "macos", "windows"];
|
|
31
|
+
const breakpointKeys = Object.keys(breakpoints);
|
|
32
|
+
const keysType = getKeysType(
|
|
33
|
+
currentValue,
|
|
34
|
+
platformKeys,
|
|
35
|
+
breakpointKeys
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (keysType === "default") {
|
|
39
|
+
return createThemeValuesProxy(
|
|
40
|
+
currentValue,
|
|
41
|
+
breakpoints,
|
|
42
|
+
deviceWidth,
|
|
43
|
+
devicePlatform
|
|
44
|
+
);
|
|
45
|
+
} else if (keysType === "platform") {
|
|
46
|
+
return getPlatformValue(
|
|
47
|
+
currentValue,
|
|
48
|
+
breakpoints,
|
|
49
|
+
deviceWidth,
|
|
50
|
+
devicePlatform
|
|
51
|
+
);
|
|
52
|
+
} else if (keysType === "breakpoint") {
|
|
53
|
+
return getBreakpointValue(
|
|
54
|
+
currentValue,
|
|
55
|
+
breakpoints,
|
|
56
|
+
deviceWidth,
|
|
57
|
+
devicePlatform
|
|
58
|
+
);
|
|
59
|
+
} else {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
set: () => {
|
|
65
|
+
throw new Error("Theme is read only, cannot be modified at runtime");
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getKeysType(
|
|
71
|
+
value: ThemeValues,
|
|
72
|
+
platformKeys: string[],
|
|
73
|
+
breakpointKeys: string[]
|
|
74
|
+
): "platform" | "breakpoint" | "default" {
|
|
75
|
+
const hasPlatformKeys = platformKeys.some((key) => value[key] !== undefined);
|
|
76
|
+
const hasBreakpointKeys = breakpointKeys.some(
|
|
77
|
+
(key) => value[key] !== undefined
|
|
78
|
+
);
|
|
79
|
+
const hasUserDefinedKeys = Object.keys(value).some(
|
|
80
|
+
(key) =>
|
|
81
|
+
!platformKeys.includes(key) &&
|
|
82
|
+
!breakpointKeys.includes(key) &&
|
|
83
|
+
key !== "default"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
!onlyOneTrue(hasPlatformKeys, hasBreakpointKeys, hasUserDefinedKeys) &&
|
|
88
|
+
!allFalse(hasPlatformKeys, hasBreakpointKeys, hasUserDefinedKeys)
|
|
89
|
+
) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
"Cannot mix usage of platform keys, breakpoint keys, and custom defined keys on the same level"
|
|
92
|
+
);
|
|
93
|
+
} else if (hasPlatformKeys) {
|
|
94
|
+
return "platform";
|
|
95
|
+
} else if (hasBreakpointKeys) {
|
|
96
|
+
return "breakpoint";
|
|
97
|
+
} else {
|
|
98
|
+
return "default";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function onlyOneTrue(a: boolean, b: boolean, c: boolean) {
|
|
103
|
+
return [a, b, c].filter((x) => x).length === 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function allFalse(a: boolean, b: boolean, c: boolean) {
|
|
107
|
+
return [a, b, c].filter((x) => x).length === 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getPlatformValue(
|
|
111
|
+
value: ThemeValues,
|
|
112
|
+
breakpoints: Breakpoints,
|
|
113
|
+
deviceWidth: number,
|
|
114
|
+
devicePlatform: typeof Platform.OS
|
|
115
|
+
) {
|
|
116
|
+
const currentPlatformValue = value[devicePlatform] ?? value.default;
|
|
117
|
+
if (!isObject(currentPlatformValue)) {
|
|
118
|
+
return currentPlatformValue;
|
|
119
|
+
} else {
|
|
120
|
+
return createThemeValuesProxy(
|
|
121
|
+
currentPlatformValue,
|
|
122
|
+
breakpoints,
|
|
123
|
+
deviceWidth,
|
|
124
|
+
devicePlatform
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getBreakpointValue(
|
|
130
|
+
value: ThemeValues,
|
|
131
|
+
breakpoints: Breakpoints,
|
|
132
|
+
deviceWidth: number,
|
|
133
|
+
devicePlatform: typeof Platform.OS
|
|
134
|
+
) {
|
|
135
|
+
const keysToBreakpointValue: [string, number][] = Object.keys(value).map(
|
|
136
|
+
(key) => [key, breakpoints[key]]
|
|
137
|
+
);
|
|
138
|
+
const orderedBreakpoints = keysToBreakpointValue.sort(([_, val]) => val);
|
|
139
|
+
let currentBreakpointKey = "";
|
|
140
|
+
for (const [breakpointKey, breakpointValue] of orderedBreakpoints) {
|
|
141
|
+
if (deviceWidth >= breakpointValue) {
|
|
142
|
+
currentBreakpointKey = breakpointKey;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const currentBreakpointValue = value[currentBreakpointKey] ?? value.default;
|
|
146
|
+
if (!isObject(currentBreakpointValue)) {
|
|
147
|
+
return currentBreakpointValue;
|
|
148
|
+
} else {
|
|
149
|
+
return createThemeValuesProxy(
|
|
150
|
+
currentBreakpointValue,
|
|
151
|
+
breakpoints,
|
|
152
|
+
deviceWidth,
|
|
153
|
+
devicePlatform
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/index.js
ADDED
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQzD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,QAAQ,IAAI,aAAa,EACzB,cAAc,EACd,QAAQ,EACR,SAAS,GACV,MAAM,YAAY,CAAC"}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { default as DefaultTheme } from "./DefaultTheme";
|
|
2
|
+
export type {
|
|
3
|
+
Theme,
|
|
4
|
+
ReadTheme,
|
|
5
|
+
ValidatedTheme,
|
|
6
|
+
Breakpoints,
|
|
7
|
+
ColorPalettes,
|
|
8
|
+
} from "./types";
|
|
9
|
+
export { default as createTheme } from "./createTheme";
|
|
10
|
+
export {
|
|
11
|
+
Provider as ThemeProvider,
|
|
12
|
+
useChangeTheme,
|
|
13
|
+
useTheme,
|
|
14
|
+
withTheme,
|
|
15
|
+
} from "./Provider";
|
package/src/types.js
ADDED
package/src/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":""}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type ThemeValues = {
|
|
2
|
+
[key: string]: string | number | ThemeValues;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export type Theme = {
|
|
6
|
+
name: string;
|
|
7
|
+
colors: {
|
|
8
|
+
branding?: ThemeValues;
|
|
9
|
+
text?: ThemeValues;
|
|
10
|
+
background?: ThemeValues;
|
|
11
|
+
foreground?: ThemeValues;
|
|
12
|
+
border?: ThemeValues;
|
|
13
|
+
};
|
|
14
|
+
typography: { [key: string]: any }; //TODO: Update theme to support typography. Left as 'any' for legacy support until updated and migrated
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ValidatedTheme = Theme & {
|
|
18
|
+
validated: true;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type ReadTheme = Theme & {
|
|
22
|
+
colors: {
|
|
23
|
+
branding?: any;
|
|
24
|
+
text?: any;
|
|
25
|
+
background?: any;
|
|
26
|
+
foreground?: any;
|
|
27
|
+
border?: any;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ColorPaletteValues = {
|
|
32
|
+
[key: string]: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type ColorPalettes = {
|
|
36
|
+
[key: string]: ColorPaletteValues;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type Breakpoints = {
|
|
40
|
+
[key: string]: number;
|
|
41
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function validatePalettes(palettes) {
|
|
2
|
+
for (const key of Object.keys(palettes)) {
|
|
3
|
+
if (typeof key !== "string") {
|
|
4
|
+
throw new Error(`Invalid palettes object, ${key} is not a string`);
|
|
5
|
+
}
|
|
6
|
+
for (const [innerKey, value] of Object.entries(palettes[key])) {
|
|
7
|
+
if (typeof innerKey !== "string") {
|
|
8
|
+
throw new Error(`Invalid palette color key, ${innerKey} is not a string`);
|
|
9
|
+
}
|
|
10
|
+
if (typeof value !== "string") {
|
|
11
|
+
throw new Error(`Invalid palette color value, ${value} is not a string`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function validateBreakpoints(breakpoints) {
|
|
17
|
+
for (const [key, value] of Object.entries(breakpoints)) {
|
|
18
|
+
if (typeof key !== "string") {
|
|
19
|
+
throw new Error(`Invalid breakpoint key, ${key} is not a string`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== "number") {
|
|
22
|
+
throw new Error(`Invalid breakpoint value, ${value} is not a number`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function validateTheme(theme) {
|
|
27
|
+
function validateThemeValues(themeValue) {
|
|
28
|
+
if (themeValue === undefined || themeValue === null) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
for (const [key, value] of Object.entries(themeValue)) {
|
|
32
|
+
if (typeof key !== "string") {
|
|
33
|
+
throw new Error(`Invalid theme key, ${key} is not a string`);
|
|
34
|
+
}
|
|
35
|
+
if (value === undefined || value === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
else if (typeof value === "object") {
|
|
39
|
+
validateThemeValues(value);
|
|
40
|
+
}
|
|
41
|
+
else if (typeof value !== "string" && typeof value !== "number") {
|
|
42
|
+
throw new Error(`Invalid theme value, ${value} is not a string, number, or object`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
validateThemeValues(theme.colors.branding);
|
|
47
|
+
validateThemeValues(theme.colors.text);
|
|
48
|
+
validateThemeValues(theme.colors.foreground);
|
|
49
|
+
validateThemeValues(theme.colors.background);
|
|
50
|
+
validateThemeValues(theme.colors.border);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["validators.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,QAAuB;IACtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACvC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,kBAAkB,CAAC,CAAC;SACpE;QACD,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE;YAC7D,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;gBAChC,MAAM,IAAI,KAAK,CACb,8BAA8B,QAAQ,kBAAkB,CACzD,CAAC;aACH;YACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBAC7B,MAAM,IAAI,KAAK,CACb,gCAAgC,KAAK,kBAAkB,CACxD,CAAC;aACH;SACF;KACF;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,WAAwB;IAC1D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QACtD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,kBAAkB,CAAC,CAAC;SACnE;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,kBAAkB,CAAC,CAAC;SACvE;KACF;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,SAAS,mBAAmB,CAAC,UAAwB;QACnD,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,EAAE;YACnD,OAAO;SACR;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;YACrD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;gBAC3B,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,kBAAkB,CAAC,CAAC;aAC9D;YACD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE;gBACzC,SAAS;aACV;iBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBACpC,mBAAmB,CAAC,KAAK,CAAC,CAAC;aAC5B;iBAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;gBACjE,MAAM,IAAI,KAAK,CACb,wBAAwB,KAAK,qCAAqC,CACnE,CAAC;aACH;SACF;IACH,CAAC;IACD,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3C,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7C,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7C,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ColorPalettes, Breakpoints, Theme, ThemeValues } from "./types";
|
|
2
|
+
|
|
3
|
+
export function validatePalettes(palettes: ColorPalettes) {
|
|
4
|
+
for (const key of Object.keys(palettes)) {
|
|
5
|
+
if (typeof key !== "string") {
|
|
6
|
+
throw new Error(`Invalid palettes object, ${key} is not a string`);
|
|
7
|
+
}
|
|
8
|
+
for (const [innerKey, value] of Object.entries(palettes[key])) {
|
|
9
|
+
if (typeof innerKey !== "string") {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Invalid palette color key, ${innerKey} is not a string`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Invalid palette color value, ${value} is not a string`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function validateBreakpoints(breakpoints: Breakpoints) {
|
|
24
|
+
for (const [key, value] of Object.entries(breakpoints)) {
|
|
25
|
+
if (typeof key !== "string") {
|
|
26
|
+
throw new Error(`Invalid breakpoint key, ${key} is not a string`);
|
|
27
|
+
}
|
|
28
|
+
if (typeof value !== "number") {
|
|
29
|
+
throw new Error(`Invalid breakpoint value, ${value} is not a number`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function validateTheme(theme: Theme) {
|
|
35
|
+
function validateThemeValues(themeValue?: ThemeValues) {
|
|
36
|
+
if (themeValue === undefined || themeValue === null) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
for (const [key, value] of Object.entries(themeValue)) {
|
|
40
|
+
if (typeof key !== "string") {
|
|
41
|
+
throw new Error(`Invalid theme key, ${key} is not a string`);
|
|
42
|
+
}
|
|
43
|
+
if (value === undefined || value === null) {
|
|
44
|
+
continue;
|
|
45
|
+
} else if (typeof value === "object") {
|
|
46
|
+
validateThemeValues(value);
|
|
47
|
+
} else if (typeof value !== "string" && typeof value !== "number") {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Invalid theme value, ${value} is not a string, number, or object`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
validateThemeValues(theme.colors.branding);
|
|
55
|
+
validateThemeValues(theme.colors.text);
|
|
56
|
+
validateThemeValues(theme.colors.foreground);
|
|
57
|
+
validateThemeValues(theme.colors.background);
|
|
58
|
+
validateThemeValues(theme.colors.border);
|
|
59
|
+
}
|