@tamagui/web 1.53.2 → 1.54.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 (179) hide show
  1. package/dist/cjs/CSSColorNames.js +17 -0
  2. package/dist/cjs/CSSColorNames.js.map +6 -0
  3. package/dist/cjs/GetRef.js +17 -0
  4. package/dist/cjs/GetRef.js.map +6 -0
  5. package/dist/cjs/Role.js +17 -0
  6. package/dist/cjs/Role.js.map +6 -0
  7. package/dist/cjs/contexts/ComponentContext.js +47 -0
  8. package/dist/cjs/contexts/ComponentContext.js.map +6 -0
  9. package/dist/cjs/createComponent.js +67 -20
  10. package/dist/cjs/createComponent.js.map +2 -2
  11. package/dist/cjs/createTamagui.js +1 -0
  12. package/dist/cjs/createTamagui.js.map +1 -1
  13. package/dist/cjs/helpers/createMediaStyle.js +13 -5
  14. package/dist/cjs/helpers/createMediaStyle.js.map +1 -1
  15. package/dist/cjs/helpers/createShallowSetState.js +39 -0
  16. package/dist/cjs/helpers/createShallowSetState.js.map +6 -0
  17. package/dist/cjs/helpers/createStyledContext.js +7 -2
  18. package/dist/cjs/helpers/createStyledContext.js.map +1 -1
  19. package/dist/cjs/helpers/getSplitStyles.js +40 -22
  20. package/dist/cjs/helpers/getSplitStyles.js.map +2 -2
  21. package/dist/cjs/helpers/getStylesAtomic.js.map +1 -1
  22. package/dist/cjs/helpers/getVariantExtras.js +3 -3
  23. package/dist/cjs/helpers/getVariantExtras.js.map +1 -1
  24. package/dist/cjs/helpers/propMapper.js +3 -3
  25. package/dist/cjs/helpers/propMapper.js.map +1 -1
  26. package/dist/cjs/helpers/pseudoDescriptors.js +9 -2
  27. package/dist/cjs/helpers/pseudoDescriptors.js.map +1 -1
  28. package/dist/cjs/hooks/useAnimationDriver.js +2 -2
  29. package/dist/cjs/hooks/useAnimationDriver.js.map +1 -1
  30. package/dist/cjs/hooks/useMedia.js +6 -3
  31. package/dist/cjs/hooks/useMedia.js.map +1 -1
  32. package/dist/cjs/hooks/useStyle.js +3 -7
  33. package/dist/cjs/hooks/useStyle.js.map +1 -1
  34. package/dist/cjs/index.js +8 -8
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/interfaces/CSSColorNames.js +17 -0
  37. package/dist/cjs/interfaces/CSSColorNames.js.map +6 -0
  38. package/dist/cjs/interfaces/GetRef.js +17 -0
  39. package/dist/cjs/interfaces/GetRef.js.map +6 -0
  40. package/dist/cjs/interfaces/Role.js +17 -0
  41. package/dist/cjs/interfaces/Role.js.map +6 -0
  42. package/dist/cjs/styled.js.map +1 -1
  43. package/dist/cjs/types/CSSColorNames.js +17 -0
  44. package/dist/cjs/types/CSSColorNames.js.map +6 -0
  45. package/dist/cjs/types/GetRef.js +17 -0
  46. package/dist/cjs/types/GetRef.js.map +6 -0
  47. package/dist/cjs/types/Role.js +17 -0
  48. package/dist/cjs/types/Role.js.map +6 -0
  49. package/dist/cjs/views/AnimationDriverProvider.js +2 -2
  50. package/dist/cjs/views/AnimationDriverProvider.js.map +1 -1
  51. package/dist/cjs/views/FontLanguage.native.js +3 -3
  52. package/dist/cjs/views/FontLanguage.native.js.map +1 -1
  53. package/dist/cjs/views/TamaguiProvider.js +3 -5
  54. package/dist/cjs/views/TamaguiProvider.js.map +1 -1
  55. package/dist/esm/CSSColorNames.js +1 -0
  56. package/dist/esm/CSSColorNames.js.map +6 -0
  57. package/dist/esm/GetRef.js +1 -0
  58. package/dist/esm/GetRef.js.map +6 -0
  59. package/dist/esm/Role.js +1 -0
  60. package/dist/esm/Role.js.map +6 -0
  61. package/dist/esm/contexts/ComponentContext.js +23 -0
  62. package/dist/esm/contexts/ComponentContext.js.map +6 -0
  63. package/dist/esm/createComponent.js +68 -20
  64. package/dist/esm/createComponent.js.map +2 -2
  65. package/dist/esm/createTamagui.js +1 -0
  66. package/dist/esm/createTamagui.js.map +1 -1
  67. package/dist/esm/helpers/createMediaStyle.js +13 -5
  68. package/dist/esm/helpers/createMediaStyle.js.map +1 -1
  69. package/dist/esm/helpers/createShallowSetState.js +15 -0
  70. package/dist/esm/helpers/createShallowSetState.js.map +6 -0
  71. package/dist/esm/helpers/createStyledContext.js +7 -2
  72. package/dist/esm/helpers/createStyledContext.js.map +1 -1
  73. package/dist/esm/helpers/getSplitStyles.js +41 -23
  74. package/dist/esm/helpers/getSplitStyles.js.map +2 -2
  75. package/dist/esm/helpers/getStylesAtomic.js.map +1 -1
  76. package/dist/esm/helpers/getVariantExtras.js +3 -3
  77. package/dist/esm/helpers/getVariantExtras.js.map +1 -1
  78. package/dist/esm/helpers/propMapper.js +3 -3
  79. package/dist/esm/helpers/propMapper.js.map +1 -1
  80. package/dist/esm/helpers/pseudoDescriptors.js +7 -1
  81. package/dist/esm/helpers/pseudoDescriptors.js.map +1 -1
  82. package/dist/esm/hooks/useAnimationDriver.js +2 -2
  83. package/dist/esm/hooks/useAnimationDriver.js.map +1 -1
  84. package/dist/esm/hooks/useMedia.js +6 -3
  85. package/dist/esm/hooks/useMedia.js.map +1 -1
  86. package/dist/esm/hooks/useStyle.js +3 -7
  87. package/dist/esm/hooks/useStyle.js.map +1 -1
  88. package/dist/esm/index.js +5 -5
  89. package/dist/esm/index.js.map +1 -1
  90. package/dist/esm/interfaces/CSSColorNames.js +1 -0
  91. package/dist/esm/interfaces/CSSColorNames.js.map +6 -0
  92. package/dist/esm/interfaces/GetRef.js +1 -0
  93. package/dist/esm/interfaces/GetRef.js.map +6 -0
  94. package/dist/esm/interfaces/Role.js +1 -0
  95. package/dist/esm/interfaces/Role.js.map +6 -0
  96. package/dist/esm/styled.js.map +1 -1
  97. package/dist/esm/types/CSSColorNames.js +1 -0
  98. package/dist/esm/types/CSSColorNames.js.map +6 -0
  99. package/dist/esm/types/GetRef.js +1 -0
  100. package/dist/esm/types/GetRef.js.map +6 -0
  101. package/dist/esm/types/Role.js +1 -0
  102. package/dist/esm/types/Role.js.map +6 -0
  103. package/dist/esm/views/AnimationDriverProvider.js +2 -2
  104. package/dist/esm/views/AnimationDriverProvider.js.map +1 -1
  105. package/dist/esm/views/FontLanguage.native.js +3 -3
  106. package/dist/esm/views/FontLanguage.native.js.map +1 -1
  107. package/dist/esm/views/TamaguiProvider.js +3 -5
  108. package/dist/esm/views/TamaguiProvider.js.map +1 -1
  109. package/package.json +10 -10
  110. package/src/contexts/ComponentContext.tsx +22 -0
  111. package/src/createComponent.tsx +108 -26
  112. package/src/createTamagui.ts +1 -0
  113. package/src/helpers/createMediaStyle.ts +14 -5
  114. package/src/helpers/{useShallowSetState.tsx → createShallowSetState.tsx} +1 -1
  115. package/src/helpers/createStyledContext.tsx +7 -2
  116. package/src/helpers/getSplitStyles.tsx +47 -19
  117. package/src/helpers/getStylesAtomic.ts +1 -1
  118. package/src/helpers/getVariantExtras.tsx +3 -3
  119. package/src/helpers/propMapper.ts +5 -5
  120. package/src/helpers/pseudoDescriptors.ts +7 -0
  121. package/src/hooks/useAnimationDriver.tsx +2 -2
  122. package/src/hooks/useMedia.tsx +10 -3
  123. package/src/hooks/useStyle.tsx +3 -7
  124. package/src/index.ts +7 -6
  125. package/src/interfaces/CSSColorNames.tsx +141 -0
  126. package/src/interfaces/GetRef.tsx +23 -0
  127. package/src/interfaces/Role.tsx +68 -0
  128. package/src/styled.tsx +1 -1
  129. package/src/types.tsx +88 -271
  130. package/src/views/AnimationDriverProvider.tsx +3 -3
  131. package/src/views/FontLanguage.native.tsx +3 -3
  132. package/src/views/TamaguiProvider.tsx +11 -19
  133. package/types/CSSColorNames.d.ts +2 -0
  134. package/types/CSSColorNames.d.ts.map +1 -0
  135. package/types/GetRef.d.ts +23 -0
  136. package/types/GetRef.d.ts.map +1 -0
  137. package/types/Role.d.ts +2 -0
  138. package/types/Role.d.ts.map +1 -0
  139. package/types/contexts/ComponentContext.d.ts +3 -0
  140. package/types/contexts/ComponentContext.d.ts.map +1 -0
  141. package/types/createComponent.d.ts +2 -2
  142. package/types/createComponent.d.ts.map +1 -1
  143. package/types/createTamagui.d.ts.map +1 -1
  144. package/types/helpers/createMediaStyle.d.ts.map +1 -1
  145. package/types/helpers/createShallowSetState.d.ts +4 -0
  146. package/types/helpers/createShallowSetState.d.ts.map +1 -0
  147. package/types/helpers/createStyledContext.d.ts.map +1 -1
  148. package/types/helpers/getSplitStyles.d.ts +2 -3
  149. package/types/helpers/getSplitStyles.d.ts.map +1 -1
  150. package/types/helpers/pseudoDescriptors.d.ts +5 -0
  151. package/types/helpers/pseudoDescriptors.d.ts.map +1 -1
  152. package/types/hooks/useAnimationDriver.d.ts +3 -1
  153. package/types/hooks/useAnimationDriver.d.ts.map +1 -1
  154. package/types/hooks/useMedia.d.ts +1 -1
  155. package/types/hooks/useMedia.d.ts.map +1 -1
  156. package/types/hooks/useStyle.d.ts.map +1 -1
  157. package/types/index.d.ts +5 -5
  158. package/types/index.d.ts.map +1 -1
  159. package/types/interfaces/CSSColorNames.d.ts +2 -0
  160. package/types/interfaces/CSSColorNames.d.ts.map +1 -0
  161. package/types/interfaces/GetRef.d.ts +6 -0
  162. package/types/interfaces/GetRef.d.ts.map +1 -0
  163. package/types/interfaces/Role.d.ts +2 -0
  164. package/types/interfaces/Role.d.ts.map +1 -0
  165. package/types/styled.d.ts +2 -1
  166. package/types/styled.d.ts.map +1 -1
  167. package/types/types/CSSColorNames.d.ts +2 -0
  168. package/types/types/CSSColorNames.d.ts.map +1 -0
  169. package/types/types/GetRef.d.ts +6 -0
  170. package/types/types/GetRef.d.ts.map +1 -0
  171. package/types/types/Role.d.ts +2 -0
  172. package/types/types/Role.d.ts.map +1 -0
  173. package/types/types.d.ts +57 -30
  174. package/types/types.d.ts.map +1 -1
  175. package/types/views/TamaguiProvider.d.ts.map +1 -1
  176. package/src/contexts/AnimationDriverContext.tsx +0 -5
  177. package/src/contexts/ButtonNestingContext.tsx +0 -3
  178. package/src/contexts/FontLanguageContext.tsx +0 -5
  179. package/src/contexts/TextAncestorContext.tsx +0 -3
