@tamagui/core 1.0.1-rc.8 → 1.0.1

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 (200) hide show
  1. package/dist/cjs/Pressability.js +32 -0
  2. package/dist/cjs/Pressability.js.map +7 -0
  3. package/dist/cjs/Pressability.native.js +32 -0
  4. package/dist/cjs/Pressability.native.js.map +7 -0
  5. package/dist/cjs/ThemeContext.js +30 -0
  6. package/dist/cjs/ThemeContext.js.map +7 -0
  7. package/dist/cjs/ThemeManager.js +211 -0
  8. package/dist/cjs/ThemeManager.js.map +7 -0
  9. package/dist/cjs/conf.js +70 -0
  10. package/dist/cjs/conf.js.map +7 -0
  11. package/dist/cjs/constants/platform.js +4 -10
  12. package/dist/cjs/constants/platform.js.map +2 -2
  13. package/dist/cjs/constants/rnw.native.js +28 -0
  14. package/dist/cjs/constants/rnw.native.js.map +7 -0
  15. package/dist/cjs/contexts/ButtonInsideButtonContext.js.map +1 -1
  16. package/dist/cjs/createComponent.js +25 -27
  17. package/dist/cjs/createComponent.js.map +2 -2
  18. package/dist/cjs/createTamagui.js +4 -1
  19. package/dist/cjs/createTamagui.js.map +2 -2
  20. package/dist/cjs/defaultComponentState.js +2 -0
  21. package/dist/cjs/defaultComponentState.js.map +7 -0
  22. package/dist/cjs/helpers/ThemeManager.js +4 -2
  23. package/dist/cjs/helpers/ThemeManager.js.map +2 -2
  24. package/dist/cjs/helpers/Unwrap.js +29 -0
  25. package/dist/cjs/helpers/Unwrap.js.map +7 -0
  26. package/dist/cjs/helpers/assignNativePropsToWeb.js.map +1 -1
  27. package/dist/cjs/helpers/assignNativePropsToWeb.native.js.map +1 -1
  28. package/dist/cjs/helpers/cache.js +7 -11
  29. package/dist/cjs/helpers/cache.js.map +2 -2
  30. package/dist/cjs/helpers/calc.js.map +2 -2
  31. package/dist/cjs/helpers/createShallowUpdate.js +9 -12
  32. package/dist/cjs/helpers/createShallowUpdate.js.map +1 -1
  33. package/dist/cjs/helpers/createTamaguiProvider.js +65 -0
  34. package/dist/cjs/helpers/createTamaguiProvider.js.map +7 -0
  35. package/dist/cjs/helpers/cssHash.js +51 -0
  36. package/dist/cjs/helpers/cssHash.js.map +7 -0
  37. package/dist/cjs/helpers/expandStyle.js +29 -41
  38. package/dist/cjs/helpers/expandStyle.js.map +2 -2
  39. package/dist/cjs/helpers/expandStyle.test.js +10 -0
  40. package/dist/cjs/helpers/expandStyle.test.js.map +7 -0
  41. package/dist/cjs/helpers/getFont.js +2 -6
  42. package/dist/cjs/helpers/getFont.js.map +2 -2
  43. package/dist/cjs/helpers/getSplitStyles.js +22 -23
  44. package/dist/cjs/helpers/getSplitStyles.js.map +2 -2
  45. package/dist/cjs/helpers/getStackSize.js.map +1 -1
  46. package/dist/cjs/helpers/getStylesAtomic.js +10 -11
  47. package/dist/cjs/helpers/getStylesAtomic.js.map +2 -2
  48. package/dist/cjs/helpers/insertStyleRule.js +58 -27
  49. package/dist/cjs/helpers/insertStyleRule.js.map +3 -3
  50. package/dist/cjs/helpers/nativeGestureHandler.native.js +19 -0
  51. package/dist/cjs/helpers/nativeGestureHandler.native.js.map +7 -0
  52. package/dist/cjs/helpers/normalizeValueWithProperty.js +3 -3
  53. package/dist/cjs/helpers/normalizeValueWithProperty.js.map +2 -2
  54. package/dist/cjs/helpers/startTransition.js +1 -4
  55. package/dist/cjs/helpers/startTransition.js.map +2 -2
  56. package/dist/cjs/helpers/themeable.js +2 -2
  57. package/dist/cjs/helpers/themeable.js.map +2 -2
  58. package/dist/cjs/helpers/wrapThemeManagerContext.js +37 -0
  59. package/dist/cjs/helpers/wrapThemeManagerContext.js.map +7 -0
  60. package/dist/cjs/hooks/useConstant.js +0 -3
  61. package/dist/cjs/hooks/useConstant.js.map +2 -2
  62. package/dist/cjs/hooks/useElementLayout.js +1 -1
  63. package/dist/cjs/hooks/useElementLayout.js.map +2 -2
  64. package/dist/cjs/hooks/useFeatures.js +201 -0
  65. package/dist/cjs/hooks/useFeatures.js.map +7 -0
  66. package/dist/cjs/hooks/useIsMounted.js.map +1 -1
  67. package/dist/cjs/hooks/useIsSSR.js +6 -10
  68. package/dist/cjs/hooks/useIsSSR.js.map +2 -2
  69. package/dist/cjs/hooks/useMedia.js +5 -0
  70. package/dist/cjs/hooks/useMedia.js.map +2 -2
  71. package/dist/cjs/hooks/usePressable.js.map +1 -1
  72. package/dist/cjs/hooks/usePrevious.js +1 -0
  73. package/dist/cjs/hooks/usePrevious.js.map +7 -0
  74. package/dist/cjs/hooks/useTheme.js +45 -50
  75. package/dist/cjs/hooks/useTheme.js.map +3 -3
  76. package/dist/cjs/hooks/useUnmountEffect.js.map +1 -1
  77. package/dist/cjs/styled.js +12 -0
  78. package/dist/cjs/styled.js.map +2 -2
  79. package/dist/cjs/views/TamaguiProvider.js +3 -0
  80. package/dist/cjs/views/TamaguiProvider.js.map +2 -2
  81. package/dist/cjs/views/TextAncestorContext.js +44 -0
  82. package/dist/cjs/views/TextAncestorContext.js.map +7 -0
  83. package/dist/cjs/views/Theme.js +15 -21
  84. package/dist/cjs/views/Theme.js.map +3 -3
  85. package/dist/cjs/views/ThemeInverse.js +20 -21
  86. package/dist/cjs/views/ThemeInverse.js.map +2 -2
  87. package/dist/cjs/views/ThemeReset.js +5 -10
  88. package/dist/cjs/views/ThemeReset.js.map +3 -3
  89. package/dist/esm/Pressability.js +7 -0
  90. package/dist/esm/Pressability.js.map +7 -0
  91. package/dist/esm/Pressability.native.js +7 -0
  92. package/dist/esm/Pressability.native.js.map +7 -0
  93. package/dist/esm/ThemeContext.js +6 -0
  94. package/dist/esm/ThemeContext.js.map +7 -0
  95. package/dist/esm/ThemeManager.js +185 -0
  96. package/dist/esm/ThemeManager.js.map +7 -0
  97. package/dist/esm/conf.js +39 -0
  98. package/dist/esm/conf.js.map +7 -0
  99. package/dist/esm/constants/platform.js +3 -7
  100. package/dist/esm/constants/platform.js.map +2 -2
  101. package/dist/esm/constants/rnw.native.js +5 -0
  102. package/dist/esm/constants/rnw.native.js.map +7 -0
  103. package/dist/esm/contexts/ButtonInsideButtonContext.js.map +1 -1
  104. package/dist/esm/createComponent.js +26 -28
  105. package/dist/esm/createComponent.js.map +2 -2
  106. package/dist/esm/createTamagui.js +9 -2
  107. package/dist/esm/createTamagui.js.map +2 -2
  108. package/dist/esm/defaultComponentState.js +1 -0
  109. package/dist/esm/defaultComponentState.js.map +7 -0
  110. package/dist/esm/helpers/ThemeManager.js +4 -2
  111. package/dist/esm/helpers/ThemeManager.js.map +2 -2
  112. package/dist/esm/helpers/Unwrap.js +5 -0
  113. package/dist/esm/helpers/Unwrap.js.map +7 -0
  114. package/dist/esm/helpers/assignNativePropsToWeb.js.map +1 -1
  115. package/dist/esm/helpers/assignNativePropsToWeb.native.js.map +1 -1
  116. package/dist/esm/helpers/cache.js +7 -10
  117. package/dist/esm/helpers/cache.js.map +2 -2
  118. package/dist/esm/helpers/calc.js.map +2 -2
  119. package/dist/esm/helpers/createShallowUpdate.js +9 -12
  120. package/dist/esm/helpers/createShallowUpdate.js.map +1 -1
  121. package/dist/esm/helpers/createTamaguiProvider.js +38 -0
  122. package/dist/esm/helpers/createTamaguiProvider.js.map +7 -0
  123. package/dist/esm/helpers/cssHash.js +27 -0
  124. package/dist/esm/helpers/cssHash.js.map +7 -0
  125. package/dist/esm/helpers/expandStyle.js +26 -38
  126. package/dist/esm/helpers/expandStyle.js.map +2 -2
  127. package/dist/esm/helpers/expandStyle.test.js +9 -0
  128. package/dist/esm/helpers/expandStyle.test.js.map +7 -0
  129. package/dist/esm/helpers/getFont.js +2 -6
  130. package/dist/esm/helpers/getFont.js.map +2 -2
  131. package/dist/esm/helpers/getSplitStyles.js +24 -24
  132. package/dist/esm/helpers/getSplitStyles.js.map +2 -2
  133. package/dist/esm/helpers/getStackSize.js.map +1 -1
  134. package/dist/esm/helpers/getStylesAtomic.js +10 -11
  135. package/dist/esm/helpers/getStylesAtomic.js.map +2 -2
  136. package/dist/esm/helpers/insertStyleRule.js +56 -26
  137. package/dist/esm/helpers/insertStyleRule.js.map +3 -3
  138. package/dist/esm/helpers/nativeGestureHandler.native.js +2 -0
  139. package/dist/esm/helpers/nativeGestureHandler.native.js.map +7 -0
  140. package/dist/esm/helpers/normalizeValueWithProperty.js +3 -3
  141. package/dist/esm/helpers/normalizeValueWithProperty.js.map +2 -2
  142. package/dist/esm/helpers/startTransition.js.map +1 -1
  143. package/dist/esm/helpers/themeable.js +2 -2
  144. package/dist/esm/helpers/themeable.js.map +2 -2
  145. package/dist/esm/helpers/wrapThemeManagerContext.js +11 -0
  146. package/dist/esm/helpers/wrapThemeManagerContext.js.map +7 -0
  147. package/dist/esm/hooks/useConstant.js +1 -4
  148. package/dist/esm/hooks/useConstant.js.map +2 -2
  149. package/dist/esm/hooks/useElementLayout.js +1 -1
  150. package/dist/esm/hooks/useElementLayout.js.map +2 -2
  151. package/dist/esm/hooks/useFeatures.js +174 -0
  152. package/dist/esm/hooks/useFeatures.js.map +7 -0
  153. package/dist/esm/hooks/useIsMounted.js.map +1 -1
  154. package/dist/esm/hooks/useIsSSR.js +8 -12
  155. package/dist/esm/hooks/useIsSSR.js.map +2 -2
  156. package/dist/esm/hooks/useMedia.js +4 -0
  157. package/dist/esm/hooks/useMedia.js.map +2 -2
  158. package/dist/esm/hooks/usePressable.js.map +1 -1
  159. package/dist/esm/hooks/usePrevious.js +1 -0
  160. package/dist/esm/hooks/usePrevious.js.map +7 -0
  161. package/dist/esm/hooks/useTheme.js +45 -50
  162. package/dist/esm/hooks/useTheme.js.map +3 -3
  163. package/dist/esm/hooks/useUnmountEffect.js.map +1 -1
  164. package/dist/esm/styled.js +12 -0
  165. package/dist/esm/styled.js.map +2 -2
  166. package/dist/esm/views/TamaguiProvider.js +3 -0
  167. package/dist/esm/views/TamaguiProvider.js.map +2 -2
  168. package/dist/esm/views/TextAncestorContext.js +15 -0
  169. package/dist/esm/views/TextAncestorContext.js.map +7 -0
  170. package/dist/esm/views/Theme.js +14 -14
  171. package/dist/esm/views/Theme.js.map +2 -2
  172. package/dist/esm/views/ThemeInverse.js +15 -20
  173. package/dist/esm/views/ThemeInverse.js.map +2 -2
  174. package/dist/esm/views/ThemeReset.js +4 -6
  175. package/dist/esm/views/ThemeReset.js.map +2 -2
  176. package/package.json +13 -14
  177. package/src/createComponent.tsx +47 -43
  178. package/src/createTamagui.ts +10 -2
  179. package/src/helpers/ThemeManager.tsx +7 -2
  180. package/src/helpers/cache.ts +7 -9
  181. package/src/helpers/expandStyle.ts +27 -41
  182. package/src/helpers/getSplitStyles.tsx +40 -23
  183. package/src/helpers/getStylesAtomic.ts +15 -15
  184. package/src/helpers/insertStyleRule.tsx +68 -38
  185. package/src/helpers/normalizeValueWithProperty.ts +3 -3
  186. package/src/helpers/themeable.tsx +3 -3
  187. package/src/hooks/useElementLayout.tsx +1 -1
  188. package/src/hooks/useMedia.tsx +3 -0
  189. package/src/hooks/useTheme.tsx +67 -48
  190. package/src/styled.tsx +15 -0
  191. package/src/views/TamaguiProvider.tsx +6 -0
  192. package/src/views/Theme.tsx +15 -22
  193. package/types/createComponent.d.ts +1 -1
  194. package/types/helpers/ThemeManager.d.ts +1 -1
  195. package/types/helpers/cache.d.ts +2 -4
  196. package/types/helpers/insertStyleRule.d.ts +2 -1
  197. package/types/helpers/themeable.d.ts +1 -1
  198. package/types/hooks/useMedia.d.ts +1 -0
  199. package/types/hooks/useTheme.d.ts +1 -1
  200. package/types/views/Theme.d.ts +1 -1
