@draftbit/theme 50.5.3 → 50.5.4-629c6c.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.
Files changed (36) hide show
  1. package/lib/commonjs/DefaultTheme.js +1 -1
  2. package/lib/commonjs/Provider.js +1 -1
  3. package/lib/commonjs/createTheme.js +1 -1
  4. package/lib/commonjs/createThemeValuesProxy.js +1 -1
  5. package/lib/commonjs/types.js +1 -0
  6. package/lib/commonjs/validators.js +1 -1
  7. package/lib/typescript/src/DefaultTheme.js +38 -38
  8. package/lib/typescript/src/DefaultTheme.js.map +1 -1
  9. package/lib/typescript/src/Provider.js +14 -10
  10. package/lib/typescript/src/Provider.js.map +1 -1
  11. package/lib/typescript/src/createTheme.js +25 -3
  12. package/lib/typescript/src/createTheme.js.map +1 -1
  13. package/lib/typescript/src/createThemeValuesProxy.js +23 -19
  14. package/lib/typescript/src/createThemeValuesProxy.js.map +1 -1
  15. package/lib/typescript/src/types.d.ts +30 -2
  16. package/lib/typescript/src/validators.d.ts +3 -1
  17. package/lib/typescript/src/validators.js +81 -42
  18. package/lib/typescript/src/validators.js.map +1 -1
  19. package/lib/typescript/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +5 -5
  21. package/src/DefaultTheme.js +38 -38
  22. package/src/DefaultTheme.js.map +1 -1
  23. package/src/DefaultTheme.ts +38 -38
  24. package/src/Provider.js +14 -10
  25. package/src/Provider.js.map +1 -1
  26. package/src/Provider.tsx +20 -40
  27. package/src/createTheme.js +25 -3
  28. package/src/createTheme.js.map +1 -1
  29. package/src/createTheme.ts +30 -2
  30. package/src/createThemeValuesProxy.js +23 -19
  31. package/src/createThemeValuesProxy.js.map +1 -1
  32. package/src/createThemeValuesProxy.ts +32 -23
  33. package/src/types.ts +33 -2
  34. package/src/validators.js +81 -42
  35. package/src/validators.js.map +1 -1
  36. package/src/validators.ts +103 -46
@@ -1,6 +1,6 @@
1
1
  import { ThemeValues, Breakpoints } from "./types";
2
- import { Platform } from "react-native";
3
- import { isObject } from "lodash";
2
+ import { Platform, TextStyle } from "react-native";
3
+ import { asThemeValuesObject } from "./validators";
4
4
 
