@tamagui/web 2.0.0-1768586279389 → 2.0.0-1768696252732
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.
- package/dist/cjs/animationDriverTypes.test-d.cjs +49 -0
- package/dist/cjs/animationDriverTypes.test-d.js +51 -0
- package/dist/cjs/animationDriverTypes.test-d.js.map +6 -0
- package/dist/cjs/animationDriverTypes.test-d.native.js +52 -0
- package/dist/cjs/animationDriverTypes.test-d.native.js.map +1 -0
- package/dist/cjs/config.cjs +14 -0
- package/dist/cjs/config.js +14 -0
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/config.native.js +14 -0
- package/dist/cjs/config.native.js.map +1 -1
- package/dist/cjs/createComponent.cjs +39 -16
- package/dist/cjs/createComponent.js +35 -15
- package/dist/cjs/createComponent.js.map +1 -1
- package/dist/cjs/createComponent.native.js +48 -19
- package/dist/cjs/createComponent.native.js.map +1 -1
- package/dist/cjs/createTamagui.cjs +3 -2
- package/dist/cjs/createTamagui.js +3 -2
- package/dist/cjs/createTamagui.js.map +1 -1
- package/dist/cjs/createTamagui.native.js +1 -1
- package/dist/cjs/createTamagui.native.js.map +1 -1
- package/dist/cjs/helpers/getSplitStyles.cjs +8 -2
- package/dist/cjs/helpers/getSplitStyles.js +7 -2
- package/dist/cjs/helpers/getSplitStyles.js.map +1 -1
- package/dist/cjs/helpers/getSplitStyles.native.js +7 -1
- package/dist/cjs/helpers/getSplitStyles.native.js.map +1 -1
- package/dist/cjs/helpers/getThemeCSSRules.cjs +23 -26
- package/dist/cjs/helpers/getThemeCSSRules.js +2 -4
- package/dist/cjs/helpers/getThemeCSSRules.js.map +1 -1
- package/dist/cjs/{createTheme.cjs → helpers/mergeRenderElementProps.cjs} +11 -5
- package/dist/cjs/{createTheme.js → helpers/mergeRenderElementProps.js} +10 -6
- package/dist/cjs/helpers/mergeRenderElementProps.js.map +6 -0
- package/dist/cjs/{createTheme.native.js → helpers/mergeRenderElementProps.native.js} +12 -8
- package/dist/cjs/helpers/mergeRenderElementProps.native.js.map +1 -0
- package/dist/cjs/helpers/mergeSlotStyleProps.cjs +39 -0
- package/dist/cjs/helpers/mergeSlotStyleProps.js +29 -0
- package/dist/cjs/helpers/mergeSlotStyleProps.js.map +6 -0
- package/dist/cjs/helpers/mergeSlotStyleProps.native.js +42 -0
- package/dist/cjs/helpers/mergeSlotStyleProps.native.js.map +1 -0
- package/dist/cjs/helpers/skipProps.cjs +1 -1
- package/dist/cjs/helpers/skipProps.js +1 -1
- package/dist/cjs/helpers/skipProps.js.map +1 -1
- package/dist/cjs/helpers/skipProps.native.js +1 -1
- package/dist/cjs/helpers/skipProps.native.js.map +1 -1
- package/dist/cjs/helpers/useRenderElement.cjs +48 -0
- package/dist/cjs/helpers/useRenderElement.js +42 -0
- package/dist/cjs/helpers/useRenderElement.js.map +6 -0
- package/dist/cjs/helpers/useRenderElement.native.js +52 -0
- package/dist/cjs/helpers/useRenderElement.native.js.map +1 -0
- package/dist/cjs/hooks/getThemeProxied.js.map +1 -1
- package/dist/cjs/hooks/getThemeProxied.native.js +1 -1
- package/dist/cjs/hooks/getThemeProxied.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/styled.js.map +1 -1
- package/dist/cjs/styled.native.js.map +1 -1
- package/dist/cjs/views/Configuration.js.map +1 -1
- package/dist/cjs/views/Configuration.native.js.map +1 -1
- package/dist/cjs/views/Slot.cjs +7 -21
- package/dist/cjs/views/Slot.js +9 -13
- package/dist/cjs/views/Slot.js.map +1 -1
- package/dist/cjs/views/Slot.native.js +5 -21
- package/dist/cjs/views/Slot.native.js.map +1 -1
- package/dist/cjs/views/TamaguiProvider.cjs +6 -2
- package/dist/cjs/views/TamaguiProvider.js +5 -2
- package/dist/cjs/views/TamaguiProvider.js.map +1 -1
- package/dist/cjs/views/TamaguiProvider.native.js +5 -1
- package/dist/cjs/views/TamaguiProvider.native.js.map +1 -1
- package/dist/cjs/views/Theme.js.map +1 -1
- package/dist/cjs/views/Theme.native.js.map +1 -1
- package/dist/esm/animationDriverTypes.test-d.js +51 -0
- package/dist/esm/animationDriverTypes.test-d.js.map +6 -0
- package/dist/esm/animationDriverTypes.test-d.mjs +50 -0
- package/dist/esm/animationDriverTypes.test-d.mjs.map +1 -0
- package/dist/esm/animationDriverTypes.test-d.native.js +50 -0
- package/dist/esm/animationDriverTypes.test-d.native.js.map +1 -0
- package/dist/esm/config.js +14 -0
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/config.mjs +14 -1
- package/dist/esm/config.mjs.map +1 -1
- package/dist/esm/config.native.js +14 -1
- package/dist/esm/config.native.js.map +1 -1
- package/dist/esm/createComponent.js +35 -15
- package/dist/esm/createComponent.js.map +1 -1
- package/dist/esm/createComponent.mjs +40 -17
- package/dist/esm/createComponent.mjs.map +1 -1
- package/dist/esm/createComponent.native.js +49 -20
- package/dist/esm/createComponent.native.js.map +1 -1
- package/dist/esm/createTamagui.js +3 -2
- package/dist/esm/createTamagui.js.map +1 -1
- package/dist/esm/createTamagui.mjs +3 -2
- package/dist/esm/createTamagui.mjs.map +1 -1
- package/dist/esm/createTamagui.native.js +1 -1
- package/dist/esm/createTamagui.native.js.map +1 -1
- package/dist/esm/helpers/getSplitStyles.js +7 -2
- package/dist/esm/helpers/getSplitStyles.js.map +1 -1
- package/dist/esm/helpers/getSplitStyles.mjs +8 -2
- package/dist/esm/helpers/getSplitStyles.mjs.map +1 -1
- package/dist/esm/helpers/getSplitStyles.native.js +7 -1
- package/dist/esm/helpers/getSplitStyles.native.js.map +1 -1
- package/dist/esm/helpers/getThemeCSSRules.js +2 -4
- package/dist/esm/helpers/getThemeCSSRules.js.map +1 -1
- package/dist/esm/helpers/getThemeCSSRules.mjs +23 -26
- package/dist/esm/helpers/getThemeCSSRules.mjs.map +1 -1
- package/dist/esm/helpers/mergeRenderElementProps.js +9 -0
- package/dist/esm/helpers/mergeRenderElementProps.js.map +6 -0
- package/dist/esm/helpers/mergeRenderElementProps.mjs +9 -0
- package/dist/esm/helpers/mergeRenderElementProps.mjs.map +1 -0
- package/dist/esm/helpers/mergeRenderElementProps.native.js +9 -0
- package/dist/esm/helpers/mergeRenderElementProps.native.js.map +1 -0
- package/dist/esm/helpers/mergeSlotStyleProps.js +14 -0
- package/dist/esm/helpers/mergeSlotStyleProps.js.map +6 -0
- package/dist/esm/helpers/mergeSlotStyleProps.mjs +16 -0
- package/dist/esm/helpers/mergeSlotStyleProps.mjs.map +1 -0
- package/dist/esm/helpers/mergeSlotStyleProps.native.js +16 -0
- package/dist/esm/helpers/mergeSlotStyleProps.native.js.map +1 -0
- package/dist/esm/helpers/skipProps.js +1 -1
- package/dist/esm/helpers/skipProps.js.map +1 -1
- package/dist/esm/helpers/skipProps.mjs +1 -1
- package/dist/esm/helpers/skipProps.mjs.map +1 -1
- package/dist/esm/helpers/skipProps.native.js +1 -1
- package/dist/esm/helpers/skipProps.native.js.map +1 -1
- package/dist/esm/helpers/useRenderElement.js +28 -0
- package/dist/esm/helpers/useRenderElement.js.map +6 -0
- package/dist/esm/helpers/useRenderElement.mjs +25 -0
- package/dist/esm/helpers/useRenderElement.mjs.map +1 -0
- package/dist/esm/helpers/useRenderElement.native.js +26 -0
- package/dist/esm/helpers/useRenderElement.native.js.map +1 -0
- package/dist/esm/hooks/getThemeProxied.js.map +1 -1
- package/dist/esm/hooks/getThemeProxied.mjs.map +1 -1
- package/dist/esm/hooks/getThemeProxied.native.js +1 -1
- package/dist/esm/hooks/getThemeProxied.native.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/styled.js.map +1 -1
- package/dist/esm/styled.mjs.map +1 -1
- package/dist/esm/styled.native.js.map +1 -1
- package/dist/esm/views/Configuration.js.map +1 -1
- package/dist/esm/views/Configuration.mjs.map +1 -1
- package/dist/esm/views/Configuration.native.js.map +1 -1
- package/dist/esm/views/Slot.js +10 -14
- package/dist/esm/views/Slot.js.map +1 -1
- package/dist/esm/views/Slot.mjs +7 -21
- package/dist/esm/views/Slot.mjs.map +1 -1
- package/dist/esm/views/Slot.native.js +7 -23
- package/dist/esm/views/Slot.native.js.map +1 -1
- package/dist/esm/views/TamaguiProvider.js +5 -2
- package/dist/esm/views/TamaguiProvider.js.map +1 -1
- package/dist/esm/views/TamaguiProvider.mjs +6 -2
- package/dist/esm/views/TamaguiProvider.mjs.map +1 -1
- package/dist/esm/views/TamaguiProvider.native.js +5 -1
- package/dist/esm/views/TamaguiProvider.native.js.map +1 -1
- package/dist/esm/views/Theme.js.map +1 -1
- package/dist/esm/views/Theme.mjs.map +1 -1
- package/dist/esm/views/Theme.native.js.map +1 -1
- package/package.json +12 -12
- package/src/animationDriverTypes.test-d.ts +256 -0
- package/src/config.ts +41 -0
- package/src/createComponent.tsx +69 -31
- package/src/createTamagui.ts +2 -2
- package/src/helpers/getSplitStyles.tsx +10 -3
- package/src/helpers/getThemeCSSRules.ts +2 -9
- package/src/helpers/mergeRenderElementProps.ts +17 -0
- package/src/helpers/mergeSlotStyleProps.ts +46 -0
- package/src/helpers/skipProps.ts +1 -1
- package/src/helpers/useRenderElement.tsx +66 -0
- package/src/hooks/getThemeProxied.ts +2 -1
- package/src/index.ts +1 -0
- package/src/interfaces/TamaguiComponentPropsBaseBase.tsx +19 -4
- package/src/styled.tsx +4 -3
- package/src/types.tsx +85 -25
- package/src/views/Configuration.tsx +0 -2
- package/src/views/Slot.tsx +14 -32
- package/src/views/TamaguiProvider.tsx +12 -1
- package/src/views/Theme.tsx +0 -6
- package/types/animationDriverTypes.type-test.d.ts +37 -0
- package/types/config.d.ts +15 -1
- package/types/config.d.ts.map +1 -1
- package/types/createComponent.d.ts.map +1 -1
- package/types/helpers/getSplitStyles.d.ts.map +1 -1
- package/types/helpers/getThemeCSSRules.d.ts.map +1 -1
- package/types/helpers/mergeRenderElementProps.d.ts +7 -0
- package/types/helpers/mergeRenderElementProps.d.ts.map +1 -0
- package/types/helpers/mergeSlotStyleProps.d.ts +10 -0
- package/types/helpers/mergeSlotStyleProps.d.ts.map +1 -0
- package/types/helpers/skipProps.d.ts +1 -1
- package/types/helpers/useRenderElement.d.ts +13 -0
- package/types/helpers/useRenderElement.d.ts.map +1 -0
- package/types/hooks/getThemeProxied.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/TamaguiComponentPropsBaseBase.d.ts +8 -4
- package/types/interfaces/TamaguiComponentPropsBaseBase.d.ts.map +1 -1
- package/types/styled.d.ts +1 -0
- package/types/styled.d.ts.map +1 -1
- package/types/types.d.ts +50 -22
- package/types/types.d.ts.map +1 -1
- package/types/views/Configuration.d.ts +0 -2
- package/types/views/Configuration.d.ts.map +1 -1
- package/types/views/Slot.d.ts.map +1 -1
- package/types/views/TamaguiProvider.d.ts.map +1 -1
- package/dist/cjs/createTheme.js.map +0 -6
- package/dist/cjs/createTheme.native.js.map +0 -1
- package/dist/esm/createTheme.js +0 -5
- package/dist/esm/createTheme.js.map +0 -6
- package/dist/esm/createTheme.mjs +0 -3
- package/dist/esm/createTheme.mjs.map +0 -1
- package/dist/esm/createTheme.native.js +0 -5
- package/dist/esm/createTheme.native.js.map +0 -1
- package/types/createTheme.d.ts.map +0 -1
package/src/createComponent.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { composeRefs } from '@tamagui/compose-refs'
|
|
2
2
|
import {
|
|
3
3
|
IS_REACT_19,
|
|
4
|
-
isAndroid,
|
|
5
4
|
isClient,
|
|
6
5
|
isServer,
|
|
7
6
|
isWeb,
|
|
@@ -19,6 +18,7 @@ import { defaultComponentStateMounted } from './defaultComponentState'
|
|
|
19
18
|
import { getSplitStyles, useSplitStyles } from './helpers/getSplitStyles'
|
|
20
19
|
import { log } from './helpers/log'
|
|
21
20
|
import { type GenericProps, mergeComponentProps } from './helpers/mergeProps'
|
|
21
|
+
import { mergeRenderElementProps } from './helpers/mergeRenderElementProps'
|
|
22
22
|
import { objectIdentityKey } from './helpers/objectIdentityKey'
|
|
23
23
|
import { setElementProps } from './helpers/setElementProps'
|
|
24
24
|
import { subscribeToContextGroup } from './helpers/subscribeToContextGroup'
|
|
@@ -415,7 +415,23 @@ export function createComponent<
|
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
const groupContextParent = React.useContext(GroupContext)
|
|
418
|
-
|
|
418
|
+
|
|
419
|
+
// Get animation driver - either from animatedBy prop lookup or context
|
|
420
|
+
const animationDriver = (() => {
|
|
421
|
+
if (props.animatedBy && config?.animations) {
|
|
422
|
+
const animations = config.animations
|
|
423
|
+
// If animations is an object with named drivers (has 'default' key)
|
|
424
|
+
if ('default' in animations) {
|
|
425
|
+
return (
|
|
426
|
+
(animations as Record<string, any>)[props.animatedBy] ?? animations.default
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
// Single driver config - only 'default' makes sense
|
|
430
|
+
return props.animatedBy === 'default' ? animations : null
|
|
431
|
+
}
|
|
432
|
+
return componentContext.animationDriver
|
|
433
|
+
})()
|
|
434
|
+
|
|
419
435
|
const useAnimations = animationDriver?.useAnimations as UseAnimationHook | undefined
|
|
420
436
|
|
|
421
437
|
const componentState = useComponentState(
|
|
@@ -498,7 +514,7 @@ export function createComponent<
|
|
|
498
514
|
}
|
|
499
515
|
}, [stateRef, groupName, groupContextParent])
|
|
500
516
|
|
|
501
|
-
// if our animation driver supports
|
|
517
|
+
// if our animation driver supports avoidReRenders, we'll replace this below with
|
|
502
518
|
// a version that essentially uses an internall emitter rather than setting state
|
|
503
519
|
// but still stores the current state and applies if it it needs to during render
|
|
504
520
|
let setStateShallow = componentState.setStateShallow
|
|
@@ -506,9 +522,9 @@ export function createComponent<
|
|
|
506
522
|
if (process.env.NODE_ENV === 'development' && time) time`use-state`
|
|
507
523
|
|
|
508
524
|
const isTaggable = !Component || typeof Component === 'string'
|
|
509
|
-
const
|
|
510
|
-
// default to
|
|
511
|
-
const element = isWeb ? (isTaggable ?
|
|
525
|
+
const renderProp = props.render
|
|
526
|
+
// default to render prop, fallback to component (when both strings)
|
|
527
|
+
const element = isWeb ? (isTaggable ? renderProp || Component : Component) : Component
|
|
512
528
|
|
|
513
529
|
const BaseTextComponent = BaseText || element || 'span'
|
|
514
530
|
const BaseViewComponent = BaseView || element || (hasTextAncestor ? 'span' : 'div')
|
|
@@ -627,7 +643,7 @@ export function createComponent<
|
|
|
627
643
|
|
|
628
644
|
if (process.env.NODE_ENV === 'development' && time) time`theme`
|
|
629
645
|
|
|
630
|
-
elementType =
|
|
646
|
+
elementType = element || elementType
|
|
631
647
|
const isStringElement = typeof elementType === 'string'
|
|
632
648
|
|
|
633
649
|
const mediaState = useMedia(componentContext, debugProp)
|
|
@@ -919,8 +935,8 @@ export function createComponent<
|
|
|
919
935
|
}
|
|
920
936
|
}
|
|
921
937
|
|
|
922
|
-
if (
|
|
923
|
-
viewProps.
|
|
938
|
+
if (renderProp && elementType['acceptTagProp']) {
|
|
939
|
+
viewProps.render = renderProp
|
|
924
940
|
}
|
|
925
941
|
|
|
926
942
|
// once you set animation prop don't remove it, you can set to undefined/false
|
|
@@ -950,6 +966,7 @@ export function createComponent<
|
|
|
950
966
|
componentState: state,
|
|
951
967
|
styleProps,
|
|
952
968
|
theme,
|
|
969
|
+
themeName,
|
|
953
970
|
pseudos: pseudos || null,
|
|
954
971
|
staticConfig,
|
|
955
972
|
stateRef,
|
|
@@ -1068,37 +1085,45 @@ export function createComponent<
|
|
|
1068
1085
|
})
|
|
1069
1086
|
}
|
|
1070
1087
|
|
|
1071
|
-
//
|
|
1088
|
+
// Animation enter state machine: true -> 'should-enter' -> false
|
|
1089
|
+
// Stage 1: Set 'should-enter' synchronously before paint to apply enterStyle classes
|
|
1090
|
+
// Stage 2: After browser paint, set false to trigger CSS transition
|
|
1091
|
+
//
|
|
1092
|
+
// CRITICAL: useEffect does NOT guarantee post-paint execution!
|
|
1093
|
+
// See: https://thoughtspile.github.io/2021/11/15/unintentional-layout-effect/
|
|
1094
|
+
// When layoutEffect updates state → re-render before paint → useEffect flushes pre-paint
|
|
1095
|
+
// Solution: Double RAF ensures browser has actually painted before we transition
|
|
1072
1096
|
useIsomorphicLayoutEffect(() => {
|
|
1073
|
-
// Note: We removed the early return on disabled to allow animations to work
|
|
1074
|
-
// This fixes the issue where animations wouldn't work on disabled components
|
|
1075
1097
|
if (state.unmounted === true && hasEnterStyle) {
|
|
1076
1098
|
setStateShallow({ unmounted: 'should-enter' })
|
|
1077
1099
|
return
|
|
1078
1100
|
}
|
|
1079
1101
|
|
|
1080
|
-
let tm: NodeJS.Timeout
|
|
1081
1102
|
if (state.unmounted) {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1103
|
+
// For CSS transitions, we need browser to paint enterStyle before removing it.
|
|
1104
|
+
// Double RAF guarantees paint: first RAF schedules after current frame,
|
|
1105
|
+
// second RAF schedules after that frame completes (including paint).
|
|
1106
|
+
if (supportsCSS) {
|
|
1107
|
+
let cancelled = false
|
|
1108
|
+
requestAnimationFrame(() => {
|
|
1109
|
+
if (cancelled) return
|
|
1110
|
+
requestAnimationFrame(() => {
|
|
1111
|
+
if (cancelled) return
|
|
1112
|
+
setStateShallow({ unmounted: false })
|
|
1113
|
+
})
|
|
1087
1114
|
})
|
|
1088
|
-
return () =>
|
|
1089
|
-
|
|
1115
|
+
return () => {
|
|
1116
|
+
cancelled = true
|
|
1117
|
+
}
|
|
1090
1118
|
}
|
|
1091
|
-
|
|
1119
|
+
// Non-CSS drivers handle their own animation timing
|
|
1092
1120
|
setStateShallow({ unmounted: false })
|
|
1093
|
-
return
|
|
1094
1121
|
}
|
|
1095
1122
|
|
|
1096
|
-
// Only subscribe to context group if not disabled
|
|
1097
|
-
|
|
1098
1123
|
return () => {
|
|
1099
1124
|
componentSetStates.delete(setState)
|
|
1100
1125
|
}
|
|
1101
|
-
}, [state.unmounted,
|
|
1126
|
+
}, [state.unmounted, supportsCSS])
|
|
1102
1127
|
|
|
1103
1128
|
useIsomorphicLayoutEffect(() => {
|
|
1104
1129
|
if (disabled) return
|
|
@@ -1377,11 +1402,28 @@ export function createComponent<
|
|
|
1377
1402
|
if (useChildrenResult) {
|
|
1378
1403
|
content = useChildrenResult
|
|
1379
1404
|
} else {
|
|
1380
|
-
|
|
1405
|
+
// Handle render prop variants: function, JSX element, or string
|
|
1406
|
+
if (typeof renderProp === 'function') {
|
|
1407
|
+
// Render function: full control with props and state
|
|
1408
|
+
const renderProps = { ...viewProps, children: content }
|
|
1409
|
+
content = renderProp(renderProps, state)
|
|
1410
|
+
} else if (
|
|
1411
|
+
renderProp &&
|
|
1412
|
+
typeof renderProp === 'object' &&
|
|
1413
|
+
React.isValidElement(renderProp)
|
|
1414
|
+
) {
|
|
1415
|
+
// JSX element: clone with merged props
|
|
1416
|
+
const elementProps = (renderProp as React.ReactElement).props || {}
|
|
1417
|
+
const mergedProps = mergeRenderElementProps(elementProps, viewProps, content)
|
|
1418
|
+
content = React.cloneElement(renderProp as React.ReactElement, mergedProps)
|
|
1419
|
+
} else {
|
|
1420
|
+
content = React.createElement(elementType, viewProps, content)
|
|
1421
|
+
}
|
|
1381
1422
|
}
|
|
1382
1423
|
|
|
1383
1424
|
// needs to reset the presence state for nested children
|
|
1384
|
-
|
|
1425
|
+
// Use the resolved animationDriver (handles multi-driver config)
|
|
1426
|
+
const ResetPresence = animationDriver?.ResetPresence
|
|
1385
1427
|
const needsReset = Boolean(
|
|
1386
1428
|
// not when passing down to child
|
|
1387
1429
|
!asChild &&
|
|
@@ -1463,10 +1505,6 @@ export function createComponent<
|
|
|
1463
1505
|
}
|
|
1464
1506
|
}
|
|
1465
1507
|
|
|
1466
|
-
if (debugProp) {
|
|
1467
|
-
console.info('overriddenContextProps', overriddenContextProps)
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
1508
|
content = (
|
|
1471
1509
|
<Provider __disableMergeDefaultValues {...overriddenContextProps}>
|
|
1472
1510
|
{content}
|
package/src/createTamagui.ts
CHANGED
|
@@ -166,9 +166,9 @@ export function createTamagui<Conf extends CreateTamaguiProps>(
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
const sep =
|
|
169
|
+
const sep = ` `
|
|
170
170
|
function declarationsToRuleSet(decs: string[], selector = '') {
|
|
171
|
-
return `:root${selector} {${sep}${[...decs].join(`;${sep}`)}
|
|
171
|
+
return `:root${selector} {${sep}${[...decs].join(`;${sep}`)}\n}`
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
// non-font
|
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
GetStyleState,
|
|
38
38
|
PseudoStyles,
|
|
39
39
|
RulesToInsert,
|
|
40
|
+
SpaceTokens,
|
|
40
41
|
SplitStyleProps,
|
|
41
42
|
StaticConfig,
|
|
42
43
|
StyleObject,
|
|
@@ -203,6 +204,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
203
204
|
process.env.TAMAGUI_TARGET === 'native' ? (undefined as any) : {}
|
|
204
205
|
const classNames: ClassNamesObject = {}
|
|
205
206
|
|
|
207
|
+
let space: SpaceTokens | null = props.space
|
|
206
208
|
let pseudos: PseudoStyles | null = null
|
|
207
209
|
let hasMedia: boolean | Set<string> = false
|
|
208
210
|
let dynamicThemeAccess: boolean | undefined
|
|
@@ -923,10 +925,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
923
925
|
dynamicThemeAccess = true
|
|
924
926
|
// only apply if this is the current theme
|
|
925
927
|
if (isCurrentScheme) {
|
|
926
|
-
// update
|
|
928
|
+
// update mediaStyle so the later merge loop uses correct value
|
|
927
929
|
mediaStyle[subKey] = val
|
|
928
930
|
} else {
|
|
929
|
-
//
|
|
931
|
+
// remove from mediaStyle so it doesn't get merged with wrong theme's value
|
|
930
932
|
delete mediaStyle[subKey]
|
|
931
933
|
}
|
|
932
934
|
continue
|
|
@@ -1599,9 +1601,14 @@ const animatableDefaults = {
|
|
|
1599
1601
|
),
|
|
1600
1602
|
opacity: 1,
|
|
1601
1603
|
scale: 1,
|
|
1604
|
+
scaleX: 1,
|
|
1605
|
+
scaleY: 1,
|
|
1602
1606
|
rotate: '0deg',
|
|
1603
|
-
rotateY: '0deg',
|
|
1604
1607
|
rotateX: '0deg',
|
|
1608
|
+
rotateY: '0deg',
|
|
1609
|
+
rotateZ: '0deg',
|
|
1610
|
+
skewX: '0deg',
|
|
1611
|
+
skewY: '0deg',
|
|
1605
1612
|
x: 0,
|
|
1606
1613
|
y: 0,
|
|
1607
1614
|
borderRadius: 0,
|
|
@@ -36,8 +36,6 @@ export function getThemeCSSRules(props: {
|
|
|
36
36
|
const CNP = `.${THEME_CLASSNAME_PREFIX}`
|
|
37
37
|
let vars = ''
|
|
38
38
|
|
|
39
|
-
// themeToVariableToValueMap.set(theme, {})
|
|
40
|
-
// const varToValMap = themeToVariableToValueMap.get(theme)
|
|
41
39
|
for (const themeKey in theme) {
|
|
42
40
|
const variable = theme[themeKey] as Variable
|
|
43
41
|
let value: any = null
|
|
@@ -60,9 +58,9 @@ export function getThemeCSSRules(props: {
|
|
|
60
58
|
const selectorsSet = new Set(isDarkBase || isLightBase ? baseSelectors : [])
|
|
61
59
|
|
|
62
60
|
// since we dont specify dark/light in classnames we have to do an awkward specificity war
|
|
63
|
-
//
|
|
61
|
+
// hardcoded to support 2 levels of nesting (e.g. light > dark or dark > light)
|
|
64
62
|
if (hasDarkLight) {
|
|
65
|
-
const maxDepth =
|
|
63
|
+
const maxDepth = 2
|
|
66
64
|
|
|
67
65
|
for (const subName of names) {
|
|
68
66
|
const isDark = isDarkBase || subName.startsWith('dark_')
|
|
@@ -83,7 +81,6 @@ export function getThemeCSSRules(props: {
|
|
|
83
81
|
for (let depth = 0; depth < numSelectors; depth++) {
|
|
84
82
|
const isOdd = depth % 2 === 1
|
|
85
83
|
|
|
86
|
-
// wtf is this continue:
|
|
87
84
|
if (isOdd && depth < 3) {
|
|
88
85
|
continue
|
|
89
86
|
}
|
|
@@ -106,9 +103,6 @@ export function getThemeCSSRules(props: {
|
|
|
106
103
|
// for light/dark/light:
|
|
107
104
|
const parentSelectorString = parentSelectors.join(' ')
|
|
108
105
|
selectorsSet.add(`${parentSelectorString} ${nextChildSelector}`)
|
|
109
|
-
// selectorsSet.add(
|
|
110
|
-
// `${parentSelectors.join(' ')} ${nextChildSelector}.is_inversed`.trim()
|
|
111
|
-
// )
|
|
112
106
|
}
|
|
113
107
|
}
|
|
114
108
|
}
|
|
@@ -116,7 +110,6 @@ export function getThemeCSSRules(props: {
|
|
|
116
110
|
const selectors = [...selectorsSet].sort(sortString)
|
|
117
111
|
|
|
118
112
|
// only do our :root attach if it's not light/dark - not support sub themes on root saves a lot of effort/size
|
|
119
|
-
// this isBaseTheme logic could probably be done more efficiently above
|
|
120
113
|
const selectorsString =
|
|
121
114
|
selectors
|
|
122
115
|
.map((x) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { mergeSlotStyleProps } from './mergeSlotStyleProps'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merges props from a render element with viewProps from Tamagui.
|
|
5
|
+
* viewProps takes precedence, elementProps provides fallbacks.
|
|
6
|
+
* Style/className are merged, refs and event handlers are composed.
|
|
7
|
+
*/
|
|
8
|
+
export function mergeRenderElementProps(
|
|
9
|
+
elementProps: Record<string, any>,
|
|
10
|
+
viewProps: Record<string, any>,
|
|
11
|
+
children: any
|
|
12
|
+
): Record<string, any> {
|
|
13
|
+
// elementProps as base, viewProps as overlay (viewProps wins)
|
|
14
|
+
const merged = mergeSlotStyleProps({ ...elementProps }, viewProps)
|
|
15
|
+
merged.children = children
|
|
16
|
+
return merged
|
|
17
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { composeRefs } from '@tamagui/compose-refs'
|
|
2
|
+
import { composeEventHandlers } from '@tamagui/helpers'
|
|
3
|
+
|
|
4
|
+
const isEventHandler = /^on[A-Z]/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Merges props with special handling for style, className, ref, and event handlers.
|
|
8
|
+
* Used by Slot and render prop implementations.
|
|
9
|
+
*
|
|
10
|
+
* @param base - Base props (typically from parent/slot)
|
|
11
|
+
* @param overlay - Props to merge on top (typically from child/element)
|
|
12
|
+
* @returns Merged props object (mutates and returns base for perf)
|
|
13
|
+
*/
|
|
14
|
+
export function mergeSlotStyleProps(
|
|
15
|
+
base: Record<string, any>,
|
|
16
|
+
overlay: Record<string, any>
|
|
17
|
+
): Record<string, any> {
|
|
18
|
+
for (const key in overlay) {
|
|
19
|
+
const baseVal = base[key]
|
|
20
|
+
const overlayVal = overlay[key]
|
|
21
|
+
|
|
22
|
+
if (overlayVal === undefined) continue
|
|
23
|
+
|
|
24
|
+
if (key === 'style') {
|
|
25
|
+
base.style =
|
|
26
|
+
baseVal && overlayVal ? { ...baseVal, ...overlayVal } : overlayVal || baseVal
|
|
27
|
+
} else if (key === 'className') {
|
|
28
|
+
base.className =
|
|
29
|
+
baseVal && overlayVal ? `${baseVal} ${overlayVal}` : overlayVal || baseVal
|
|
30
|
+
} else if (key === 'ref') {
|
|
31
|
+
base.ref =
|
|
32
|
+
baseVal && overlayVal ? composeRefs(baseVal, overlayVal) : overlayVal || baseVal
|
|
33
|
+
} else if (
|
|
34
|
+
isEventHandler.test(key) &&
|
|
35
|
+
typeof baseVal === 'function' &&
|
|
36
|
+
typeof overlayVal === 'function'
|
|
37
|
+
) {
|
|
38
|
+
base[key] = composeEventHandlers(baseVal, overlayVal)
|
|
39
|
+
} else {
|
|
40
|
+
// overlay wins for regular props
|
|
41
|
+
base[key] = overlayVal
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return base
|
|
46
|
+
}
|
package/src/helpers/skipProps.ts
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
import { cloneElement, createElement, isValidElement } from 'react'
|
|
3
|
+
import { composeRefs } from '@tamagui/compose-refs'
|
|
4
|
+
import type { TamaguiComponentState } from '../types'
|
|
5
|
+
import { mergeSlotStyleProps } from './mergeSlotStyleProps'
|
|
6
|
+
|
|
7
|
+
export type RenderProp<Props = Record<string, any>> =
|
|
8
|
+
| string
|
|
9
|
+
| React.ReactElement
|
|
10
|
+
| ((props: Props, state: TamaguiComponentState) => React.ReactElement)
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Evaluates a render prop and returns the element to render.
|
|
14
|
+
*
|
|
15
|
+
* @param render - The render prop (tag string, JSX element, or function)
|
|
16
|
+
* @param props - Props to pass to the rendered element (including ref)
|
|
17
|
+
* @param state - Component state for render functions
|
|
18
|
+
* @param defaultElement - Fallback element if render prop is not provided
|
|
19
|
+
*/
|
|
20
|
+
export function evaluateRenderProp(
|
|
21
|
+
render: RenderProp | undefined,
|
|
22
|
+
props: Record<string, any>,
|
|
23
|
+
state: TamaguiComponentState,
|
|
24
|
+
defaultElement: React.ReactElement<any>
|
|
25
|
+
): React.ReactElement {
|
|
26
|
+
if (!render) {
|
|
27
|
+
return defaultElement
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const defaultChildren = defaultElement.props.children
|
|
31
|
+
|
|
32
|
+
// String tag - swap element type, reuse props from defaultElement
|
|
33
|
+
if (typeof render === 'string') {
|
|
34
|
+
return createElement(render, props, defaultChildren)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Render function - call with props and state
|
|
38
|
+
if (typeof render === 'function') {
|
|
39
|
+
return render(props, state)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// JSX element - clone with merged props
|
|
43
|
+
if (isValidElement(render)) {
|
|
44
|
+
const renderProps = render.props as Record<string, any>
|
|
45
|
+
const renderRef = renderProps?.ref
|
|
46
|
+
|
|
47
|
+
// Fast path: no props to merge
|
|
48
|
+
if (!renderProps || Object.keys(renderProps).length === 0) {
|
|
49
|
+
if (renderRef) {
|
|
50
|
+
return cloneElement(
|
|
51
|
+
render,
|
|
52
|
+
{ ...props, ref: composeRefs(props.ref, renderRef) } as any,
|
|
53
|
+
defaultChildren
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
return cloneElement(render, props as any, defaultChildren)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Merge props (component props as base, render props as overlay)
|
|
60
|
+
const merged = mergeSlotStyleProps({ ...props }, renderProps)
|
|
61
|
+
const children = renderProps.children ?? defaultChildren
|
|
62
|
+
return cloneElement(render, merged as any, children)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return defaultElement
|
|
66
|
+
}
|
|
@@ -112,12 +112,13 @@ export function getThemeProxied(
|
|
|
112
112
|
|
|
113
113
|
if (process.env.TAMAGUI_TARGET === 'native') {
|
|
114
114
|
// ios can avoid re-rendering in some cases when we are using a root light/dark
|
|
115
|
-
// disabled in cases where we have animations
|
|
115
|
+
// disabled in cases where we have animations or when scheme changes from parent (isInverse)
|
|
116
116
|
const shouldOptimize =
|
|
117
117
|
scheme &&
|
|
118
118
|
platform !== 'web' &&
|
|
119
119
|
isIos &&
|
|
120
120
|
!curProps.deopt &&
|
|
121
|
+
!curState.isInverse &&
|
|
121
122
|
getSetting('fastSchemeChange') &&
|
|
122
123
|
doesRootSchemeMatchSystem()
|
|
123
124
|
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DebugProp,
|
|
3
|
+
ThemeName,
|
|
4
|
+
GroupNames,
|
|
5
|
+
Role,
|
|
6
|
+
TamaguiComponentState,
|
|
7
|
+
} from '../types'
|
|
2
8
|
|
|
3
9
|
export type TamaguiComponentPropsBaseBase = {
|
|
4
10
|
target?: string
|
|
@@ -41,10 +47,19 @@ export type TamaguiComponentPropsBaseBase = {
|
|
|
41
47
|
id?: string
|
|
42
48
|
|
|
43
49
|
/**
|
|
44
|
-
* Controls the
|
|
45
|
-
*
|
|
50
|
+
* Controls the rendered element on web.
|
|
51
|
+
* - String: renders as that HTML element (e.g., `render="button"`)
|
|
52
|
+
* - JSX Element: clones element with merged props (e.g., `render={<a href="/" />}`)
|
|
53
|
+
* - Function: full control with props and state (e.g., `render={(props) => <Custom {...props} />}`)
|
|
46
54
|
*/
|
|
47
|
-
|
|
55
|
+
render?:
|
|
56
|
+
| keyof HTMLElementTagNameMap
|
|
57
|
+
| (string & {})
|
|
58
|
+
| React.ReactElement
|
|
59
|
+
| ((
|
|
60
|
+
props: Record<string, any> & { ref?: React.Ref<any> },
|
|
61
|
+
state: TamaguiComponentState
|
|
62
|
+
) => React.ReactElement)
|
|
48
63
|
|
|
49
64
|
/**
|
|
50
65
|
* Applies a theme to this element
|
package/src/styled.tsx
CHANGED
|
@@ -46,6 +46,7 @@ export function styled<
|
|
|
46
46
|
variants?: Variants | undefined
|
|
47
47
|
defaultVariants?: GetVariantAcceptedValues<Variants>
|
|
48
48
|
context?: StyledContext
|
|
49
|
+
render?: string
|
|
49
50
|
},
|
|
50
51
|
config?: StyledConfig
|
|
51
52
|
) {
|
|
@@ -289,13 +290,13 @@ export function styled<
|
|
|
289
290
|
// })
|
|
290
291
|
|
|
291
292
|
// const Test2 = styled(Text1, {
|
|
292
|
-
//
|
|
293
|
+
// render: 'p',
|
|
293
294
|
// userSelect: 'auto',
|
|
294
295
|
// color: '$color',
|
|
295
296
|
// })
|
|
296
297
|
|
|
297
298
|
// const Test3 = styled(Test2, {
|
|
298
|
-
//
|
|
299
|
+
// render: 'p',
|
|
299
300
|
// userSelect: 'auto',
|
|
300
301
|
// color: '$color',
|
|
301
302
|
|
|
@@ -307,7 +308,7 @@ export function styled<
|
|
|
307
308
|
// })
|
|
308
309
|
|
|
309
310
|
// const Test = styled(Paragraph, {
|
|
310
|
-
//
|
|
311
|
+
// render: 'p',
|
|
311
312
|
// userSelect: 'auto',
|
|
312
313
|
// color: '$color',
|
|
313
314
|
|