@tamagui/core 1.0.0-alpha.6 → 1.0.0-alpha.7

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.
@@ -11,11 +11,13 @@ const Theme = /* @__PURE__ */ __name((props) => {
11
11
  const themes = useContext(ThemeContext);
12
12
  const [parentName, setParentName] = useState(parent.name || "light");
13
13
  if (!themes) {
14
- console.warn("no themes???", props);
14
+ console.warn("Error, no themes in context", props);
15
15
  return props.children;
16
16
  }
17
17
  const name = props.name;
18
- const activeName = !name ? null : name in themes ? name : `${name}-${parentName}`;
18
+ const nextNameParent = `${name}-${parentName}`;
19
+ const nextNameParentParent = `${name}-${parent.parentName}`;
20
+ const activeName = !name ? null : name in themes ? name : nextNameParent in themes ? nextNameParent : nextNameParentParent in themes ? nextNameParentParent : null;
19
21
  const theme = activeName ? themes[activeName] : null;
20
22
  const themeManager = useConstant(() => {
21
23
  if (!theme) {
@@ -23,7 +25,7 @@ const Theme = /* @__PURE__ */ __name((props) => {
23
25
  }
24
26
  const manager = new ThemeManager();
25
27
  if (name) {
26
- manager.setActiveTheme(name, themes[name]);
28
+ manager.setActiveTheme({ name, theme: themes[name], parentName });
27
29
  }
28
30
  return manager;
29
31
  });
@@ -31,14 +33,14 @@ const Theme = /* @__PURE__ */ __name((props) => {
31
33
  if (!themeManager) {
32
34
  return;
33
35
  }
34
- themeManager.setActiveTheme(activeName, theme);
36
+ themeManager.setActiveTheme({ name: activeName, theme, parentName });
35
37
  return parent.onChangeTheme((next) => {
36
38
  if (next) {
37
- themeManager.setActiveTheme(next, themes[next]);
39
+ themeManager.setActiveTheme({ name: next, theme: themes[next], parentName });
38
40
  setParentName(next);
39
41
  }
40
42
  });
41
- }, [themes, activeName]);
43
+ }, [themes, activeName, parentName]);
42
44
  if (!name || !theme) {
43
45
  return props.children;
44
46
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/views/Theme.tsx"],
4
- "sourcesContent": ["import React, { useContext, useState } from 'react'\n\nimport { isWeb, useIsomorphicLayoutEffect } from '../constants/platform'\nimport { getThemeParentClassName } from '../createTamagui'\nimport { useConstant } from '../hooks/useConstant'\nimport { ThemeName, Themes } from '../types'\nimport { ThemeContext } from './ThemeContext'\nimport { ThemeManager, ThemeManagerContext } from './ThemeManagerContext'\n\nexport type ThemeProps = {\n disableThemeClass?: boolean\n name: ThemeName | null\n children?: any\n}\n\nexport const Theme = (props: ThemeProps) => {\n const parent = useContext(ThemeManagerContext)\n const themes = useContext(ThemeContext)\n const [parentName, setParentName] = useState(parent.name || 'light')\n if (!themes) {\n console.warn('no themes???', props)\n return props.children\n }\n const name = props.name as string\n const activeName = !name ? null : name in themes ? name : `${name}-${parentName}`\n const theme = activeName ? themes[activeName] : null\n const themeManager = useConstant<ThemeManager | null>(() => {\n if (!theme) {\n return null\n }\n const manager = new ThemeManager()\n if (name) {\n manager.setActiveTheme(name, themes[name])\n }\n return manager\n })\n\n useIsomorphicLayoutEffect(() => {\n if (!themeManager) {\n return\n }\n themeManager.setActiveTheme(activeName, theme)\n return parent.onChangeTheme((next) => {\n if (next) {\n themeManager.setActiveTheme(next, themes[next])\n setParentName(next)\n }\n })\n }, [themes, activeName])\n\n if (!name || !theme) {\n return props.children\n }\n\n const contents = themeManager ? (\n <ThemeManagerContext.Provider value={themeManager}>\n {props.children}\n </ThemeManagerContext.Provider>\n ) : (\n props.children\n )\n\n if (isWeb) {\n if (props.disableThemeClass) {\n return contents\n }\n const color = themes[name]?.['color']?.['variable']\n return (\n <div\n className={getThemeParentClassName(name)}\n // in order to provide currentColor, set color by default\n style={{ display: 'contents', color }}\n >\n {contents}\n </div>\n )\n }\n\n return contents\n}\n"],
5
- "mappings": ";;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAQO,MAAM,QAAQ,wBAAC,UAAsB;AAC1C,QAAM,SAAS,WAAW;AAC1B,QAAM,SAAS,WAAW;AAC1B,QAAM,CAAC,YAAY,iBAAiB,SAAS,OAAO,QAAQ;AAC5D,MAAI,CAAC,QAAQ;AACX,YAAQ,KAAK,gBAAgB;AAC7B,WAAO,MAAM;AAAA;AAEf,QAAM,OAAO,MAAM;AACnB,QAAM,aAAa,CAAC,OAAO,OAAO,QAAQ,SAAS,OAAO,GAAG,QAAQ;AACrE,QAAM,QAAQ,aAAa,OAAO,cAAc;AAChD,QAAM,eAAe,YAAiC,MAAM;AAC1D,QAAI,CAAC,OAAO;AACV,aAAO;AAAA;AAET,UAAM,UAAU,IAAI;AACpB,QAAI,MAAM;AACR,cAAQ,eAAe,MAAM,OAAO;AAAA;AAEtC,WAAO;AAAA;AAGT,4BAA0B,MAAM;AAC9B,QAAI,CAAC,cAAc;AACjB;AAAA;AAEF,iBAAa,eAAe,YAAY;AACxC,WAAO,OAAO,cAAc,CAAC,SAAS;AACpC,UAAI,MAAM;AACR,qBAAa,eAAe,MAAM,OAAO;AACzC,sBAAc;AAAA;AAAA;AAAA,KAGjB,CAAC,QAAQ;AAEZ,MAAI,CAAC,QAAQ,CAAC,OAAO;AACnB,WAAO,MAAM;AAAA;AAGf,QAAM,WAAW,eACf,oCAAC,oBAAoB,UAArB;AAAA,IAA8B,OAAO;AAAA,KAClC,MAAM,YAGT,MAAM;AAGR,MAAI,OAAO;AACT,QAAI,MAAM,mBAAmB;AAC3B,aAAO;AAAA;AAET,UAAM,QAAQ,OAAO,QAAQ,WAAW;AACxC,WACE,oCAAC,OAAD;AAAA,MACE,WAAW,wBAAwB;AAAA,MAEnC,OAAO,EAAE,SAAS,YAAY;AAAA,OAE7B;AAAA;AAKP,SAAO;AAAA,GA/DY;",
4
+ "sourcesContent": ["import React, { useContext, useState } from 'react'\n\nimport { isWeb, useIsomorphicLayoutEffect } from '../constants/platform'\nimport { getThemeParentClassName } from '../createTamagui'\nimport { useConstant } from '../hooks/useConstant'\nimport { ThemeName } from '../types'\nimport { ThemeContext } from './ThemeContext'\nimport { ThemeManager, ThemeManagerContext } from './ThemeManagerContext'\n\nexport type ThemeProps = {\n disableThemeClass?: boolean\n name: ThemeName | null\n children?: any\n}\n\nexport const Theme = (props: ThemeProps) => {\n const parent = useContext(ThemeManagerContext)\n const themes = useContext(ThemeContext)\n const [parentName, setParentName] = useState(parent.name || 'light')\n if (!themes) {\n console.warn('Error, no themes in context', props)\n return props.children\n }\n const name = props.name as string\n const nextNameParent = `${name}-${parentName}`\n const nextNameParentParent = `${name}-${parent.parentName}`\n const activeName = !name\n ? null\n : name in themes\n ? name\n : nextNameParent in themes\n ? nextNameParent\n : nextNameParentParent in themes\n ? nextNameParentParent\n : null\n const theme = activeName ? themes[activeName] : null\n const themeManager = useConstant<ThemeManager | null>(() => {\n if (!theme) {\n return null\n }\n const manager = new ThemeManager()\n if (name) {\n manager.setActiveTheme({ name, theme: themes[name], parentName })\n }\n return manager\n })\n\n useIsomorphicLayoutEffect(() => {\n if (!themeManager) {\n return\n }\n themeManager.setActiveTheme({ name: activeName, theme, parentName })\n return parent.onChangeTheme((next) => {\n if (next) {\n themeManager.setActiveTheme({ name: next, theme: themes[next], parentName })\n setParentName(next)\n }\n })\n }, [themes, activeName, parentName])\n\n if (!name || !theme) {\n return props.children\n }\n\n const contents = themeManager ? (\n <ThemeManagerContext.Provider value={themeManager}>\n {props.children}\n </ThemeManagerContext.Provider>\n ) : (\n props.children\n )\n\n if (isWeb) {\n if (props.disableThemeClass) {\n return contents\n }\n const color = themes[name]?.['color']?.['variable']\n return (\n <div\n className={getThemeParentClassName(name)}\n // in order to provide currentColor, set color by default\n style={{ display: 'contents', color }}\n >\n {contents}\n </div>\n )\n }\n\n return contents\n}\n"],
5
+ "mappings": ";;AAAA;AAEA;AACA;AACA;AAEA;AACA;AAQO,MAAM,QAAQ,wBAAC,UAAsB;AAC1C,QAAM,SAAS,WAAW;AAC1B,QAAM,SAAS,WAAW;AAC1B,QAAM,CAAC,YAAY,iBAAiB,SAAS,OAAO,QAAQ;AAC5D,MAAI,CAAC,QAAQ;AACX,YAAQ,KAAK,+BAA+B;AAC5C,WAAO,MAAM;AAAA;AAEf,QAAM,OAAO,MAAM;AACnB,QAAM,iBAAiB,GAAG,QAAQ;AAClC,QAAM,uBAAuB,GAAG,QAAQ,OAAO;AAC/C,QAAM,aAAa,CAAC,OAChB,OACA,QAAQ,SACR,OACA,kBAAkB,SAClB,iBACA,wBAAwB,SACxB,uBACA;AACJ,QAAM,QAAQ,aAAa,OAAO,cAAc;AAChD,QAAM,eAAe,YAAiC,MAAM;AAC1D,QAAI,CAAC,OAAO;AACV,aAAO;AAAA;AAET,UAAM,UAAU,IAAI;AACpB,QAAI,MAAM;AACR,cAAQ,eAAe,EAAE,MAAM,OAAO,OAAO,OAAO;AAAA;AAEtD,WAAO;AAAA;AAGT,4BAA0B,MAAM;AAC9B,QAAI,CAAC,cAAc;AACjB;AAAA;AAEF,iBAAa,eAAe,EAAE,MAAM,YAAY,OAAO;AACvD,WAAO,OAAO,cAAc,CAAC,SAAS;AACpC,UAAI,MAAM;AACR,qBAAa,eAAe,EAAE,MAAM,MAAM,OAAO,OAAO,OAAO;AAC/D,sBAAc;AAAA;AAAA;AAAA,KAGjB,CAAC,QAAQ,YAAY;AAExB,MAAI,CAAC,QAAQ,CAAC,OAAO;AACnB,WAAO,MAAM;AAAA;AAGf,QAAM,WAAW,eACf,oCAAC,oBAAoB,UAArB;AAAA,IAA8B,OAAO;AAAA,KAClC,MAAM,YAGT,MAAM;AAGR,MAAI,OAAO;AACT,QAAI,MAAM,mBAAmB;AAC3B,aAAO;AAAA;AAET,UAAM,QAAQ,OAAO,QAAQ,WAAW;AACxC,WACE,oCAAC,OAAD;AAAA,MACE,WAAW,wBAAwB;AAAA,MAEnC,OAAO,EAAE,SAAS,YAAY;AAAA,OAE7B;AAAA;AAKP,SAAO;AAAA,GAzEY;",
6
6
  "names": []
7
7
  }
@@ -4,16 +4,22 @@ import { createContext } from "react";
4
4
  class ThemeManager {
5
5
  constructor() {
6
6
  this.name = "light";
7
+ this.parentName = null;
7
8
  this.keys = new Map();
8
9
  this.listeners = new Map();
9
10
  this.callbacks = new Set();
10
11
  this.theme = null;
11
12
  }
12
- setActiveTheme(name, theme) {
13
+ setActiveTheme({
14
+ name,
15
+ theme,
16
+ parentName
17
+ }) {
13
18
  if (name === this.name)
14
19
  return;
15
20
  this.name = name;
16
21
  this.theme = theme;
22
+ this.parentName = parentName || null;
17
23
  this.update();
18
24
  }
19
25
  track(uuid, keys) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/views/ThemeManagerContext.tsx"],
4
- "sourcesContent": ["import { createContext } from 'react'\n\nexport class ThemeManager {\n name: string | null = 'light'\n keys = new Map<any, Set<string>>()\n listeners = new Map<any, Function>()\n callbacks = new Set<Function>()\n theme = null\n\n setActiveTheme(name: string | null, theme?: any) {\n if (name === this.name) return\n this.name = name\n this.theme = theme\n this.update()\n }\n\n track(uuid: any, keys: Set<string>) {\n if (!this.name) return\n this.keys.set(uuid, keys)\n }\n\n update() {\n if (!this.name) {\n this.keys.clear()\n }\n for (const [uuid, keys] of this.keys.entries()) {\n if (keys.size) {\n this.listeners.get(uuid)?.()\n }\n }\n this.callbacks.forEach((cb) => cb(this.name))\n }\n\n onChangeTheme(cb: (name: string | null) => void) {\n this.callbacks.add(cb)\n return () => {\n this.callbacks.delete(cb)\n }\n }\n\n onUpdate(uuid: any, cb: Function) {\n this.listeners.set(uuid, cb)\n return () => {\n this.listeners.delete(uuid)\n this.keys.delete(uuid)\n }\n }\n}\n\nexport const ThemeManagerContext = createContext<ThemeManager>(new ThemeManager())\n"],
5
- "mappings": ";;AAAA;AAEO,mBAAmB;AAAA,EAAnB,cAFP;AAGE,gBAAsB;AACtB,gBAAO,IAAI;AACX,qBAAY,IAAI;AAChB,qBAAY,IAAI;AAChB,iBAAQ;AAAA;AAAA,EAER,eAAe,MAAqB,OAAa;AAC/C,QAAI,SAAS,KAAK;AAAM;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK;AAAA;AAAA,EAGP,MAAM,MAAW,MAAmB;AAClC,QAAI,CAAC,KAAK;AAAM;AAChB,SAAK,KAAK,IAAI,MAAM;AAAA;AAAA,EAGtB,SAAS;AACP,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,KAAK;AAAA;AAEZ,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,WAAW;AAC9C,UAAI,KAAK,MAAM;AACb,aAAK,UAAU,IAAI;AAAA;AAAA;AAGvB,SAAK,UAAU,QAAQ,CAAC,OAAO,GAAG,KAAK;AAAA;AAAA,EAGzC,cAAc,IAAmC;AAC/C,SAAK,UAAU,IAAI;AACnB,WAAO,MAAM;AACX,WAAK,UAAU,OAAO;AAAA;AAAA;AAAA,EAI1B,SAAS,MAAW,IAAc;AAChC,SAAK,UAAU,IAAI,MAAM;AACzB,WAAO,MAAM;AACX,WAAK,UAAU,OAAO;AACtB,WAAK,KAAK,OAAO;AAAA;AAAA;AAAA;AA1ChB;AA+CA,MAAM,sBAAsB,cAA4B,IAAI;",
4
+ "sourcesContent": ["import { createContext } from 'react'\n\nexport class ThemeManager {\n name: string | null = 'light'\n parentName: string | null = null\n keys = new Map<any, Set<string>>()\n listeners = new Map<any, Function>()\n callbacks = new Set<Function>()\n theme = null\n\n setActiveTheme({\n name,\n theme,\n parentName,\n }: {\n parentName?: string | null\n name: string | null\n theme?: any\n }) {\n if (name === this.name) return\n this.name = name\n this.theme = theme\n this.parentName = parentName || null\n this.update()\n }\n\n track(uuid: any, keys: Set<string>) {\n if (!this.name) return\n this.keys.set(uuid, keys)\n }\n\n update() {\n if (!this.name) {\n this.keys.clear()\n }\n for (const [uuid, keys] of this.keys.entries()) {\n if (keys.size) {\n this.listeners.get(uuid)?.()\n }\n }\n this.callbacks.forEach((cb) => cb(this.name))\n }\n\n onChangeTheme(cb: (name: string | null) => void) {\n this.callbacks.add(cb)\n return () => {\n this.callbacks.delete(cb)\n }\n }\n\n onUpdate(uuid: any, cb: Function) {\n this.listeners.set(uuid, cb)\n return () => {\n this.listeners.delete(uuid)\n this.keys.delete(uuid)\n }\n }\n}\n\nexport const ThemeManagerContext = createContext<ThemeManager>(new ThemeManager())\n"],
5
+ "mappings": ";;AAAA;AAEO,mBAAmB;AAAA,EAAnB,cAFP;AAGE,gBAAsB;AACtB,sBAA4B;AAC5B,gBAAO,IAAI;AACX,qBAAY,IAAI;AAChB,qBAAY,IAAI;AAChB,iBAAQ;AAAA;AAAA,EAER,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,KAKC;AACD,QAAI,SAAS,KAAK;AAAM;AACxB,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,aAAa,cAAc;AAChC,SAAK;AAAA;AAAA,EAGP,MAAM,MAAW,MAAmB;AAClC,QAAI,CAAC,KAAK;AAAM;AAChB,SAAK,KAAK,IAAI,MAAM;AAAA;AAAA,EAGtB,SAAS;AACP,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,KAAK;AAAA;AAEZ,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,WAAW;AAC9C,UAAI,KAAK,MAAM;AACb,aAAK,UAAU,IAAI;AAAA;AAAA;AAGvB,SAAK,UAAU,QAAQ,CAAC,OAAO,GAAG,KAAK;AAAA;AAAA,EAGzC,cAAc,IAAmC;AAC/C,SAAK,UAAU,IAAI;AACnB,WAAO,MAAM;AACX,WAAK,UAAU,OAAO;AAAA;AAAA;AAAA,EAI1B,SAAS,MAAW,IAAc;AAChC,SAAK,UAAU,IAAI,MAAM;AACzB,WAAO,MAAM;AACX,WAAK,UAAU,OAAO;AACtB,WAAK,KAAK,OAAO;AAAA;AAAA;AAAA;AApDhB;AAyDA,MAAM,sBAAsB,cAA4B,IAAI;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/core",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.7",
4
4
  "source": "src/index.ts",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -26,6 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@tamagui/build": "^1.0.0-alpha.2",
29
+ "@tamagui/ow": "^0.28.1",
29
30
  "@types/react": "^17.0.35",
30
31
  "esbuild": "^0.11.20",
31
32
  "typescript": "^4.4.4"
@@ -36,5 +37,5 @@
36
37
  "engines": {
37
38
  "node": ">=16"
38
39
  },
39
- "gitHead": "18a942d7a08109c8f5b8692a2adb20c69d56a7e1"
40
+ "gitHead": "13694a98d31535846699004532630788177537e9"
40
41
  }
package/src/createFont.ts CHANGED
@@ -1,6 +1,11 @@
1
+ import { validateFont } from './helpers/validate'
1
2
  import { GenericFont } from './types'
2
3
 
3
4
  export const createFont = <A extends GenericFont>(font: A): A => {
5
+ if (process.env.NODE_ENV === 'development') {
6
+ validateFont(font)
7
+ }
8
+
4
9
  // fills in any missing values based on size keys being standard
5
10
  const sizeKeys = Object.keys(font.size)
6
11
 
@@ -2,6 +2,7 @@ import { getStyleRules } from '@tamagui/helpers'
2
2
 
3
3
  import { Variable, createVariable, isVariable } from './createVariable'
4
4
  import { createTamaguiProvider } from './helpers/createTamaguiProvider'
5
+ import { validateConfig } from './helpers/validate'
5
6
  import { configureMedia } from './hooks/useMedia'
6
7
  import {
7
8
  CreateTamaguiConfig,
@@ -46,6 +47,12 @@ export function createTamagui<Conf extends CreateTamaguiProps>(
46
47
  ): Conf extends CreateTamaguiConfig<infer A, infer B, infer C, infer D>
47
48
  ? TamaguiInternalConfig<A, B, C, D>
48
49
  : unknown {
50
+ if (process.env.NODE_ENV === 'development') {
51
+ // config is re-run by the @tamagui/static, dont double validate
52
+ if (!config['parsed']) {
53
+ validateConfig(config)
54
+ }
55
+ }
49
56
  // test env loads a few times as it runs diff tests
50
57
  if (conf) {
51
58
  console.warn('Called createTamagui twice! Should never do so')
@@ -157,6 +164,7 @@ export function createTamagui<Conf extends CreateTamaguiProps>(
157
164
  themeConfig,
158
165
  themeParsed,
159
166
  tokensParsed,
167
+ parsed: true,
160
168
  getCSS() {
161
169
  return `${themeConfig.css}\n${[...getStyleRules()].join('\n')}`
162
170
  },
@@ -1,9 +1,14 @@
1
1
  import { Variable, createVariable } from './createVariable'
2
+ import { validateTokens } from './helpers/validate'
2
3
  import { CreateTokens } from './types'
3
4
 
4
- // tokens.color.dark.red ===>{ var: `color-dark-red`, val: '' }
5
+ // tokens.color.dark.red ===> { var: `color-dark-red`, val: '' }
5
6
 
6
7
  export function createTokens<T extends CreateTokens>(tokens: T): MakeTokens<T> {
8
+ if (process.env.NODE_ENV === 'development') {
9
+ validateTokens(tokens)
10
+ }
11
+
7
12
  return setupTokens(tokens) as any
8
13
  }
9
14
 
@@ -0,0 +1,59 @@
1
+ // published a patched version that works on native
2
+ import ow from '@tamagui/ow'
3
+
4
+ export const validateConfig = (conf: any) => {
5
+ if (!conf) {
6
+ console.warn('no conf?')
7
+ return
8
+ }
9
+ try {
10
+ ow(conf, validConfig)
11
+ } catch (err: any) {
12
+ console.log('Given config:\n', JSON.stringify(conf, null, 2))
13
+ throw err
14
+ }
15
+ }
16
+
17
+ const validConfig = ow.object.exactShape({
18
+ defaultTheme: ow.optional.string,
19
+ disableRootThemeClass: ow.optional.boolean,
20
+ shorthands: ow.optional.object.valuesOfType(ow.string),
21
+ themes: ow.object.valuesOfType(ow.object),
22
+ tokens: ow.object.valuesOfType(ow.object),
23
+ media: ow.object.valuesOfType(ow.object),
24
+ })
25
+
26
+ const validFont = ow.object.exactShape({
27
+ family: ow.string,
28
+ size: ow.object.nonEmpty.valuesOfType(ow.number),
29
+ lineHeight: ow.object.nonEmpty.valuesOfType(ow.number),
30
+ weight: ow.object.nonEmpty.valuesOfType(ow.string),
31
+ letterSpacing: ow.object.nonEmpty.valuesOfType(ow.number),
32
+ })
33
+
34
+ const validTokens = ow.object.exactShape({
35
+ font: ow.object.valuesOfType(validFont),
36
+ size: ow.object.nonEmpty.valuesOfType(ow.number),
37
+ space: ow.object.nonEmpty.valuesOfType(ow.number),
38
+ zIndex: ow.object.nonEmpty.valuesOfType(ow.number),
39
+ radius: ow.object.nonEmpty.valuesOfType(ow.number),
40
+ color: ow.object.nonEmpty.valuesOfType(ow.string),
41
+ })
42
+
43
+ export const validateTokens = (tokens: any) => {
44
+ try {
45
+ ow(tokens, validTokens)
46
+ } catch (err: any) {
47
+ console.log('Given tokens:\n', JSON.stringify(tokens, null, 2))
48
+ throw err
49
+ }
50
+ }
51
+
52
+ export const validateFont = (font: any) => {
53
+ try {
54
+ ow(font, validFont)
55
+ } catch (err: any) {
56
+ console.log('Given font:\n', JSON.stringify(font, null, 2))
57
+ throw err
58
+ }
59
+ }
package/src/types.tsx CHANGED
@@ -110,6 +110,7 @@ export type TamaguiInternalConfig<
110
110
  tokensParsed: CreateTokens<Variable>
111
111
  themeConfig: any
112
112
  getCSS: () => string
113
+ parsed: boolean
113
114
  }
114
115
 
115
116
  // prevents const intersections from being clobbered into string, keeping the consts
@@ -3,7 +3,7 @@ import React, { useContext, useState } from 'react'
3
3
  import { isWeb, useIsomorphicLayoutEffect } from '../constants/platform'
4
4
  import { getThemeParentClassName } from '../createTamagui'
5
5
  import { useConstant } from '../hooks/useConstant'
6
- import { ThemeName, Themes } from '../types'
6
+ import { ThemeName } from '../types'
7
7
  import { ThemeContext } from './ThemeContext'
8
8
  import { ThemeManager, ThemeManagerContext } from './ThemeManagerContext'
9
9
 
@@ -18,11 +18,21 @@ export const Theme = (props: ThemeProps) => {
18
18
  const themes = useContext(ThemeContext)
19
19
  const [parentName, setParentName] = useState(parent.name || 'light')
20
20
  if (!themes) {
21
- console.warn('no themes???', props)
21
+ console.warn('Error, no themes in context', props)
22
22
  return props.children
23
23
  }
24
24
  const name = props.name as string
25
- const activeName = !name ? null : name in themes ? name : `${name}-${parentName}`
25
+ const nextNameParent = `${name}-${parentName}`
26
+ const nextNameParentParent = `${name}-${parent.parentName}`
27
+ const activeName = !name
28
+ ? null
29
+ : name in themes
30
+ ? name
31
+ : nextNameParent in themes
32
+ ? nextNameParent
33
+ : nextNameParentParent in themes
34
+ ? nextNameParentParent
35
+ : null
26
36
  const theme = activeName ? themes[activeName] : null
27
37
  const themeManager = useConstant<ThemeManager | null>(() => {
28
38
  if (!theme) {
@@ -30,7 +40,7 @@ export const Theme = (props: ThemeProps) => {
30
40
  }
31
41
  const manager = new ThemeManager()
32
42
  if (name) {
33
- manager.setActiveTheme(name, themes[name])
43
+ manager.setActiveTheme({ name, theme: themes[name], parentName })
34
44
  }
35
45
  return manager
36
46
  })
@@ -39,14 +49,14 @@ export const Theme = (props: ThemeProps) => {
39
49
  if (!themeManager) {
40
50
  return
41
51
  }
42
- themeManager.setActiveTheme(activeName, theme)
52
+ themeManager.setActiveTheme({ name: activeName, theme, parentName })
43
53
  return parent.onChangeTheme((next) => {
44
54
  if (next) {
45
- themeManager.setActiveTheme(next, themes[next])
55
+ themeManager.setActiveTheme({ name: next, theme: themes[next], parentName })
46
56
  setParentName(next)
47
57
  }
48
58
  })
49
- }, [themes, activeName])
59
+ }, [themes, activeName, parentName])
50
60
 
51
61
  if (!name || !theme) {
52
62
  return props.children
@@ -2,15 +2,25 @@ import { createContext } from 'react'
2
2
 
3
3
  export class ThemeManager {
4
4
  name: string | null = 'light'
5
+ parentName: string | null = null
5
6
  keys = new Map<any, Set<string>>()
6
7
  listeners = new Map<any, Function>()
7
8
  callbacks = new Set<Function>()
8
9
  theme = null
9
10
 
10
- setActiveTheme(name: string | null, theme?: any) {
11
+ setActiveTheme({
12
+ name,
13
+ theme,
14
+ parentName,
15
+ }: {
16
+ parentName?: string | null
17
+ name: string | null
18
+ theme?: any
19
+ }) {
11
20
  if (name === this.name) return
12
21
  this.name = name
13
22
  this.theme = theme
23
+ this.parentName = parentName || null
14
24
  this.update()
15
25
  }
16
26
 
package/types.d.ts CHANGED
@@ -97,6 +97,7 @@ export declare type TamaguiInternalConfig<A extends GenericTokens = GenericToken
97
97
  tokensParsed: CreateTokens<Variable>;
98
98
  themeConfig: any;
99
99
  getCSS: () => string;
100
+ parsed: boolean;
100
101
  };
101
102
  export declare type UnionableString = string & {};
102
103
  export declare type UnionableNumber = number & {};