@@ -1,5 +1,5 @@
1
1
  import { useComposedRefs } from '@tamagui/compose-refs'
2
- import { isClient, isServer, isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
2
+ import { isClient, isServer, isWeb } from '@tamagui/constants'
3
3
  import { validStyles } from '@tamagui/helpers'
4
4
  import { useDidFinishSSR } from '@tamagui/use-did-finish-ssr'
5
5
  import React, {
@@ -12,35 +12,38 @@ import React, {
12
12
  useContext,
13
13
  useEffect,
14
14
  useId,
15
+ useMemo,
15
16
  useRef,
16
17
  useState,
17
18
  } from 'react'
18
19
 
19
20
  import { getConfig, onConfiguredOnce } from './config'
20
21
  import { stackDefaultStyles } from './constants/constants'
21
- import { FontLanguageContext } from './contexts/FontLanguageContext'
22
- import { TextAncestorContext } from './contexts/TextAncestorContext'
22
+ import { ComponentContext } from './contexts/ComponentContext'
23
23
  import { didGetVariableValue, setDidGetVariableValue } from './createVariable'
24
+ import { createShallowSetState } from './helpers/createShallowSetState'
24
25
  import { useSplitStyles } from './helpers/getSplitStyles'
25
26
  import { mergeProps } from './helpers/mergeProps'
26
27
  import { proxyThemeVariables } from './helpers/proxyThemeVariables'
27
28
  import { themeable } from './helpers/themeable'
28
- import { useShallowSetState } from './helpers/useShallowSetState'
29
- import { useAnimationDriver } from './hooks/useAnimationDriver'
30
29
  import { setMediaShouldUpdate, useMedia } from './hooks/useMedia'
31
30
  import { useThemeWithState } from './hooks/useTheme'
32
31
  import { hooks } from './setupHooks'
33
32
  import {
34
33
  DebugProp,
34
+ DisposeFn,
35
+ GroupContextType,
35
36
  SpaceDirection,
36
37
  SpaceValue,
37
38
  SpacerProps,
39
+ StackProps,
38
40
  StaticConfig,
39
41
  TamaguiComponent,
40
42
  TamaguiComponentEvents,
41
43
  TamaguiComponentState,
42
44
  TamaguiElement,
43
45
  TamaguiInternalConfig,
46
+ TextProps,
44
47
  UseAnimationHook,
45
48
  UseAnimationProps,
46
49
  } from './types'
@@ -97,7 +100,7 @@ let BaseView: any
97
100
  let hasSetupBaseViews = false
98
101
 
99
102
  export function createComponent<
100
- ComponentPropTypes extends Object = {},
103
+ ComponentPropTypes extends StackProps | TextProps = {},
101
104
  Ref = TamaguiElement,
102
105
  BaseProps = never
103
106
  >(staticConfig: StaticConfig) {
@@ -142,7 +145,7 @@ export function createComponent<
142
145
  }
143
146
  }
144
147
 
145
- const component = forwardRef<Ref, ComponentPropTypes>((propsIn: any, forwardedRef) => {
148
+ const component = forwardRef<Ref, ComponentPropTypes>((propsIn, forwardedRef) => {
146
149
  if (process.env.TAMAGUI_TARGET === 'native') {
147
150
  // todo this could be moved to a cleaner location
148
151
  if (!hasSetupBaseViews) {
@@ -163,6 +166,8 @@ export function createComponent<
163
166
  }
164
167
  }
165
168
 
169
+ const componentContext = useContext(ComponentContext)
170
+
166
171
  // set variants through context
167
172
  // order is after default props but before props
168
173
  let styledContextProps: Object | undefined
@@ -204,7 +209,7 @@ export function createComponent<
204
209
 
205
210
  // React inserts default props after your props for some reason...
206
211
  // order important so we do loops, you can't just spread because JS does weird things
207
- let props: any
212
+ let props: StackProps | TextProps
208
213
  if (curDefaultProps) {
209
214
  props = mergeProps(curDefaultProps, propsIn)
210
215
  } else {
@@ -244,7 +249,7 @@ export function createComponent<
244
249
  /**
245
250
  * Component state for tracking animations, pseudos
246
251
  */
247
- const animationsConfig = useAnimationDriver()
252
+ const animationsConfig = componentContext.animationDriver
248
253
  const useAnimations = animationsConfig?.useAnimations as UseAnimationHook | undefined
249
254
 
250
255
  // after we get states mount we need to turn off isAnimated for server side
@@ -283,8 +288,24 @@ export function createComponent<
283
288
  : states[0]
284
289
  const setState = states[1]
285
290
 
286
- // TODO performance optimization could avoid useCallback and just have this be setStateShallow(setState, state) at call-sites
287
- const setStateShallow = useShallowSetState(setState)
291
+ let setStateShallow = createShallowSetState(setState)
292
+
293
+ const groupName = props.group as any as string
294
+ if (groupName) {
295
+ // when we set state we also set our group state and emit an event for children listening:
296
+ const groupContextState = componentContext.groups.state
297
+ const og = setStateShallow
298
+ setStateShallow = (state) => {
299
+ og(state)
300
+ componentContext.groups.emit(groupName, state)
301
+ // and mutate the current since its concurrent safe (children throw it in useState on mount)
302
+ const next = {
303
+ ...groupContextState[groupName],
304
+ ...state,
305
+ }
306
+ groupContextState[groupName] = next
307
+ }
308
+ }
288
309
 
289
310
  if (process.env.NODE_ENV === 'development' && time) time`use-state`
290
311
 
@@ -308,8 +329,8 @@ export function createComponent<
308
329
  : props.componentName
309
330
  ? `is_${props.componentName}`
310
331
  : defaultComponentClassName
311
- const hasTextAncestor = !!(isWeb && isText ? useContext(TextAncestorContext) : false)
312
- const languageContext = useContext(FontLanguageContext)
332
+
333
+ const hasTextAncestor = !!(isWeb && isText ? componentContext.inText : false)
313
334
  const isDisabled = props.disabled ?? props.accessibilityState?.disabled
314
335
 
315
336
  if (process.env.NODE_ENV === 'development' && time) time`use-context`
@@ -372,8 +393,6 @@ export function createComponent<
372
393
  const themeStateProps = {
373
394
  name: props.theme,
374
395
  componentName,
375
- reset: props.reset,
376
- inverse: props.themeInverse,
377
396
  // @ts-ignore this is internal use only
378
397
  disable: disableTheme,
379
398
  shallow: stateRef.current.themeShallow,
@@ -464,7 +483,6 @@ export function createComponent<
464
483
  const styleProps = {
465
484
  mediaState,
466
485
  noClassNames,
467
- hasTextAncestor,
468
486
  resolveVariablesAs,
469
487
  isExiting,
470
488
  isAnimated,
@@ -479,7 +497,7 @@ export function createComponent<
479
497
  state,
480
498
  styleProps,
481
499
  null,
482
- languageContext || undefined,
500
+ componentContext,
483
501
  elementType,
484
502
  debugProp
485
503
  )
@@ -559,7 +577,6 @@ export function createComponent<
559
577
  styleProps,
560
578
  theme: themeState.state.theme!,
561
579
  pseudos: pseudos || null,
562
- onDidAnimate: props.onDidAnimate,
563
580
  hostRef,
564
581
  staticConfig,
565
582
  })
@@ -601,7 +618,10 @@ export function createComponent<
601
618
 
602
619
  if (process.env.NODE_ENV === 'development' && time) time`destructure`
603
620
 
604
- const disabled = props.accessibilityState?.disabled || props.accessibilityDisabled
621
+ const disabled =
622
+ props.accessibilityState?.disabled ||
623
+ // @ts-expect-error (comes from core)
624
+ props.accessibilityDisabled
605
625
 
606
626
  // these can ultimately be for DOM, react-native-web views, or animated views
607
627
  // so the type is pretty loose
@@ -647,12 +667,12 @@ export function createComponent<
647
667
  })
648
668
  }, [])
649
669
 
650
- const shouldSetMounted = needsMount && state.unmounted
651
-
652
670
  // combined multiple effects into one for performance so be careful with logic
653
671
  // should not be a layout effect because otherwise it wont render the initial state
654
672
  // for example css driver needs to render once with the first styles, then again with the next
655
673
  // if its a layout effect it will just skip that first render output
674
+ const shouldSetMounted = needsMount && state.unmounted
675
+ const { pseudoGroups } = splitStyles
656
676
  useEffect(() => {
657
677
  if (shouldSetMounted) {
658
678
  const unmounted =
@@ -664,10 +684,35 @@ export function createComponent<
664
684
  // no need for mouseUp removal effect if its not even mounted yet
665
685
  }
666
686
 
687
+ // parent group pseudo listening
688
+ let disposeGroupsListener: DisposeFn | undefined
689
+ if (pseudoGroups) {
690
+ const current = {}
691
+ disposeGroupsListener = componentContext.groups.subscribe((name, next) => {
692
+ if (pseudoGroups.has(name)) {
693
+ // merge because we emit a partial of the state each time
694
+ Object.assign(current, next)
695
+ const group = {
696
+ ...state.group,
697
+ [name]: current,
698
+ }
699
+ setStateShallow({
700
+ // force it to be referentially different so it always updates
701
+ group,
702
+ })
703
+ }
704
+ })
705
+ }
706
+
667
707
  return () => {
708
+ disposeGroupsListener?.()
668
709
  mouseUps.delete(unPress)
669
710
  }
670
- }, [shouldSetMounted, state.unmounted])
711
+ }, [
712
+ shouldSetMounted,
713
+ state.unmounted,
714
+ pseudoGroups ? Object.keys([...pseudoGroups]).join('') : 0,
715
+ ])
671
716
 
