@tamagui/core 1.0.1-beta.192 → 1.0.1-beta.194

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 (232) hide show
  1. package/dist/cjs/Tamagui.js +4 -2
  2. package/dist/cjs/Tamagui.js.map +2 -2
  3. package/dist/cjs/config.js +1 -2
  4. package/dist/cjs/config.js.map +2 -2
  5. package/dist/cjs/constants/constants.js +13 -8
  6. package/dist/cjs/constants/constants.js.map +2 -2
  7. package/dist/cjs/contexts/TextAncestorContext.js +3 -16
  8. package/dist/cjs/contexts/TextAncestorContext.js.map +3 -3
  9. package/dist/cjs/createComponent.js +196 -284
  10. package/dist/cjs/createComponent.js.map +2 -2
  11. package/dist/cjs/createTamagui.js +15 -16
  12. package/dist/cjs/createTamagui.js.map +3 -3
  13. package/dist/cjs/createVariable.js +1 -2
  14. package/dist/cjs/createVariable.js.map +2 -2
  15. package/dist/cjs/helpers/ThemeManager.js +7 -14
  16. package/dist/cjs/helpers/ThemeManager.js.map +2 -2
  17. package/dist/cjs/helpers/{createShallowUpdate.js → cache.js} +20 -21
  18. package/dist/cjs/helpers/cache.js.map +7 -0
  19. package/dist/cjs/helpers/createMediaStyle.js +14 -8
  20. package/dist/cjs/helpers/createMediaStyle.js.map +2 -2
  21. package/dist/cjs/helpers/extendStaticConfig.js +0 -1
  22. package/dist/cjs/helpers/extendStaticConfig.js.map +2 -2
  23. package/dist/cjs/helpers/getSplitStyles.js +214 -94
  24. package/dist/cjs/helpers/getSplitStyles.js.map +3 -3
  25. package/dist/cjs/helpers/getStylesAtomic.js +16 -36
  26. package/dist/cjs/helpers/getStylesAtomic.js.map +3 -3
  27. package/dist/cjs/helpers/getVariantExtras.js +2 -1
  28. package/dist/cjs/helpers/getVariantExtras.js.map +2 -2
  29. package/dist/cjs/helpers/insertStyleRule.js +14 -6
  30. package/dist/cjs/helpers/insertStyleRule.js.map +2 -2
  31. package/dist/cjs/helpers/matchMedia.js.map +2 -2
  32. package/dist/cjs/helpers/mergeTransform.js.map +1 -1
  33. package/dist/cjs/helpers/normalizeValueWithProperty.js +32 -2
  34. package/dist/cjs/helpers/normalizeValueWithProperty.js.map +2 -2
  35. package/dist/cjs/helpers/proxyThemeVariables.js +2 -7
  36. package/dist/cjs/helpers/proxyThemeVariables.js.map +2 -2
  37. package/dist/cjs/helpers/useShallowSetState.js.map +2 -2
  38. package/dist/cjs/hooks/getThemeUnwrapped.js +1 -3
  39. package/dist/cjs/hooks/getThemeUnwrapped.js.map +2 -2
  40. package/dist/cjs/hooks/useElementLayout.js +92 -0
  41. package/dist/cjs/hooks/useElementLayout.js.map +7 -0
  42. package/dist/cjs/hooks/useIsTouchDevice.js +2 -10
  43. package/dist/cjs/hooks/useIsTouchDevice.js.map +2 -2
  44. package/dist/cjs/hooks/useMedia.js +79 -85
  45. package/dist/cjs/hooks/useMedia.js.map +2 -2
  46. package/dist/cjs/hooks/useMedia.test.js +2 -0
  47. package/dist/cjs/{helpers/validate.js.map → hooks/useMedia.test.js.map} +0 -0
  48. package/dist/cjs/hooks/usePressablePressResponder.js +370 -0
  49. package/dist/cjs/hooks/usePressablePressResponder.js.map +7 -0
  50. package/dist/cjs/hooks/useSafeRef.js +13 -15
  51. package/dist/cjs/hooks/useSafeRef.js.map +2 -2
  52. package/dist/cjs/hooks/useTheme.js +5 -5
  53. package/dist/cjs/hooks/useTheme.js.map +2 -2
  54. package/dist/cjs/index.js +1 -17
  55. package/dist/cjs/index.js.map +2 -2
  56. package/dist/cjs/static.js +0 -20
  57. package/dist/cjs/static.js.map +2 -2
  58. package/dist/cjs/styled.js +4 -22
  59. package/dist/cjs/styled.js.map +2 -2
  60. package/dist/cjs/types.js.map +1 -1
  61. package/dist/cjs/{hooks/useIsSSR.js → views/AbsoluteFill.js} +18 -23
  62. package/dist/cjs/views/AbsoluteFill.js.map +7 -0
  63. package/dist/cjs/views/AbsoluteFill.native.js +45 -0
  64. package/dist/cjs/views/AbsoluteFill.native.js.map +7 -0
  65. package/dist/cjs/views/FontLanguage.native.js.map +2 -2
  66. package/dist/cjs/views/Stack.js.map +1 -1
  67. package/dist/cjs/views/TamaguiProvider.js +3 -2
  68. package/dist/cjs/views/TamaguiProvider.js.map +2 -2
  69. package/dist/cjs/views/Text.js.map +1 -1
  70. package/dist/cjs/views/ThemeReset.js +10 -10
  71. package/dist/cjs/views/ThemeReset.js.map +2 -2
  72. package/dist/esm/Tamagui.js +4 -2
  73. package/dist/esm/Tamagui.js.map +2 -2
  74. package/dist/esm/config.js +1 -2
  75. package/dist/esm/config.js.map +2 -2
  76. package/dist/esm/constants/constants.js +11 -7
  77. package/dist/esm/constants/constants.js.map +2 -2
  78. package/dist/esm/contexts/TextAncestorContext.js +2 -13
  79. package/dist/esm/contexts/TextAncestorContext.js.map +2 -2
  80. package/dist/esm/createComponent.js +195 -286
  81. package/dist/esm/createComponent.js.map +2 -2
  82. package/dist/esm/createTamagui.js +15 -16
  83. package/dist/esm/createTamagui.js.map +3 -3
  84. package/dist/esm/createVariable.js +1 -2
  85. package/dist/esm/createVariable.js.map +2 -2
  86. package/dist/esm/helpers/ThemeManager.js +7 -14
  87. package/dist/esm/helpers/ThemeManager.js.map +2 -2
  88. package/dist/esm/helpers/cache.js +18 -0
  89. package/dist/esm/helpers/cache.js.map +7 -0
  90. package/dist/esm/helpers/createMediaStyle.js +14 -8
  91. package/dist/esm/helpers/createMediaStyle.js.map +2 -2
  92. package/dist/esm/helpers/extendStaticConfig.js +0 -1
  93. package/dist/esm/helpers/extendStaticConfig.js.map +2 -2
  94. package/dist/esm/helpers/getSplitStyles.js +223 -95
  95. package/dist/esm/helpers/getSplitStyles.js.map +3 -3
  96. package/dist/esm/helpers/getStylesAtomic.js +13 -26
  97. package/dist/esm/helpers/getStylesAtomic.js.map +2 -2
  98. package/dist/esm/helpers/getVariantExtras.js +2 -1
  99. package/dist/esm/helpers/getVariantExtras.js.map +2 -2
  100. package/dist/esm/helpers/insertStyleRule.js +14 -6
  101. package/dist/esm/helpers/insertStyleRule.js.map +2 -2
  102. package/dist/esm/helpers/matchMedia.js.map +2 -2
  103. package/dist/esm/helpers/mergeTransform.js.map +1 -1
  104. package/dist/esm/helpers/normalizeValueWithProperty.js +30 -1
  105. package/dist/esm/helpers/normalizeValueWithProperty.js.map +2 -2
  106. package/dist/esm/helpers/proxyThemeVariables.js +2 -7
  107. package/dist/esm/helpers/proxyThemeVariables.js.map +2 -2
  108. package/dist/esm/helpers/useShallowSetState.js.map +2 -2
  109. package/dist/esm/hooks/getThemeUnwrapped.js +1 -3
  110. package/dist/esm/hooks/getThemeUnwrapped.js.map +2 -2
  111. package/dist/esm/hooks/useElementLayout.js +66 -0
  112. package/dist/esm/hooks/useElementLayout.js.map +7 -0
  113. package/dist/esm/hooks/useIsTouchDevice.js +3 -11
  114. package/dist/esm/hooks/useIsTouchDevice.js.map +2 -2
  115. package/dist/esm/hooks/useMedia.js +76 -84
  116. package/dist/esm/hooks/useMedia.js.map +2 -2
  117. package/dist/esm/hooks/useMedia.test.js +1 -0
  118. package/dist/esm/{helpers/validate.js.map → hooks/useMedia.test.js.map} +0 -0
  119. package/dist/esm/hooks/usePressablePressResponder.js +346 -0
  120. package/dist/esm/hooks/usePressablePressResponder.js.map +7 -0
  121. package/dist/esm/hooks/useSafeRef.js +13 -15
  122. package/dist/esm/hooks/useSafeRef.js.map +2 -2
  123. package/dist/esm/hooks/useTheme.js +5 -5
  124. package/dist/esm/hooks/useTheme.js.map +2 -2
  125. package/dist/esm/index.js +1 -14
  126. package/dist/esm/index.js.map +2 -2
  127. package/dist/esm/static.js +0 -14
  128. package/dist/esm/static.js.map +2 -2
  129. package/dist/esm/styled.js +4 -22
  130. package/dist/esm/styled.js.map +2 -2
  131. package/dist/esm/views/AbsoluteFill.js +17 -0
  132. package/dist/esm/views/AbsoluteFill.js.map +7 -0
  133. package/dist/esm/views/AbsoluteFill.native.js +21 -0
  134. package/dist/esm/views/AbsoluteFill.native.js.map +7 -0
  135. package/dist/esm/views/FontLanguage.native.js.map +2 -2
  136. package/dist/esm/views/Stack.js.map +1 -1
  137. package/dist/esm/views/TamaguiProvider.js +5 -4
  138. package/dist/esm/views/TamaguiProvider.js.map +2 -2
  139. package/dist/esm/views/Text.js.map +1 -1
  140. package/dist/esm/views/ThemeReset.js +10 -10
  141. package/dist/esm/views/ThemeReset.js.map +2 -2
  142. package/package.json +14 -14
  143. package/src/Tamagui.ts +7 -3
  144. package/src/config.ts +2 -3
  145. package/src/constants/constants.ts +10 -6
  146. package/src/contexts/TextAncestorContext.tsx +1 -8
  147. package/src/createComponent.tsx +250 -412
  148. package/src/createTamagui.ts +16 -19
  149. package/src/createVariable.ts +1 -3
  150. package/src/helpers/ThemeManager.tsx +7 -15
  151. package/src/helpers/cache.ts +18 -0
  152. package/src/helpers/createMediaStyle.ts +16 -9
  153. package/src/helpers/createProxy.ts +16 -0
  154. package/src/helpers/extendStaticConfig.ts +0 -1
  155. package/src/helpers/getSplitStyles.tsx +259 -152
  156. package/src/helpers/getStylesAtomic.ts +14 -34
  157. package/src/helpers/getVariantExtras.tsx +2 -1
  158. package/src/helpers/insertStyleRule.tsx +16 -8
  159. package/src/helpers/matchMedia.ts +1 -0
  160. package/src/helpers/mergeTransform.ts +1 -1
  161. package/src/helpers/normalizeValueWithProperty.ts +30 -0
  162. package/src/helpers/proxyThemeVariables.tsx +3 -8
  163. package/src/helpers/useShallowSetState.tsx +0 -1
  164. package/src/hooks/getThemeUnwrapped.tsx +1 -2
  165. package/src/hooks/useElementLayout.tsx +90 -0
  166. package/src/hooks/useIsTouchDevice.tsx +3 -17
  167. package/src/hooks/useMedia.tsx +82 -88
  168. package/src/hooks/useSafeRef.ts +17 -18
  169. package/src/hooks/useTheme.tsx +6 -5
  170. package/src/index.ts +1 -11
  171. package/src/static.ts +0 -10
  172. package/src/styled.tsx +15 -33
  173. package/src/types.tsx +58 -55
  174. package/src/views/FontLanguage.native.tsx +1 -0
  175. package/src/views/Stack.tsx +1 -1
  176. package/src/views/TamaguiProvider.tsx +5 -5
  177. package/src/views/Text.tsx +1 -1
  178. package/src/views/ThemeReset.tsx +11 -11
  179. package/types/Tamagui.d.ts +6 -4
  180. package/types/constants/constants.d.ts +14 -3
  181. package/types/contexts/TextAncestorContext.d.ts +1 -3
  182. package/types/createComponent.d.ts +0 -15
  183. package/types/helpers/ThemeManager.d.ts +2 -3
  184. package/types/helpers/cache.d.ts +12 -0
  185. package/types/helpers/calc.d.ts +0 -16
  186. package/types/helpers/createMediaStyle.d.ts +1 -1
  187. package/types/helpers/createProxy.d.ts +2 -0
  188. package/types/helpers/getSplitStyles.d.ts +6 -10
  189. package/types/helpers/getStylesAtomic.d.ts +1 -5
  190. package/types/helpers/insertStyleRule.d.ts +2 -2
  191. package/types/helpers/mergeTransform.d.ts +1 -1
  192. package/types/helpers/normalizeValueWithProperty.d.ts +1 -0
  193. package/types/hooks/useElementLayout.d.ts +27 -0
  194. package/types/hooks/useMedia.d.ts +3 -1
  195. package/types/hooks/useMedia.test.d.ts +1 -0
  196. package/types/hooks/usePressablePressResponder.d.ts +84 -0
  197. package/types/index.d.ts +1 -11
  198. package/types/static.d.ts +0 -1
  199. package/types/types.d.ts +42 -44
  200. package/types/views/AbsoluteFill.d.ts +5 -0
  201. package/types/views/AbsoluteFill.native.d.ts +3 -0
  202. package/types/views/Stack.d.ts +1 -1
  203. package/types/views/Text.d.ts +1 -1
  204. package/dist/cjs/constants/platform.js +0 -74
  205. package/dist/cjs/constants/platform.js.map +0 -7
  206. package/dist/cjs/constants/platform.native.js +0 -62
  207. package/dist/cjs/constants/platform.native.js.map +0 -7
  208. package/dist/cjs/helpers/createShallowUpdate.js.map +0 -7
  209. package/dist/cjs/helpers/validate.js +0 -2
  210. package/dist/cjs/hooks/useDidFinishSSR.js +0 -60
  211. package/dist/cjs/hooks/useDidFinishSSR.js.map +0 -7
  212. package/dist/cjs/hooks/useIsSSR.js.map +0 -7
  213. package/dist/esm/constants/platform.js +0 -39
  214. package/dist/esm/constants/platform.js.map +0 -7
  215. package/dist/esm/constants/platform.native.js +0 -28
  216. package/dist/esm/constants/platform.native.js.map +0 -7
  217. package/dist/esm/helpers/createShallowUpdate.js +0 -20
  218. package/dist/esm/helpers/createShallowUpdate.js.map +0 -7
  219. package/dist/esm/helpers/validate.js +0 -1
  220. package/dist/esm/hooks/useDidFinishSSR.js +0 -35
  221. package/dist/esm/hooks/useDidFinishSSR.js.map +0 -7
  222. package/dist/esm/hooks/useIsSSR.js +0 -22
  223. package/dist/esm/hooks/useIsSSR.js.map +0 -7
  224. package/src/helpers/assignNativePropsToWeb.tsx +0 -178
  225. package/src/helpers/calc.ts +0 -68
  226. package/src/helpers/getFont.tsx +0 -43
  227. package/src/helpers/getStackSize.ts +0 -22
  228. package/src/helpers/startTransition.ts +0 -4
  229. package/src/hooks/useConstant.tsx +0 -19
  230. package/src/hooks/useIsMounted.ts +0 -16
  231. package/src/hooks/usePressable.tsx +0 -9
  232. package/src/hooks/useUnmountEffect.ts +0 -5