5
5
  /**
6
6
  * Creates a proxy for theme value objects to select a value whenever
@@ -23,16 +23,18 @@ export default function createThemeValuesProxy(
23
23
  get: (
24
24
  target: ThemeValues,
25
25
  key: string
26
- ): string | number | ThemeValues | undefined => {
26
+ ): string | number | ThemeValues | TextStyle | undefined => {
27
27
  const currentValue = target[key];
28
- if (!isObject(currentValue)) {
29
- return currentValue;
30
- } else {
28
+
29
+ const valueAsThemeValues = asThemeValuesObject(currentValue);
30
+
31
+ if (valueAsThemeValues) {
31
32
  const platformKeys = ["ios", "android", "web", "macos", "windows"];
32
33
  const breakpointKeys = Object.keys(breakpoints);
33
34
  const lightDarkKeys = ["light", "dark"];
35
+
34
36
  const keysType = getKeysType(
35
- currentValue,
37
+ valueAsThemeValues,
36
38
  platformKeys,
37
39
  breakpointKeys,
38
40
  lightDarkKeys
@@ -40,7 +42,7 @@ export default function createThemeValuesProxy(
40
42
 
41
43
  if (keysType === "default") {
42
44
  return createThemeValuesProxy(
43
- currentValue,
45
+ valueAsThemeValues,
44
46
  breakpoints,
45
47
  deviceWidth,
46
48
  devicePlatform,
@@ -48,7 +50,7 @@ export default function createThemeValuesProxy(
48
50
  );
49
51
  } else if (keysType === "platform") {
50
52
  return getPlatformValue(
51
- currentValue,
53
+ valueAsThemeValues,
52
54
  breakpoints,
53
55
  deviceWidth,
54
56
  devicePlatform,
@@ -56,7 +58,7 @@ export default function createThemeValuesProxy(
56
58
  );
57
59
  } else if (keysType === "breakpoint") {
58
60
  return getBreakpointValue(
59
- currentValue,
61
+ valueAsThemeValues,
60
62
  breakpoints,
61
63
  deviceWidth,
62
64
  devicePlatform,
@@ -64,7 +66,7 @@ export default function createThemeValuesProxy(
64
66
  );
65
67
  } else if (keysType === "lightDark") {
66
68
  return getLightDarkValue(
67
- currentValue,
69
+ valueAsThemeValues,
68
70
  breakpoints,
69
71
  deviceWidth,
70
72
  devicePlatform,
@@ -73,6 +75,8 @@ export default function createThemeValuesProxy(
73
75
  } else {
74
76
  return undefined;
75
77
  }
78
+ } else {
79
+ return currentValue;
76
80
  }
77
81
  },
78
82
  set: () => {
@@ -147,16 +151,18 @@ function getPlatformValue(
147
151
  currentLightDarkSelection: "light" | "dark"
148
152
  ) {
149
153
  const currentPlatformValue = value[devicePlatform] ?? value.default;
150
- if (!isObject(currentPlatformValue)) {
151
- return currentPlatformValue;
152
- } else {
154
+ const valueAsThemeValues = asThemeValuesObject(currentPlatformValue);
155
+
156
+ if (valueAsThemeValues) {
153
157
  return createThemeValuesProxy(
154
- currentPlatformValue,
158
+ valueAsThemeValues,
155
159
  breakpoints,
156
160
  deviceWidth,
157
161
  devicePlatform,
158
162
  currentLightDarkSelection
159
163
  );
164
+ } else {
165
+ return currentPlatformValue;
160
166
  }
161
167
  }
162
168
 
@@ -178,16 +184,18 @@ function getBreakpointValue(
178
184
  }
179
185
  }
180
186
  const currentBreakpointValue = value[currentBreakpointKey] ?? value.default;
181
- if (!isObject(currentBreakpointValue)) {
182
- return currentBreakpointValue;
183
- } else {
187
+ const valueAsThemeValues = asThemeValuesObject(currentBreakpointValue);
188
+
189
+ if (valueAsThemeValues) {
184
190
  return createThemeValuesProxy(
185
- currentBreakpointValue,
191
+ valueAsThemeValues,
186
192
  breakpoints,
187
193
  deviceWidth,
188
194
  devicePlatform,
189
195
  currentLightDarkSelection
190
196
  );
197
+ } else {
198
+ return currentBreakpointValue;
191
199
  }
192
200
  }
193
201
 
@@ -200,16 +208,17 @@ function getLightDarkValue(
200
208
  ) {
201
209
  const currentLightDarkValue =
202
210
  value[currentLightDarkSelection] ?? value.default;
211
+ const valueAsThemeValues = asThemeValuesObject(currentLightDarkValue);
203
212
 
204
- if (!isObject(currentLightDarkValue)) {
205
- return currentLightDarkValue;
206
- } else {
213
+ if (valueAsThemeValues) {
207
214
  return createThemeValuesProxy(
208
- currentLightDarkValue,
215
+ valueAsThemeValues,
209
216
  breakpoints,
210
217
  deviceWidth,
211
218
  devicePlatform,
212
219
  currentLightDarkSelection
213
220
  );
221
+ } else {
222
+ return currentLightDarkValue;
214
223
  }
215
224
  }
package/src/types.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { TextStyle } from "react-native";
2
+
1
3
  export type ThemeValues = {
2
- [key: string]: string | number | ThemeValues;
4
+ [key: string]: string | number | TextStyle | ThemeValues;
3
5
  };
4
6
 
5
7
  export type Theme = {
@@ -11,7 +13,21 @@ export type Theme = {
11
13
  foreground?: ThemeValues;
12
14
  border?: ThemeValues;
13
15
  };
14
- typography: { [key: string]: any }; //TODO: Update theme to support typography. Left as 'any' for legacy support until updated and migrated
16
+ typography: {
17
+ body1?: ThemeValues | TextStyle;
18
+ body2?: ThemeValues | TextStyle;
19
+ button?: ThemeValues | TextStyle;
20
+ caption?: ThemeValues | TextStyle;
21
+ headline1?: ThemeValues | TextStyle;
22
+ headline2?: ThemeValues | TextStyle;
23
+ headline3?: ThemeValues | TextStyle;
24
+ headline4?: ThemeValues | TextStyle;
25
+ headline5?: ThemeValues | TextStyle;
26
+ headline6?: ThemeValues | TextStyle;
27
+ overline?: ThemeValues | TextStyle;
28
+ subtitle1?: ThemeValues | TextStyle;
29
+ subtitle2?: ThemeValues | TextStyle;
30
+ };
15
31
  };
16
32
 
17
33
  export type ValidatedTheme = Theme & {
@@ -26,6 +42,21 @@ export type ReadTheme = Theme & {
26
42
  foreground?: any;
27
43
  border?: any;
28
44
  };
45
+ typography: {
46
+ body1?: any;
47
+ body2?: any;
48
+ button?: any;
49
+ caption?: any;
50
+ headline1?: any;
51
+ headline2?: any;
52
+ headline3?: any;
53
+ headline4?: any;
54
+ headline5?: any;
55
+ headline6?: any;
56
+ overline?: any;
57
+ subtitle1?: any;
58
+ subtitle2?: any;
59
+ };
29
60
  };
30
61
 
31
62
  export type ColorPaletteValues = {
package/src/validators.js CHANGED
@@ -1,52 +1,91 @@
1
+ import { z } from "zod";
2
+ const PaletteSchema = z.record(z.string(), z.record(z.string(), z.string()));
3
+ const BreakpointSchema = z.record(z.string(), z.number());
4
+ const TextStyleSchema = z.union([
5
+ z.object({
6
+ fontSize: z.number(),
7
+ }),
8
+ z.object({
9
+ fontFamily: z.string(),
10
+ }),
11
+ z.object({
12
+ fontWeight: z.enum([
13
+ "normal",
14
+ "bold",
15
+ "100",
16
+ "200",
17
+ "300",
18
+ "400",
19
+ "500",
20
+ "600",
21
+ "700",
22
+ "800",
23
+ "900",
24
+ ]),
25
+ }),
26
+ z.object({
27
+ fontStyle: z.enum(["normal", "italic"]),
28
+ }),
29
+ z.object({
30
+ letterSpacing: z.number(),
31
+ }),
32
+ z.object({
33
+ lineHeight: z.number(),
34
+ }),
35
+ ]);
36
+ const ThemeValuesSchema = z.record(z.string(), z.lazy(() => z.union([z.string(), z.number(), TextStyleSchema, ThemeValuesSchema])));
37
+ const TextStyleOrThemeValuesSchema = z.union([TextStyleSchema, ThemeValuesSchema]);
38
+ const ThemeSchema = z.object({
39
+ name: z.string(),
40
+ colors: z.object({
41
+ branding: ThemeValuesSchema.optional(),
42
+ text: ThemeValuesSchema.optional(),
43
+ background: ThemeValuesSchema.optional(),
44
+ foreground: ThemeValuesSchema.optional(),
45
+ border: ThemeValuesSchema.optional(),
46
+ }),
47
+ typography: z.object({
48
+ body1: TextStyleOrThemeValuesSchema.optional(),
49
+ body2: TextStyleOrThemeValuesSchema.optional(),
50
+ button: TextStyleOrThemeValuesSchema.optional(),
51
+ caption: TextStyleOrThemeValuesSchema.optional(),
52
+ headline1: TextStyleOrThemeValuesSchema.optional(),
53
+ headline2: TextStyleOrThemeValuesSchema.optional(),
54
+ headline3: TextStyleOrThemeValuesSchema.optional(),
55
+ headline4: TextStyleOrThemeValuesSchema.optional(),
56
+ headline5: TextStyleOrThemeValuesSchema.optional(),
57
+ headline6: TextStyleOrThemeValuesSchema.optional(),
58
+ overline: TextStyleOrThemeValuesSchema.optional(),
59
+ subtitle1: TextStyleOrThemeValuesSchema.optional(),
60
+ subtitle2: TextStyleOrThemeValuesSchema.optional(),
61
+ }),
62
+ });
1
63
  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
- }
64
+ const result = PaletteSchema.safeParse(palettes);
65
+ if (!result.success) {
66
+ throw new Error("Invalid palettes object: " + result.error.message);
14
67
  }
15
68
  }
16
69
  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
- }
70
+ const result = BreakpointSchema.safeParse(breakpoints);
71
+ if (!result.success) {
72
+ throw new Error("Invalid breakpoints object: " + result.error.message);
24
73
  }
25
74
  }
26
75
  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
- }
76
+ const result = ThemeSchema.safeParse(theme);
77
+ if (!result.success) {
78
+ throw new Error("Invalid theme object: " + result.error.message);
45
79
  }
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);
80
+ }
81
+ export function isTextStyleObject(value) {
82
+ return TextStyleSchema.safeParse(value).success;
83
+ }
84
+ export function asThemeValuesObject(value) {
85
+ // Text style matches the shape of ThemeValues, but we don't want to treat it as a ThemeValues
86
+ if (isTextStyleObject(value)) {
87
+ return null;
88
+ }
89
+ return ThemeValuesSchema.safeParse(value).success ? value : null;
51
90
  }
52
91
  //# sourceMappingURL=validators.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["validators.ts"],"names":[],"mappings":"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"}
package/src/validators.ts CHANGED
@@ -1,59 +1,116 @@
1
1
  import { ColorPalettes, Breakpoints, Theme, ThemeValues } from "./types";
2
+ import { TextStyle } from "react-native";
3
+ import { z } from "zod";
4
+
5
+ const PaletteSchema: z.ZodType<ColorPalettes> = z.record(
6
+ z.string(),
7
+ z.record(z.string(), z.string())
8
+ );
9
+
10
+ const BreakpointSchema: z.ZodType<Breakpoints> = z.record(
11
+ z.string(),
12
+ z.number()
13
+ );
14
+
15
+ const TextStyleSchema: z.ZodType<TextStyle> = z.union([
16
+ z.object({
17
+ fontSize: z.number(),
18
+ }),
19
+ z.object({
20
+ fontFamily: z.string(),
21
+ }),
22
+ z.object({
23
+ fontWeight: z.enum([
24
+ "normal",
25
+ "bold",
26
+ "100",
27
+ "200",
28
+ "300",
29
+ "400",
30
+ "500",
31
+ "600",
32
+ "700",
33
+ "800",
34
+ "900",
35
+ ]),
36
+ }),
37
+ z.object({
38
+ fontStyle: z.enum(["normal", "italic"]),
39
+ }),
40
+ z.object({
41
+ letterSpacing: z.number(),
42
+ }),
43
+ z.object({
44
+ lineHeight: z.number(),
45
+ }),
46
+ ]);
47
+
48
+ const ThemeValuesSchema: z.ZodType<ThemeValues> = z.record(
49
+ z.string(),
50
+ z.lazy(() =>
51
+ z.union([z.string(), z.number(), TextStyleSchema, ThemeValuesSchema])
52
+ )
53
+ );
54
+
55
+ const TextStyleOrThemeValuesSchema: z.ZodType<ThemeValues | TextStyle> =
56
+ z.union([TextStyleSchema, ThemeValuesSchema]);
57
+
58
+ const ThemeSchema: z.ZodType<Theme> = z.object({
59
+ name: z.string(),
60
+ colors: z.object({
61
+ branding: ThemeValuesSchema.optional(),
62
+ text: ThemeValuesSchema.optional(),
63
+ background: ThemeValuesSchema.optional(),
64
+ foreground: ThemeValuesSchema.optional(),
65
+ border: ThemeValuesSchema.optional(),
66
+ }),
67
+ typography: z.object({
68
+ body1: TextStyleOrThemeValuesSchema.optional(),
69
+ body2: TextStyleOrThemeValuesSchema.optional(),
70
+ button: TextStyleOrThemeValuesSchema.optional(),
71
+ caption: TextStyleOrThemeValuesSchema.optional(),
72
+ headline1: TextStyleOrThemeValuesSchema.optional(),
73
+ headline2: TextStyleOrThemeValuesSchema.optional(),
74
+ headline3: TextStyleOrThemeValuesSchema.optional(),
75
+ headline4: TextStyleOrThemeValuesSchema.optional(),
76
+ headline5: TextStyleOrThemeValuesSchema.optional(),
77
+ headline6: TextStyleOrThemeValuesSchema.optional(),
78
+ overline: TextStyleOrThemeValuesSchema.optional(),
79
+ subtitle1: TextStyleOrThemeValuesSchema.optional(),
80
+ subtitle2: TextStyleOrThemeValuesSchema.optional(),
81
+ }),
82
+ });
2
83
 
3
84
  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
- }
85
+ const result = PaletteSchema.safeParse(palettes);
86
+ if (!result.success) {
87
+ throw new Error("Invalid palettes object: " + result.error.message);
20
88
  }
21
89
  }
22
90
 
23
91
  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
- }
92
+ const result = BreakpointSchema.safeParse(breakpoints);
93
+ if (!result.success) {
94
+ throw new Error("Invalid breakpoints object: " + result.error.message);
31
95
  }
32
96
  }
33
97
 
34
98
  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
- }
99
+ const result = ThemeSchema.safeParse(theme);
100
+ if (!result.success) {
101
+ throw new Error("Invalid theme object: " + result.error.message);
53
102
  }
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);
103
+ }
104
+
105
+ export function isTextStyleObject(value: any): boolean {
106
+ return TextStyleSchema.safeParse(value).success;
107
+ }
108
+
109
+ export function asThemeValuesObject(value: any): ThemeValues | null {
110
+ // Text style matches the shape of ThemeValues, but we don't want to treat it as a ThemeValues
111
+ if (isTextStyleObject(value)) {
112
+ return null;
113
+ }
114
+
115
+ return ThemeValuesSchema.safeParse(value).success ? value : null;
59
116
  }