@tamagui/web 1.123.17 → 1.124.0

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 (246) hide show
  1. package/dist/cjs/Tamagui.cjs +1 -6
  2. package/dist/cjs/Tamagui.js +1 -5
  3. package/dist/cjs/Tamagui.js.map +1 -1
  4. package/dist/cjs/Tamagui.native.js +2 -8
  5. package/dist/cjs/Tamagui.native.js.map +2 -2
  6. package/dist/cjs/createComponent.cjs +9 -13
  7. package/dist/cjs/createComponent.js +10 -8
  8. package/dist/cjs/createComponent.js.map +1 -1
  9. package/dist/cjs/createComponent.native.js +17 -19
  10. package/dist/cjs/createComponent.native.js.map +2 -2
  11. package/dist/cjs/helpers/createShallowSetState.cjs +7 -4
  12. package/dist/cjs/helpers/createShallowSetState.js +12 -6
  13. package/dist/cjs/helpers/createShallowSetState.js.map +1 -1
  14. package/dist/cjs/helpers/createShallowSetState.native.js +10 -4
  15. package/dist/cjs/helpers/createShallowSetState.native.js.map +2 -2
  16. package/dist/cjs/hooks/{shouldDeoptDueToParentScheme.cjs → doesRootSchemeMatchSystem.cjs} +5 -5
  17. package/dist/cjs/hooks/{shouldDeoptDueToParentScheme.js → doesRootSchemeMatchSystem.js} +6 -6
  18. package/dist/cjs/hooks/doesRootSchemeMatchSystem.js.map +6 -0
  19. package/dist/cjs/hooks/doesRootSchemeMatchSystem.native.js +30 -0
  20. package/dist/cjs/hooks/doesRootSchemeMatchSystem.native.js.map +6 -0
  21. package/dist/cjs/hooks/getThemeProxied.cjs +58 -0
  22. package/dist/cjs/hooks/getThemeProxied.js +53 -0
  23. package/dist/cjs/hooks/getThemeProxied.js.map +6 -0
  24. package/dist/cjs/hooks/getThemeProxied.native.js +76 -0
  25. package/dist/cjs/hooks/getThemeProxied.native.js.map +6 -0
  26. package/dist/cjs/hooks/useComponentState.cjs +16 -16
  27. package/dist/cjs/hooks/useComponentState.js +22 -15
  28. package/dist/cjs/hooks/useComponentState.js.map +1 -1
  29. package/dist/cjs/hooks/useComponentState.native.js +15 -15
  30. package/dist/cjs/hooks/useComponentState.native.js.map +2 -2
  31. package/dist/cjs/hooks/useProps.cjs +2 -2
  32. package/dist/cjs/hooks/useProps.js +2 -2
  33. package/dist/cjs/hooks/useProps.js.map +1 -1
  34. package/dist/cjs/hooks/useProps.native.js +2 -2
  35. package/dist/cjs/hooks/useProps.native.js.map +2 -2
  36. package/dist/cjs/hooks/useTheme.cjs +10 -208
  37. package/dist/cjs/hooks/useTheme.js +7 -207
  38. package/dist/cjs/hooks/useTheme.js.map +2 -2
  39. package/dist/cjs/hooks/useTheme.native.js +8 -252
  40. package/dist/cjs/hooks/useTheme.native.js.map +2 -2
  41. package/dist/cjs/hooks/useThemeName.cjs +15 -27
  42. package/dist/cjs/hooks/useThemeName.js +6 -20
  43. package/dist/cjs/hooks/useThemeName.js.map +2 -2
  44. package/dist/cjs/hooks/useThemeName.native.js +16 -22
  45. package/dist/cjs/hooks/useThemeName.native.js.map +2 -2
  46. package/dist/cjs/hooks/useThemeState.cjs +187 -0
  47. package/dist/cjs/hooks/useThemeState.js +159 -0
  48. package/dist/cjs/hooks/useThemeState.js.map +6 -0
  49. package/dist/cjs/hooks/useThemeState.native.js +206 -0
  50. package/dist/cjs/hooks/useThemeState.native.js.map +6 -0
  51. package/dist/cjs/index.cjs +2 -0
  52. package/dist/cjs/index.js +2 -0
  53. package/dist/cjs/index.js.map +1 -1
  54. package/dist/cjs/index.native.js +3 -0
  55. package/dist/cjs/index.native.js.map +1 -1
  56. package/dist/cjs/views/TamaguiProvider.js.map +1 -1
  57. package/dist/cjs/views/TamaguiProvider.native.js.map +1 -1
  58. package/dist/cjs/views/Theme.cjs +81 -85
  59. package/dist/cjs/views/Theme.js +44 -51
  60. package/dist/cjs/views/Theme.js.map +2 -2
  61. package/dist/cjs/views/Theme.native.js +58 -58
  62. package/dist/cjs/views/Theme.native.js.map +2 -2
  63. package/dist/cjs/views/ThemeDebug.cjs +21 -43
  64. package/dist/cjs/views/ThemeDebug.js +18 -38
  65. package/dist/cjs/views/ThemeDebug.js.map +2 -2
  66. package/dist/cjs/views/ThemeProvider.cjs +1 -1
  67. package/dist/cjs/views/ThemeProvider.js +1 -1
  68. package/dist/cjs/views/ThemeProvider.js.map +1 -1
  69. package/dist/cjs/views/ThemeProvider.native.js +1 -1
  70. package/dist/cjs/views/ThemeProvider.native.js.map +2 -2
  71. package/dist/esm/Tamagui.js +0 -5
  72. package/dist/esm/Tamagui.js.map +1 -1
  73. package/dist/esm/Tamagui.mjs +0 -5
  74. package/dist/esm/Tamagui.mjs.map +1 -1
  75. package/dist/esm/Tamagui.native.js +1 -8
  76. package/dist/esm/Tamagui.native.js.map +2 -2
  77. package/dist/esm/createComponent.js +9 -8
  78. package/dist/esm/createComponent.js.map +1 -1
  79. package/dist/esm/createComponent.mjs +9 -13
  80. package/dist/esm/createComponent.mjs.map +1 -1
  81. package/dist/esm/createComponent.native.js +16 -19
  82. package/dist/esm/createComponent.native.js.map +2 -2
  83. package/dist/esm/helpers/createShallowSetState.js +13 -7
  84. package/dist/esm/helpers/createShallowSetState.js.map +1 -1
  85. package/dist/esm/helpers/createShallowSetState.mjs +8 -5
  86. package/dist/esm/helpers/createShallowSetState.mjs.map +1 -1
  87. package/dist/esm/helpers/createShallowSetState.native.js +11 -5
  88. package/dist/esm/helpers/createShallowSetState.native.js.map +2 -2
  89. package/dist/esm/hooks/doesRootSchemeMatchSystem.js +7 -0
  90. package/dist/esm/hooks/doesRootSchemeMatchSystem.js.map +6 -0
  91. package/dist/esm/hooks/doesRootSchemeMatchSystem.mjs +5 -0
  92. package/dist/esm/hooks/doesRootSchemeMatchSystem.mjs.map +1 -0
  93. package/dist/esm/hooks/doesRootSchemeMatchSystem.native.js +10 -0
  94. package/dist/esm/hooks/doesRootSchemeMatchSystem.native.js.map +6 -0
  95. package/dist/esm/hooks/getThemeProxied.js +40 -0
  96. package/dist/esm/hooks/getThemeProxied.js.map +6 -0
  97. package/dist/esm/hooks/getThemeProxied.mjs +35 -0
  98. package/dist/esm/hooks/getThemeProxied.mjs.map +1 -0
  99. package/dist/esm/hooks/getThemeProxied.native.js +59 -0
  100. package/dist/esm/hooks/getThemeProxied.native.js.map +6 -0
  101. package/dist/esm/hooks/useComponentState.js +22 -15
  102. package/dist/esm/hooks/useComponentState.js.map +1 -1
  103. package/dist/esm/hooks/useComponentState.mjs +16 -16
  104. package/dist/esm/hooks/useComponentState.mjs.map +1 -1
  105. package/dist/esm/hooks/useComponentState.native.js +15 -15
  106. package/dist/esm/hooks/useComponentState.native.js.map +2 -2
  107. package/dist/esm/hooks/useProps.js +2 -2
  108. package/dist/esm/hooks/useProps.js.map +1 -1
  109. package/dist/esm/hooks/useProps.mjs +2 -2
  110. package/dist/esm/hooks/useProps.mjs.map +1 -1
  111. package/dist/esm/hooks/useProps.native.js +2 -2
  112. package/dist/esm/hooks/useProps.native.js.map +2 -2
  113. package/dist/esm/hooks/useTheme.js +9 -220
  114. package/dist/esm/hooks/useTheme.js.map +2 -2
  115. package/dist/esm/hooks/useTheme.mjs +10 -205
  116. package/dist/esm/hooks/useTheme.mjs.map +1 -1
  117. package/dist/esm/hooks/useTheme.native.js +11 -257
  118. package/dist/esm/hooks/useTheme.native.js.map +2 -2
  119. package/dist/esm/hooks/useThemeName.js +4 -12
  120. package/dist/esm/hooks/useThemeName.js.map +2 -2
  121. package/dist/esm/hooks/useThemeName.mjs +11 -12
  122. package/dist/esm/hooks/useThemeName.mjs.map +1 -1
  123. package/dist/esm/hooks/useThemeName.native.js +15 -14
  124. package/dist/esm/hooks/useThemeName.native.js.map +2 -2
  125. package/dist/esm/hooks/useThemeState.js +151 -0
  126. package/dist/esm/hooks/useThemeState.js.map +6 -0
  127. package/dist/esm/hooks/useThemeState.mjs +159 -0
  128. package/dist/esm/hooks/useThemeState.mjs.map +1 -0
  129. package/dist/esm/hooks/useThemeState.native.js +182 -0
  130. package/dist/esm/hooks/useThemeState.native.js.map +6 -0
  131. package/dist/esm/index.js +2 -0
  132. package/dist/esm/index.js.map +1 -1
  133. package/dist/esm/index.mjs +2 -1
  134. package/dist/esm/index.mjs.map +1 -1
  135. package/dist/esm/index.native.js +2 -0
  136. package/dist/esm/index.native.js.map +2 -2
  137. package/dist/esm/views/TamaguiProvider.js.map +1 -1
  138. package/dist/esm/views/TamaguiProvider.mjs.map +1 -1
  139. package/dist/esm/views/TamaguiProvider.native.js.map +1 -1
  140. package/dist/esm/views/Theme.js +57 -54
  141. package/dist/esm/views/Theme.js.map +2 -2
  142. package/dist/esm/views/Theme.mjs +81 -85
  143. package/dist/esm/views/Theme.mjs.map +1 -1
  144. package/dist/esm/views/Theme.native.js +61 -61
  145. package/dist/esm/views/Theme.native.js.map +2 -2
  146. package/dist/esm/views/ThemeDebug.js +16 -29
  147. package/dist/esm/views/ThemeDebug.js.map +1 -1
  148. package/dist/esm/views/ThemeDebug.mjs +16 -27
  149. package/dist/esm/views/ThemeDebug.mjs.map +1 -1
  150. package/dist/esm/views/ThemeProvider.js +2 -2
  151. package/dist/esm/views/ThemeProvider.js.map +1 -1
  152. package/dist/esm/views/ThemeProvider.mjs +2 -2
  153. package/dist/esm/views/ThemeProvider.mjs.map +1 -1
  154. package/dist/esm/views/ThemeProvider.native.js +2 -2
  155. package/dist/esm/views/ThemeProvider.native.js.map +2 -2
  156. package/package.json +11 -11
  157. package/src/Tamagui.ts +0 -6
  158. package/src/createComponent.tsx +19 -28
  159. package/src/helpers/createShallowSetState.tsx +17 -8
  160. package/src/hooks/doesRootSchemeMatchSystem.native.ts +7 -0
  161. package/src/hooks/doesRootSchemeMatchSystem.ts +4 -0
  162. package/src/hooks/getThemeProxied.ts +165 -0
  163. package/src/hooks/useComponentState.ts +26 -21
  164. package/src/hooks/useProps.tsx +2 -2
  165. package/src/hooks/useTheme.tsx +22 -633
  166. package/src/hooks/useThemeName.tsx +6 -18
  167. package/src/hooks/useThemeState.ts +341 -0
  168. package/src/index.ts +1 -0
  169. package/src/types.tsx +2 -2
  170. package/src/views/TamaguiProvider.tsx +1 -1
  171. package/src/views/Theme.tsx +129 -132
  172. package/src/views/ThemeDebug.native.tsx +2 -2
  173. package/src/views/ThemeDebug.tsx +17 -39
  174. package/src/views/ThemeProvider.tsx +2 -2
  175. package/types/Tamagui.d.ts +0 -2
  176. package/types/Tamagui.d.ts.map +1 -1
  177. package/types/createComponent.d.ts.map +1 -1
  178. package/types/helpers/createShallowSetState.d.ts +1 -1
  179. package/types/helpers/createShallowSetState.d.ts.map +1 -1
  180. package/types/hooks/doesRootSchemeMatchSystem.d.ts +2 -0
  181. package/types/hooks/doesRootSchemeMatchSystem.d.ts.map +1 -0
  182. package/types/hooks/doesRootSchemeMatchSystem.native.d.ts +2 -0
  183. package/types/hooks/doesRootSchemeMatchSystem.native.d.ts.map +1 -0
  184. package/types/hooks/getThemeProxied.d.ts +25 -0
  185. package/types/hooks/getThemeProxied.d.ts.map +1 -0
  186. package/types/hooks/useComponentState.d.ts.map +1 -1
  187. package/types/hooks/useConfiguration.d.ts.map +1 -1
  188. package/types/hooks/useTheme.d.ts +9 -36
  189. package/types/hooks/useTheme.d.ts.map +1 -1
  190. package/types/hooks/useThemeName.d.ts +1 -3
  191. package/types/hooks/useThemeName.d.ts.map +1 -1
  192. package/types/hooks/useThemeState.d.ts +22 -0
  193. package/types/hooks/useThemeState.d.ts.map +1 -0
  194. package/types/index.d.ts +1 -0
  195. package/types/index.d.ts.map +1 -1
  196. package/types/types.d.ts +2 -2
  197. package/types/types.d.ts.map +1 -1
  198. package/types/views/Theme.d.ts +3 -3
  199. package/types/views/Theme.d.ts.map +1 -1
  200. package/types/views/ThemeDebug.d.ts +2 -2
  201. package/types/views/ThemeDebug.d.ts.map +1 -1
  202. package/types/views/ThemeDebug.native.d.ts +2 -2
  203. package/types/views/ThemeDebug.native.d.ts.map +1 -1
  204. package/dist/cjs/helpers/ThemeManager.cjs +0 -202
  205. package/dist/cjs/helpers/ThemeManager.js +0 -183
  206. package/dist/cjs/helpers/ThemeManager.js.map +0 -6
  207. package/dist/cjs/helpers/ThemeManager.native.js +0 -282
  208. package/dist/cjs/helpers/ThemeManager.native.js.map +0 -6
  209. package/dist/cjs/helpers/ThemeManagerContext.cjs +0 -38
  210. package/dist/cjs/helpers/ThemeManagerContext.js +0 -30
  211. package/dist/cjs/helpers/ThemeManagerContext.js.map +0 -6
  212. package/dist/cjs/helpers/ThemeManagerContext.native.js +0 -34
  213. package/dist/cjs/helpers/ThemeManagerContext.native.js.map +0 -6
  214. package/dist/cjs/hooks/shouldDeoptDueToParentScheme.js.map +0 -6
  215. package/dist/cjs/hooks/shouldDeoptDueToParentScheme.native.js +0 -50
  216. package/dist/cjs/hooks/shouldDeoptDueToParentScheme.native.js.map +0 -6
  217. package/dist/esm/helpers/ThemeManager.js +0 -169
  218. package/dist/esm/helpers/ThemeManager.js.map +0 -6
  219. package/dist/esm/helpers/ThemeManager.mjs +0 -177
  220. package/dist/esm/helpers/ThemeManager.mjs.map +0 -1
  221. package/dist/esm/helpers/ThemeManager.native.js +0 -261
  222. package/dist/esm/helpers/ThemeManager.native.js.map +0 -6
  223. package/dist/esm/helpers/ThemeManagerContext.js +0 -6
  224. package/dist/esm/helpers/ThemeManagerContext.js.map +0 -6
  225. package/dist/esm/helpers/ThemeManagerContext.mjs +0 -4
  226. package/dist/esm/helpers/ThemeManagerContext.mjs.map +0 -1
  227. package/dist/esm/helpers/ThemeManagerContext.native.js +0 -6
  228. package/dist/esm/helpers/ThemeManagerContext.native.js.map +0 -6
  229. package/dist/esm/hooks/shouldDeoptDueToParentScheme.js +0 -7
  230. package/dist/esm/hooks/shouldDeoptDueToParentScheme.js.map +0 -6
  231. package/dist/esm/hooks/shouldDeoptDueToParentScheme.mjs +0 -5
  232. package/dist/esm/hooks/shouldDeoptDueToParentScheme.mjs.map +0 -1
  233. package/dist/esm/hooks/shouldDeoptDueToParentScheme.native.js +0 -29
  234. package/dist/esm/hooks/shouldDeoptDueToParentScheme.native.js.map +0 -6
  235. package/src/helpers/ThemeManager.tsx +0 -405
  236. package/src/helpers/ThemeManagerContext.tsx +0 -4
  237. package/src/hooks/shouldDeoptDueToParentScheme.native.tsx +0 -37
  238. package/src/hooks/shouldDeoptDueToParentScheme.tsx +0 -6
  239. package/types/helpers/ThemeManager.d.ts +0 -45
  240. package/types/helpers/ThemeManager.d.ts.map +0 -1
  241. package/types/helpers/ThemeManagerContext.d.ts +0 -4
  242. package/types/helpers/ThemeManagerContext.d.ts.map +0 -1
  243. package/types/hooks/shouldDeoptDueToParentScheme.d.ts +0 -3
  244. package/types/hooks/shouldDeoptDueToParentScheme.d.ts.map +0 -1
  245. package/types/hooks/shouldDeoptDueToParentScheme.native.d.ts +0 -3
  246. package/types/hooks/shouldDeoptDueToParentScheme.native.d.ts.map +0 -1