@@ -4,11 +4,11 @@
4
4
  */
5
5
 
6
6
  import { StyleObject, simpleHash } from '@tamagui/helpers'
7
- import hyphenateStyleName from 'hyphenate-style-name'
8
- import type { TextStyle, ViewStyle } from 'react-native'
7
+ import type { TextStyle, ViewStyle } from '@tamagui/types-react-native'
9
8
 
10
9
  import { getConfig } from '../config'
11
10
  import { TamaguiInternalConfig } from '../types'
11
+ import { Style, cache } from './cache'
12
12
  import { defaultOffset } from './expandStyles'
13
13
  import { normalizeValueWithProperty } from './normalizeValueWithProperty.js'
14
14
  import { PseudoDescriptor, pseudoDescriptors } from './pseudoDescriptors'
@@ -166,31 +166,23 @@ const mapTransform = (transform) => {
166
166
  }
167
167
  }
168
168
 
169
- // cache
170
- type Value = Object | Array<any> | string | number
171
- export type Style = { [key: string]: Value }
172
- class Cache {
173
- values: Record<string, any> = {}
174
-
175
- get(key: string, valStr: string) {
176
- return this.values[key]?.[valStr]
177
- }
178
-
179
- set(key: string, valStr: string, object: any) {
180
- this.values[key] ??= {}
181
- this.values[key][valStr] = object
182
- }
183
- }
184
- const cache = new Cache()
185
-
186
169
  function createDeclarationBlock(style: Style, important = false) {
187
- let declarations = ''
170
+ let next = ''
188
171
  for (const key in style) {
189
172
  const prop = hyphenateStyleName(key)
190
173
  const value = style[key]
191
- declarations += `${prop}:${value}${important ? ` !important` : ''};`
174
+ next += `${prop}:${value}${important ? ` !important` : ''};`
192
175
  }
193
- return `{${declarations}}`
176
+ return `{${next}}`
177
+ }
178
+
179
+ const hcache = {}
180
+ const toHyphenLower = (match: string) => `-${match.toLowerCase()}`
181
+ const hyphenateStyleName = (key: string) => {
182
+ let val = hcache[key]
183
+ if (val) return val
184
+ hcache[key] = val = key.replace(/[A-Z]/g, toHyphenLower)
185
+ return val
194
186
  }
