@tamagui/core 1.0.1-beta.149 → 1.0.1-beta.150

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.
@@ -20,6 +20,7 @@ import React, {
20
20
  memo,
21
21
  useCallback,
22
22
  useContext,
23
+ useEffect,
23
24
  } from 'react'
24
25
  import { StyleSheet, Text, View, ViewStyle } from 'react-native'
25
26
 
@@ -32,12 +33,16 @@ import { assignNativePropsToWeb } from './helpers/assignNativePropsToWeb'
32
33
  import { getReturnVariablesAs } from './helpers/createPropMapper'
33
34
  import { createShallowUpdate } from './helpers/createShallowUpdate'
34
35
  import { extendStaticConfig, parseStaticConfig } from './helpers/extendStaticConfig'
35
- import { SplitStyleResult, insertSplitStyles, useSplitStyles } from './helpers/getSplitStyles'
36
+ import {
37
+ SplitStyleResult,
38
+ getSubStyle,
39
+ insertSplitStyles,
40
+ useSplitStyles,
41
+ } from './helpers/getSplitStyles'
36
42
  import { getAllSelectors } from './helpers/insertStyleRule'
37
43
  import { mergeProps } from './helpers/mergeProps'
38
44
  import { proxyThemeVariables } from './helpers/proxyThemeVariables'
39
- import { useFeatures } from './hooks/useFeatures'
40
- import { mediaState } from './hooks/useMedia'
45
+ import { addMediaQueryListener, mediaState, removeMediaQueryListener } from './hooks/useMedia'
41
46
  import { usePressable } from './hooks/usePressable'
42
47
  import { useServerRef, useServerState } from './hooks/useServerHooks'
43
48
  import { getThemeManager, useTheme } from './hooks/useTheme'
@@ -54,6 +59,7 @@ import {
54
59
  TamaguiElement,
55
60
  TamaguiInternalConfig,
56
61
  UseAnimationHook,
62
+ UseAnimationProps,
57
63
  } from './types'
58
64
  import { usePressability } from './vendor/Pressability'
59
65
  import { Slot, mergeEvent } from './views/Slot'
@@ -92,6 +98,7 @@ function mergeShorthands({ defaultProps }: StaticConfigParsed, { shorthands }: T
92
98
 
93
99
  let initialTheme: any
94
100
  let config: TamaguiInternalConfig
101
+ let mediaPropNames: string[] = []
95
102
 
96
103
  export function createComponent<
97
104
  ComponentPropTypes extends Object = {},
@@ -199,25 +206,114 @@ export function createComponent<
199
206
  }
200
207
  }
201
208
 
202
- const { viewProps: viewPropsIn, pseudos, medias, style, classNames } = splitStyles
209
+ const { viewProps: viewPropsIn, pseudos, medias, style, classNames, mediaKeys } = splitStyles
210
+
211
+ // media queries
212
+ useIsomorphicLayoutEffect(() => {
213
+ for (const key of mediaKeys) {
214
+ addMediaQueryListener(key, forceUpdate)
215
+ }
216
+ return () => {
217
+ for (const key of mediaKeys) {
218
+ removeMediaQueryListener(key, forceUpdate)
219
+ }
220
+ }
221
+ }, [mediaKeys.join(',')])
222
+
223
+ // animations
203
224
  const useAnimations = tamaguiConfig?.animations?.useAnimations as UseAnimationHook | undefined
204
225
  const isAnimated = !!(useAnimations && props.animation)
205
226
  const hasEnterStyle = !!props.enterStyle
206
-
207
227
  const animationFeatureStylesIn = props.animation ? { ...defaultNativeStyle, ...style } : null