672
717
  const avoidAnimationStyle = keepStyleSSR && state.unmounted === true
673
718
 
@@ -690,6 +735,7 @@ export function createComponent<
690
735
  componentName ? componentClassName : '',
691
736
  fontFamilyClassName,
692
737
  classNames ? Object.values(classNames).join(' ') : '',
738
+ props.group ? `t_group_${props.group}` : '',
693
739
  ]
694
740
  className = classList.join(' ')
695
741
 
@@ -724,14 +770,29 @@ export function createComponent<
724
770
  viewProps.style = style
725
771
  }
726
772
 
773
+ // if its a group its gotta listen for pseudos to emit them to children
774
+
727
775
  const runtimePressStyle = !disabled && noClassNames && pseudos?.pressStyle
728
776
  const attachPress = Boolean(
729
- runtimePressStyle || onPress || onPressOut || onPressIn || onLongPress || onClick
777
+ groupName ||
778
+ runtimePressStyle ||
779
+ onPress ||
780
+ onPressOut ||
781
+ onPressIn ||
782
+ onLongPress ||
783
+ onClick
730
784
  )
731
785
  const runtimeHoverStyle = !disabled && noClassNames && pseudos?.hoverStyle
732
786
  const isHoverable =
733
787
  isWeb &&
734
- !!(runtimeHoverStyle || onHoverIn || onHoverOut || onMouseEnter || onMouseLeave)
788
+ !!(
789
+ groupName ||
790
+ runtimeHoverStyle ||
791
+ onHoverIn ||
792
+ onHoverOut ||
793
+ onMouseEnter ||
794
+ onMouseLeave
795
+ )
735
796
 
