@tamagui/core 1.0.1-beta.220 → 1.0.1-beta.221

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 (284) hide show
  1. package/dist/cjs/addTheme.js +7 -10
  2. package/dist/cjs/addTheme.js.map +3 -3
  3. package/dist/cjs/config.js +8 -4
  4. package/dist/cjs/config.js.map +2 -2
  5. package/dist/cjs/{hooks/useUnmountEffect.js → contexts/ButtonNestingContext.js} +7 -9
  6. package/dist/cjs/contexts/ButtonNestingContext.js.map +7 -0
  7. package/dist/cjs/createComponent.js +63 -67
  8. package/dist/cjs/createComponent.js.map +3 -3
  9. package/dist/cjs/createTamagui.js +13 -12
  10. package/dist/cjs/createTamagui.js.map +2 -2
  11. package/dist/cjs/createTokens.js.map +2 -2
  12. package/dist/cjs/createTokens.test.js +27 -0
  13. package/dist/cjs/createTokens.test.js.map +7 -0
  14. package/dist/cjs/createVariable.js +4 -9
  15. package/dist/cjs/createVariable.js.map +2 -2
  16. package/dist/cjs/createVariable.test.js +7 -0
  17. package/dist/cjs/createVariable.test.js.map +7 -0
  18. package/dist/cjs/createVariables.js +6 -7
  19. package/dist/cjs/createVariables.js.map +2 -2
  20. package/dist/cjs/helpers/ThemeManager.js +161 -147
  21. package/dist/cjs/helpers/ThemeManager.js.map +3 -3
  22. package/dist/cjs/helpers/ThemeManager.test.js +421 -34
  23. package/dist/cjs/helpers/ThemeManager.test.js.map +2 -2
  24. package/dist/cjs/helpers/{getTokens.js → ThemeManagerContext.js} +8 -8
  25. package/dist/cjs/helpers/ThemeManagerContext.js.map +7 -0
  26. package/dist/cjs/helpers/createPropMapper.js +4 -4
  27. package/dist/cjs/helpers/createPropMapper.js.map +2 -2
  28. package/dist/cjs/helpers/extendStaticConfig.js +2 -4
  29. package/dist/cjs/helpers/extendStaticConfig.js.map +2 -2
  30. package/dist/cjs/helpers/{assignNativePropsToWeb.native.js → getBoundingClientRect.js} +15 -8
  31. package/dist/cjs/helpers/getBoundingClientRect.js.map +7 -0
  32. package/dist/cjs/{hooks/useConstant.js → helpers/getRect.js} +14 -17
  33. package/dist/cjs/helpers/getRect.js.map +7 -0
  34. package/dist/cjs/helpers/getSplitStyles.js +82 -85
  35. package/dist/cjs/helpers/getSplitStyles.js.map +3 -3
  36. package/dist/cjs/helpers/getThemeCSSRules.js +6 -2
  37. package/dist/cjs/helpers/getThemeCSSRules.js.map +2 -2
  38. package/dist/cjs/helpers/insertStyleRule.js +1 -1
  39. package/dist/cjs/helpers/insertStyleRule.js.map +2 -2
  40. package/dist/cjs/helpers/mergeTransform.js +11 -8
  41. package/dist/cjs/helpers/mergeTransform.js.map +2 -2
  42. package/dist/cjs/helpers/pseudoDescriptors.js +1 -0
  43. package/dist/cjs/helpers/pseudoDescriptors.js.map +2 -2
  44. package/dist/cjs/helpers/themeable.js +9 -9
  45. package/dist/cjs/helpers/themeable.js.map +2 -2
  46. package/dist/cjs/hooks/useElementLayout.js +4 -12
  47. package/dist/cjs/hooks/useElementLayout.js.map +2 -2
  48. package/dist/cjs/hooks/useMedia.js +2 -1
  49. package/dist/cjs/hooks/useMedia.js.map +2 -2
  50. package/dist/cjs/hooks/useTheme.js +114 -156
  51. package/dist/cjs/hooks/useTheme.js.map +3 -3
  52. package/dist/cjs/hooks/{useIsMounted.js → useThemeName.js} +23 -14
  53. package/dist/cjs/hooks/useThemeName.js.map +7 -0
  54. package/dist/cjs/index.js +2 -5
  55. package/dist/cjs/index.js.map +2 -2
  56. package/dist/cjs/setupReactNative.js +20 -7
  57. package/dist/cjs/setupReactNative.js.map +2 -2
  58. package/dist/cjs/styled.js +2 -19
  59. package/dist/cjs/styled.js.map +2 -2
  60. package/dist/cjs/types.js.map +1 -1
  61. package/dist/cjs/updateTheme.js +9 -9
  62. package/dist/cjs/updateTheme.js.map +2 -2
  63. package/dist/cjs/views/TamaguiProvider.js +3 -3
  64. package/dist/cjs/views/TamaguiProvider.js.map +2 -2
  65. package/dist/cjs/views/Theme.js +39 -28
  66. package/dist/cjs/views/Theme.js.map +3 -3
  67. package/dist/cjs/views/ThemeProvider.js +8 -33
  68. package/dist/cjs/views/ThemeProvider.js.map +3 -3
  69. package/dist/esm/addTheme.js +7 -10
  70. package/dist/esm/addTheme.js.map +3 -3
  71. package/dist/esm/config.js +8 -3
  72. package/dist/esm/config.js.map +2 -2
  73. package/dist/esm/contexts/ButtonNestingContext.js +6 -0
  74. package/dist/esm/contexts/ButtonNestingContext.js.map +7 -0
  75. package/dist/esm/createComponent.js +65 -70
  76. package/dist/esm/createComponent.js.map +3 -3
  77. package/dist/esm/createTamagui.js +13 -12
  78. package/dist/esm/createTamagui.js.map +2 -2
  79. package/dist/esm/createTokens.js.map +2 -2
  80. package/dist/esm/createTokens.test.js +26 -0
  81. package/dist/esm/createTokens.test.js.map +7 -0
  82. package/dist/esm/createVariable.js +4 -9
  83. package/dist/esm/createVariable.js.map +2 -2
  84. package/dist/esm/createVariable.test.js +6 -0
  85. package/dist/esm/createVariable.test.js.map +7 -0
  86. package/dist/esm/createVariables.js +6 -7
  87. package/dist/esm/createVariables.js.map +2 -2
  88. package/dist/esm/helpers/ThemeManager.js +140 -144
  89. package/dist/esm/helpers/ThemeManager.js.map +3 -3
  90. package/dist/esm/helpers/ThemeManager.test.js +421 -34
  91. package/dist/esm/helpers/ThemeManager.test.js.map +2 -2
  92. package/dist/esm/helpers/ThemeManagerContext.js +6 -0
  93. package/dist/esm/helpers/ThemeManagerContext.js.map +7 -0
  94. package/dist/esm/helpers/createPropMapper.js +4 -4
  95. package/dist/esm/helpers/createPropMapper.js.map +2 -2
  96. package/dist/esm/helpers/extendStaticConfig.js +2 -4
  97. package/dist/esm/helpers/extendStaticConfig.js.map +2 -2
  98. package/dist/esm/helpers/getBoundingClientRect.js +13 -0
  99. package/dist/esm/helpers/getBoundingClientRect.js.map +7 -0
  100. package/dist/esm/helpers/getRect.js +12 -0
  101. package/dist/esm/helpers/getRect.js.map +7 -0
  102. package/dist/esm/helpers/getSplitStyles.js +90 -87
  103. package/dist/esm/helpers/getSplitStyles.js.map +3 -3
  104. package/dist/esm/helpers/getThemeCSSRules.js +6 -2
  105. package/dist/esm/helpers/getThemeCSSRules.js.map +2 -2
  106. package/dist/esm/helpers/insertStyleRule.js +1 -1
  107. package/dist/esm/helpers/insertStyleRule.js.map +2 -2
  108. package/dist/esm/helpers/mergeTransform.js +8 -6
  109. package/dist/esm/helpers/mergeTransform.js.map +2 -2
  110. package/dist/esm/helpers/pseudoDescriptors.js +1 -0
  111. package/dist/esm/helpers/pseudoDescriptors.js.map +2 -2
  112. package/dist/esm/helpers/themeable.js +9 -9
  113. package/dist/esm/helpers/themeable.js.map +2 -2
  114. package/dist/esm/hooks/useElementLayout.js +2 -9
  115. package/dist/esm/hooks/useElementLayout.js.map +2 -2
  116. package/dist/esm/hooks/useMedia.js +3 -2
  117. package/dist/esm/hooks/useMedia.js.map +2 -2
  118. package/dist/esm/hooks/useTheme.js +114 -159
  119. package/dist/esm/hooks/useTheme.js.map +3 -3
  120. package/dist/esm/hooks/useThemeName.js +25 -0
  121. package/dist/esm/hooks/useThemeName.js.map +7 -0
  122. package/dist/esm/index.js +3 -5
  123. package/dist/esm/index.js.map +2 -2
  124. package/dist/esm/setupReactNative.js +18 -6
  125. package/dist/esm/setupReactNative.js.map +2 -2
  126. package/dist/esm/styled.js +3 -20
  127. package/dist/esm/styled.js.map +2 -2
  128. package/dist/esm/updateTheme.js +9 -9
  129. package/dist/esm/updateTheme.js.map +2 -2
  130. package/dist/esm/views/TamaguiProvider.js +4 -4
  131. package/dist/esm/views/TamaguiProvider.js.map +2 -2
  132. package/dist/esm/views/Theme.js +40 -29
  133. package/dist/esm/views/Theme.js.map +3 -3
  134. package/dist/esm/views/ThemeProvider.js +8 -27
  135. package/dist/esm/views/ThemeProvider.js.map +2 -2
  136. package/package.json +12 -12
  137. package/src/__snapshots__/createTokens.test.ts.snap +877 -0
  138. package/src/addTheme.ts +9 -12
  139. package/src/config.ts +10 -2
  140. package/src/contexts/ButtonNestingContext.tsx +3 -0
  141. package/src/createComponent.tsx +117 -108
  142. package/src/createTamagui.ts +16 -14
  143. package/src/createTokens.test.ts +29 -0
  144. package/src/createTokens.ts +1 -1
  145. package/src/createVariable.test.ts +7 -0
  146. package/src/createVariable.ts +4 -10
  147. package/src/createVariables.ts +7 -7
  148. package/src/helpers/ThemeManager.test.tsx +480 -0
  149. package/src/helpers/ThemeManager.tsx +171 -166
  150. package/src/helpers/ThemeManagerContext.tsx +5 -0
  151. package/src/helpers/createPropMapper.ts +4 -4
  152. package/src/helpers/extendStaticConfig.ts +4 -4
  153. package/src/helpers/getBoundingClientRect.tsx +7 -0
  154. package/src/helpers/getRect.tsx +8 -0
  155. package/src/helpers/getSplitStyles.tsx +98 -93
  156. package/src/helpers/getThemeCSSRules.ts +9 -2
  157. package/src/helpers/insertStyleRule.tsx +2 -2
  158. package/src/helpers/mergeTransform.ts +13 -6
  159. package/src/helpers/pseudoDescriptors.ts +1 -0
  160. package/src/helpers/themeable.tsx +17 -9
  161. package/src/hooks/useElementLayout.tsx +3 -8
  162. package/src/hooks/useMedia.tsx +3 -3
  163. package/src/hooks/useTheme.tsx +138 -211
  164. package/src/hooks/useThemeName.tsx +25 -0
  165. package/src/index.ts +3 -4
  166. package/src/setupReactNative.ts +16 -5
  167. package/src/styled.tsx +4 -23
  168. package/src/types.tsx +42 -17
  169. package/src/updateTheme.ts +12 -9
  170. package/src/views/TamaguiProvider.tsx +5 -5
  171. package/src/views/Theme.tsx +48 -39
  172. package/src/views/ThemeProvider.tsx +11 -33
  173. package/types/Tamagui.d.ts +1 -1
  174. package/types/addTheme.d.ts +23 -2
  175. package/types/config.d.ts +0 -1
  176. package/types/contexts/ButtonNestingContext.d.ts +3 -0
  177. package/types/createTokens.test.d.ts +2 -0
  178. package/types/createVariable.d.ts +2 -2
  179. package/types/createVariable.test.d.ts +2 -0
  180. package/types/helpers/ThemeManager.d.ts +20 -30
  181. package/types/helpers/ThemeManager.test.d.ts +2 -0
  182. package/types/helpers/ThemeManagerContext.d.ts +4 -0
  183. package/types/helpers/getBoundingClientRect.d.ts +2 -0
  184. package/types/helpers/getRect.d.ts +9 -0
  185. package/types/helpers/insertStyleRule.d.ts +1 -1
  186. package/types/helpers/mergeTransform.d.ts +5 -3
  187. package/types/helpers/themeable.d.ts +3 -0
  188. package/types/hooks/useElementLayout.d.ts +0 -8
  189. package/types/hooks/useTheme.d.ts +10 -20
  190. package/types/hooks/useThemeName.d.ts +5 -0
  191. package/types/index.d.ts +3 -4
  192. package/types/setupReactNative.d.ts +4 -2
  193. package/types/types.d.ts +35 -10
  194. package/types/updateTheme.d.ts +23 -2
  195. package/types/views/Theme.d.ts +3 -4
  196. package/dist/cjs/constants/platform.js +0 -74
  197. package/dist/cjs/constants/platform.js.map +0 -7
  198. package/dist/cjs/constants/platform.native.js +0 -62
  199. package/dist/cjs/constants/platform.native.js.map +0 -7
  200. package/dist/cjs/helpers/assignNativePropsToWeb.js +0 -188
  201. package/dist/cjs/helpers/assignNativePropsToWeb.js.map +0 -7
  202. package/dist/cjs/helpers/assignNativePropsToWeb.native.js.map +0 -7
  203. package/dist/cjs/helpers/calc.js +0 -71
  204. package/dist/cjs/helpers/calc.js.map +0 -7
  205. package/dist/cjs/helpers/getFont.js +0 -66
  206. package/dist/cjs/helpers/getFont.js.map +0 -7
  207. package/dist/cjs/helpers/getStackSize.js +0 -46
  208. package/dist/cjs/helpers/getStackSize.js.map +0 -7
  209. package/dist/cjs/helpers/getTokens.js.map +0 -7
  210. package/dist/cjs/helpers/startTransition.js +0 -36
  211. package/dist/cjs/helpers/startTransition.js.map +0 -7
  212. package/dist/cjs/helpers/validate.js +0 -2
  213. package/dist/cjs/helpers/validate.js.map +0 -7
  214. package/dist/cjs/hooks/useConstant.js.map +0 -7
  215. package/dist/cjs/hooks/useIsMounted.js.map +0 -7
  216. package/dist/cjs/hooks/usePressable.js +0 -36
  217. package/dist/cjs/hooks/usePressable.js.map +0 -7
  218. package/dist/cjs/hooks/useUnmountEffect.js.map +0 -7
  219. package/dist/cjs/injectStyles.js +0 -40
  220. package/dist/cjs/injectStyles.js.map +0 -7
  221. package/dist/cjs/react-native.js +0 -22
  222. package/dist/cjs/react-native.js.map +0 -7
  223. package/dist/esm/constants/platform.js +0 -39
  224. package/dist/esm/constants/platform.js.map +0 -7
  225. package/dist/esm/constants/platform.native.js +0 -28
  226. package/dist/esm/constants/platform.native.js.map +0 -7
  227. package/dist/esm/helpers/assignNativePropsToWeb.js +0 -164
  228. package/dist/esm/helpers/assignNativePropsToWeb.js.map +0 -7
  229. package/dist/esm/helpers/assignNativePropsToWeb.native.js +0 -6
  230. package/dist/esm/helpers/assignNativePropsToWeb.native.js.map +0 -7
  231. package/dist/esm/helpers/calc.js +0 -47
  232. package/dist/esm/helpers/calc.js.map +0 -7
  233. package/dist/esm/helpers/getFont.js +0 -42
  234. package/dist/esm/helpers/getFont.js.map +0 -7
  235. package/dist/esm/helpers/getStackSize.js +0 -22
  236. package/dist/esm/helpers/getStackSize.js.map +0 -7
  237. package/dist/esm/helpers/getTokens.js +0 -6
  238. package/dist/esm/helpers/getTokens.js.map +0 -7
  239. package/dist/esm/helpers/startTransition.js +0 -6
  240. package/dist/esm/helpers/startTransition.js.map +0 -7
  241. package/dist/esm/helpers/validate.js +0 -1
  242. package/dist/esm/helpers/validate.js.map +0 -7
  243. package/dist/esm/hooks/useConstant.js +0 -15
  244. package/dist/esm/hooks/useConstant.js.map +0 -7
  245. package/dist/esm/hooks/useIsMounted.js +0 -16
  246. package/dist/esm/hooks/useIsMounted.js.map +0 -7
  247. package/dist/esm/hooks/usePressable.js +0 -12
  248. package/dist/esm/hooks/usePressable.js.map +0 -7
  249. package/dist/esm/hooks/useUnmountEffect.js +0 -8
  250. package/dist/esm/hooks/useUnmountEffect.js.map +0 -7
  251. package/dist/esm/injectStyles.js +0 -16
  252. package/dist/esm/injectStyles.js.map +0 -7
  253. package/dist/esm/react-native.js +0 -3
  254. package/dist/esm/react-native.js.map +0 -7
  255. package/src/contexts/ButtonInsideButtonContext.tsx +0 -3
  256. package/src/contexts/ThemeContext.tsx +0 -5
  257. package/src/helpers/areEqualSets.tsx +0 -9
  258. package/src/views/ThemeInverse.tsx +0 -43
  259. package/src/views/ThemeReset.tsx +0 -43
  260. package/types/constants/platform.d.ts +0 -13
  261. package/types/constants/platform.native.d.ts +0 -13
  262. package/types/contexts/ButtonInsideButtonContext.d.ts +0 -3
  263. package/types/contexts/ThemeContext.d.ts +0 -7
  264. package/types/helpers/areEqualSets.d.ts +0 -2
  265. package/types/helpers/assignNativePropsToWeb.d.ts +0 -2
  266. package/types/helpers/assignNativePropsToWeb.native.d.ts +0 -2
  267. package/types/helpers/calc.d.ts +0 -1
  268. package/types/helpers/getFont.d.ts +0 -3
  269. package/types/helpers/getStackSize.d.ts +0 -8
  270. package/types/helpers/getTokens.d.ts +0 -2
  271. package/types/helpers/startTransition.d.ts +0 -3
  272. package/types/helpers/validate.d.ts +0 -1
  273. package/types/hooks/useConstant.d.ts +0 -2
  274. package/types/hooks/useIsMounted.d.ts +0 -3
  275. package/types/hooks/useMedia.test.d.ts +0 -1
  276. package/types/hooks/usePressable.d.ts +0 -3
  277. package/types/hooks/usePressablePressResponder.d.ts +0 -84
  278. package/types/hooks/useUnmountEffect.d.ts +0 -2
  279. package/types/injectStyles.d.ts +0 -7
  280. package/types/react-native.d.ts +0 -8
  281. package/types/views/AbsoluteFill.d.ts +0 -5
  282. package/types/views/AbsoluteFill.native.d.ts +0 -3
  283. package/types/views/ThemeInverse.d.ts +0 -9
  284. package/types/views/ThemeReset.d.ts +0 -4
