@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,9 +1,9 @@
1
- import { createContext } from 'react'
1
+ import { isWeb } from '@tamagui/constants'
2
2
 
3
3
  import { getThemes } from '../config'
4
4
  import { THEME_CLASSNAME_PREFIX, THEME_NAME_SEPARATOR } from '../constants/constants'
5
5
  import { getThemeUnwrapped } from '../hooks/getThemeUnwrapped'
6
- import { ThemeParsed, Themes } from '../types'
6
+ import { ThemeParsed, ThemeProps } from '../types'
7
7
 
8
8
  type ThemeListener = (name: string | null, themeManager: ThemeManager) => void
9
9
 
@@ -12,216 +12,221 @@ export type SetActiveThemeProps = {
12
12
  parentManager?: ThemeManager | null
13
13
  name?: string | null
14
14
  theme?: any
15
+ reset?: boolean
15
16
  }
16
17
 
17
- export type GetNextThemeProps = {
18
- themes?: Themes
19
- name?: string | null
20
- componentName?: string | null
21
- reset?: boolean
18
+ export type ThemeManagerState = {
19
+ name: string
20
+ theme?: ThemeParsed | null
21
+ className?: string
22
+ parentName?: string
23
+ }
24
+
25
+ const emptyState: ThemeManagerState = { name: '' }
26
+
27
+ export function hasNoThemeUpdatingProps(props: ThemeProps) {
28
+ return !props.name && !props.componentName && !props.inverse && !props.reset
22
29
  }
23
30
 
24
31
  export class ThemeManager {
25
- keys = new Map<any, Set<string>>()
26
- listeners = new Map<any, Function>()
27
32
  themeListeners = new Set<ThemeListener>()
33
+ parentManager: ThemeManager | null = null
34
+ state: ThemeManagerState = emptyState
28
35
 
29
36
  constructor(
30
- public name: string = '',
31
- public className: string = '',
32
- public theme: ThemeParsed | null = null,
33
- public parentManager: ThemeManager | null = null,
34
- public reset: boolean = false
37
+ public props: ThemeProps = {},
38
+ parentManager?: ThemeManager | 'root' | null | undefined
35
39
  ) {
36
- // find the nearest different parentManager
37
- let parent = parentManager
38
- let tries = 0
39
- while (true) {
40
- tries++
41
- if (tries > 10) {
42
- throw new Error(`Nested 10 of the same theme in a row, likely error`)
40
+ if (parentManager === 'root') {
41
+ this.updateState(props, false)
42
+ return
43
+ }
44
+ if (!parentManager) {
45
+ throw new Error(`Must set up root first`)
46
+ }
47
+ // no change no props
48
+ if (hasNoThemeUpdatingProps(props)) {
49
+ return parentManager
50
+ }
51
+ if (parentManager) {
52
+ this.parentManager = parentManager
53
+ }
54
+ const updatedState = this.getStateIfChanged(props)
55
+ if (updatedState) {
56
+ this.state = updatedState
57
+ return
58
+ }
59
+ return parentManager || this
60
+ }
61
+
62
+ updateState(props: ThemeProps & { forceTheme?: ThemeParsed } = this.props || {}, notify = true) {
63
+ const shouldFlush = (() => {
64
+ if (props.forceTheme) {
65
+ this.state.theme = props.forceTheme
66
+ this.state.name = props.name || ''
67
+ return true
43
68
  }
44
- if (!parent) break
45
- if (parent.name === name) {
46
- // go up if same
47
- parent = parent.parentManager
48
- } else {
49
- this.parentManager = parent
50
- break
69
+ const nextState = this.getStateIfChanged(props)
70
+ if (nextState) {
71
+ this.state = nextState
72
+ return true
51
73
  }
74
+ })()
75
+ if (shouldFlush) {
76
+ // reset any derived state
77
+ this.#allKeys = null
78
+ notify && this.notify()
79
+ return this.state
52
80
  }
53
81
  }
54
82
 
55
- get didChangeTheme() {
56
- return this.parentManager && this.name !== this.parentManager.fullName
83
+ getStateIfChanged(props = this.props, state = this.state, parentManager = this.parentManager) {
84
+ const _ =
85
+ getState(props, parentManager) ||
86
+ (process.env.TAMAGUI_TARGET === 'native' ? parentManager?.state : undefined)
87
+ if (!_ || !_.theme || _.theme === state.theme) {
88
+ return null
89
+ }
90
+ return _
57
91
  }
58
92
 
59
- get parentName() {
60
- return this.parentManager?.name || null
93
+ getState(props = this.props) {
94
+ return getState(props, this.parentManager)
61
95
  }
62
96
 
63
- get fullName(): string {
64
- return this.getNextTheme().name || this.name || ''
65
- // const parentName = this.parentManager?.fullName || ''
66
- // const name = this.name || ''
67
- // const parts = [...new Set([...`${parentName}_${name}`.split('_')])].filter(Boolean)
68
- // return parts.join('_')
97
+ #allKeys: Set<string> | null = null
98
+ get allKeys() {
99
+ this.#allKeys ??= new Set([
100
+ ...(this.parentManager?.allKeys || []),
101
+ ...Object.keys(this.state.theme || {}),
102
+ ])
103
+ return this.#allKeys
69
104
  }
70
105
 
71
106
  // gets value going up to parents
72
- getValue(key: string) {
73
- let theme = this.theme
107
+ getValue(key: string, state?: ThemeManagerState) {
108
+ let theme = (state || this.state).theme
74
109
  let manager = this as ThemeManager | null
75
- while (true) {
76
- if (!theme) return
110
+ while (theme && manager) {
77
111
  if (key in theme) {
78
112
  return theme[key]
79
113
  }
80
- manager = this.parentManager
81
- if (!manager || manager.theme === theme) {
82
- return
83
- }
84
- theme = manager.theme
114
+ manager = manager.parentManager
115
+ theme = manager?.state.theme
85
116
  }
86
117
  }
87
118
 
88
- isTracking(uuid: Object) {
89
- return Boolean(this.keys.get(uuid)?.size)
119
+ notify() {
120
+ this.themeListeners.forEach((cb) => cb(this.state.name, this))
90
121
  }
91
122
 
92
- update({ name, theme, className }: SetActiveThemeProps = {}, force = false, notify = true) {
93
- if (!force) {
94
- // className compare on web, avoids light/dark re-renders
95
- const nameChanged = name !== this.name
96
- if (!nameChanged) {
97
- return false
98
- }
99
- }
100
- this.className = className || ''
101
- this.name = name || ''
102
- this.theme = theme
103
- if (notify) {
104
- this.notify()
123
+ onChangeTheme(cb: ThemeListener) {
124
+ this.themeListeners.add(cb)
125
+ return () => {
126
+ this.themeListeners.delete(cb)
105
127
  }
106
- return true
107
128
  }
129
+ }
108
130
 
109
- getNextTheme(
110
- props: GetNextThemeProps = {},
111
- debug?: any
112
- ): {
113
- name: string
114
- theme: ThemeParsed | null
115
- className: string | undefined
116
- } {
117
- const { themes = getThemes(), name, componentName } = props
118
-
119
- if (props.reset && name) {
120
- return {
121
- name: name,
122
- theme: themes[name] as ThemeParsed,
123
- className: this.getCN(name),
124
- }
125
- }
131
+ function getNextThemeClassName(name: string, isInverting = false) {
132
+ const next = `${THEME_CLASSNAME_PREFIX}${name}`
133
+ if (isInverting) {
134
+ return next
135
+ }
136
+ return next.replace('light_', '').replace('dark_', '')
137
+ }
126
138
 
127
- const parentIsReset = this.parentManager?.reset
128
-
129
- if (!name) {
130
- if (componentName) {
131
- // allow for _Card_Button or just _Button
132
- const names = [
133
- `${this.name}_${componentName}`,
134
- `${withoutComponentName(this.name)}_${componentName}`,
135
- ]
136
- for (const name of names) {
137
- if (name in themes) {
138
- const className = this.getCN(name)
139
- return { name, theme: themes[name], className }
140
- }
141
- }
142
- }
143
- return {
144
- name: this.name,
145
- theme: this.theme,
146
- className: this.className,
147
- }
148
- }
139
+ function getState(
140
+ props: ThemeProps,
141
+ parentManager?: ThemeManager | null
142
+ ): ThemeManagerState | null {
143
+ const themes = getThemes()
149
144
 
150
- let nextName = name || this.name || ''
151
- let parentName = parentIsReset ? this.parentName || this.fullName : this.fullName
152
-
153
- while (true) {
154
- if (nextName in themes) break
155
- nextName = `${parentName}_${name}`
156
- if (nextName in themes) break
157
- // this is fine - some themes can not have parents
158
- if (!parentName.includes(THEME_NAME_SEPARATOR)) break
159
- // go up one
160
- parentName = parentName.slice(0, parentName.lastIndexOf(THEME_NAME_SEPARATOR))
161
- }
145
+ if (props.name && props.reset) {
146
+ throw new Error(`Cannot reset + set new name`)
147
+ }
148
+ if (props.reset && !parentManager?.parentManager) {
149
+ throw new Error(`Cannot reset no grandparent exists`)
150
+ }
162
151
 
163
- if (componentName) {
164
- // allow for _Card_Button or just _Button
165
- const names = [
166
- `${nextName}_${componentName}`,
167
- `${withoutComponentName(nextName)}_${componentName}`,
168
- ]
169
- for (const name of names) {
170
- if (name in themes) {
171
- nextName = name
172
- }
173
- }
174
- }
152
+ const nextName = props.reset ? parentManager?.parentManager?.state?.name || '' : props.name || ''
153
+ const { componentName } = props
154
+ const parentName = parentManager?.state?.name || ''
175
155
 
176
- let theme = themes[nextName]
177
- if (!theme) {
178
- theme = themes[`light_${nextName}`]
156
+ // components look for most specific, fallback upwards
157
+ const base = parentName.split(THEME_NAME_SEPARATOR)
158
+ const lastSegment = base[base.length - 1]
159
+ const isParentAComponentTheme = parentName && lastSegment[0].toUpperCase() === lastSegment[0]
160
+ if (isParentAComponentTheme) {
161
+ base.pop() // always remove componentName they can't nest
162
+ }
163
+ const parentBaseTheme = isParentAComponentTheme
164
+ ? base.slice(0, base.length).join(THEME_NAME_SEPARATOR)
165
+ : parentName
166
+ const max = base.length
167
+ const min = componentName
168
+ ? max // component themes don't search upwards
169
+ : 0
170
+
171
+ // prettier-ignore
172
+ // eslint-disable-next-line no-console
173
+ if (process.env.NODE_ENV === 'development' && props.debug === 'verbose') console.log('getState', props, { parentName, parentBaseTheme, base, min, max, isParentAComponentTheme, parentManager })
174
+
175
+ for (let i = max; i >= min; i--) {
176
+ let prefix = base.slice(0, i).join(THEME_NAME_SEPARATOR)
177
+ if (props.inverse) {
178
+ prefix = inverseThemeName(prefix)
179
179
  }
180
-
181
- if (process.env.NODE_ENV === 'development') {
182
- if (debug) {
183
- // eslint-disable-next-line no-console
184
- console.log('getNextTheme', this.getCN(nextName), { props, nextName, parentName })
180
+ const potentials: string[] = []
181
+ if (prefix && prefix !== parentBaseTheme) {
182
+ potentials.push(prefix)
183
+ }
184
+ if (nextName) {
185
+ potentials.unshift(prefix ? `${prefix}_${nextName}` : nextName)
186
+ }
187
+ if (i === 1) {
188
+ const lastSegment = potentials.findIndex((x) => !x.includes('_'))
189
+ if (lastSegment > 0) {
190
+ potentials.splice(lastSegment, 0, nextName) // last try prefer our new name to parent
185
191
  }
186
192
  }
187
-
188
- return {
189
- name: nextName,
190
- theme: getThemeUnwrapped(theme),
191
- className: this.getCN(nextName),
193
+ if (componentName) {
194
+ // components only look for component themes
195
+ if (nextName) {
196
+ potentials.push(`${prefix.replace(/_.*$/, '')}_${nextName}_${componentName}`)
197
+ }
198
+ potentials.push(`${prefix}_${componentName}`)
199
+ if (nextName) {
200
+ potentials.unshift(`${prefix}_${nextName}_${componentName}`)
201
+ }
192
202
  }
193
- }
194
-
195
- getCN(name: string) {
196
- return `${THEME_CLASSNAME_PREFIX}${name} t_Theme`.replace('light_', '').replace('dark_', '')
197
- }
198
203
 
199
- track(uuid: any, keys: Set<string>) {
200
- if (!this.name) return
201
- this.keys.set(uuid, keys)
202
- }
204
+ // prettier-ignore
205
+ // eslint-disable-next-line no-console
206
+ if (process.env.NODE_ENV === 'development' && props.debug === 'verbose') console.log('getState potentials', potentials)
203
207
 
204
- notify() {
205
- if (!this.name) {
206
- this.keys.clear()
207
- }
208
- for (const [uuid, keys] of this.keys.entries()) {
209
- if (keys.size) {
210
- this.listeners.get(uuid)?.()
208
+ const found = potentials.find((t) => t in themes)
209
+ if (found) {
210
+ // optimization return null if not changed
211
+ if (found === parentName) {
212
+ return null
213
+ }
214
+ const theme = themes[found]
215
+ return {
216
+ // need to put concurrent safe things here
217
+ name: found,
218
+ theme: getThemeUnwrapped(theme),
219
+ className: getNextThemeClassName(found, props.inverse),
220
+ parentName,
211
221
  }
212
222
  }
213
- this.themeListeners.forEach((cb) => cb(this.name, this))
214
223
  }
215
224
 
216
- onChangeTheme(cb: ThemeListener) {
217
- this.themeListeners.add(cb)
218
- return () => {
219
- this.themeListeners.delete(cb)
220
- }
221
- }
225
+ return null
222
226
  }
223
227
 
224
- export const ThemeManagerContext = createContext<ThemeManager | null>(null)
225
- export const emptyManager = new ThemeManager()
226
-
227
- const withoutComponentName = (name: string) => name.replace(/(_[A-Z][a-zA-Z]+)+$/g, '')
228
+ const inverseThemeName = (themeName: string) => {
229
+ return themeName.startsWith('light')
230
+ ? themeName.replace(/^light/, 'dark')
231
+ : themeName.replace(/^dark/, 'light')
232
+ }
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'react'
2
+
3
+ import { ThemeManager } from './ThemeManager'
4
+
5
+ export const ThemeManagerContext = createContext<ThemeManager | null>(null)
@@ -412,7 +412,7 @@ const getToken = (
412
412
  : conf.fontsParsed
413
413
  let valOrVar: any
414
414
  let hasSet = false
415
- if (theme[value]) {
415
+ if (value in theme) {
416
416
  valOrVar = theme[value]
417
417
  hasSet = true
418
418
  } else {
@@ -449,15 +449,15 @@ const getToken = (
449
449
  if (hasSet) {
450
450
  if (process.env.NODE_ENV === 'development' && isDevTools && debug === 'verbose') {
451
451
  // eslint-disable-next-line no-console
452
- console.log(` ﹒ propMapper getToken`, key, valOrVar)
452
+ console.log(` ﹒ propMapper getToken`, key, valOrVar, theme)
453
453
  }
454
454
  return resolveVariableValue(key, valOrVar, resolveAs)
455
455
  }
456
456
  if (process.env.NODE_ENV === 'development') {
457
457
  if (value && value[0] === '$') {
458
458
  // eslint-disable-next-line no-console
459
- console.trace(
460
- `⚠️ You've passed the value "${value}" to the style property "${key}", but there's no theme or token with the key "${value}". Using theme "${theme.name}".
459
+ console.warn(
460
+ `⚠️ You passed the value "${value}" to the style property "${key}", but there's no theme or token with the key "${value}". Using theme "${theme.name}".
461
461
 
462
462
  Set the debug prop to true to see more detailed debug information.`
463
463
  )
@@ -34,15 +34,15 @@ export function extendStaticConfig(config: Partial<StaticConfig>, parent?: Styla
34
34
  parentNames.push(parentStaticConf.componentName)
35
35
  }
36
36
 
37
+ const deoptProps = config.deoptProps || new Set<string>()
38
+ // deoptProps.add('style')
39
+
37
40
  return parseStaticConfig({
38
41
  ...parentStaticConf,
39
42
  ...config,
43
+ deoptProps,
40
44
  variants,
41
45
  parentNames,
42
- isZStack: config.isZStack || parentStaticConf.isZStack,
43
- isText: config.isText || parentStaticConf.isText || false,
44
- neverFlatten: config.neverFlatten || parentStaticConf.neverFlatten,
45
- ensureOverriddenProp: config.ensureOverriddenProp ?? parentStaticConf.ensureOverriddenProp,
46
46
  validStyles: config.validStyles
47
47
  ? {
48
48
  ...parentStaticConf.validStyles,
@@ -0,0 +1,7 @@
1
+ export const getBoundingClientRect = (node: HTMLElement | null): void | DOMRect => {
2
+ if (!node) return
3
+ if (node.nodeType !== 1) return
4
+ if (node.getBoundingClientRect) {
5
+ return node.getBoundingClientRect()
6
+ }
7
+ }
@@ -0,0 +1,8 @@
1
+ import { getBoundingClientRect } from './getBoundingClientRect'
2
+
3
+ export const getRect = (node: HTMLElement) => {
4
+ const rect = getBoundingClientRect(node)
5
+ if (!rect) return
6
+ const { x, y, top, left } = rect
7
+ return { x, y, width: node.offsetWidth, height: node.offsetHeight, top, left }
8
+ }