@tamagui/core 1.0.1-beta.214 → 1.0.1-beta.215

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 (46) hide show
  1. package/dist/cjs/constants/isDevTools.js +9 -1
  2. package/dist/cjs/constants/isDevTools.js.map +2 -2
  3. package/dist/cjs/createComponent.js +71 -152
  4. package/dist/cjs/createComponent.js.map +3 -3
  5. package/dist/cjs/helpers/getSplitStyles.js +74 -47
  6. package/dist/cjs/helpers/getSplitStyles.js.map +2 -2
  7. package/dist/cjs/helpers/getStylesAtomic.js +5 -1
  8. package/dist/cjs/helpers/getStylesAtomic.js.map +2 -2
  9. package/dist/cjs/helpers/normalizeValueWithProperty.js +9 -5
  10. package/dist/cjs/helpers/normalizeValueWithProperty.js.map +2 -2
  11. package/dist/cjs/helpers/pseudoDescriptors.js +10 -0
  12. package/dist/cjs/helpers/pseudoDescriptors.js.map +2 -2
  13. package/dist/cjs/helpers/useShallowSetState.js +1 -1
  14. package/dist/cjs/helpers/useShallowSetState.js.map +2 -2
  15. package/dist/cjs/types.js.map +1 -1
  16. package/dist/cjs/views/TamaguiProvider.js +2 -5
  17. package/dist/cjs/views/TamaguiProvider.js.map +3 -3
  18. package/dist/esm/constants/isDevTools.js +9 -1
  19. package/dist/esm/constants/isDevTools.js.map +2 -2
  20. package/dist/esm/createComponent.js +72 -152
  21. package/dist/esm/createComponent.js.map +3 -3
  22. package/dist/esm/helpers/getSplitStyles.js +75 -48
  23. package/dist/esm/helpers/getSplitStyles.js.map +2 -2
  24. package/dist/esm/helpers/getStylesAtomic.js +5 -1
  25. package/dist/esm/helpers/getStylesAtomic.js.map +2 -2
  26. package/dist/esm/helpers/normalizeValueWithProperty.js +9 -5
  27. package/dist/esm/helpers/normalizeValueWithProperty.js.map +2 -2
  28. package/dist/esm/helpers/pseudoDescriptors.js +10 -0
  29. package/dist/esm/helpers/pseudoDescriptors.js.map +2 -2
  30. package/dist/esm/helpers/useShallowSetState.js +1 -1
  31. package/dist/esm/helpers/useShallowSetState.js.map +2 -2
  32. package/dist/esm/views/TamaguiProvider.js +2 -5
  33. package/dist/esm/views/TamaguiProvider.js.map +3 -3
  34. package/package.json +12 -12
  35. package/src/constants/isDevTools.ts +10 -4
  36. package/src/createComponent.tsx +85 -200
  37. package/src/helpers/getSplitStyles.tsx +91 -57
  38. package/src/helpers/getStylesAtomic.ts +7 -2
  39. package/src/helpers/normalizeValueWithProperty.ts +11 -5
  40. package/src/helpers/pseudoDescriptors.ts +24 -3
  41. package/src/helpers/useShallowSetState.tsx +1 -1
  42. package/src/types.tsx +1 -1
  43. package/src/views/TamaguiProvider.tsx +5 -6
  44. package/types/helpers/getSplitStyles.d.ts +1 -1
  45. package/types/helpers/pseudoDescriptors.d.ts +8 -14
  46. package/types/types.d.ts +1 -1
@@ -3,6 +3,7 @@ import { isClient, isRSC, isWeb, useIsomorphicLayoutEffect } from '@tamagui/cons
3
3
  /* eslint-disable react-hooks/rules-of-hooks */