@@ -1,124 +1,70 @@
1
- import { isRSC, isServer, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import { useForceUpdate } from '@tamagui/use-force-update'
3
- import React, { useContext, useLayoutEffect, useMemo, useRef, useState } from 'react'
1
+ /* eslint-disable no-console */
2
+ import { isClient, isRSC, isServer, isWeb } from '@tamagui/constants'
3
+ import { useContext, useLayoutEffect, useMemo, useState } from 'react'
4
4
 
5
5
  import { getConfig } from '../config'
6
6
  import { isDevTools } from '../constants/isDevTools'
7
- import { ThemeContext } from '../contexts/ThemeContext'
8
- import { areEqualSets } from '../helpers/areEqualSets'
9
7
  import { createProxy } from '../helpers/createProxy'
10
- import {
11
- GetNextThemeProps,
12
- ThemeManager,
13
- ThemeManagerContext,
14
- emptyManager,
15
- } from '../helpers/ThemeManager'
16
- import { ThemeName, ThemeParsed } from '../types'
8
+ import { ThemeManager, ThemeManagerState, hasNoThemeUpdatingProps } from '../helpers/ThemeManager'
9
+ import { ThemeManagerContext } from '../helpers/ThemeManagerContext'
10
+ import { ThemeParsed, ThemeProps } from '../types'
17
11
  import { GetThemeUnwrapped } from './getThemeUnwrapped'
18
- import { useServerRef } from './useServerHooks'
19
12
 
20
- export interface ThemeProps {
13
+ export type ChangedThemeResponse = {
14
+ themes: Record<string, ThemeParsed>
15
+ themeManager: ThemeManager | null
16
+ name: string
17
+ isNewTheme?: boolean
18
+ theme?: ThemeParsed | null
21
19
  className?: string
22
- disableThemeClass?: boolean
23
- name: Exclude<ThemeName, number> | null
24
- componentName?: string
25
- children?: any
26
- reset?: boolean
27
- debug?: boolean | 'verbose'
28
- }
29
-
30
- interface UseThemeState {
31
- uuid: Object
32
- keys: Set<string>
33
- isRendering: boolean
34
20
  }
35
21
 
36
- export const useTheme = (
37
- themeName?: string | null,
38
- componentName?: string,
39
- props?: ThemeProps,
40
- forceUpdate?: any,
41
- disableTracking?: boolean
42
- ): ThemeParsed => {
43
- // TODO this can use useChangeThemeEffect almost ready
22
+ const emptyProps = { name: null }
23
+ export const useTheme = (props: ThemeProps = emptyProps): ThemeParsed => {
44
24
  if (isRSC) {
45
25
  const config = getConfig()
46
- // @ts-ignore
47
- return getThemeProxied(config.themes[config.defaultTheme], config.defaultTheme)
48
- }
49
-
50
- const state = useServerRef() as React.MutableRefObject<UseThemeState>
51
- if (!state.current) {
52
- state.current = {
53
- uuid: {},
54
- keys: new Set(),
55
- isRendering: true,
56
- }
26
+ const name = Object.keys(config.themes)[0]
27
+ return getThemeProxied({
28
+ theme: config.themes[name],
29
+ name,
30
+ })
57
31
  }
58
32
 
59
- const { name, theme, themes, themeManager, className } = useChangeThemeEffect(
60
- themeName,
61
- componentName,
62
- props,
63
- forceUpdate,
64
- state.current.uuid
65
- )
33
+ const { name, theme, themes, themeManager, className, isNewTheme } = useChangeThemeEffect(props)
66
34
 
67
35
  if (process.env.NODE_ENV === 'development') {
68
- if (props?.debug === 'verbose') {
69
- // eslint-disable-next-line no-console
36
+ // ensure we aren't creating too many ThemeManagers
37
+ if (isWeb && isNewTheme && className === themeManager?.parentManager?.state.className) {
38
+ console.error(`Should always change, duplicating ThemeMananger bug`, themeManager)
39
+ // eslint-disable-next-line no-debugger
40
+ debugger
41
+ }
42
+ if (props.debug === 'verbose') {
70
43
  console.groupCollapsed(' 🔹 useTheme =>', name)
71
- const logs = { themeName, componentName, name, className, ...(isDevTools && { theme }) }
44
+ const logs = { ...props, name, className, ...(isDevTools && { theme }) }
72
45
  for (const key in logs) {
73
- // eslint-disable-next-line no-console
74
46
  console.log(' ', key, logs[key])
75
47
  }
76
- // eslint-disable-next-line no-console
77
48
  console.groupEnd()
78
49
  }
79
50
  }
80
51
 
81
- // track usage
82
- state.current.isRendering = true
83
- useIsomorphicLayoutEffect(() => {
84
- const st = state.current
85
- st.isRendering = false
86
- const cur = themeManager?.keys.get(st.uuid)
87
- if (!cur || !areEqualSets(st.keys, cur)) {
88
- themeManager?.track(st.uuid, st.keys)
89
- }
90
- })
91
-
92
- const debugProp = props && props['debug']
93
-
94
52
  if (!theme) {
95
53
  if (process.env.NODE_ENV === 'development') {
96
- // eslint-disable-next-line no-console
97
- console.warn('No theme with name', themeName)
54
+ console.warn('No theme found', name, props, themeManager)
98
55
  }
99
56
  return themes[Object.keys(themes)[0]]
100
57
  }
101
58
 
102
59
  return useMemo(() => {
103
60
  return getThemeProxied({
61
+ isNewTheme,
104
62
  theme,
105
63
  name,
106
64
  className,
107
65
  themeManager,
108
- onStringKeyAccess(key) {
109
- if (disableTracking) return
110
- if (state) {
111
- if (state.current.isRendering && !state.current.keys.has(key)) {
112
- state.current.keys.add(key)
113
- if (process.env.NODE_ENV === 'development' && debugProp === 'verbose') {
114
- // eslint-disable-next-line no-console
115
- console.log(' 🔸 tracking theme', key)
116
- }
117
- }
118
- }
119
- },
120
66
  })
121
- }, [theme, name, className, themeManager, debugProp, disableTracking])
67
+ }, [theme, isNewTheme, name, className, themeManager])
122
68
  }
123
69
 
124
70
  function getThemeProxied({
@@ -126,190 +72,171 @@ function getThemeProxied({
126
72
  name,
127
73
  className,
128
74
  themeManager,
129
- onStringKeyAccess,
75
+ isNewTheme,
130
76
  }: {
131
77
  theme: any
132
78
  name: string
133
- onStringKeyAccess?: (cb: string) => void
134
79
  className?: string
135
80
  themeManager?: ThemeManager | null
81
+ isNewTheme?: boolean
136
82
  }) {
137
83
  return createProxy(theme, {
138
84
  has(_, key) {
139
- if (typeof key === 'string' && key[0] === '$') {
140
- key = key.slice(1)
85
+ if (typeof key === 'string') {
86
+ if (key[0] === '$') key = key.slice(1)
87
+ return themeManager?.allKeys.has(key)
141
88
  }
142
89
  return Reflect.has(theme, key)
143
90
  },
144
91
  get(_, key) {
145
92
  if (key === GetThemeUnwrapped) {
146
93
  return theme
147
- }
148
- if (key === GetThemeManager) {
94
+ } else if (key === GetThemeManager) {
149
95
  return themeManager
150
- }
151
- if (key === 'name') {
96
+ } else if (key === GetIsNewTheme) {
97
+ return isNewTheme
98
+ } else if (key === 'name') {
152
99
  return name
153
- }
154
- if (key === 'className') {
100
+ } else if (key === 'className') {
155
101
  return className
156
- }
157
- if (!name || key === '__proto__' || typeof key === 'symbol' || key === '$typeof') {
102
+ } else if (!name || key === '__proto__' || typeof key === 'symbol' || key === '$typeof') {
158
103
  return Reflect.get(_, key)
159
104
  }
160
105
  // auto convert variables to plain
161
- if (key[0] === '$') {
162
- key = key.slice(1)
163
- }
106
+ if (key[0] === '$') key = key.slice(1)
164
107
  if (!themeManager) {
165
108
  return theme[key]
166
109
  }
167
- onStringKeyAccess?.(key)
168
110
  return themeManager.getValue(key)
169
111
  },
170
112
  })
171
113
  }
172
114
 
173
115
  const GetThemeManager = Symbol()
116
+ const GetIsNewTheme = Symbol()
174
117
 
175
- export const getThemeManager = (theme: any): ThemeManager | undefined => {
176
- if (!theme) return
177
- return theme[GetThemeManager]
178
- }
179
-
180
- export function useThemeName(opts?: { parent?: true }): ThemeName {
181
- if (isRSC) {
182
- const config = getConfig()
183
- return config.themes[Object.keys(config.themes)[0]] as any
184
- }
185
- const parent = useContext(ThemeManagerContext)
186
- const [name, setName] = useState(parent?.name || '')
187
-
188
- useIsomorphicLayoutEffect(() => {
189
- if (!parent) return
190
- return parent.onChangeTheme((next, manager) => {
191
- const name = opts?.parent ? manager.parentName || next : next
192
- if (!name) return
193
- setName(name)
194
- })
195
- }, [parent])
196
-
197
- return name
198
- }
199
-
200
- export const useDefaultThemeName = () => {
201
- return useContext(ThemeContext)?.defaultTheme
202
- }
118
+ export const getThemeManager = (theme: any): ThemeManager | undefined => theme?.[GetThemeManager]
119
+ export const getThemeIsNewTheme = (theme: any): ThemeManager | undefined => theme?.[GetIsNewTheme]
203
120
 
204
121
  export const activeThemeManagers = new Set<ThemeManager>()
205
122
 
206
- export const useChangeThemeEffect = (
207
- name?: string | null,
208
- componentName?: string,
209
- props?: ThemeProps,
210
- forceUpdateProp?: any,
211
- uuid?: Object
212
- ): {
213
- themes: Record<string, ThemeParsed>
214
- themeManager: ThemeManager | null
215
- name: string
216
- theme: ThemeParsed | null
217
- className?: string
218
- } => {
123
+ export const useChangeThemeEffect = (props: ThemeProps, root = false): ChangedThemeResponse => {
124
+ const parentManager = useContext(ThemeManagerContext)
219
125
  const config = getConfig()
220
-
221
- if (process.env.NODE_ENV === 'development') {
222
- if (!config) {
223
- throw new Error(
224
- `Missing tamagui config, you either have a duplicate config, or haven't set it up. Be sure createTamagui is called before rendering.`
225
- )
226
- }
227
- }
228
-
126
+ const { debug } = props
127
+ // @ts-expect-error internal use only
128
+ const disable = props.disable
229
129
  const { themes } = config
230
-
231
130
  if (isRSC) {
232
131
  // we need context working for this to work well
233
- const parentManager = new ThemeManager('light', 'light', themes.light)
234
- const next = parentManager.getNextTheme({
235
- name,
236
- componentName,
237
- themes,
238
- reset: props?.reset,
239
- })
240
132
  return {
241
- ...next,
133
+ ...createState().state,
242
134
  themes,
243
135
  themeManager: null,
244
136
  }
245
137
  }
246
-
247
- const debug = props && props['debug']
248
- const parentManager = useContext(ThemeManagerContext) || emptyManager
249
- const reset = props?.reset || false
250
- const getThemeProps: GetNextThemeProps = {
251
- name,
252
- componentName,
253
- themes,
254
- reset,
138
+ if (disable) {
139
+ if (!parentManager) throw new Error(`Disable topmost`)
140
+ return {
141
+ ...parentManager.state,
142
+ isNewTheme: false,
143
+ themes,
144
+ themeManager: parentManager,
145
+ }
255
146
  }
256
- const next = parentManager.getNextTheme(getThemeProps, debug)
257
- // eslint-disable-next-line react-hooks/rules-of-hooks
258
- const forceUpdate = forceUpdateProp || useForceUpdate()
259
147
 
260
- const themeManager = useMemo(() => {
261
- return new ThemeManager(next.name, next.className, next.theme, parentManager, reset)
262
- // eslint-disable-next-line react-hooks/exhaustive-deps
263
- }, [])
264
-
265
- // not concurrent safe but fixes native (but breaks SSR and not needed on web (i think) so leave only on native)
266
- let didChange = false
267
- if (process.env.TAMAGUI_TARGET === 'native') {
268
- didChange = Boolean(
269
- next?.name !== themeManager.name || next?.className !== themeManager.className
270
- )
271
- if (didChange) {
272
- themeManager.update(next, false, false)
273
- }
148
+ type State = {
149
+ isNewTheme: boolean
150
+ state: ThemeManagerState
151
+ themeManager: ThemeManager
152
+ mounted?: boolean
274
153
  }
275
154
 
155
+ const [themeState, setThemeState] = useState<State>(createState)
156
+ const { isNewTheme, state, themeManager, mounted } = themeState
157
+
276
158
  if (!isServer) {
277
159
  useLayoutEffect(() => {
278
- themeManager.update(next, didChange)
160
+ if (disable) return
161
+ // SSR safe inverse (because server can't know prefers scheme)
162
+ // could be done through fancy selectors like how we do prefers-media
163
+ // but may be a bit of explosion of selectors
164
+ if (props.inverse && !mounted) {
165
+ setThemeState({ ...themeState, mounted: true })
166
+ return
167
+ }
279
168
  activeThemeManagers.add(themeManager)
280
-
281
- const disposeParentOnChange = parentManager.onChangeTheme(() => {
282
- const next = parentManager.getNextTheme(getThemeProps, debug)
283
- if (!next) return
284
- if (themeManager.update(next)) {
285
- if (uuid && !themeManager.isTracking(uuid)) {
286
- // no need to re-render if not tracking any keys
287
- return
288
- }
289
- if (process.env.NODE_ENV === 'development' && debug) {
290
- // eslint-disable-next-line no-console
291
- console.log('Changed theme', componentName, next, { props })
292
- }
293
- forceUpdate()
169
+ const disposeChangeListener = parentManager?.onChangeTheme(() => {
170
+ if (!isNewTheme && !isWeb) {
171
+ setThemeState({
172
+ ...themeState,
173
+ state: { ...themeManager.state },
174
+ isNewTheme: false,
175
+ })
176
+ } else {
177
+ updateState()
294
178
  }
295
179
  })
296
-
297
180
  return () => {
298
181
  activeThemeManagers.delete(themeManager)
299
- disposeParentOnChange()
182
+ disposeChangeListener?.()
300
183
  }
301
- // eslint-disable-next-line react-hooks/exhaustive-deps
302
- }, [didChange, themes, next?.name, next?.className, componentName, debug])
184
+ }, [disable, state, isNewTheme, debug])
185
+ }
186
+
187
+ // prettier-ignore
188
+ if (process.env.NODE_ENV === 'development' && debug) console.log('useTheme.render isNewTheme', isNewTheme, props, themeManager.state.name, parentManager?.state.name)
189
+
190
+ // this seems unnecessary
191
+ if (hasNoThemeUpdatingProps(props)) {
192
+ if (!parentManager) throw new Error(`Nada`)
193
+ return {
194
+ ...parentManager.state,
195
+ themes,
196
+ themeManager: parentManager,
197
+ isNewTheme: false,
198
+ }
199
+ }
200
+
201
+ // run inline in render
202
+ updateState()
203
+
204
+ if (!mounted && !parentManager) {
205
+ throw new Error(`Impossible`)
303
206
  }
304
207
 
305
208
  return {
306
- ...(parentManager && {
307
- name: parentManager.name,
308
- theme: parentManager.theme,
309
- }),
310
- ...next,
311
- className: next.className === parentManager.className ? undefined : next.className,
209
+ ...state,
210
+ isNewTheme,
312
211
  themes,
313
212
  themeManager,
213
+ ...(!mounted &&
214
+ parentManager && {
215
+ ...parentManager.state,
216
+ isNewTheme: false,
217
+ themeManager: parentManager,
218
+ }),
219
+ }
220
+
221
+ function createState(prev?: State) {
222
+ const _ = new ThemeManager(props, root ? 'root' : parentManager)
223
+ const isNewTheme = _ !== parentManager
224
+ // prettier-ignore
225
+ if (process.env.NODE_ENV === 'development' && debug) [console.groupCollapsed('useTheme create() isNewTheme', isNewTheme), console.log('parent.state.name', parentManager?.state.name, '\n', 'props', props, '\n', isClient ? parentManager : '', 'getState', isNewTheme && isClient ? _.getState(props) : '', '\n', 'state', isClient ? { ..._.state } : ''), console.groupEnd()]
226
+ // only inverse relies on this for ssr
227
+ const mounted = !props.inverse ? true : root || prev?.mounted || false
228
+ return {
229
+ // ThemeManager returns parentManager if no change
230
+ isNewTheme,
231
+ state: { ..._.state },
232
+ themeManager: _,
233
+ mounted,
234
+ }
235
+ }
236
+
237
+ function updateState() {
238
+ if (themeManager.getStateIfChanged(props, state, parentManager)) {
239
+ setThemeState(createState)
240
+ }
314
241
  }
315
242
  }
@@ -0,0 +1,25 @@
1
+ import { isRSC, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
+ import { useContext, useState } from 'react'
3
+
4
+ import { getConfig } from '../config'
5
+ import { ThemeManagerContext } from '../helpers/ThemeManagerContext'
6
+ import { ThemeName } from '../types'
7
+
8
+ export function useThemeName(opts?: { parent?: true }): ThemeName {
9
+ if (isRSC) {
10
+ const config = getConfig()
11
+ return config.themes[Object.keys(config.themes)[0]] as any
12
+ }
13
+ const manager = useContext(ThemeManagerContext)
14
+ const [name, setName] = useState(manager?.state.name || '')
15
+
16
+ useIsomorphicLayoutEffect(() => {
17
+ return manager?.onChangeTheme((next, manager) => {
18
+ const name = opts?.parent ? manager.state.parentName || next : next
19
+ if (!name) return
20
+ setName(name)
21
+ })
22
+ }, [manager])
23
+
24
+ return name
25
+ }
package/src/index.ts CHANGED
@@ -16,7 +16,7 @@ export * from './setupReactNative'
16
16
 
17
17
  // for type portability
18
18
  export type { RNWTextProps, RNWViewProps } from './types-rnw'
19
- export { getHasConfigured, getConfig, getThemes, getTokens, onConfiguredOnce } from './config'
19
+ export { getConfig, getThemes, getTokens, onConfiguredOnce } from './config'
20
20
 
21
21
  export * from './constants/constants'
22
22
 
@@ -36,7 +36,7 @@ export * from './helpers/matchMedia'
36
36
  export * from './helpers/themeable'
37
37
  export * from './helpers/withStaticProperties'
38
38
 
39
- export * from './contexts/ButtonInsideButtonContext'
39
+ export * from './contexts/ButtonNestingContext'
40
40
 
41
41
  export * from './hooks/useId'
42
42
  export * from './hooks/useIsTouchDevice'
@@ -53,6 +53,7 @@ export {
53
53
  mediaQueryConfig,
54
54
  } from './hooks/useMedia'
55
55
  export * from './hooks/useTheme'
56
+ export * from './hooks/useThemeName'
56
57
  export * from './hooks/useSafeRef'
57
58
 
58
59
  export * from './views/Slot'
@@ -60,9 +61,7 @@ export * from './views/Stack'
60
61
  export * from './views/Text'
61
62
  export * from './contexts/TextAncestorContext'
62
63
  export * from './views/Theme'
63
- export * from './views/ThemeInverse'
64
64
  export * from './views/ThemeProvider'
65
- export * from './views/ThemeReset'
66
65
  export * from './views/FontLanguage'
67
66
  export * from './views/TamaguiProvider'
68
67
 
@@ -1,9 +1,20 @@
1
- let rn
1
+ import { StaticConfig } from './types'
2
2
 
3
- export function getReactNative() {
4
- return rn
3
+ export const ReactNativeStaticConfigs = new WeakMap<any, Partial<StaticConfig> | null>()
4
+
5
+ export function getReactNativeConfig(Component: any) {
6
+ return ReactNativeStaticConfigs.get(Component)
5
7
  }
6
8
 
7
- export function setupReactNative(_) {
8
- rn = _
9
+ export function setupReactNative(rnExports: Record<string, any>) {
10
+ for (const key in rnExports) {
11
+ if (key[0].toLowerCase() === key[0]) continue
12
+ if (!rnExports[key]) continue
13
+ ReactNativeStaticConfigs.set(rnExports[key], {
14
+ isReactNative: true,
15
+ isText: key === 'Text' || key === 'TextInput',
16
+ isInput: key === 'TextInput',
17
+ inlineProps: key === 'Image' ? new Set(['src', 'width', 'height']) : undefined,
18
+ })
19
+ }
9
20
  }
package/src/styled.tsx CHANGED
@@ -1,6 +1,5 @@
1
- import { getConfig } from './config'
2
1
  import { createComponent } from './createComponent'
3
- import { getReactNative } from './setupReactNative'
2
+ import { ReactNativeStaticConfigs } from './setupReactNative'
4
3
  import {
5
4
  GetProps,
6
5
  GetVariantValues,
@@ -35,8 +34,6 @@ type GetVariantProps<A extends StylableComponent> =
35
34
  // :
36
35
  A extends TamaguiComponent<any, any, any, infer V> ? V : {}
37
36
 
38
- let RNComponentConfigs: WeakMap<any, Partial<StaticConfig>> | null = null
39
-
40
37
  export function styled<
41
38
  ParentComponent extends StylableComponent,
42
39
  Variants extends VariantDefinitions<ParentComponent> | void = VariantDefinitions<ParentComponent> | void
@@ -62,21 +59,6 @@ export function styled<
62
59
  // }
63
60
  // }
64
61
 
65
- const RN = getReactNative()
66
- if (RN && typeof RN === 'object' && !RNComponentConfigs) {
67
- RNComponentConfigs = new WeakMap()
68
- for (const key in RN) {
69
- const val = RN[key]
70
- if (!val) continue
71
- if (key[0].toLowerCase() === key[0]) continue
72
- RNComponentConfigs.set(val, {
73
- isReactNative: true,
74
- isText: key === 'Text' || key === 'TextInput',
75
- inlineProps: key === 'Image' ? new Set(['src', 'width', 'height']) : undefined,
76
- })
77
- }
78
- }
79
-
80
62
  const staticConfigProps = (() => {
81
63
  const parentStaticConfig =
82
64
  'staticConfig' in Component ? (Component.staticConfig as StaticConfig) : null
@@ -105,16 +87,15 @@ export function styled<
105
87
  Object.assign(defaultProps, defaultVariants)
106
88
  }
107
89
  const isReactNative = Boolean(
108
- (RNComponentConfigs &&
109
- (RNComponentConfigs.has(Component) ||
110
- RNComponentConfigs.has(parentStaticConfig?.Component))) ||
90
+ ReactNativeStaticConfigs.has(Component) ||
91
+ ReactNativeStaticConfigs.has(parentStaticConfig?.Component) ||
111
92
  parentStaticConfig?.isReactNative ||
112
93
  staticExtractionOptions?.isReactNative
113
94
  )
114
95
  const isTamagui = !isReactNative && !!parentStaticConfig
115
96
 
116
97
  const Comp = isReactNative ? parentStaticConfig?.Component || Component : (Component as any)
117
- const nativeConf = (isReactNative && RNComponentConfigs?.get(Comp)) || null
98
+ const nativeConf = (isReactNative && ReactNativeStaticConfigs.get(Comp)) || null
118
99
 
119
100
  const isText = Boolean(staticExtractionOptions?.isText || parentStaticConfig?.isText)
120
101
  const acceptsClassName = acceptsClassNameProp ?? (isTamagui || isReactNative)