228
+ const propsWithAnimation = props as UseAnimationProps
229
+
230
+ // lets make changing the key mandatory for adding/removing animations
231
+ // reason is animations are heavy no way around it, and must be run inline here (🙅 loading as a sub-component)
232
+ // because they need to sync update on first render. it's pretty rare too going to/from no animation => animation
233
+ // so requiring key change on animation change is least-bad option.
234
+ // plus, React use() may let us get away with conditional hooks soon :)
235
+ if (!isRSC && isAnimated) {
236
+ const animationState = useAnimations(propsWithAnimation, {
237
+ state,
238
+ pseudos,
239
+ onDidAnimate: props.onDidAnimate,
240
+ hostRef,
241
+ staticConfig,
242
+ getStyle({ isExiting, isEntering, exitVariant, enterVariant } = {}) {
243
+ // we have to merge such that transforms keys all exist
244
+ const style = animationFeatureStylesIn
245
+
246
+ const enterStyle = isEntering
247
+ ? enterVariant && staticConfig.variants?.[enterVariant]['true']
248
+ ? getSubStyle(
249
+ '',
250
+ staticConfig.variants?.[enterVariant]['true'],
251
+ staticConfig,
252
+ theme,
253
+ props,
254
+ state,
255
+ tamaguiConfig
256
+ ) || pseudos.enterStyle
257
+ : null || pseudos.enterStyle
258
+ : null
259
+
260
+ const exitStyle = isExiting
261
+ ? exitVariant && staticConfig.variants?.[exitVariant]['true']
262
+ ? getSubStyle(
263
+ '',
264
+ staticConfig.variants?.[exitVariant]['true'],
265
+ staticConfig,
266
+ theme,
267
+ props,
268
+ state,
269
+ tamaguiConfig
270
+ ) || pseudos.exitStyle
271
+ : null || pseudos.exitStyle
272
+ : null
273
+
274
+ // if you have hoverStyle={{ scale: 1.1 }} and don't have scale set on the base style
275
+ // no animation will run! this is really confusing. this will look at all variants,
276
+ // and fill in base styles if they don't exist. eg:
277
+ // input:
278
+ // base: {}
279
+ // hoverStyle: { scale: 2 }
280
+ // enterStyle: { x: 100 }
281
+ // output:
282
+ // base: { x: 0, scale: 1 }
283
+ ensureBaseHasDefaults(
284
+ style,
285
+ // enterStyle,
286
+ pseudos.hoverStyle,
287
+ pseudos.focusStyle,
288
+ pseudos.pressStyle,
289
+ pseudos.pressStyle
290
+ )
208
291
 
209
- const features = useFeatures(props, {
210
- forceUpdate,
211
- setStateShallow,
212
- useAnimations,
213
- state,
214
- style: animationFeatureStylesIn,
215
- pseudos,
216
- staticConfig,
217
- theme,
218
- hostRef,
219
- onDidAnimate: props.onDidAnimate,
220
- })
292
+ enterStyle && isEntering && merge(style, enterStyle)
293
+ state.hover && pseudos.hoverStyle && merge(style, pseudos.hoverStyle)
294
+ state.focus && pseudos.focusStyle && merge(style, pseudos.focusStyle)
295
+ state.press && pseudos.pressStyle && merge(style, pseudos.pressStyle)
296
+ exitStyle && isExiting && merge(style, exitStyle)
297
+
298
+ if (process.env.NODE_ENV === 'development') {
299
+ if (props['debug']) {
300
+ // eslint-disable-next-line no-console
301
+ console.log('animation style', style)
302
+ }
303
+ }
304
+
305
+ return style
306
+ },
307
+ //, delay
308
+ })
309
+
310
+ // must be properly memoized
311
+ if (animationState !== state.animation) {
312
+ setStateShallow({
313
+ animation: animationState,
314
+ })
315
+ }
316
+ }
221
317
 
222
318
  const {
223
319
  tag,
@@ -434,7 +530,27 @@ export function createComponent<
434
530
 
435
531
  // from react-native-web
436
532
  const platformMethodsRef = usePlatformMethods(viewProps)
437
- const setRef = useMergeRefs(hostRef, platformMethodsRef, forwardedRef as any)
533
+
534
+ const shouldSetMounted = Boolean(
535
+ isClient && (hasEnterStyle || props.animation) && !state.mounted
536
+ )
537
+
538
+ const setRef = useMergeRefs(
539
+ hostRef,
540
+ platformMethodsRef,
541
+ forwardedRef as any,
542
+ shouldSetMounted
543
+ ? () => {
544
+ // for some reason without some small delay it doesn't animate css
545
+ setTimeout(() => {
546
+ setStateShallow({
547
+ mounted: true,
548
+ })
549
+ }, 10)
550
+ }
551
+ : undefined
552
+ )
553
+
438
554
  viewProps.ref = setRef
439
555
  }