@@ -0,0 +1,341 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useId,
6
+ useLayoutEffect,
7
+ useSyncExternalStore,
8
+ type MutableRefObject,
9
+ } from 'react'
10
+ import { getConfig } from '../config'
11
+ import type { ThemeParsed, ThemeProps, UseThemeWithStateProps } from '../types'
12
+
13
+ type ID = string
14
+
15
+ export type ThemeState = {
16
+ id: ID
17
+ name: string
18
+ theme: ThemeParsed
19
+ inverses: number
20
+ parentName?: string
21
+ isInverse?: boolean
22
+ isNew?: boolean
23
+ parentId?: ID
24
+ scheme?: 'light' | 'dark'
25
+ }
26
+
27
+ export const ThemeStateContext = createContext<ID>('')
28
+
29
+ const allListeners = new Map<ID, Function>()
30
+ const listenersByParent: Record<ID, Set<ID>> = {}
31
+
32
+ // TODO this will gain memory over time but its not going to be a ton
33
+ const states: Map<ID, ThemeState | undefined> = new Map()
34
+
35
+ export const forceUpdateThemes = () => {
36
+ allListeners.forEach((cb) => cb())
37
+ }
38
+
39
+ export const getThemeState = (id: ID) => states.get(id)
40
+
41
+ let rootThemeState: ThemeState | null = null
42
+ export const getRootThemeState = () => rootThemeState
43
+
44
+ const HasRenderedOnce = new WeakMap<any, boolean>()
45
+
46
+ export const useThemeState = (
47
+ props: UseThemeWithStateProps,
48
+ isRoot = false,
49
+ keys: MutableRefObject<Set<string> | null>
50
+ ): ThemeState => {
51
+ const { disable } = props
52
+ const parentId = useContext(ThemeStateContext)
53
+
54
+ if (disable) {
55
+ return (
56
+ states.get(parentId) || {
57
+ id: '',
58
+ name: 'light',
59
+ theme: getConfig().themes.light,
60
+ inverses: 0,
61
+ }
62
+ )
63
+ }
64
+
65
+ const id = useId()
66
+ const subscribe = useCallback(
67
+ (cb: Function) => {
68
+ listenersByParent[parentId] ||= new Set()
69
+ listenersByParent[parentId].add(id)
70
+ allListeners.set(id, cb)
71
+ return () => {
72
+ allListeners.delete(id)
73
+ listenersByParent[parentId].delete(id)
74
+ }
75
+ },
76
+ [id, parentId, keys]
77
+ )
78
+
79
+ const propsKey = getPropsKey(props)
80
+
81
+ const getSnapshot = () => {
82
+ return getSnapshotFrom(props, propsKey, isRoot, id, parentId, keys)
83
+ }
84
+
85
+ if (process.env.NODE_ENV === 'development' && globalThis.time)
86
+ globalThis.time`theme-prep-uses`
87
+
88
+ const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
89
+
90
+ useLayoutEffect(() => {
91
+ if (!propsKey) return
92
+ if (!HasRenderedOnce.has(keys)) {
93
+ HasRenderedOnce.set(keys, true)
94
+ return
95
+ }
96
+ if (
97
+ process.env.NODE_ENV === 'development' &&
98
+ props.debug &&
99
+ props.debug !== 'profile'
100
+ ) {
101
+ console.warn(` · useTheme(${id}) scheduleUpdate`, propsKey, states.get(id)?.name)
102
+ }
103
+ scheduleUpdate(id)
104
+ }, [keys, propsKey])
105
+
106
+ if (process.env.NODE_ENV === 'development' && props.debug) {
107
+ console.groupCollapsed(
108
+ ` · useTheme(${id}) =>`,
109
+ state.name,
110
+ id === state.id ? '🎉' : '⏭️'
111
+ )
112
+ console.info({
113
+ state,
114
+ parentId,
115
+ props,
116
+ propsKey,
117
+ id,
118
+ parentState: states.get(parentId),
119
+ })
120
+ console.groupEnd()
121
+ }
122
+
123
+ return state.id === id ? { ...state, isNew: true } : state
124
+ }
125
+
126
+ // const cache = new Map<string, ThemeState>()
127
+ let themes: Record<string, ThemeParsed> | null = null
128
+
129
+ const getSnapshotFrom = (
130
+ props: UseThemeWithStateProps,
131
+ propsKey: string,
132
+ isRoot = false,
133
+ id: string,
134
+ parentId: string,
135
+ keys: MutableRefObject<Set<string> | null> | undefined
136
+ ): ThemeState => {
137
+ const needsUpdate = keys?.current?.size || props.needsUpdate?.()
138
+ const parentState = states.get(parentId)
139
+
140
+ // const cacheKey = `${id}${propsKey}${needsUpdate}${parentState?.name || ''}${isRoot}`
141
+ // if (cache.has(cacheKey)) {
142
+ // return cache.get(cacheKey)!
143
+ // }
144
+
145
+ if (!themes) {
146
+ themes = getConfig().themes
147
+ }
148
+
149
+ const lastState = states.get(id)
150
+
151
+ const name = !propsKey ? null : getNewThemeName(parentState?.name, props, !!needsUpdate)
152
+
153
+ if (
154
+ process.env.NODE_ENV === 'development' &&
155
+ props.debug &&
156
+ props.debug !== 'profile'
157
+ ) {
158
+ const message = ` · useTheme(${id}) snapshot ${name}, parent ${parentState?.id} needsUpdate ${needsUpdate}`
159
+ if (process.env.TAMAGUI_TARGET === 'native') {
160
+ console.info(message)
161
+ } else {
162
+ console.groupCollapsed(message)
163
+ console.info({ lastState, parentState, props, propsKey, id, keys })
164
+ console.groupEnd()
165
+ }
166
+ }
167
+
168
+ const isSameAsParent = !name && propsKey // name = null if matching parent and has props
169
+ if (parentState && isSameAsParent) {
170
+ return parentState
171
+ }
172
+
173
+ if (!name) {
174
+ if (lastState && !needsUpdate) {
175
+ return lastState
176
+ }
177
+ states.set(id, parentState)
178
+ // cache.set(cacheKey, parentState!)
179
+ return parentState!
180
+ }
181
+
182
+ if (
183
+ lastState &&
184
+ lastState.name === name
185
+ // &&
186
+ // (!parentState || parentState.name === lastState.parentName)
187
+ ) {
188
+ // cache.set(cacheKey, lastState!)
189
+ return lastState
190
+ }
191
+
192
+ const scheme = getScheme(name)
193
+ const parentInverses = parentState?.inverses ?? 0
194
+ const isInverse = parentState && scheme !== parentState.scheme
195
+ const inverses = parentInverses + (isInverse ? 1 : 0)
196
+
197
+ const nextState = {
198
+ id,
199
+ name,
200
+ theme: themes[name],
201
+ scheme,
202
+ parentId,
203
+ parentName: parentState?.name,
204
+ inverses,
205
+ isInverse,
206
+ } satisfies ThemeState
207
+
208
+ if (
209
+ process.env.NODE_ENV === 'development' &&
210
+ props.debug &&
211
+ props.debug !== 'profile'
212
+ ) {
213
+ console.groupCollapsed(` · useTheme(${id}) ⏭️ ${name}`)
214
+ console.info('state', nextState)
215
+ console.groupEnd()
216
+ }
217
+
218
+ states.set(id, nextState)
219
+ // cache.set(cacheKey, nextState)
220
+
221
+ if (isRoot) {
222
+ rootThemeState = nextState
223
+ }
224
+
225
+ return nextState
226
+ }
227
+
228
+ function scheduleUpdate(id: string) {
229
+ const queue = [id]
230
+ const visited = new Set<string>()
231
+
232
+ while (queue.length) {
233
+ const parent = queue.shift()!
234
+ const children = listenersByParent[parent]
235
+ if (children) {
236
+ for (const childId of children) {
237
+ if (!visited.has(childId)) {
238
+ visited.add(childId)
239
+ queue.push(childId)
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ visited.forEach((childId) => {
246
+ const cb = allListeners.get(childId)
247
+ cb?.()
248
+ })
249
+ }
250
+
251
+ const validSchemes = {
252
+ light: 'light',
253
+ dark: 'dark',
254
+ } as const
255
+
256
+ function getScheme(name: string) {
257
+ return validSchemes[name.split('_')[0]]
258
+ }
259
+
260
+ function getNewThemeName(
261
+ parentName = '',
262
+ { name, reset, componentName, inverse }: UseThemeWithStateProps,
263
+ forceUpdate = false
264
+ ): string | null {
265
+ if (name && reset) {
266
+ throw new Error(
267
+ process.env.NODE_ENV === 'production'
268
+ ? `❌004`
269
+ : 'Cannot reset and set a new name at the same time.'
270
+ )
271
+ }
272
+
273
+ if (reset) {
274
+ if (!parentName) throw new Error(`‼️`)
275
+ const lastPartIndex = parentName.lastIndexOf('_')
276
+ return lastPartIndex <= 0 ? parentName : parentName.slice(lastPartIndex)
277
+ }
278
+
279
+ const { themes } = getConfig()
280
+ const parentParts = parentName.split('_')
281
+
282
+ // always remove component theme if it exists, we never sub a component theme
283
+ const lastName = parentParts[parentParts.length - 1]
284
+ if (lastName && lastName[0].toLowerCase() !== lastName[0]) {
285
+ parentParts.pop()
286
+ }
287
+
288
+ const subNames = [
289
+ name && componentName ? `${name}_${componentName}` : undefined,
290
+ name,
291
+ componentName,
292
+ ].filter(Boolean) as string[]
293
+
294
+ let found: string | null = null
295
+
296
+ const max = parentParts.length
297
+
298
+ for (let i = 0; i <= max; i++) {
299
+ const base = (i === 0 ? parentParts : parentParts.slice(0, -i)).join('_')
300
+
301
+ for (const subName of subNames) {
302
+ const potential = base ? `${base}_${subName}` : subName
303
+
304
+ if (potential in themes) {
305
+ found = potential
306
+ break
307
+ }
308
+ }
309
+
310
+ if (found) break
311
+ }
312
+
313
+ if (found && inverse) {
314
+ const scheme = found.split('_')[0]
315
+ found = found.replace(new RegExp(`^${scheme}`), scheme === 'light' ? 'dark' : 'light')
316
+ }
317
+
318
+ if (
319
+ !forceUpdate &&
320
+ found === parentName &&
321
+ // if its a scheme only sub-theme, we always consider it "new" because it likely inverses
322
+ // and we want to avoid reparenting
323
+ !validSchemes[found]
324
+ ) {
325
+ return null
326
+ }
327
+
328
+ return found
329
+ }
330
+
331
+ const getPropsKey = ({
332
+ name,
333
+ reset,
334
+ inverse,
335
+ forceClassName,
336
+ componentName,
337
+ }: ThemeProps) =>
338
+ `${name || ''}${inverse || ''}${reset || ''}${forceClassName || ''}${componentName || ''}`
339
+
340
+ export const hasThemeUpdatingProps = (props: ThemeProps) =>
341
+ 'inverse' in props || 'name' in props || 'reset' in props || 'forceClassName' in props
package/src/index.ts CHANGED
@@ -77,6 +77,7 @@ export {
77
77
  mediaQueryConfig,
78
78
  } from './hooks/useMedia'
79
79
  export * from './hooks/useTheme'
80
+ export { forceUpdateThemes } from './hooks/useThemeState'
80
81
  export * from './hooks/useThemeName'
81
82
  export * from './hooks/useConfiguration'
82
83
  export * from './hooks/useIsTouchDevice'
package/src/types.tsx CHANGED
@@ -421,7 +421,7 @@ export interface ThemeProps {
421
421
  componentName?: string
422
422
  children?: any
423
423
  reset?: boolean
424
- debug?: DebugProp | any
424
+ debug?: DebugProp
425
425
  inverse?: boolean
426
426
  // on the web, for portals we need to re-insert className
427
427
  forceClassName?: boolean
@@ -2520,7 +2520,7 @@ export type TamaguiComponentStateRef = {
2520
2520
  hasMeasured?: boolean
2521
2521
  hasAnimated?: boolean
2522
2522
  themeShallow?: boolean
2523
- hasEverThemed?: boolean
2523
+ hasEverThemed?: boolean | 'wrapped'
2524
2524
  isListeningToTheme?: boolean
2525
2525
  unPress?: Function
2526
2526
  group?: {
@@ -1,5 +1,5 @@
1
1
  import { isClient, isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import React from 'react'
2
+ import React, { useId } from 'react'
3
3
  import { getSetting } from '../config'
4
4
  import { ComponentContext } from '../contexts/ComponentContext'
5
5
  import type { TamaguiProviderProps } from '../types'