4
4
  import {
5
5
  composeEventHandlers,
6
+ stylePropsTransform,
6
7
  stylePropsView,
7
8
  validPseudoKeys,
8
9
  validStyles,
@@ -36,6 +37,7 @@ import {
36
37
  } from './helpers/getSplitStyles'
37
38
  import { getAllSelectors } from './helpers/insertStyleRule'
38
39
  import { mergeProps } from './helpers/mergeProps'
40
+ import { mergeTransform } from './helpers/mergeTransform'
39
41
  import { proxyThemeVariables } from './helpers/proxyThemeVariables'
40
42
  import { useShallowSetState } from './helpers/useShallowSetState'
41
43
  import { getRect, measureLayout, useElementLayout } from './hooks/useElementLayout'
@@ -46,6 +48,7 @@ import {
46
48
  SpaceDirection,
47
49
  SpaceTokens,
48
50
  SpacerProps,
51
+ SplitStyleState,
49
52
  StaticConfig,
50
53
  StaticConfigParsed,
51
54
  StylableComponent,
@@ -162,16 +165,6 @@ export function createComponent<
162
165
  ? `is_${props.componentName}`
163
166
  : defaultComponentClassName
164
167
 
165
- if (debugProp) {
166
- // eslint-disable-next-line no-console
167
- console.log(`render ${componentName}`)
168
- }
169
-
170
- const forceUpdate = useForceUpdate()
171
- const theme = useTheme(props.theme, componentName, props, forceUpdate)
172
-
173
- // time`useTheme`
174
-
175
168
  /**
176
169
  * Component state for tracking animations, pseudos
177
170
  */
@@ -185,14 +178,46 @@ export function createComponent<
185
178
  const setState = states[1]
186
179
  const setStateShallow = useShallowSetState(setState, debugProp, componentName)
187
180
 
188
- const shouldSetMounted = needsMount && !state.mounted
181
+ const useAnimations = tamaguiConfig.animations?.useAnimations as UseAnimationHook | undefined
182
+ // conditional but if ever true stays true
183
+ const hasEverAnimated = useRef(false)
184
+ const isAnimated = (() => {
185
+ const next = !!(useAnimations && props.animation)
186
+ if (next && !hasEverAnimated.current) {
187
+ hasEverAnimated.current = true
188
+ }
189
+ return next || hasEverAnimated.current
190
+ })()
191
+ const isReactNative = Boolean(
192
+ staticConfig.isReactNative || (isAnimated && tamaguiConfig.animations.isReactNative)
193
+ )
194
+
195
+ if (process.env.NODE_ENV === 'development') {
196
+ if (props['debug']) {
197
+ const name = `${
198
+ componentName || Component?.displayName || Component?.name || '[Unnamed Component]'
199
+ }`
200
+ const type = isReactNative ? '(rnw)' : ''
201
+ const dataIs = propsIn['data-is'] || ''
202
+ const banner = `${name}${dataIs ? ` ${dataIs}` : ''} ${type}`
203
+ // eslint-disable-next-line no-console
204
+ console.group(`%c ${banner}`, 'background: yellow;')
205
+ // eslint-disable-next-line no-console
206
+ console.log(`state`, state)
207
+ }
208
+ }
209
+
210
+ const forceUpdate = useForceUpdate()
211
+ const theme = useTheme(props.theme, componentName, props, forceUpdate)
212
+
213
+ const shouldSetMounted = needsMount && state.unmounted
189
214
  const setMounted = shouldSetMounted
190
215
  ? () => {
191
216
  // for some reason without some small delay it doesn't animate css
192
217
  setTimeout(
193
218
  () => {
194
219
  setStateShallow({
195
- mounted: true,
220
+ unmounted: false,
196
221
  })
197
222
  },
198
223
  isWeb ? 10 : 0
@@ -201,41 +226,14 @@ export function createComponent<
201
226
  : undefined
202
227
 
203
228
  const shouldAvoidClasses =
204
- !!(props.animation && avoidClassesWhileAnimating) || !staticConfig.acceptsClassName
229
+ !isWeb || !!(props.animation && avoidClassesWhileAnimating) || !staticConfig.acceptsClassName
205
230
 
206
231
  const shouldForcePseudo = !!propsIn.forceStyle
207
232
  const hasTextAncestor = !!(isWeb && isText ? useContext(TextAncestorContext) : false)
208
- const splitStyleState =
209
- !shouldAvoidClasses && !shouldForcePseudo
210
- ? {
211
- ...state,
212
- hasTextAncestor,
213
- dynamicStylesInline: true,
214
- }
215
- : ({
216
- ...state,
217
- noClassNames: true,
218
- dynamicStylesInline: true,
219
- hasTextAncestor,
220
- resolveVariablesAs: 'value',
221
- } as const)
222
-
233
+ const noClassNames = shouldAvoidClasses || shouldForcePseudo
223
234
  const languageContext = isRSC ? null : useContext(FontLanguageContext)
224
-
225
235
  const isDisabled = props.disabled ?? props.accessibilityState?.disabled
226
236
 
227
- const useAnimations = tamaguiConfig.animations?.useAnimations as UseAnimationHook | undefined
228
-
229
- // conditional but if ever true stays true
230
- const hasEverAnimated = useRef(false)
231
- const isAnimated = (() => {
232
- const next = !!(useAnimations && props.animation)
233
- if (next && !hasEverAnimated.current) {
234
- hasEverAnimated.current = true
235
- }
236
- return next || hasEverAnimated.current
237
- })()
238
-
239
237
  const isTaggable = !Component || typeof Component === 'string'
240
238
  // default to tag, fallback to component (when both strings)
241
239
  const element = isWeb
@@ -255,6 +253,13 @@ export function createComponent<
255
253
 
256
254
  // time`setupStateConf`
257
255
 
256
+ const splitStyleState: SplitStyleState = {
257
+ ...state,
258
+ noClassNames,
259
+ dynamicStylesInline: noClassNames,
260
+ hasTextAncestor,
261
+ resolveVariablesAs: 'value',
262
+ }
258
263
  const splitStyles = useSplitStyles(
259
264
  props,
260
265
  staticConfig,
@@ -271,9 +276,6 @@ export function createComponent<
271
276
  const hostRef = useServerRef<TamaguiElement>(null)
272
277
 
273
278
  // animation setup
274
- const isReactNative = Boolean(
275
- staticConfig.isReactNative || (isAnimated && tamaguiConfig.animations.isReactNative)
276
- )
277
279
  const isAnimatedReactNativeWeb = isAnimated && avoidClassesWhileAnimating
278
280
 
279
281
  if (process.env.NODE_ENV === 'development') {
@@ -283,22 +285,15 @@ export function createComponent<
283
285
  }
284
286
 
285
287
  if (debugProp) {
286
- const name = `${
287
- componentName || Component?.displayName || Component?.name || '[Unnamed Component]'
288
- }`
289
- const type = isReactNative ? 'rnw' : 'tamagui'
290
- const banner = `${name} ${propsIn['data-is'] || ''} ${type}`
291
288
  // eslint-disable-next-line no-console
292
- console.group(`%c 🐛 ${banner}`, 'background: yellow;')
289
+ console.groupCollapsed('props')
293
290
  // eslint-disable-next-line no-console
294
- console.groupCollapsed('initial props/state')
295
- // eslint-disable-next-line no-console
296
- console.log('propsIn', propsIn, 'turned into', props, 'order', Object.keys(props))
291
+ console.log('props in', propsIn, 'mapped to', props, 'in order', Object.keys(props))
297
292
  // eslint-disable-next-line no-console
298
293
  console.log('splitStyles', splitStyles)
299
294
  // eslint-disable-next-line no-console
300
295
  console.log('className', Object.values(splitStyles.classNames))
301
- if (typeof window !== 'undefined') {
296
+ if (isClient) {
302
297
  // eslint-disable-next-line no-console
303
298
  console.log('ref', hostRef, '(click to view)')
304
299
  }
@@ -320,9 +315,6 @@ export function createComponent<
320
315
  mediaKeys,
321
316
  } = splitStyles
322
317
 
323
- const animationFeatureStylesIn = props.animation
324
- ? { ...defaultNativeStyle, ...splitStylesStyle }
325
- : null
326
318
  const propsWithAnimation = props as UseAnimationProps
327
319
 
328
320
  // once you set animation prop don't remove it, you can set to undefined/false
@@ -336,9 +328,14 @@ export function createComponent<
336
328
  hostRef,
337
329
  staticConfig,
338
330
  getStyle({ isExiting, isEntering, exitVariant, enterVariant } = {}) {
331
+ // TODO this function can mostly be moved entirely inside getSplitStyles
332
+ // I think theres bugs in merge(), and we can remove that fn entirely then
333
+ // also i think we need to merge both the exitVariant AND psuedos.exitStyle right now it chooses one
334
+
339
335
  // we have to merge such that transforms keys all exist
340
- const style = animationFeatureStylesIn
336
+ const animationStyle = { ...defaultNativeStyle, ...splitStylesStyle }
341
337
 
338
+ // allows for enter/exit variants on styled()
342
339
  const enterStyle = isEntering
343
340
  ? enterVariant && staticConfig.variants?.[enterVariant]['true']
344
341
  ? getSubStyle(
@@ -349,8 +346,8 @@ export function createComponent<
349
346
  props,
350
347
  state,
351
348
  tamaguiConfig
352
- ) || pseudos.enterStyle
353
- : pseudos.enterStyle
349
+ )
350
+ : null
354
351
  : null
355
352
 
356
353
  const exitStyle = isExiting
@@ -363,41 +360,18 @@ export function createComponent<
363
360
  props,
364
361
  state,
365
362
  tamaguiConfig
366
- ) || pseudos.exitStyle
363
+ )
367
364
  : pseudos.exitStyle
368
365
  : null
369
366
 
370
- // if you have hoverStyle={{ scale: 1.1 }} and don't have scale set on the base style
371
- // no animation will run! this is really confusing. this will look at all variants,
372
- // and fill in base styles if they don't exist. eg:
373
- // input:
374
- // base: {}
375
- // hoverStyle: { scale: 2 }
376
- // enterStyle: { x: 100 }
377
- // output:
378
- // base: { x: 0, scale: 1 }
379
- ensureBaseHasDefaults(
380
- style,
381
- // enterStyle,
382
- pseudos.hoverStyle,
383
- pseudos.focusStyle,
384
- pseudos.pressStyle
385
- )
386
-
387
- enterStyle && isEntering && merge(style, enterStyle)
388
- state.hover && pseudos.hoverStyle && merge(style, pseudos.hoverStyle)
389
- state.focus && pseudos.focusStyle && merge(style, pseudos.focusStyle)
390
- state.press && pseudos.pressStyle && merge(style, pseudos.pressStyle)
391
- exitStyle && isExiting && merge(style, exitStyle)
392
-
393
- if (process.env.NODE_ENV === 'development') {
394
- if (debugProp === 'verbose') {
395
- // eslint-disable-next-line no-console
396
- console.log('animation style', style)
397
- }
367
+ if (isEntering && enterStyle) {
368
+ merge(animationStyle, enterStyle)
369
+ }
370
+ if (isExiting && exitStyle) {
371
+ merge(animationStyle, exitStyle)
398
372
  }
399
373
 
400
- return style
374
+ return animationStyle
401
375
  },
402
376
  //, delay
403
377
  })
@@ -628,16 +602,7 @@ export function createComponent<
628
602
  }
629
603
  } else {
630
604
  styles = [isWeb ? null : defaultNativeStyle, animationStyles ?? splitStylesStyle, medias]
631
- if (!animationStyles && initialSplitStyles) {
632
- const initPseudos = initialSplitStyles.pseudos
633
- !state.mounted && addPseudoToStyles(styles, initPseudos, pseudos, 'enterStyle')
634
- state.hover &&
635
- addPseudoToStyles(styles, initPseudos, pseudos, 'hoverStyle', shouldForcePseudo)
636
- state.focus &&
637
- addPseudoToStyles(styles, initPseudos, pseudos, 'focusStyle', shouldForcePseudo)
638
- state.press &&
639
- addPseudoToStyles(styles, initPseudos, pseudos, 'pressStyle', shouldForcePseudo)
640
- }
605
+
641
606
  // ugly but for now...
642
607
  if (shouldForcePseudo) {
643
608
  const next = {}
@@ -723,19 +688,14 @@ export function createComponent<
723
688
 
724
689
  // TODO need to loop active variants and see if they have matchin pseudos and apply as well
725
690
  const initialPseudos = initialSplitStyles?.pseudos
726
- const attachPress = !!(
727
- pseudos?.pressStyle ||
728
- initialPseudos?.pressStyle ||
729
- onPress ||
730
- onPressOut ||
731
- onPressIn ||
732
- onClick
733
- )
691
+ const runtimePressStyle = noClassNames && (pseudos?.pressStyle || initialPseudos?.pressStyle)
692
+ const attachPress = !!(runtimePressStyle || onPress || onPressOut || onPressIn || onClick)
734
693
 
735
694
  const isHoverable = isWeb
695
+ const runtimeHoverStyle = noClassNames && (pseudos?.hoverStyle || initialPseudos.hoverStyle)
736
696
  const attachHover =
737
697
  isHoverable &&
738
- !!((pseudos && pseudos.hoverStyle) || onHoverIn || onHoverOut || onMouseEnter || onMouseLeave)
698
+ !!(runtimeHoverStyle || onHoverIn || onHoverOut || onMouseEnter || onMouseLeave)
739
699
 
740
700
  const handlesPressEvents = !isWeb && !asChild
741
701
 
@@ -768,7 +728,7 @@ export function createComponent<
768
728
  onMouseUp?.(e)
769
729
  }
770
730
  : undefined,
771
- ...(isHoverable && {
731
+ ...(attachHover && {
772
732
  onMouseEnter: attachHover
773
733
  ? (e) => {
774
734
  const next: Partial<typeof state> = {}
@@ -894,10 +854,7 @@ export function createComponent<
894
854
  // EVENTS native
895
855
  if (process.env.TAMAGUI_TARGET === 'native') {
896
856
  // add focus events
897
- const attachFocus = !!(
898
- (pseudos && pseudos.focusStyle) ||
899
- (initialPseudos && initialPseudos.focusStyle)
900
- )
857
+ const attachFocus = !!(pseudos.focusStyle || (initialPseudos && initialPseudos.focusStyle))
901
858
  if (attachFocus) {
902
859
  viewProps.onFocus = mergeEvent(viewProps.onFocus, () => {
903
860
  setStateShallow({ focus: true })
@@ -909,11 +866,8 @@ export function createComponent<
909
866
 
910
867
  // use Pressability to get smooth unPress when you press + hold + move out
911
868
  // only ever create once, use .configure() to update later
912
- const pressability = usePressability(events)
869
+ const pressability = usePressability(events ? { ...events, hitSlop } : {})
913
870
  if (attachPress && events) {
914
- if (hitSlop) {
915
- viewProps.hitSlop = hitSlop
916
- }
917
871
  for (const key in pressability) {
918
872
  const og = props[key]
919
873
  const val = pressability[key]
@@ -989,12 +943,12 @@ export function createComponent<
989
943
  press: false,
990
944
  pressIn: false,
991
945
  focus: false,
992
- mounted: false,
946
+ unmounted: true,
993
947
  mediaState: getMediaStateObject(getInitialMediaState()),
994
948
  }
995
949
  defaultComponentStateMounted = {
996
950
  ...defaultComponentState,
997
- mounted: true,
951
+ unmounted: false,
998
952
  }
999
953
  }
1000
954
 
@@ -1052,14 +1006,13 @@ export function createComponent<
1052
1006
  staticConfig,
1053
1007
  initialTheme,
1054
1008
  {
1055
- mounted: true,
1009
+ unmounted: false,
1056
1010
  hover: false,
1057
1011
  press: false,
1058
1012
  pressIn: false,
1059
1013
  focus: false,
1060
1014
  resolveVariablesAs: 'both',
1061
1015
  noClassNames,
1062
- keepVariantsAsProps: true,
1063
1016
  },
1064
1017
  undefined,
1065
1018
  undefined,
@@ -1071,7 +1024,7 @@ export function createComponent<
1071
1024
  // otherwise it confuses variant functions getting className props
1072
1025
  const [defaults, defaultsClassnames] = mergeProps(
1073
1026
  component.defaultProps as any,
1074
- initialSplitStyles.viewProps,
1027
+ { ...initialSplitStyles.pseudos, ...initialSplitStyles.viewProps },
1075
1028
  true
1076
1029
  // conf.inverseShorthands
1077
1030
  )
@@ -1330,23 +1283,6 @@ function isUnspaced(child: React.ReactNode) {
1330
1283
  return child?.['type']?.['isVisuallyHidden'] || child?.['type']?.['isUnspaced']
1331
1284
  }
1332
1285
 
1333
- function addPseudoToStyles(
1334
- styles: any[],
1335
- initialPseudos: SplitStyleResult['pseudos'],
1336
- pseudos: any,
1337
- name: string,
1338
- force = false
1339
- ) {
1340
- // on web use pseudo object { hoverStyle } to keep specificity with concatClassName
1341
- const pseudoStyle = pseudos[name]
1342
- const shouldNestObject = isWeb && name !== 'enterStyle' && name !== 'exitStyle'
1343
- const defaultPseudoStyle = initialPseudos[name]
1344
- const style = defaultPseudoStyle ? { ...defaultPseudoStyle, ...pseudoStyle } : pseudoStyle
1345
- if (style) {
1346
- styles.push(shouldNestObject && !force ? { [name]: style } : style)
1347
- }
1348
- }
1349
-
1350
1286
  const DefaultProps = new Map()
1351
1287
 
1352
1288
  function mergeConfigDefaultProps(
@@ -1385,68 +1321,17 @@ function mergeConfigDefaultProps(
1385
1321
  return props
1386
1322
  }
1387
1323
 
1388
- const defaults = {
1389
- left: 0,
1390
- right: 0,
1391
- top: 0,
1392
- bottom: 0,
1393
- opacity: 1,
1394
- translateX: 0,
1395
- translateY: 0,
1396
- skew: 0,
1397
- skewX: 0,
1398
- skewY: 0,
1399
- scale: 1,
1400
- rotate: '0deg',
1401
- rotateY: '0deg',
1402
- rotateX: '0deg',
1403
- }
1404
-
1405
- function ensureBaseHasDefaults(base: ViewStyle, ...pseudos: (ViewStyle | null | undefined)[]) {
1406
- for (const pseudo of pseudos) {
1407
- if (!pseudo) continue
1408
- for (const key in pseudo) {
1409
- const val = pseudo[key]
1410
- // can we just use merge() here?
1411
- if (key === 'transform') {
1412
- if (typeof val === 'string') {
1413
- continue
1414
- }
1415
- for (const t of val) {
1416
- const tkey = Object.keys(t)[0]
1417
- const defaultVal = defaults[tkey]
1418
- const tDefaultVal = { [tkey]: defaultVal } as any
1419
- if (!base.transform) {
1420
- base.transform = [tDefaultVal]
1421
- } else {
1422
- if (!base.transform.find((x) => x[tkey])) {
1423
- base.transform.push(tDefaultVal)
1424
- }
1425
- }
1426
- }
1427
- } else {
1428
- if (!(key in base)) {
1429
- base[key] = defaults[key]
1430
- }
1431
- }
1432
- }
1433
- }
1434
- }
1435
-
1324
+ // TODO need to delete existing prop because of weirdness in how we do this
1325
+ // real solution is to handle all this in getSplitStyle and have animation drviers
1326
+ // return a useAnimationIsExiting() so we can pass that as part of state: { exiting: true }
1327
+ // then we can remove this function and the getStyle() altogether
1436
1328
  function merge(base: ViewStyle, next: ViewStyle) {
1437
- if (!next.transform || !base.transform) {
1438
- Object.assign(base, next)
1439
- return
1440
- }
1441
- const { transform, ...rest } = next
1442
- Object.assign(base, rest)
1443
- for (const t of transform) {
1444
- const key = Object.keys(t)[0]
1445
- const existing = base.transform.find((x) => key in x)
1446
- if (existing) {
1447
- existing[key] = t[key]
1329
+ for (const key in next) {
1330
+ const val = next[key]
1331
+ if (key in stylePropsTransform) {
1332
+ mergeTransform(base, key, val, true)
1448
1333
  } else {
1449
- base.transform.push(t)
1334
+ base[key] = val
1450
1335
  }
1451
1336
  }
1452
1337
  }