440
556
 
@@ -503,31 +619,14 @@ export function createComponent<
503
619
  })
504
620
  }
505
621
 
506
- // isMounted
507
- const internal = useServerRef<{ isMounted: boolean; unmountEffects?: Function[] }>()
508
- if (!internal.current) {
509
- internal.current = {
510
- isMounted: true,
511
- }
622
+ if (!isRSC) {
623
+ useEffect(() => {
624
+ return () => {
625
+ mouseUps.delete(unPress)
626
+ }
627
+ }, [])
512
628
  }
513
629
 
514
- useIsomorphicLayoutEffect(() => {
515
- // we need to use state to properly have mounted go from false => true
516
- if (isClient && (hasEnterStyle || props.animation) && !state.mounted) {
517
- // for SSR we never set mounted, ensuring enterStyle={{}} is set by default
518
- setStateShallow({
519
- mounted: true,
520
- })
521
- }
522
-
523
- internal.current!.isMounted = true
524
- return () => {
525
- mouseUps.delete(unPress)
526
- internal.current!.isMounted = false
527
- internal.current!.unmountEffects?.forEach((cb) => cb())
528
- }
529
- }, [hasEnterStyle, props.animation])
530
-
531
630
  let styles: any[]
532
631
 
533
632
  const animationStyles = state.animation ? state.animation.style : null
@@ -546,11 +645,13 @@ export function createComponent<
546
645
  ]
547
646
  if (!animationStyles && initialSplitStyles) {
548
647
  const initPseudos = initialSplitStyles.pseudos
549
- const force = shouldForcePseudo
550
648
  !state.mounted && addPseudoToStyles(styles, initPseudos, pseudos, 'enterStyle')
551
- state.hover && addPseudoToStyles(styles, initPseudos, pseudos, 'hoverStyle', force)
552
- state.focus && addPseudoToStyles(styles, initPseudos, pseudos, 'focusStyle', force)
553
- state.press && addPseudoToStyles(styles, initPseudos, pseudos, 'pressStyle', force)
649
+ state.hover &&
650
+ addPseudoToStyles(styles, initPseudos, pseudos, 'hoverStyle', shouldForcePseudo)
651
+ state.focus &&
652
+ addPseudoToStyles(styles, initPseudos, pseudos, 'focusStyle', shouldForcePseudo)
653
+ state.press &&
654
+ addPseudoToStyles(styles, initPseudos, pseudos, 'pressStyle', shouldForcePseudo)
554
655
  }
555
656
  // ugly but for now...
556
657
  if (shouldForcePseudo) {
@@ -648,7 +749,6 @@ export function createComponent<
648
749
  'onMouseLeave' in props)))
649
750
 