736
797
  const handlesPressEvents = !(isWeb || asChild)
737
798
 
@@ -785,7 +846,6 @@ export function createComponent<
785
846
  setStateShallow({
786
847
  press: true,
787
848
  pressIn: true,
788
- hover: false,
789
849
  })
790
850
  onPressIn?.(e)
791
851
  onMouseDown?.(e)
@@ -872,6 +932,28 @@ export function createComponent<
872
932
 
873
933
  content = createElement(elementType, viewProps, content)
874
934
 
935
+ // must override context so siblings don't clobber initial state
936
+ const subGroupContext = useMemo(() => {
937
+ if (!groupName) return null
938
+ // change reference so context value updates
939
+ return {
940
+ ...componentContext.groups,
941
+ // change reference so as we mutate it doesn't affect siblings etc
942
+ state: {
943
+ ...componentContext.groups.state,
944
+ [groupName]: initialState,
945
+ },
946
+ }
947
+ }, [groupName])
948
+
949
+ if (groupName && subGroupContext) {
950
+ content = (
951
+ <ComponentContext.Provider groups={subGroupContext}>
952
+ {content}
953
+ </ComponentContext.Provider>
954
+ )
955
+ }
956
+
875
957
  // disable theme prop is deterministic so conditional hook ok here
876
958
  content = disableThemeProp