195
187
 
196
188
  const pseudoSelectorPrefixes = (() => {
@@ -239,7 +231,6 @@ function createAtomicRules(
239
231
  }
240
232
 
241
233
  // Polyfill for additional 'pointer-events' values
242
- // See d13f78622b233a0afc0c7a200c0a0792c8ca9e58
243
234
  case 'pointerEvents': {
244
235
  let finalValue = value
245
236
  if (value === 'auto' || value === 'box-only') {
@@ -258,17 +249,6 @@ function createAtomicRules(
258
249
  break
259
250
  }
260
251
 
261
- // Polyfill for draft spec
262
- // https://drafts.csswg.org/css-scrollbars-1/
263
- case 'scrollbarWidth': {
264
- if (value === 'none') {
265
- rules.push(`${selector}::-webkit-scrollbar{display:none}`)
266
- }
267
- const block = createDeclarationBlock({ scrollbarWidth: value }, important)
268
- rules.push(`${selector}${block}`)
269
- break
270
- }
271
-
272
252
  default: {
273
253
  const block = createDeclarationBlock({ [property]: value }, important)
274
254
  rules.push(`${selector}${block}`)
@@ -1,6 +1,7 @@
1
1
  import { getConfig } from '../config'
2
2
  import { GenericFonts } from '../types'
3
3
  import { LanguageContextType } from '../views/FontLanguage.types'
4
+ import { createProxy } from './createProxy'
4
5
 
5
6
  const extrasCache = new WeakMap()
6
7
 
@@ -30,7 +31,7 @@ export function getVariantExtras(
30
31
  // we avoid passing in default props for media queries because that would confuse things like SizableText.size:
31
32
  props: avoidDefaultProps
32
33
  ? props
33
- : new Proxy(props, {
34
+ : createProxy(props, {
34
35
  // handles shorthands
35
36
  get(target, key) {
36
37
  const shorthand = conf.inverseShorthands[key as any]
@@ -4,8 +4,8 @@
4
4
 
5
5
  import type { StyleObject } from '@tamagui/helpers'
6
6
 
7
- const allSelectors = {}
8
- const allRules = {}
7
+ const allSelectors: Record<string, string> = {}
8
+ const allRules: Record<string, boolean | string[]> = {}
9
9
  export const insertedTransforms = {}
10
10
 
11
11
  export const getAllSelectors = () => allSelectors
@@ -84,7 +84,7 @@ export function updateInserted() {
84
84
  const isTransform = identifier.startsWith('_transform')
85
85
  const shouldInsert = isTransform ? addTransform(identifier, cssRule.cssText, cssRule) : true
86
86
  if (shouldInsert) {
87
- allSelectors[identifier] = process.env.NODE_ENV === 'development' ? cssRule.cssText : true
87
+ allSelectors[identifier] = cssRule.cssText
88
88
  }
89
89
  }
90
90
  }
@@ -134,13 +134,21 @@ export type PartialStyleObject = Pick<StyleObject, 'identifier' | 'property' | '
134
134
  export type RulesToInsert = PartialStyleObject[]
135
135
 
136
136
  export function insertStyleRules(rulesToInsert: RulesToInsert) {
137
- // // pointerevents can have more than one, others could to needs some refactor
137
+ if (isClient && !sheet) {
138
+ if (process.env.NODE_ENV === 'development') {
139
+ // eslint-disable-next-line no-console
140
+ console.warn(`Missing sheet`)
141
+ }
142
+ return
143
+ }
138
144
  for (const { identifier, rules } of rulesToInsert) {
139
- if (allSelectors[identifier] || !sheet) return
140
- allSelectors[identifier] = process.env.NODE_ENV === 'development' ? rules : true
145
+ if (allSelectors[identifier]) continue
146
+ allSelectors[identifier] = process.env.NODE_ENV === 'development' ? rules.join('\n') : ''
141
147
  updateRules(identifier, rules)
142
- for (const rule of rules) {
143
- sheet.insertRule(rule, sheet.cssRules.length)
148
+ if (sheet) {
149
+ for (const rule of rules) {
150
+ sheet.insertRule(rule, sheet.cssRules.length)
151
+ }
144
152
  }
145
153
  }
146
154
  }
@@ -6,6 +6,7 @@ function matchMediaFallback(query: string): MediaQueryList {
6
6
  !process.env.IS_STATIC &&
7
7
  process.env.TAMAGUI_TARGET !== 'web'
8
8
  ) {
9
+ // eslint-disable-next-line no-console
9
10
  console.log('warning: matchMedia not loading! Native not picking up .native.js files?')
10
11
  }
11
12
  return {
@@ -1,4 +1,4 @@
1
- import type { ViewStyle } from 'react-native'
1
+ import type { ViewStyle } from '@tamagui/types-react-native'
2
2
 
3
3
  export const mergeTransform = (obj: ViewStyle, key: string, val: any, backwards = false) => {
4
4
  obj.transform ||= []
@@ -3,11 +3,15 @@
3
3
  * Copyright (c) Nicolas Gallagher licensed under the MIT license.
4
4
  */
5
5
 
6
+ import { getAllSelectors } from './insertStyleRule'
6
7
  import { normalizeColor } from './normalizeColor'
7
8
 
8
9
  const cache = {}
9
10
 
10
11
  export function normalizeValueWithProperty(value: any, property?: string): any {
12
+ if (property && unitlessNumbers[property]) {
13
+ return value
14
+ }
11
15
  const cached = cache[value]
12
16
  if (cached) {
13
17
  return cached
@@ -78,3 +82,29 @@ const unitlessNumbers = {
78
82
  scaleZ: true,
79
83
  shadowOpacity: true,
80
84
  }
85
+
86
+ // getting real values for colors for animations (reverse mapped from CSS)
87
+ // this isn't beautiful, but will do relatively fine performance for now
88
+ const rcache = {}
89
+ export function reverseMapClassNameToValue(key: string, className: string) {
90
+ const selectors = getAllSelectors()
91
+ const cssRule = selectors[className]
92
+ if (rcache[cssRule]) return rcache[cssRule]
93
+ if (!cssRule) {
94
+ return
95
+ }
96
+ const cssVal = cssRule.replace(/.*:/, '').replace(/;.*/, '')
97
+ let res: any
98
+ if (unitlessNumbers[key]) {
99
+ res = +cssVal
100
+ } else if (cssVal.endsWith('px')) {
101
+ res = +cssVal.replace('px', '')
102
+ } else {
103
+ res = cssVal
104
+ }
105
+ if (typeof res === 'string') {
106
+ res = res.trim()
107
+ }
108
+ rcache[cssRule] = res
109
+ return res
110
+ }
@@ -1,12 +1,7 @@
1
+ import { createProxy } from './createProxy'
2
+
1
3
  export function proxyThemeVariables<A extends Object>(obj: A): A {
2
- if (process.env.NODE_ENV === 'development') {
3
- if (!obj || typeof obj !== 'object') {
4
- // eslint-disable-next-line no-console
5
- console.trace(`Empty value for proxyThemeVariables, broken config`, obj)
6
- return obj
7
- }
8
- }
9
- return new Proxy<A>(obj || {}, {
4
+ return createProxy<A>(obj || {}, {
10
5
  has(target, key) {
11
6
  return Reflect.has(target, removeStarting$(key))
12
7
  },
@@ -3,7 +3,6 @@ import React, { useCallback } from 'react'
3
3
  export function useShallowSetState<State extends Object>(
4
4
  setter: React.Dispatch<React.SetStateAction<State>>
5
5
  ) {
6
- // eslint-disable-next-line react-hooks/rules-of-hooks
7
6
  return useCallback(
8
7
  (next: Partial<State>) => {
9
8
  setter((prev) => {
@@ -1,8 +1,7 @@
1
1
  export const getThemeUnwrapped = (theme: any) => {
2
2
  if (!theme) return null
3
3
  const first = theme[GetThemeUnwrapped]
4
- if (first) return first[GetThemeUnwrapped] || first
5
- return theme
4
+ return first?.[GetThemeUnwrapped] || first || theme
6
5
  }
7
6
 
8
7
  export const GetThemeUnwrapped = Symbol()
@@ -0,0 +1,90 @@
1
+ import { useIsomorphicLayoutEffect } from '@tamagui/constants'
2
+ import { getBoundingClientRect } from '@tamagui/react-native-use-responder-events'
3
+ import { useEvent } from '@tamagui/use-event'
4
+ import { RefObject } from 'react'
5
+
6
+ const LayoutHandlers = new WeakMap<Element, Function>()
7
+
8
+ export type LayoutValue = {
9
+ x: number
10
+ y: number
11
+ width: number
12
+ height: number
13
+ left: number
14
+ top: number
15
+ }
16
+
17
+ export type LayoutEvent = {
18
+ nativeEvent: {
19
+ layout: LayoutValue
20
+ target: any
21
+ }
22
+ timeStamp: number
23
+ }
24
+
25
+ let resizeObserver: ResizeObserver | null = null
26
+
27
+ if (typeof window !== 'undefined' && 'ResizeObserver' in window) {
28
+ resizeObserver = new ResizeObserver((entries) => {
29
+ for (const { target } of entries) {
30
+ const onLayout = LayoutHandlers.get(target)
31
+ if (typeof onLayout !== 'function') return
32
+ measureLayout(target as HTMLElement, null, (x, y, width, height, left, top) => {
33
+ onLayout({
34
+ nativeEvent: {
35
+ layout: { x, y, width, height, left, top },
36
+ target,
37
+ },
38
+ timeStamp: Date.now(),
39
+ })
40
+ })
41
+ }
42
+ })
43
+ }
44
+
45
+ export const getRect = (node: HTMLElement) => {
46
+ const rect = getBoundingClientRect(node)
47
+ if (!rect) return
48
+ const { x, y, top, left } = rect
49
+ return { x, y, width: node.offsetWidth, height: node.offsetHeight, top, left }
50
+ }
51
+
52
+ export const measureLayout = (
53
+ node: HTMLElement,
54
+ relativeTo: HTMLElement | null,
55
+ callback: Function
56
+ ) => {
57
+ const relativeNode = relativeTo || node?.parentNode
58
+ if (relativeNode instanceof HTMLElement) {
59
+ setTimeout(() => {
60
+ const relativeRect = getBoundingClientRect(relativeNode)!
61
+ const { height, left, top, width } = getRect(node)!
62
+ const x = left - relativeRect.left
63
+ const y = top - relativeRect.top
64
+ callback(x, y, width, height, left, top)
65
+ }, 0)
66
+ }
67
+ }
68
+
69
+ const idFn = () => {}
70
+
71
+ export function useElementLayout(
72
+ ref: RefObject<Element>,
73
+ onLayout?: ((e: LayoutEvent) => void) | null
74
+ ) {
75
+ const hasLayoutEvent = !!onLayout
76
+ const onLayoutEvent = useEvent(onLayout || idFn)
77
+
78
+ // Observing is done in a separate effect to avoid this effect running
79
+ // when 'onLayout' changes.
80
+ useIsomorphicLayoutEffect(() => {
81
+ if (!resizeObserver || !hasLayoutEvent) return
82
+ const node = ref.current
83
+ if (!node) return
84
+ LayoutHandlers.set(node, onLayoutEvent)
85
+ resizeObserver.observe(node)
86
+ return () => {
87
+ resizeObserver?.unobserve(node)
88
+ }
89
+ }, [ref, hasLayoutEvent])
90
+ }
@@ -1,20 +1,6 @@
1
- import { isServer, isTouchable, isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import { useState } from 'react'
3
-
4
- // ssr friendly
1
+ import { isTouchable, isWeb } from '@tamagui/constants'
2
+ import { useDidFinishSSR } from '@tamagui/use-did-finish-ssr'
5
3
 
6
4
  export const useIsTouchDevice = () => {
7
- const [touchOnly, setTouchOnly] = useState(false)
8
-
9
- if (isWeb && !isServer) {
10
- // only ever false on web SSR (env), so run effect conditionally
11
- // eslint-disable-next-line react-hooks/rules-of-hooks
12
- useIsomorphicLayoutEffect(() => {
13
- if (isTouchable) {
14
- setTouchOnly(true)
15
- }
16
- }, [])
17
- }
18
-
19
- return touchOnly
5
+ return !isWeb ? true : useDidFinishSSR() ? isTouchable : false
20
6
  }
@@ -1,7 +1,8 @@
1
1
  import { useIsomorphicLayoutEffect } from '@tamagui/constants'
2
- import { startTransition, useEffect, useMemo, useState } from 'react'
2
+ import { MutableRefObject, useEffect, useMemo, useState } from 'react'
3
3
 
4
4
  import { getConfig } from '../config'
5
+ import { createProxy } from '../helpers/createProxy'
5
6
  import { matchMedia } from '../helpers/matchMedia'
6
7
  import {
7
8
  MediaQueries,
@@ -15,7 +16,7 @@ import { useSafeRef } from './useSafeRef'
15
16
  export const mediaState: MediaQueryState =
16
17
  // development time safeguard
17
18
  process.env.NODE_ENV === 'development'
18
- ? new Proxy(
19
+ ? createProxy(
19
20
  {},
20
21
  {
21
22
  get(target, key) {
@@ -30,20 +31,20 @@ export const mediaState: MediaQueryState =
30
31
 
31
32
  type MediaListener = (next: boolean) => void
32
33
 
33
- const mediaQueryListeners: { [key: string]: Set<MediaListener> } = {}
34
+ const listeners: { [key: string]: Set<MediaListener> } = {}
34
35
 
35
36
  export function addMediaQueryListener(key: MediaQueryKey, cb: MediaListener) {
36
37
  if (process.env.NODE_ENV === 'development' && key[0] === '$') {
37
38
  // eslint-disable-next-line no-console
38
39
  console.warn(`Warning, listening to media queries shouldn't use the "$" prefix`)
39
40
  }
40
- mediaQueryListeners[key] = mediaQueryListeners[key] || new Set()
41
- mediaQueryListeners[key].add(cb)
41
+ listeners[key] = listeners[key] || new Set()
42
+ listeners[key].add(cb)
42
43
  return () => removeMediaQueryListener(key, cb)
43
44
  }
44
45
 
45
46
  export function removeMediaQueryListener(key: MediaQueryKey, cb: MediaListener) {
46
- mediaQueryListeners[key]?.delete(cb)
47
+ listeners[key]?.delete(cb)
47
48
  }
48
49
 
49
50
  export const mediaQueryConfig: MediaQueries = {}
@@ -53,16 +54,17 @@ export const getMedia = () => {
53
54
  }
54
55
 
55
56
  // for SSR capture it at time of startup
56
- let currentMediaState: MediaQueryState | null
57
- let initialMediaState: MediaQueryState | null
58
-
57
+ let curState: MediaQueryState
58
+ let initState: MediaQueryState
59
59
  export const getInitialMediaState = () => {
60
- if (getConfig().disableSSR) {
61
- return currentMediaState || {}
62
- }
63
- return initialMediaState || {}
60
+ return (getConfig().disableSSR ? curState : initState) || {}
64
61
  }
65
62
 
63
+ let mediaKeysOrdered: string[]
64
+ export const getMediaKeyImportance = (key: string) =>
65
+ // + 2 because we set base usedKeys=1 in getSplitStyles and all media go above 1
66
+ mediaKeysOrdered.indexOf(key[0] === '$' ? key.slice(1) : key) + 2
67
+
66
68
  const dispose = new Set<Function>()
67
69
 
68
70
  export const configureMedia = (config: TamaguiInternalConfig) => {
@@ -72,8 +74,9 @@ export const configureMedia = (config: TamaguiInternalConfig) => {
72
74
  mediaState[key] = mediaQueryDefaultActive?.[key] || false
73
75
  }
74
76
  Object.assign(mediaQueryConfig, media)
75
- currentMediaState = { ...mediaState }
76
- initialMediaState = currentMediaState
77
+ curState = { ...mediaState }
78
+ initState = curState
79
+ mediaKeysOrdered = Object.keys(media)
77
80
  if (config.disableSSR) {
78
81
  setupMediaListeners()
79
82
  }
@@ -101,40 +104,34 @@ function setupMediaListeners() {
101
104
  // hmr, undo existing before re-binding
102
105
  unlisten()
103
106
 
104
- // theoretically this should make media reactions smoother but potentially delayed?
105
- // but i believe consistent, so that's a nicer default (we'll see)
106
- startTransition(() => {
107
- for (const key in mediaQueryConfig) {
108
- const str = mediaObjectToString(mediaQueryConfig[key])
109
- const getMatch = () => matchMedia(str)
110
- const match = getMatch()
111
- if (!match) {
112
- throw new Error('⚠️ No match')
113
- }
114
- // react native needs these deprecated apis for now
115
- match.addListener(update)
116
- dispose.add(() => match.removeListener(update))
107
+ for (const key in mediaQueryConfig) {
108
+ const str = mediaObjectToString(mediaQueryConfig[key])
109
+ const getMatch = () => matchMedia(str)
110
+ const match = getMatch()
111
+ if (!match) {
112
+ throw new Error('⚠️ No match')
113
+ }
114
+ // react native needs these deprecated apis for now
115
+ match.addListener(update)
116
+ dispose.add(() => match.removeListener(update))
117
117
 
118
- update()
118
+ update()
119
119
 
120
- function update() {
121
- const next = !!getMatch().matches
122
- if (next === mediaState[key]) return
123
- mediaState[key] = next
124
- currentMediaState = { ...mediaState }
125
- const listeners = mediaQueryListeners[key]
126
- if (listeners?.size) {
127
- listeners.forEach((cb) => cb(next))
128
- }
120
+ function update() {
121
+ const next = !!getMatch().matches
122
+ if (next === mediaState[key]) return
123
+ mediaState[key] = next
124
+ curState = { ...mediaState }
125
+ const cur = listeners[key]
126
+ if (cur?.size) {
127
+ cur.forEach((cb) => cb(next))
129
128
  }
130
129
  }
131
- })
130
+ }
132
131
  }
133
132
 
134
- export function useMediaQueryListeners(config: TamaguiInternalConfig) {
135
- if (config.disableSSR) {
136
- return
137
- }
133
+ export function useMediaListeners(config: TamaguiInternalConfig) {
134
+ if (config.disableSSR) return
138
135
  useEffect(() => {
139
136
  setupMediaListeners()
140
137
  return unlisten
@@ -144,14 +141,14 @@ export function useMediaQueryListeners(config: TamaguiInternalConfig) {
144
141
  export function useMedia(): {
145
142
  [key in MediaQueryKey]: boolean
146
143
  } {
147
- const [state, setState] = useState<MediaQueryState>(initialMediaState || {})
148
144
  const keys = useSafeRef({} as Record<string, boolean>)
145
+ const [state, setState] = useState<MediaQueryState>(() => getMediaState(initState || {}, keys))
149
146
 
150
147
  function updateState() {
151
148
  setState((prev) => {
152
149
  for (const key in keys.current) {
153
150
  if (prev[key] !== mediaState[key]) {
154
- return { ...mediaState }
151
+ return getMediaState({ ...mediaState }, keys)
155
152
  }
156
153
  }
157
154
  return prev
@@ -159,34 +156,27 @@ export function useMedia(): {
159
156
  }
160
157
 
161
158
  useIsomorphicLayoutEffect(() => {
162
- const disposes: Function[] = Object.keys(keys.current).map((key) =>
159
+ updateState()
160
+ const disposers = Object.keys(keys.current).map((key) =>
163
161
  addMediaQueryListener(key, updateState)
164
162
  )
165
- updateState()
166
163
  return () => {
167
- disposes.forEach((cb) => cb())
164
+ disposers.forEach((cb) => cb())
168
165
  }
169
- })
166
+ }, [keys.current])
170
167
 
171
- return useMemo(
172
- () =>
173
- new Proxy(state, {
174
- get(_, key: string) {
175
- if (process.env.NODE_ENV === 'development') {
176
- if (!currentMediaState) {
177
- throw new Error(
178
- `To use useMedia() you must pass in media configuration to createTamagui, none was found.`
179
- )
180
- }
181
- }
182
- if (!keys.current[key]) {
183
- keys.current[key] = true
184
- }
185
- return Reflect.get(state, key)
186
- },
187
- }),
188
- [state, keys]
189
- )
168
+ return state
169
+ }
170
+
171
+ function getMediaState(state: MediaQueryState, keys: MutableRefObject<Record<string, boolean>>) {
172
+ return new Proxy(state, {
173
+ get(_, key: string) {
174
+ if (!keys.current[key]) {
175
+ keys.current[key] = true
176
+ }
177
+ return Reflect.get(state, key)
178
+ },
179
+ })
190
180
  }
191
181
 
192
182
  /**
@@ -205,44 +195,48 @@ export function useMediaPropsActive<A extends Object>(
205
195
  const media = useMedia()
206
196
 
207
197
  return useMemo(() => {
208
- const evaluated = {} as A
209
- const lastSetImportanceForKey = {}
198
+ const next = {} as A
199
+ const importancesUsed = {}
210
200
  const propNames = Object.keys(props)
211
- const mediaKeysOrdered = Object.keys(getMedia())
212
-
213
- function update(k: string, v: any, importance = 0) {
214
- if (lastSetImportanceForKey[k] > importance) {
215
- return
216
- }
217
- lastSetImportanceForKey[k] = importance
218
- evaluated[k] = v
219
- }
220
201
 
221
202
  for (let i = propNames.length - 1; i >= 0; i--) {
222
203
  const key = propNames[i]
223
204
  const val = props[key]
224
205
  if (key[0] === '$') {
225
- const shortKey = key.slice(1)
226
- if (!media[shortKey]) {
206
+ if (!media[key.slice(1)]) {
227
207
  continue
228
208
  }
229
209
  if (val && typeof val === 'object') {
230
- const subPropNames = Object.keys(val)
231
- const mediaImportance = mediaKeysOrdered.indexOf(shortKey)
232
- for (let j = subPropNames.length; j--; j >= 0) {
233
- const skey = subPropNames[j]
234
- update(skey, val[skey], mediaImportance)
210
+ const subKeys = Object.keys(val)
211
+ for (let j = subKeys.length; j--; j >= 0) {
212
+ const subKey = subKeys[j]
213
+ mergeMediaByImportance(next, subKey, val[subKey], importancesUsed)
235
214
  }
236
215
  }
237
216
  } else {
238
- update(key, val)
217
+ mergeMediaByImportance(next, key, val, importancesUsed)
239
218
  }
240
219
  }
241
220
 
242
- return evaluated
221
+ return next
243
222
  }, [media, props])
244
223
  }
245
224
 
225
+ export function mergeMediaByImportance(
226
+ onto: Record<string, any>,
227
+ key: string,
228
+ value: any,
229
+ importancesUsed: Record<string, number>
230
+ ) {
231
+ const importance = getMediaKeyImportance(key)
232
+ if (importancesUsed[key] > importance) {
233
+ return false
234
+ }
235
+ importancesUsed[key] = importance
236
+ onto[key] = value
237
+ return true
238
+ }
239
+
246
240
  function camelToHyphen(str: string) {
247
241
  return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`).toLowerCase()
248
242
  }