650
751
  const unPress = useCallback(() => {
651
- if (!internal.current!.isMounted) return
652
752
  setStateShallow({
653
753
  press: false,
654
754
  pressIn: false,
@@ -739,8 +839,9 @@ export function createComponent<
739
839
  : null
740
840
 
741
841
  let space = spaceProp
842
+
742
843
  // find space by media query
743
- if (features.enabled.mediaQuery) {
844
+ if (mediaKeys.length) {
744
845
  for (const key in mediaState) {
745
846
  if (!mediaState[key]) continue
746
847
  if (props[key] && props[key].space !== undefined) {
@@ -854,7 +955,9 @@ export function createComponent<
854
955
  // only necessary when animating because some AnimatedView which wraps RNW View doesn't forward dataSet className
855
956
  const isAnimatedRNWView = isAnimated && typeof elementType !== 'string' // assuming for now as reanimated is only driver
856
957
  const shouldWrapWithComponentTheme =
857
- isAnimatedRNWView && getReturnVariablesAs(props, splitStyleState) === 'non-color-value'
958
+ !!theme.className &&
959
+ isAnimatedRNWView &&
960
+ getReturnVariablesAs(props, splitStyleState) === 'non-color-value'
858
961
  const shouldWrapWithHover = Boolean(events || isRSC) && attachHover
859
962
 
860
963
  if (shouldWrapWithHover || shouldWrapWithComponentTheme) {
@@ -901,14 +1004,7 @@ export function createComponent<
901
1004
  }
902
1005
  }
903
1006
 
904
- return !isRSC && features.elements.length ? (
905
- <>
906
- {features.elements}
907
- {content}
908
- </>
909
- ) : (
910
- content
911
- )
1007
+ return content
912
1008
  })
913
1009
 
914
1010
  if (staticConfig.componentName) {
@@ -1335,3 +1431,69 @@ function mergeConfigDefaultProps(
1335
1431
  }
1336
1432
  return props
1337
1433
  }
1434
+
1435
+ const defaults = {
1436
+ left: 0,
1437
+ right: 0,
1438
+ top: 0,
1439
+ bottom: 0,
1440
+ opacity: 1,
1441
+ translateX: 0,
1442
+ translateY: 0,
1443
+ skew: 0,
1444
+ skewX: 0,
1445
+ skewY: 0,
1446
+ scale: 1,
1447
+ rotate: '0deg',
1448
+ rotateY: '0deg',
1449
+ rotateX: '0deg',
1450
+ }
1451
+
1452
+ function ensureBaseHasDefaults(base: ViewStyle, ...pseudos: (ViewStyle | null | undefined)[]) {
1453
+ for (const pseudo of pseudos) {
1454
+ if (!pseudo) continue
1455
+ for (const key in pseudo) {
1456
+ const val = pseudo[key]
1457
+ // can we just use merge() here?
1458
+ if (key === 'transform') {
1459
+ if (typeof val === 'string') {
1460
+ continue
1461
+ }
1462
+ for (const t of val) {
1463
+ const tkey = Object.keys(t)[0]
1464
+ const defaultVal = defaults[tkey]
1465
+ const tDefaultVal = { [tkey]: defaultVal } as any
1466
+ if (!base.transform) {
1467
+ base.transform = [tDefaultVal]
1468
+ } else {
1469
+ if (!base.transform.find((x) => x[tkey])) {
1470
+ base.transform.push(tDefaultVal)
1471
+ }
1472
+ }
1473
+ }
1474
+ } else {
1475
+ if (!(key in base)) {
1476
+ base[key] = defaults[key]
1477
+ }
1478
+ }
1479
+ }
1480
+ }
1481
+ }
1482
+
1483
+ function merge(base: ViewStyle, next: ViewStyle) {
1484
+ if (!next.transform || !base.transform) {
1485
+ Object.assign(base, next)
1486
+ return
1487
+ }
1488
+ const { transform, ...rest } = next
1489
+ Object.assign(base, rest)
1490
+ for (const t of transform) {
1491
+ const key = Object.keys(t)[0]
1492
+ const existing = base.transform.find((x) => key in x)
1493
+ if (existing) {
1494
+ existing[key] = t[key]
1495
+ } else {
1496
+ base.transform.push(t)
1497
+ }
1498
+ }
1499
+ }
@@ -78,6 +78,7 @@ type StyleSplitter = (
78
78
  rulesToInsert: RulesToInsert
79
79
  viewProps: StackProps
80
80
  fontFamily: string | undefined
81
+ mediaKeys: string[]
81
82
  }
82
83
 
83
84
  export const PROP_SPLIT = '-'
@@ -108,12 +109,14 @@ export const getSplitStyles: StyleSplitter = (
108
109
  ) => {
109
110
  conf = conf || getConfig()
110
111
  const validStyleProps = staticConfig.isText ? stylePropsText : validStyles
112
+ const mediaKeys: string[] = []
111
113
  const viewProps: StackProps = {}
112
114
  const pseudos: PseudoStyles = {}
113
115
  const medias: Record<MediaQueryKey, ViewStyle> = {}
114
116
  const usedKeys = new Set<string>()
115
117
  const propKeys = Object.keys(props)
116
- const shouldDoClasses = (isWeb || process.env.IS_STATIC === 'is_static') && !state.noClassNames
118
+ const shouldDoClasses =
119
+ (isWeb || process.env.IS_STATIC === 'is_static') && !state.noClassNames && !props.animation
117
120
  const len = propKeys.length
118
121
  const rulesToInsert: RulesToInsert = []
119
122
  const style: ViewStyle = {}
@@ -125,54 +128,7 @@ export const getSplitStyles: StyleSplitter = (
125
128
  let classNames: ClassNamesObject = {}
126
129
  // we need to gather these specific to each media query / pseudo
127
130
  // value is [hash, val], so ["-jnjad-asdnjk", "scaleX(1) rotate(10deg)"]
128
- let transforms = null as Record<TransformNamespaceKey, [string, string]> | null
129
-
130
- function mergeClassName(key: string, val: string, isMediaOrPseudo = false) {
131
- if (process.env.TAMAGUI_TARGET === 'web') {
132
- // empty classnames passed by compiler sometimes
133
- if (!val) return
134
- if (val.startsWith('_transform-')) {
135
- const namespace: TransformNamespaceKey = isMediaOrPseudo ? key : 'transform'
136
-
137
- let transform = insertedTransforms[val]
138
- if (isClient) {
139
- if (!transform) {
140
- // HMR or loaded a new chunk
141
- updateInserted()
142
- transform = insertedTransforms[val]
143
- if (process.env.NODE_ENV === 'development') {
144
- if (!transform) {
145
- // eslint-disable-next-line no-console
146
- console.warn('no transform found', { insertedTransforms, val })
147
- }
148
- }
149
- }
150
- if (!transform) {
151
- if (isWeb && val[0] !== '_') {
152
- // runtime insert
153
- transform = val
154
- }
155
- }
156
- if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
157
- // prettier-ignore
158
- // eslint-disable-next-line no-console
159
- console.log(' 🔹 getSplitStyles mergeClassName transform', { key, val, namespace, transform, insertedTransforms })
160
- }
161
- }
162
- transforms = transforms || {}
163
- transforms[namespace] = transforms[namespace] || ['', '']
164
- const identifier = val.replace('_transform', '')
165
- transforms[namespace][0] += identifier
166
- // ssr doesn't need to do anything just make the right classname
167
- if (transform) {
168
- transforms[namespace][1] += transform
169
- }
170
- } else {
171
- classNames[key] = val
172
- }
173
- }
174
- }
175
-
131
+ const transforms: Record<TransformNamespaceKey, [string, string]> = {}
176
132
  // fontFamily is our special baby, ensure we grab the latest set one always
177
133
  let fontFamily: string | undefined
178
134
 
@@ -237,14 +193,14 @@ export const getSplitStyles: StyleSplitter = (
237
193
  fullKey += `${PROP_SPLIT}${pseudoShortKey}`
238
194
  }
239
195
  usedKeys.add(fullKey)
240
- mergeClassName(fullKey, cn, isMediaOrPseudo)
196
+ mergeClassName(transforms, classNames, fullKey, cn, isMediaOrPseudo)
241
197
  } else {
242
198
  nonTamaguis += ' ' + cn
243
199
  }
244
200
  }
245
201
  if (nonTamaguis) {
246
202
  usedKeys.add(keyInit)
247
- mergeClassName(keyInit, nonTamaguis)
203
+ mergeClassName(transforms, classNames, keyInit, nonTamaguis)
248
204
  }
249
205
  continue
250
206
  }
@@ -259,7 +215,7 @@ export const getSplitStyles: StyleSplitter = (
259
215
  validStyles[keyInit.split(PROP_SPLIT)[0]]
260
216
  if (isValidClassName || isMediaOrPseudo) {
261
217
  usedKeys.add(keyInit)
262
- mergeClassName(keyInit, valInit, isMediaOrPseudo)
218
+ mergeClassName(transforms, classNames, keyInit, valInit, isMediaOrPseudo)
263
219
  continue
264
220
  }
265
221
  }
@@ -350,7 +306,7 @@ export const getSplitStyles: StyleSplitter = (
350
306
  continue
351
307
  }
352
308
  pseudos[key] = pseudos[key] || {}
353
- pseudos[key] = getSubStyle(
309
+ const pseudoStyleObject = getSubStyle(
354
310
  key,
355
311
  val,
356
312
  staticConfig,
@@ -361,15 +317,16 @@ export const getSplitStyles: StyleSplitter = (
361
317
  languageContext,
362
318
  true
363
319
  )
364
- if (shouldDoClasses) {
365
- const pseudoStyles = getAtomicStyle(pseudos[key], pseudoDescriptors[key])
320
+ pseudos[key] = pseudoStyleObject
321
+ if (shouldDoClasses && key !== 'enterStyle' && key !== 'exitStyle') {
322
+ const pseudoStyles = getAtomicStyle(pseudoStyleObject, pseudoDescriptors[key])
366
323
  for (const style of pseudoStyles) {
367
324
  const postfix = pseudoDescriptors[key]?.name || key // exitStyle/enterStyle dont have pseudoDescriptors
368
325
  const fullKey = `${style.property}${PROP_SPLIT}${postfix}`
369
326
  if (!usedKeys.has(fullKey)) {
370
327
  usedKeys.add(fullKey)
371
328
  addStyleToInsertRules(rulesToInsert, style)
372
- mergeClassName(fullKey, style.identifier, isMediaOrPseudo)
329
+ mergeClassName(transforms, classNames, fullKey, style.identifier, isMediaOrPseudo)
373
330
  }
374
331
  }
375
332
  }
@@ -379,6 +336,7 @@ export const getSplitStyles: StyleSplitter = (
379
336
  // media
380
337
  if (isMedia) {
381
338
  const mediaKey = key
339
+ mediaKeys.push(key)
382
340
  const mediaKeyShort = mediaKey.slice(1)
383
341
 
384
342
  if (!mediaQueryConfig[mediaKeyShort]) {
@@ -419,7 +377,7 @@ export const getSplitStyles: StyleSplitter = (
419
377
  if (!usedKeys.has(fullKey)) {
420
378
  usedKeys.add(fullKey)
421
379
  addStyleToInsertRules(rulesToInsert, out)
422
- mergeClassName(fullKey, out.identifier, true)
380
+ mergeClassName(transforms, classNames, fullKey, out.identifier, true)
423
381
  }
424
382
  }
425
383
  } else {
@@ -428,7 +386,14 @@ export const getSplitStyles: StyleSplitter = (
428
386
  // eslint-disable-next-line no-console
429
387
  console.log('apply media style', mediaKey, mediaState)
430
388
  }
431
- Object.assign(shouldDoClasses ? medias : style, mediaStyle)
389
+ if (shouldDoClasses) {
390
+ Object.assign(medias, mediaStyle)
391
+ } else {
392
+ for (const key in mediaStyle) {
393
+ usedKeys.add(key)
394
+ style[key] = mediaStyle[key]
395
+ }
396
+ }
432
397
  }
433
398
  }
434
399
  continue
@@ -447,13 +412,7 @@ export const getSplitStyles: StyleSplitter = (
447
412
  } else if (key in stylePropsTransform) {
448
413
  mergeTransform(style, key, val, true)
449
414
  } else {
450
- // if (key === 'pointerEvents') {
451
- // // pointer events can be box-none which requires css
452
- // addStyleToInsertRules(out.identifier, out.styleRule)
453
- // mergeClassName(fullKey, out.identifier, true)
454
- // } else {
455
415
  style[key] = normalizeValueWithProperty(val, key)
456
- // }
457
416
  }
458
417
  continue
459
418
  }
@@ -489,7 +448,7 @@ export const getSplitStyles: StyleSplitter = (
489
448
  // should probably have a few more exceptions here!
490
449
  if (key === 'pointerEvents' || !state.dynamicStylesInline) {
491
450
  addStyleToInsertRules(rulesToInsert, atomicStyle)
492
- mergeClassName(key, atomicStyle.identifier)
451
+ mergeClassName(transforms, classNames, key, atomicStyle.identifier)
493
452
  } else {
494
453
  style[key] = atomicStyle.value
495
454
  }
@@ -568,12 +527,58 @@ export const getSplitStyles: StyleSplitter = (
568
527
  viewProps,
569
528
  style,
570
529
  medias,
530
+ mediaKeys,
571
531
  pseudos,
572
532
  classNames,
573
533
  rulesToInsert,
574
534
  }
575
535
  }
576
536
 
537
+ function mergeClassName(
538
+ transforms: Record<string, any[]>,
539
+ classNames: Record<string, string>,
540
+ key: string,
541
+ val: string,
542
+ isMediaOrPseudo = false
543
+ ) {
544
+ if (process.env.TAMAGUI_TARGET === 'web') {
545
+ // empty classnames passed by compiler sometimes
546
+ if (!val) return
547
+ if (val.startsWith('_transform-')) {
548
+ const namespace: TransformNamespaceKey = isMediaOrPseudo ? key : 'transform'
549
+ let transform = insertedTransforms[val]
550
+ if (isClient) {
551
+ if (!transform) {
552
+ // HMR or loaded a new chunk
553
+ updateInserted()
554
+ transform = insertedTransforms[val]
555
+ if (process.env.NODE_ENV === 'development') {
556
+ if (!transform) {
557
+ // eslint-disable-next-line no-console
558
+ console.warn('no transform found', { insertedTransforms, val })
559
+ }
560
+ }
561
+ }
562
+ if (!transform) {
563
+ if (isWeb && val[0] !== '_') {
564
+ // runtime insert
565
+ transform = val
566
+ }
567
+ }
568
+ }
569
+ transforms[namespace] = transforms[namespace] || ['', '']
570
+ const identifier = val.replace('_transform', '')
571
+ transforms[namespace][0] += identifier
572
+ // ssr doesn't need to do anything just make the right classname
573
+ if (transform) {
574
+ transforms[namespace][1] += transform
575
+ }
576
+ } else {
577
+ classNames[key] = val
578
+ }
579
+ }
580
+ }
581
+
577
582
  /**
578
583
  * getSubStyle calls propMapper with props
579
584
  * those should be specific to the substyle, but fallback to the base props
@@ -4,10 +4,12 @@ export const mergeTransform = (obj: ViewStyle, key: string, val: any, backwards
4
4
  obj.transform ||= []
5
5
  obj.transform[backwards ? 'unshift' : 'push']({ [mapTransformKeys[key] || key]: val } as any)
6
6
  }
7
+
7
8
  const mapTransformKeys = {
8
9
  x: 'translateX',
9
10
  y: 'translateY',
10
11
  }
12
+
11
13
  export const invertMapTransformKeys = {
12
14
  translateX: 'x',
13
15
  translateY: 'y',
package/src/types.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  // TODO split this into own package @tamagui/types to share with animations packages
2
2
 
3
3
  import { Properties } from 'csstype'
4
- import React from 'react'
4
+ import React, { RefObject } from 'react'
5
5
  import {
6
6
  GestureResponderEvent,
7
7
  Image,
@@ -1242,6 +1242,7 @@ export type AnimationDriver<A extends AnimationConfig = AnimationConfig> = {
1242
1242
  export type UseAnimationProps = TamaguiComponentPropsBase & Record<string, any>
1243
1243
 
1244
1244
  export type UseAnimationHelpers = {
1245
+ hostRef: RefObject<HTMLElement | View>
1245
1246
  staticConfig: StaticConfigParsed
1246
1247
  getStyle: (props?: {
1247
1248
  isEntering?: boolean
@@ -1262,6 +1263,6 @@ export type UseAnimationHelpers = {
1262
1263
  export type UseAnimationHook = (
1263
1264
  props: UseAnimationProps,
1264
1265
  helpers: UseAnimationHelpers
1265
- ) => {
1266
+ ) => null | {
1266
1267
  style?: StackStylePropsBase | StackStylePropsBase[]
1267
1268
  }
@@ -15,6 +15,7 @@ declare type StyleSplitter = (props: {
15
15
  rulesToInsert: RulesToInsert;
16
16
  viewProps: StackProps;
17
17
  fontFamily: string | undefined;
18
+ mediaKeys: string[];
18
19
  };
19
20
  export declare const PROP_SPLIT = "-";
20
21
  export declare const pseudoCNInverse: {
@@ -16,7 +16,6 @@ declare type FeatureUtils = {
16
16
  export declare const useFeatures: (props: any, utils?: FeatureUtils) => {
17
17
  elements: JSX.Element[];
18
18
  enabled: {
19
- animation?: boolean | undefined;
20
19
  mediaQuery?: boolean | undefined;
21
20
  };
22
21
  };