877
959
  ? content
@@ -1038,7 +1120,7 @@ Unspaced['isUnspaced'] = true
1038
1120
 
1039
1121
  // dont used styled() here to avoid circular deps
1040
1122
  // keep inline to avoid circular deps
1041
-
1123
+ // @ts-expect-error we override
1042
1124
  export const Spacer = createComponent<SpacerProps>({
1043
1125
  acceptsClassName: true,
1044
1126
  memo: true,
@@ -252,6 +252,7 @@ ${runtimeStyles}`
252
252
  const defaultFont = `$${defaultFontName}`
253
253
 
254
254
  const config: TamaguiInternalConfig = {
255
+ groupNames: [],
255
256
  settings: {},
256
257
  onlyAllowShorthands: false,
257
258
  fontLanguages: [],
@@ -9,6 +9,12 @@ export const MEDIA_SEP = '_'
9
9
  let prefixes: Record<string, string> | null = null
10
10
  let selectors: Record<string, string> | null = null
11
11
 
12
+ const parentPseudoToSelector = {
13
+ hovered: ':hover',
14
+ focused: ':focus',
15
+ pressed: ':active',
16
+ }
17
+
12
18
  export const createMediaStyle = (
13
19
  { property, identifier, rules }: StyleObject,
14
20
  mediaKey: string,
@@ -20,7 +26,8 @@ export const createMediaStyle = (
20
26
  const enableMediaPropOrder = conf.settings.mediaPropOrder
21
27
  const isThemeMedia = mediaKey.startsWith('theme-')
22
28
  const isPlatformMedia = !isThemeMedia && mediaKey.startsWith('platform-')
23
- const isThemeOrPlatform = isThemeMedia || isPlatformMedia
29
+ const isGroupMedia = !isThemeMedia && !isPlatformMedia && mediaKey.startsWith('group-')
30
+ const isNonWindowMedia = isThemeMedia || isPlatformMedia || isGroupMedia
24
31
  const negKey = negate ? '0' : ''
25
32
  const ogPrefix = identifier.slice(0, identifier.indexOf('-') + 1)
26
33
 
@@ -29,19 +36,21 @@ export const createMediaStyle = (
29
36
  const id = `${ogPrefix}${MEDIA_SEP}${mediaKey.replace('-', '')}${negKey}${MEDIA_SEP}`
30
37
  const nextIdentifier = identifier.replace(ogPrefix, id)
31
38
 
32
- if (isThemeOrPlatform) {
39
+ if (isNonWindowMedia) {
33
40
  const precedencePrefix = new Array(priority).fill(':root').join('')
34
41
  const styleInner = rules
35
42
  .map((rule) => rule.replace(identifier, nextIdentifier))
36
43
  .join(';')
37
44
 
38
- if (isThemeMedia) {
39
- const key = mediaKey.split('-')[1]
45
+ if (isThemeMedia || isGroupMedia) {
46
+ const keyParts = mediaKey.split('-')
47
+ const name = (isGroupMedia ? 'group_' : '') + keyParts[1]
40
48
  const selectorStart = styleInner.indexOf(':root')
41
49
  const selectorEnd = styleInner.lastIndexOf('{')
42
50
  const selector = styleInner.slice(selectorStart, selectorEnd)
43
51
  const precedenceSpace = conf.themeClassNameOnRoot ? '' : ' '
44
- const nextSelector = `:root${precedencePrefix}${precedenceSpace}.t_${key} ${selector.replace(
52
+ const pseudoSelector = parentPseudoToSelector[keyParts[2]] || ''
53
+ const nextSelector = `:root${precedencePrefix}${precedenceSpace}.t_${name}${pseudoSelector} ${selector.replace(
45
54
  ':root',
46
55
  ''
47
56
  )}`
@@ -2,7 +2,7 @@ import React from 'react'
2
2
 
3
3
  import { TamaguiComponentState } from '../types'
4
4
 
5
- export function useShallowSetState<State extends TamaguiComponentState>(
5
+ export function createShallowSetState<State extends TamaguiComponentState>(
6
6
  setter: React.Dispatch<React.SetStateAction<State>>
7
7
  ) {
8
8
  return (next: Partial<State>) => setter((prev) => shallow(prev, next))
@@ -18,7 +18,7 @@ export type StyledContext<Props extends Object = any> = Omit<
18
18
  export function createStyledContext<VariantProps extends Record<string, any>>(
19
19
  props: VariantProps
20
20
  ): StyledContext<VariantProps> {
21
- const OGContext = createContext<any>(null)
21
+ const OGContext = createContext<VariantProps>(props)
22
22
  const OGProvider = OGContext.Provider
23
23
  const Context = OGContext as any as StyledContext<VariantProps>
24
24
 
@@ -26,7 +26,12 @@ export function createStyledContext<VariantProps extends Record<string, any>>(
26
26
  children,
27
27
  ...values
28
28
  }: VariantProps & { children?: React.ReactNode }) => {
29
- const value = useMemo(() => values, [objectIdentityKey(values)])
29
+ const value = useMemo(() => {
30
+ return {
31
+ ...props,
32
+ ...values,
33
+ }
34
+ }, [objectIdentityKey(values)])
30
35
  return <OGProvider value={value}>{children}</OGProvider>
31
36
  }
32
37
 
@@ -33,6 +33,7 @@ import {
33
33
  } from '../hooks/useMedia'
34
34
  import type {
35
35
  ClassNamesObject,
36
+ ComponentContextI,
36
37
  DebugProp,
37
38
  GetStyleResult,
38
39
  GetStyleState,
@@ -66,7 +67,7 @@ import {
66
67
  reverseMapClassNameToValue,
67
68
  } from './normalizeValueWithProperty'
68
69
  import { getPropMappedFontFamily, propMapper } from './propMapper'
69
- import { pseudoDescriptors } from './pseudoDescriptors'
70
+ import { pseudoDescriptors, pseudoPriorities } from './pseudoDescriptors'
70
71
 
71
72
  const fontFamilyKey = 'fontFamily'
72
73
 
@@ -89,7 +90,7 @@ type StyleSplitter = (
89
90
  componentState: TamaguiComponentState,
90
91
  styleProps: SplitStyleProps,
91
92
  parentSplitStyles?: GetStyleResult | null,
92
- languageContext?: LanguageContextType,
93
+ context?: ComponentContextI,
93
94
  // web-only
94
95
  elementType?: string,
95
96
  debug?: DebugProp
@@ -126,7 +127,7 @@ export const getSplitStyles: StyleSplitter = (
126
127
  componentState,
127
128
  styleProps,
128
129
  parentSplitStyles,
129
- languageContext,
130
+ context,
130
131
  elementType,
131
132
  debug
132
133
  ) => {
@@ -150,6 +151,7 @@ export const getSplitStyles: StyleSplitter = (
150
151
  let space: SpaceTokens | null = props.space
151
152
  let hasMedia: boolean | string[] = false
152
153
  let dynamicThemeAccess: boolean | undefined
154
+ let pseudoGroups: Set<string> | undefined
153
155
  const shouldDoClasses = acceptsClassName && isWeb && !styleProps.noClassNames
154
156
 
155
157
  let style: ViewStyleWithPseudos = {}
@@ -166,7 +168,7 @@ export const getSplitStyles: StyleSplitter = (
166
168
  * Not the biggest fan of creating this object but it is a nice API
167
169
  */
168
170
  const styleState: GetStyleState = {
169
- curProps: { ...props },
171
+ curProps: Object.assign({}, props),
170
172
  classNames,
171
173
  conf,
172
174
  props,
@@ -177,7 +179,7 @@ export const getSplitStyles: StyleSplitter = (
177
179
  theme,
178
180
  usedKeys,
179
181
  viewProps,
180
- languageContext,
182
+ context,
181
183
  debug,
182
184
  }
183
185
 
@@ -594,7 +596,7 @@ export const getSplitStyles: StyleSplitter = (
594
596
  (typeof valInit === 'string' && valInit[0] !== '$')))
595
597
 
596
598
  const expanded = avoidPropMap
597
- ? [[keyInit, valInit]]
599
+ ? ([[keyInit, valInit]] as const)
598
600
  : propMapper(keyInit, valInit, styleState)
599
601
 
600
602
  const next = getPropMappedFontFamily(expanded)
@@ -847,7 +849,7 @@ export const getSplitStyles: StyleSplitter = (
847
849
  if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
848
850
  // prettier-ignore
849
851
  // rome-ignore lint/nursery/noConsoleLog: ok
850
- console.log(` 📺 ${key}`, { key, val, mediaStyle, props, shouldDoClasses });
852
+ console.log(` 📺 ${key}`, { key, val, mediaStyle, props, shouldDoClasses, componentState });
851
853
  }
852
854
 
853
855
  // for some reason 'space' in val upsetting next ssr during prod build
@@ -901,33 +903,56 @@ export const getSplitStyles: StyleSplitter = (
901
903
  mergeClassName(transforms, classNames, fullKey, out.identifier, true, true)
902
904
  }
903
905
  } else {
904
- const isThemeMedia = mediaKeyShort.startsWith('theme-')
905
- const isPlatformMedia = mediaKeyShort.startsWith('platform-')
906
+ const isThemeMedia = !isPlatformMedia && mediaKeyShort.startsWith('theme-')
907
+ const isGroupMedia =
908
+ !isPlatformMedia && !isThemeMedia && mediaKeyShort.startsWith('group-')
906
909
 
907
- if (!isThemeMedia && !isPlatformMedia) {
910
+ if (!isThemeMedia && !isPlatformMedia && !isGroupMedia) {
908
911
  if (!mediaState[mediaKeyShort]) {
909
912
  continue
910
913
  }
911
914
  }
912
915
 
916
+ let importanceBump = 0
917
+
913
918
  if (isThemeMedia) {
914
919
  // needed to get updates when theme changes
915
920
  dynamicThemeAccess = true
916
-
917
921
  const mediaThemeName = mediaKeyShort.slice(6)
918
922
  if (!(themeName === mediaThemeName || themeName.startsWith(mediaThemeName))) {
919
923
  continue
920
924
  }
925
+ } else if (isGroupMedia) {
926
+ const [_, groupName, groupPseudoKey] = mediaKeyShort.split('-')
927
+
928
+ // $group-x
929
+ if (!context?.groups.state[groupName]) {
930
+ if (process.env.NODE_ENV === 'development' && debug) {
931
+ console.warn(`No parent with group prop, skipping styles: ${groupName}`)
932
+ }
933
+ continue
934
+ }
935
+
936
+ // $group-x-hovered
937
+ pseudoGroups ||= new Set()
938
+ pseudoGroups.add(groupName)
939
+ if (groupPseudoKey) {
940
+ const groupPseudoKeyShort = groupPseudoKey.replace('ed', '')
941
+ const groupState =
942
+ componentState.group?.[groupName] ||
943
+ // fallback to context initially
944
+ context.groups.state[groupName]
945
+
946
+ const isActive = groupState?.[groupPseudoKeyShort]
947
+ if (!isActive) {
948
+ continue
949
+ }
950
+ const priority = pseudoPriorities[groupPseudoKeyShort]
951
+ importanceBump = priority
952
+ }
921
953
  }
922
954
 
923
955
  for (const subKey in mediaStyle) {
924
- const importance = getMediaImportanceIfMoreImportant(
925
- mediaKeyShort,
926
- subKey,
927
- usedKeys,
928
- mediaState[mediaKeyShort]
929
- )
930
- if (importance === null) continue
931
956
  if (subKey === 'space') {
932
957
  space = valInit.space
933
958
  continue
@@ -938,7 +963,8 @@ export const getSplitStyles: StyleSplitter = (
938
963
  subKey,
939
964
  mediaStyle[subKey],
940
965
  usedKeys,
941
- mediaState[mediaKeyShort]
966
+ mediaState[mediaKeyShort],
967
+ importanceBump
942
968
  )
943
969
  if (key === fontFamilyKey) {
944
970
  styleState.fontFamily = mediaStyle.fontFamily as string
@@ -1171,6 +1197,7 @@ export const getSplitStyles: StyleSplitter = (
1171
1197
  classNames,
1172
1198
  rulesToInsert,
1173
1199
  dynamicThemeAccess,
1200
+ pseudoGroups,
1174
1201
  }
1175
1202
 
1176
1203
  // native: swap out the right family based on weight/style
@@ -1399,6 +1426,7 @@ const skipProps = {
1399
1426
  disableOptimization: 1,
1400
1427
  tag: 1,
1401
1428
  style: 1, // handled after loop so pseudos set usedKeys and override it if necessary
1429
+ group: 1,
1402
1430
  }
1403
1431
 
1404
1432
  if (process.env.NODE_ENV === 'test') {
@@ -58,7 +58,7 @@ export const generateAtomicStyles = (
58
58
 
59
59
  // transform
60
60
  if (key === 'transform' && Array.isArray(style.transform)) {
61
- val = val
61
+ val = (val as any[])
62
62
  .map(
63
63
  // { scale: 2 } => 'scale(2)'
64
64
  // { translateX: 20 } => 'translateX(20px)'
@@ -4,10 +4,10 @@ import { LanguageContextType } from '../views/FontLanguage.types'
4
4
  import { createProxy } from './createProxy'
5
5
 
6
6
  export function getVariantExtras(styleState: GetStyleState) {
7
- const { curProps, conf, languageContext, theme } = styleState
7
+ const { curProps, conf, context, theme } = styleState
8
8
  let fonts = conf.fontsParsed
9
- if (languageContext) {
10
- fonts = getFontsForLanguage(conf.fontsParsed, languageContext)
9
+ if (context?.language) {
10
+ fonts = getFontsForLanguage(conf.fontsParsed, context.language)
11
11
  }
12
12
 
13
13
  // should be able to just use styleState.fontFamily but no time to test for now
@@ -341,7 +341,7 @@ const getToken = (
341
341
  styleProps: SplitStyleProps,
342
342
  styleState: GetStyleState
343
343
  ) => {
344
- const { theme, conf, languageContext, fontFamily } = styleState
344
+ const { theme, conf, context, fontFamily } = styleState
345
345
 
346
346
  const tokensParsed = conf.tokensParsed
347
347
  let valOrVar: any
@@ -360,8 +360,8 @@ const getToken = (
360
360
  } else {
361
361
  switch (key) {
362
362
  case 'fontFamily': {
363
- const fontsParsed = languageContext
364
- ? getFontsForLanguage(conf.fontsParsed, languageContext)
363
+ const fontsParsed = context?.language
364
+ ? getFontsForLanguage(conf.fontsParsed, context.language)
365
365
  : conf.fontsParsed
366
366
  valOrVar = fontsParsed[value]?.family || value
367
367
  hasSet = true
@@ -373,8 +373,8 @@ const getToken = (
373
373
  case 'fontWeight': {
374
374
  const fam = fontFamily || styleState.conf.defaultFont
375
375
  if (fam) {
376
- const fontsParsed = languageContext
377
- ? getFontsForLanguage(conf.fontsParsed, languageContext)
376
+ const fontsParsed = context?.language
377
+ ? getFontsForLanguage(conf.fontsParsed, context.language)
378
378
  : conf.fontsParsed
379
379
  const font = fontsParsed[fam]
380
380
  valOrVar = font?.[fontShorthand[key] || key]?.[value] || value
@@ -17,6 +17,13 @@ export const pseudoDescriptorsBase = {
17
17
  },
18
18
  } as const
19
19
 
20
+ // MATCH TO ABOVE but note that press = press not active
21
+ export const pseudoPriorities = {
22
+ hover: 1,
23
+ press: 2,
24
+ focus: 3,
25
+ }
26
+
20
27
  export const pseudoDescriptors: Record<
21
28
  'hoverStyle' | 'pressStyle' | 'focusStyle' | 'enterStyle' | 'exitStyle',
22
29
  PseudoDescriptor
@@ -1,8 +1,8 @@
1
1
  import { useContext } from 'react'
2
2
 
3
- import { AnimationDriverContext } from '../contexts/AnimationDriverContext'
3
+ import { ComponentContext } from '../contexts/ComponentContext'
4
4
  import { getAnimationDriver } from '../helpers/getAnimationDriver'
5
5
 
6
6
  export const useAnimationDriver = () => {
7
- return useContext(AnimationDriverContext) ?? getAnimationDriver()
7
+ return useContext(ComponentContext).animationDriver ?? getAnimationDriver()
8
8
  }