@@ -1,7 +1,3 @@
1
- // this needs to check if its inserted already? 99% of the time it is
2
- // this should avoid re-inserts, but need to test the perf trade-offs
3
- // i tested with the site itself and the initial insert was trivial
4
-
5
1
  import type { StyleObject } from '@tamagui/helpers'
6
2
 
7
3
  const allSelectors: Record<string, string> = {}
@@ -17,18 +13,7 @@ function addTransform(identifier: string, css: string, rule?: CSSRule) {
17
13
  const s = css.indexOf('transform:')
18
14
  if (s === -1) {
19
15
  if (process.env.NODE_ENV === 'development') {
20
- // eslint-disable-next-line no-console
21
- console.groupCollapsed(
22
- `❌ Invalid transform, likely used deg/% improperly ${identifier}`
23
- )
24
- // eslint-disable-next-line no-console
25
- console.log('rule:', rule)
26
- // eslint-disable-next-line no-console
27
- console.trace()
28
- // eslint-disable-next-line no-console
29
- console.groupEnd()
30
- } else {
31
- // missing transform
16
+ console.error(`❌ Invalid transform, likely used deg/% improperly ${identifier}`)
32
17
  }
33
18
  return false
34
19
  }
@@ -50,9 +35,31 @@ const isClient = typeof document !== 'undefined'
50
35
  // 1. debugging at dev time
51
36
  // 2. avoid duplicate insert styles at runtime
52
37
  // 3. used now for merging transforms atomically
38
+
39
+ // multiple sheets could have the same ids so we have to count
40
+
53
41
  const scannedNum = new WeakMap<CSSStyleSheet, number>()
42
+ const totalSheetSelectors = new Map<string, number>()
43
+
44
+ export function listenForSheetChanges() {
45
+ if (!isClient) return
46
+ function handleNode(node: Node, remove = false) {
47
+ if (node instanceof HTMLStyleElement && node.sheet) {
48
+ updateSheetStyles(node.sheet, remove)
49
+ }
50
+ }
51
+ const mo = new MutationObserver((entries) => {
52
+ for (const entry of entries) {
53
+ entry.addedNodes.forEach((node) => handleNode(node))
54
+ entry.removedNodes.forEach((node) => handleNode(node, true))
55
+ }
56
+ })
57
+ mo.observe(document.head, {
58
+ childList: true,
59
+ })
60
+ }
54
61
 
55
- export function updateInserted() {
62
+ export function scanAllSheets() {
56
63
  if (process.env.NODE_ENV === 'test') return
57
64
  if (!isClient) return
58
65
  const sheets = document.styleSheets
@@ -60,28 +67,53 @@ export function updateInserted() {
60
67
  for (let i = 0; i < sheets.length; i++) {
61
68
  const sheet = sheets[i]
62
69
  if (!sheet) continue
70
+ updateSheetStyles(sheet)
71
+ }
72
+ }
63
73
 
64
- // avoid errors on cross origin sheets
65
- // https://stackoverflow.com/questions/49993633/uncaught-domexception-failed-to-read-the-cssrules-property
66
- let rules: CSSRuleList
67
- try {
68
- rules = sheet.cssRules
69
- } catch {
70
- continue
71
- }
74
+ function track(id: string, remove = false) {
75
+ const next = (totalSheetSelectors.get(id) || 0) + (remove ? -1 : 1)
76
+ totalSheetSelectors.set(id, next)
77
+ return next
78
+ }
72
79
 
73
- const len = rules.length
74
- const lastScanned = scannedNum.get(sheet) ?? 0
80
+ function updateSheetStyles(sheet: CSSStyleSheet, remove = false) {
81
+ // avoid errors on cross origin sheets
82
+ // https://stackoverflow.com/questions/49993633/uncaught-domexception-failed-to-read-the-cssrules-property
83
+ let rules: CSSRuleList
84
+ try {
85
+ rules = sheet.cssRules
86
+ } catch {
87
+ return
88
+ }
89
+
90
+ const len = rules.length
91
+ const lastScanned = scannedNum.get(sheet) || 0
92
+
93
+ if (!remove) {
94
+ // avoid work dumb but works
75
95
  if (lastScanned === len) {
76
- continue
96
+ return
97
+ }
98
+ }
99
+
100
+ for (let i = lastScanned; i < len; i++) {
101
+ const rule = rules[i]
102
+ const response = getTamaguiSelector(rule)
103
+ if (!response) {
104
+ return
77
105
  }
78
- for (let i = lastScanned; i < len; i++) {
79
- const rule = rules[i]
80
- const response = getTamaguiSelector(rule)
81
- if (!response) {
82
- continue
106
+
107
+ const [identifier, cssRule] = response
108
+
109
+ // track references
110
+ const total = track(identifier, remove)
111
+
112
+ if (remove) {
113
+ if (total === 0) {
114
+ delete allSelectors[identifier]
83
115
  }
84
- const [identifier, cssRule] = response
116
+ } else {
85
117
  if (!(identifier in allSelectors)) {
86
118
  const isTransform = identifier.startsWith('_transform')
87
119
  const shouldInsert = isTransform
@@ -92,8 +124,9 @@ export function updateInserted() {
92
124
  }
93
125
  }
94
126
  }
95
- scannedNum.set(sheet, i)
96
127
  }
128
+
129
+ scannedNum.set(sheet, len)
97
130
  }
98
131
 
99
132
  function getTamaguiSelector(
@@ -122,9 +155,6 @@ const getIdentifierFromTamaguiSelector = (selector: string) =>
122
155
  .replace(/:[a-z]+$/, '')
123
156
  .slice(1)
124
157
 
125
- // move this to probably inside createTamagui
126
- updateInserted()
127
-
128
158
  const sheet = isClient
129
159
  ? document.head.appendChild(document.createElement('style')).sheet
130
160
  : null
@@ -11,13 +11,13 @@ import { normalizeColor } from './normalizeColor'
11
11
  const cache = {}
12
12
 
13
13
  export function normalizeValueWithProperty(value: any, property?: string): any {
14
- if (property && unitlessNumbers[property]) {
15
- return value
16
- }
17
14
  const cached = cache[value]
18
15
  if (cached !== undefined) {
19
16
  return cached
20
17
  }
18
+ if (property && property in unitlessNumbers) {
19
+ return value
20
+ }
21
21
  let res = value
22
22
  if (
23
23
  process.env.TAMAGUI_TARGET === 'web' &&
@@ -6,9 +6,9 @@ import { Theme } from '../views/Theme'
6
6
  export interface ThemeableProps {
7
7
  theme?: ThemeName | null
8
8
  themeInverse?: boolean
9
+ themeReset?: boolean
9
10
  componentName?: string
10
11
  debug?: boolean | 'verbose'
11
- reset?: boolean
12
12
  }
13
13
 
14
14
  export function themeable<Component extends (props: any) => any>(
@@ -16,7 +16,7 @@ export function themeable<Component extends (props: any) => any>(
16
16
  opts?: { componentName?: string }
17
17
  ) {
18
18
  const withThemeComponent = forwardRef(function WithTheme(props: ThemeableProps, ref) {
19
- const { themeInverse, theme, componentName, reset, ...rest } = props
19
+ const { themeInverse, theme, componentName, themeReset, ...rest } = props
20
20
  const element = React.createElement(component, { ...rest, ref } as any)
21
21
 
22
22
  return (
@@ -26,7 +26,7 @@ export function themeable<Component extends (props: any) => any>(
26
26
  name={(theme as any) || null}
27
27
  data-themeable
28
28
  debug={props.debug}
29
- reset={props.reset}
29
+ reset={props.themeReset}
30
30
  >
31
31
  {element}
32
32
  </Theme>
@@ -73,7 +73,7 @@ export function useElementLayout(
73
73
  // Observing is done in a separate effect to avoid this effect running
74
74
  // when 'onLayout' changes.
75
75
  useIsomorphicLayoutEffect(() => {
76
- if (!(resizeObserver && hasLayoutEvent)) return
76
+ if (!resizeObserver || !hasLayoutEvent) return
77
77
  const node = ref.current
78
78
  if (!node) return
79
79
  LayoutHandlers.set(node, onLayoutEvent)
@@ -35,6 +35,7 @@ export const mediaState: MediaQueryState =
35
35
 
36
36
  export const mediaQueryConfig: MediaQueries = {}
37
37
  export const getMedia = () => mediaState
38
+ export const mediaKeysWithAndWithout$ = new Set<string>()
38
39
 
39
40
  // for SSR capture it at time of startup
40
41
  let initState: MediaQueryState
@@ -58,6 +59,8 @@ export const configureMedia = (config: TamaguiInternalConfig) => {
58
59
  if (!media) return
59
60
  for (const key in media) {
60
61
  mediaState[key] = mediaQueryDefaultActive?.[key] || false
62
+ mediaKeysWithAndWithout$.add(key)
63
+ mediaKeysWithAndWithout$.add(`$${key}`)
61
64
  }
62
65
  Object.assign(mediaQueryConfig, media)
63
66
  initState = { ...mediaState }
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
  import { isClient, isRSC, isServer, isWeb } from '@tamagui/constants'
3
- import { useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react'
3
+ import { useContext, useEffect, useMemo, useState } from 'react'
4
4
 
5
5
  import { getConfig } from '../config'
6
6
  import { isDevTools } from '../constants/isDevTools'
@@ -13,6 +13,7 @@ import {
13
13
  import { ThemeManagerContext } from '../helpers/ThemeManagerContext'
14
14
  import { ThemeParsed, ThemeProps } from '../types'
15
15
  import { GetThemeUnwrapped } from './getThemeUnwrapped'
16
+ import { useServerRef } from './useServerHooks'
16
17
 
17
18
  export type ChangedThemeResponse = {
18
19
  themeManager: ThemeManager | null
@@ -33,12 +34,28 @@ function getDefaultThemeProxied() {
33
34
  })
34
35
  }
35
36
 
37
+ interface UseThemeState {
38
+ keys: Set<string>
39
+ }
40
+
36
41
  export const useTheme = (props: ThemeProps = emptyProps): ThemeParsed => {
37
42
  if (isRSC) {
38
43
  return getDefaultThemeProxied()
39
44
  }
40
45
 
41
- const { name, theme, themeManager, className, isNewTheme } = useChangeThemeEffect(props)
46
+ const state = useServerRef() as React.MutableRefObject<UseThemeState>
47
+ if (isClient && !state.current) {
48
+ state.current = {
49
+ keys: new Set(),
50
+ }
51
+ }
52
+ state.current?.keys.clear()
53
+
54
+ const { name, theme, themeManager, className, isNewTheme } = useChangeThemeEffect(
55
+ props,
56
+ false,
57
+ isClient ? () => state.current.keys.size === 0 : undefined
58
+ )
42
59
 
43
60
  if (process.env.NODE_ENV === 'development') {
44
61
  // ensure we aren't creating too many ThemeManagers
@@ -73,6 +90,7 @@ export const useTheme = (props: ThemeProps = emptyProps): ThemeParsed => {
73
90
  name,
74
91
  className,
75
92
  themeManager,
93
+ state,
76
94
  })
77
95
  }, [theme, isNewTheme, name, className, themeManager])
78
96
  }
@@ -83,12 +101,14 @@ function getThemeProxied({
83
101
  className,
84
102
  themeManager,
85
103
  isNewTheme,
104
+ state,
86
105
  }: {
87
106
  theme: any
88
107
  name: string
89
108
  className?: string
90
109
  themeManager?: ThemeManager | null
91
110
  isNewTheme?: boolean
111
+ state?: React.RefObject<UseThemeState>
92
112
  }) {
93
113
  return createProxy(theme, {
94
114
  has(_, key) {
@@ -122,7 +142,20 @@ function getThemeProxied({
122
142
  if (!themeManager) {
123
143
  return theme[key]
124
144
  }
125
- return themeManager.getValue(key)
145
+ const val = themeManager.getValue(key)
146
+ if (val && state) {
147
+ if (isClient) {
148
+ return new Proxy(val as any, {
149
+ get(_, subkey) {
150
+ if (subkey === 'val') {
151
+ state.current!.keys.add(key as any)
152
+ }
153
+ return Reflect.get(val, subkey)
154
+ },
155
+ })
156
+ }
157
+ }
158
+ return val
126
159
  },
127
160
  })
128
161
  }
@@ -139,7 +172,8 @@ export const activeThemeManagers = new Set<ThemeManager>()
139
172
 
140
173
  export const useChangeThemeEffect = (
141
174
  props: ThemeProps,
142
- root = false
175
+ root = false,
176
+ disableUpdate?: () => boolean
143
177
  ): ChangedThemeResponse => {
144
178
  if (isRSC) {
145
179
  // we need context working for this to work well
@@ -186,20 +220,16 @@ export const useChangeThemeEffect = (
186
220
  return
187
221
  }
188
222
  activeThemeManagers.add(themeManager)
189
- const disposeChangeListener = parentManager?.onChangeTheme(() => {
190
- if (!(isNewTheme || isWeb)) {
191
- setThemeState({
192
- ...themeState,
193
- state: { ...themeManager.state },
194
- isNewTheme: false,
195
- })
196
- } else {
197
- updateState()
198
- }
199
- })
223
+ // const disposeChangeListener = parentManager?.onChangeTheme((name, manager) => {
224
+ // // console.log('hi', props, state, name, manager)
225
+ // if (state.theme !== manager.state.theme) {
226
+ // console.warn('now update to', manager.state.name)
227
+ // updateState(manager)
228
+ // }
229
+ // })
200
230
  return () => {
201
231
  activeThemeManagers.delete(themeManager)
202
- disposeChangeListener?.()
232
+ // disposeChangeListener?.()
203
233
  }
204
234
  }, [disable, state, isNewTheme, debug])
205
235
  }
@@ -209,13 +239,8 @@ export const useChangeThemeEffect = (
209
239
 
210
240
  if (shouldReturnParentState) {
211
241
  if (!parentManager) throw 'impossible'
212
- if (process.env.NODE_ENV === 'development' && props.debug === 'verbose')
213
- console.log(
214
- 'useTheme hasNoThemeUpdatingProps',
215
- parentManager.state.name,
216
- 'isInversingOnMount',
217
- isInversingOnMount
218
- )
242
+ // prettier-ignore
243
+ if (process.env.NODE_ENV === 'development' && props.debug === 'verbose') console.log('useTheme hasNoThemeUpdatingProps', parentManager.state.name, 'isInversingOnMount', isInversingOnMount)
219
244
  return {
220
245
  ...parentManager.state,
221
246
  className: isInversingOnMount ? '' : parentManager.state.className,
@@ -234,27 +259,19 @@ export const useChangeThemeEffect = (
234
259
  }
235
260
 
236
261
  function createState(prev?: State) {
262
+ if (prev && disableUpdate?.()) {
263
+ return prev
264
+ }
265
+ // returns previous theme manager if no change
237
266
  const _ = new ThemeManager(props, root ? 'root' : parentManager)
238
267
  const isNewTheme = _ !== parentManager
239
- if (process.env.NODE_ENV === 'development' && debug)
240
- [
241
- console.groupCollapsed('useTheme create() isNewTheme', isNewTheme),
242
- console.log(
243
- 'parent.state.name',
244
- parentManager?.state.name,
245
- '\n',
246
- 'props',
247
- props,
248
- '\n',
249
- isClient ? parentManager : '',
250
- 'getState',
251
- isNewTheme && isClient ? _.getState(props) : '',
252
- '\n',
253
- 'state',
254
- isClient ? { ..._.state } : ''
255
- ),
256
- console.groupEnd(),
257
- ]
268
+ // prettier-ignore
269
+
270
+ // if (prev?.isNewTheme && _.state.theme !== parentManager?.state.theme) {
271
+ // console.warn('notify')
272
+ // _.notify()
273
+ // }
274
+
258
275
  // only inverse relies on this for ssr
259
276
  const mounted = !props.inverse ? true : root || prev?.mounted
260
277
  return {
@@ -266,12 +283,14 @@ export const useChangeThemeEffect = (
266
283
  }
267
284
  }
268
285
 
269
- function updateState() {
270
- const next = themeManager.getState(props, parentManager)
271
- const shouldChange = themeManager.getStateShouldChange(
272
- next,
273
- isNewTheme ? state : null
274
- )
286
+ function updateState(updatingManager?: ThemeManager) {
287
+ if (disableUpdate?.()) {
288
+ return
289
+ }
290
+ const manager = updatingManager ?? themeManager
291
+ const next = manager.getState(props, parentManager)
292
+ const shouldChange = manager.getStateShouldChange(next, isNewTheme ? state : null)
293
+ // console.log(`got it`, next, isNewTheme, shouldChange, { ...updatingManager?.state })
275
294
  if (shouldChange) {
276
295
  setThemeState(createState)
277
296
  } else {
package/src/styled.tsx CHANGED
@@ -1,3 +1,5 @@
1
+ import { stylePropsAll, validStyles } from '@tamagui/helpers'
2
+
1
3
  import { createComponent } from './createComponent'
2
4
  import { ReactNativeStaticConfigs } from './setupReactNative'
3
5
  import {
@@ -60,6 +62,19 @@ export function styled<
60
62
  // }
61
63
  // }
62
64
 
65
+ // validate not using a variant over an existing valid style
66
+ if (process.env.NODE_ENV === 'development') {
67
+ if (options?.variants) {
68
+ for (const key in options.variants) {
69
+ if (key in stylePropsAll) {
70
+ console.error(
71
+ `Invalid variant key overlaps with style key: ${key}. Tamagui prevents defining variants that use valid style keys to reduce implementation complexity.`
72
+ )
73
+ }
74
+ }
75
+ }
76
+ }
77
+
63
78
  const staticConfigProps = (() => {
64
79
  const parentStaticConfig =
65
80
  'staticConfig' in Component ? (Component.staticConfig as StaticConfig) : null
@@ -32,6 +32,12 @@ export function TamaguiProvider({
32
32
  // inject CSS if asked to (not SSR compliant)
33
33
  // eslint-disable-next-line react-hooks/rules-of-hooks
34
34
  React.useLayoutEffect(() => {
35
+ // for easier support of hidden-until-js mount animations
36
+ // user must set t_unmounted on documentElement from SSR
37
+ if (document.documentElement.classList.contains('t_unmounted')) {
38
+ document.documentElement.classList.remove('t_unmounted')
39
+ }
40
+
35
41
  if (disableInjectCSS) return
36
42
  const style = document.createElement('style')
37
43
  style.appendChild(document.createTextNode(config.getCSS()))
@@ -1,5 +1,5 @@
1
1
  import { isWeb } from '@tamagui/constants'
2
- import React, { useMemo } from 'react'
2
+ import React, { Children, cloneElement, useMemo } from 'react'
3
3
 
4
4
  import { variableToString } from '../createVariable'
5
5
  import { ThemeManager } from '../helpers/ThemeManager'
@@ -14,23 +14,20 @@ export function Theme(props: ThemeProps) {
14
14
  }
15
15
 
16
16
  const isRoot = !!props['_isRoot']
17
- const { name, theme, themeManager, isNewTheme, className } = useChangeThemeEffect(
18
- props,
19
- isRoot
20
- )
21
-
17
+ const { name, theme, themeManager, className } = useChangeThemeEffect(props, isRoot)
22
18
  const disableThemeClass = props.disableThemeClass
23
19
  const missingTheme = !(name && theme)
24
20
 
25
21
  // memo here, changing theme without re-rendering all children is a critical optimization
26
22
  // may require some effort of end user to memoize but without this memo they'd have no option
27
- let contents = useMemo(() => {
28
- const next = props['data-themeable']
29
- ? React.cloneElement(props.children, { ['data-themeable']: true })
30
- : props.children
31
-
32
- return missingTheme ? next : wrapThemeManagerContext(next, themeManager)
33
- }, [missingTheme, props.children, themeManager])
23
+ let contents = useThemeManagerContext(
24
+ props['data-themeable']
25
+ ? Children.map(props.children, (child) =>
26
+ cloneElement(child, { ['data-themeable']: true })
27
+ )
28
+ : props.children,
29
+ themeManager
30
+ )
34
31
 
35
32
  if (process.env.NODE_ENV === 'development' && missingTheme) {
36
33
  // eslint-disable-next-line no-console
@@ -60,15 +57,15 @@ export function Theme(props: ThemeProps) {
60
57
  return contents
61
58
  }
62
59
 
63
- export function wrapThemeManagerContext(
60
+ export function useThemeManagerContext(
64
61
  children: any,
65
62
  themeManager?: ThemeManager | null,
66
63
  shouldReset?: boolean
67
64
  ) {
65
+ // disabled super memoize it - listener first strategy
66
+ const value = themeManager // useMemo(() => themeManager, [!!themeManager])
68
67
  // be sure to memoize themeManager to avoid reparenting
69
- if (!themeManager) {
70
- return children
71
- }
68
+ if (!value) return children
72
69
  // be sure to memoize shouldReset to avoid reparenting
73
70
  let next = children
74
71
  // TODO likely not necessary if we do reset logic now in useTheme?
@@ -76,9 +73,5 @@ export function wrapThemeManagerContext(
76
73
  if (shouldReset && themeManager) {
77
74
  next = <Theme name={themeManager.state.parentName}>{next}</Theme>
78
75
  }
79
- return (
80
- <ThemeManagerContext.Provider value={themeManager}>
81
- {next}
82
- </ThemeManagerContext.Provider>
83
- )
76
+ return <ThemeManagerContext.Provider value={value}>{next}</ThemeManagerContext.Provider>
84
77
  }
@@ -17,5 +17,5 @@ export type SpacedChildrenProps = {
17
17
  direction?: SpaceDirection;
18
18
  separator?: React.ReactNode;
19
19
  };
20
- export declare function spacedChildren({ isZStack, children, space, direction, spaceFlex, separator, }: SpacedChildrenProps): React.ReactNode;
20
+ export declare function spacedChildren(props: SpacedChildrenProps): React.ReactNode;
21
21
  //# sourceMappingURL=createComponent.d.ts.map
@@ -25,7 +25,7 @@ export declare class ThemeManager {
25
25
  forceTheme?: ThemeParsed;
26
26
  }, notify?: boolean): ThemeManagerState | undefined;
27
27
  getStateIfChanged(props?: ThemeProps, state?: ThemeManagerState | null, parentManager?: ThemeManager | null): ThemeManagerState | null | undefined;
28
- getStateShouldChange(nextState: ThemeManagerState | null, state?: ThemeManagerState | null): ThemeManagerState | null;
28
+ getStateShouldChange(nextState: ThemeManagerState | null, state?: ThemeManagerState | null): boolean;
29
29
  getState(props?: ThemeProps, parentManager?: ThemeManager | null): ThemeManagerState | null;
30
30
  get allKeys(): Set<string>;
31
31
  getValue(key: string, state?: ThemeManagerState): import("..").Variable<any> | undefined;
@@ -2,11 +2,9 @@ type Value = Object | Array<any> | string | number;
2
2
  export type Style = {
3
3
  [key: string]: Value;
4
4
  };
5
- declare class Cache {
6
- v: Record<string, any>;
5
+ export declare const cache: {
7
6
  get(key: string, valStr: string): any;
8
7
  set(key: string, valStr: string, object: any): void;
9
- }
10
- export declare const cache: Cache;
8
+ };
11
9
  export {};
12
10
  //# sourceMappingURL=cache.d.ts.map
@@ -3,7 +3,8 @@ export declare const insertedTransforms: {};
3
3
  export declare const getAllSelectors: () => Record<string, string>;
4
4
  export declare const getAllRules: () => string[];
5
5
  export declare const getAllTransforms: () => {};
6
- export declare function updateInserted(): void;
6
+ export declare function listenForSheetChanges(): void;
7
+ export declare function scanAllSheets(): void;
7
8
  export declare function updateRules(identifier: string, rules: string[]): boolean;
8
9
  export type PartialStyleObject = Pick<StyleObject, 'identifier' | 'property' | 'rules'>;
9
10
  export type RulesToInsert = PartialStyleObject[];
@@ -2,9 +2,9 @@ import { ThemeName } from '../types';
2
2
  export interface ThemeableProps {
3
3
  theme?: ThemeName | null;
4
4
  themeInverse?: boolean;
5
+ themeReset?: boolean;
5
6
  componentName?: string;
6
7
  debug?: boolean | 'verbose';
7
- reset?: boolean;
8
8
  }
9
9
  export declare function themeable<Component extends (props: any) => any>(component: Component, opts?: {
10
10
  componentName?: string;
@@ -2,6 +2,7 @@ import { MediaQueries, MediaQueryKey, MediaQueryObject, MediaQueryState, Tamagui
2
2
  export declare const mediaState: MediaQueryState;
3
3
  export declare const mediaQueryConfig: MediaQueries;
4
4
  export declare const getMedia: () => MediaQueryState;
5
+ export declare const mediaKeysWithAndWithout$: Set<string>;
5
6
  export declare const getInitialMediaState: () => MediaQueryState;
6
7
  export declare const getMediaKeyImportance: (key: string) => number;
7
8
  export declare const configureMedia: (config: TamaguiInternalConfig) => void;
@@ -11,5 +11,5 @@ export declare const useTheme: (props?: ThemeProps) => ThemeParsed;
11
11
  export declare const getThemeManager: (theme: any) => ThemeManager | undefined;
12
12
  export declare const getThemeIsNewTheme: (theme: any) => ThemeManager | undefined;
13
13
  export declare const activeThemeManagers: Set<ThemeManager>;
14
- export declare const useChangeThemeEffect: (props: ThemeProps, root?: boolean) => ChangedThemeResponse;
14
+ export declare const useChangeThemeEffect: (props: ThemeProps, root?: boolean, disableUpdate?: () => boolean) => ChangedThemeResponse;
15
15
  //# sourceMappingURL=useTheme.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { ThemeManager } from '../helpers/ThemeManager';
2
2
  import { ThemeProps } from '../types';
3
3
  export declare function Theme(props: ThemeProps): any;
4
- export declare function wrapThemeManagerContext(children: any, themeManager?: ThemeManager | null, shouldReset?: boolean): any;
4
+ export declare function useThemeManagerContext(children: any, themeManager?: ThemeManager | null, shouldReset?: boolean): any;
5
5
  //# sourceMappingURL=Theme.d.ts.map