@tamagui/web 2.3.3 → 2.4.0-1783110557100
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/.turbo/turbo-build.log +1 -1
- package/dist/cjs/Tamagui.native.js.map +1 -1
- package/dist/cjs/_withStableStyle.native.js.map +1 -1
- package/dist/cjs/animationDriverTypes.test-d.native.js.map +1 -1
- package/dist/cjs/config.native.js.map +1 -1
- package/dist/cjs/constants/constants.native.js.map +1 -1
- package/dist/cjs/constants/isDevTools.native.js.map +1 -1
- package/dist/cjs/contexts/ComponentContext.native.js.map +1 -1
- package/dist/cjs/contexts/GroupContext.native.js.map +1 -1
- package/dist/cjs/createComponent.cjs +4 -3
- package/dist/cjs/createComponent.native.js +7 -6
- package/dist/cjs/createComponent.native.js.map +1 -1
- package/dist/cjs/createFont.native.js.map +1 -1
- package/dist/cjs/createShorthands.native.js.map +1 -1
- package/dist/cjs/createTamagui.native.js.map +1 -1
- package/dist/cjs/createTokens.native.js.map +1 -1
- package/dist/cjs/createVariable.native.js.map +1 -1
- package/dist/cjs/createVariables.native.js.map +1 -1
- package/dist/cjs/defaultComponentState.native.js.map +1 -1
- package/dist/cjs/eventHandling.cjs +1 -1
- package/dist/cjs/eventHandling.native.js +12 -36
- package/dist/cjs/eventHandling.native.js.map +1 -1
- package/dist/cjs/helpers/createDesignSystem.native.js.map +1 -1
- package/dist/cjs/helpers/createMediaStyle.native.js.map +1 -1
- package/dist/cjs/helpers/createStyledContext.native.js.map +1 -1
- package/dist/cjs/helpers/defaultAnimationDriver.native.js.map +1 -1
- package/dist/cjs/helpers/defaultOffset.native.js.map +1 -1
- package/dist/cjs/helpers/expandStyle.native.js.map +1 -1
- package/dist/cjs/helpers/expandStyles.native.js.map +1 -1
- package/dist/cjs/helpers/getDefaultProps.native.js.map +1 -1
- package/dist/cjs/helpers/getDynamicVal.native.js.map +1 -1
- package/dist/cjs/helpers/getExpandedShorthands.native.js.map +1 -1
- package/dist/cjs/helpers/getFontLanguage.native.js.map +1 -1
- package/dist/cjs/helpers/getGroupPropParts.native.js.map +1 -1
- package/dist/cjs/helpers/getShorthandValue.native.js.map +1 -1
- package/dist/cjs/helpers/getSplitStyles.cjs +5 -0
- package/dist/cjs/helpers/getSplitStyles.native.js +9 -0
- package/dist/cjs/helpers/getSplitStyles.native.js.map +1 -1
- package/dist/cjs/helpers/getTokenForKey.native.js.map +1 -1
- package/dist/cjs/helpers/getVariantExtras.native.js.map +1 -1
- package/dist/cjs/helpers/insertStyleRule.native.js.map +1 -1
- package/dist/cjs/helpers/isActivePlatform.native.js.map +1 -1
- package/dist/cjs/helpers/isActiveTheme.native.js.map +1 -1
- package/dist/cjs/helpers/isObj.native.js.map +1 -1
- package/dist/cjs/helpers/isTamaguiComponent.native.js.map +1 -1
- package/dist/cjs/helpers/isTamaguiElement.native.js.map +1 -1
- package/dist/cjs/helpers/log.native.js.map +1 -1
- package/dist/cjs/helpers/mainThreadPressEvents.native.js +20 -3
- package/dist/cjs/helpers/mainThreadPressEvents.native.js.map +1 -1
- package/dist/cjs/helpers/mediaObjectToString.native.js.map +1 -1
- package/dist/cjs/helpers/mediaState.native.js.map +1 -1
- package/dist/cjs/helpers/mergeProps.native.js.map +1 -1
- package/dist/cjs/helpers/mergeRenderElementProps.native.js.map +1 -1
- package/dist/cjs/helpers/mergeSlotStyleProps.native.js.map +1 -1
- package/dist/cjs/helpers/mergeVariants.native.js.map +1 -1
- package/dist/cjs/helpers/nativeOnlyProps.native.js.map +1 -1
- package/dist/cjs/helpers/normalizeStyle.native.js.map +1 -1
- package/dist/cjs/helpers/normalizeValueWithProperty.native.js.map +1 -1
- package/dist/cjs/helpers/objectIdentityKey.native.js.map +1 -1
- package/dist/cjs/helpers/parseNativeStyle.native.js.map +1 -1
- package/dist/cjs/helpers/pointerEvents.native.js +26 -20
- package/dist/cjs/helpers/pointerEvents.native.js.map +1 -1
- package/dist/cjs/helpers/propMapper.native.js +40 -15
- package/dist/cjs/helpers/propMapper.native.js.map +1 -1
- package/dist/cjs/helpers/proxyThemeToParents.native.js.map +1 -1
- package/dist/cjs/helpers/proxyThemeVariables.native.js.map +1 -1
- package/dist/cjs/helpers/pseudoDescriptors.native.js.map +1 -1
- package/dist/cjs/helpers/pseudoTransitions.native.js.map +1 -1
- package/dist/cjs/helpers/registerCSSVariable.native.js.map +1 -1
- package/dist/cjs/helpers/resolveAnimationDriver.native.js.map +1 -1
- package/dist/cjs/helpers/resolveCompoundTokens.native.js.map +1 -1
- package/dist/cjs/helpers/skipProps.native.js.map +1 -1
- package/dist/cjs/helpers/sortString.native.js.map +1 -1
- package/dist/cjs/helpers/styleOriginalValues.native.js.map +1 -1
- package/dist/cjs/helpers/subscribeToContextGroup.native.js.map +1 -1
- package/dist/cjs/helpers/themeable.native.js.map +1 -1
- package/dist/cjs/helpers/themes.native.js.map +1 -1
- package/dist/cjs/helpers/timer.native.js.map +1 -1
- package/dist/cjs/helpers/transformsToString.native.js.map +1 -1
- package/dist/cjs/helpers/useRenderElement.native.js.map +1 -1
- package/dist/cjs/helpers/wrapStyleTags.native.js.map +1 -1
- package/dist/cjs/hooks/getThemeProxied.native.js.map +1 -1
- package/dist/cjs/hooks/useComponentState.cjs +25 -1
- package/dist/cjs/hooks/useComponentState.native.js +25 -1
- package/dist/cjs/hooks/useComponentState.native.js.map +1 -1
- package/dist/cjs/hooks/useDisableSSR.native.js.map +1 -1
- package/dist/cjs/hooks/useIsTouchDevice.native.js.map +1 -1
- package/dist/cjs/hooks/useMedia.cjs +107 -42
- package/dist/cjs/hooks/useMedia.native.js +149 -60
- package/dist/cjs/hooks/useMedia.native.js.map +1 -1
- package/dist/cjs/hooks/useProps.native.js.map +1 -1
- package/dist/cjs/hooks/useTheme.cjs +17 -4
- package/dist/cjs/hooks/useTheme.native.js +18 -4
- package/dist/cjs/hooks/useTheme.native.js.map +1 -1
- package/dist/cjs/hooks/useThemeName.native.js.map +1 -1
- package/dist/cjs/hooks/useThemeState.cjs +132 -67
- package/dist/cjs/hooks/useThemeState.native.js +144 -70
- package/dist/cjs/hooks/useThemeState.native.js.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.native.js +2 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/inject-styles.native.js.map +1 -1
- package/dist/cjs/insertFont.native.js.map +1 -1
- package/dist/cjs/interfaces/CSSColorNames.native.js.map +1 -1
- package/dist/cjs/interfaces/GetRef.native.js.map +1 -1
- package/dist/cjs/interfaces/RNExclusiveTypes.native.js.map +1 -1
- package/dist/cjs/interfaces/TamaguiComponentEvents.native.js.map +1 -1
- package/dist/cjs/interfaces/TamaguiComponentPropsBaseBase.native.js.map +1 -1
- package/dist/cjs/opacityModifier.test-d.native.js.map +1 -1
- package/dist/cjs/setupHooks.native.js.map +1 -1
- package/dist/cjs/setupReactNative.native.js.map +1 -1
- package/dist/cjs/styled.native.js.map +1 -1
- package/dist/cjs/styledHtml.test-d.native.js.map +1 -1
- package/dist/cjs/styledNonStyleProps.test-d.native.js.map +1 -1
- package/dist/cjs/type-utils.native.js.map +1 -1
- package/dist/cjs/types.native.js.map +1 -1
- package/dist/cjs/views/Configuration.native.js.map +1 -1
- package/dist/cjs/views/Slot.native.js.map +1 -1
- package/dist/cjs/views/TamaguiProvider.native.js.map +1 -1
- package/dist/cjs/views/Text.native.js.map +1 -1
- package/dist/cjs/views/Theme.cjs +1 -1
- package/dist/cjs/views/Theme.native.js +1 -1
- package/dist/cjs/views/Theme.native.js.map +1 -1
- package/dist/cjs/views/ThemeProvider.native.js.map +1 -1
- package/dist/cjs/views/View.native.js.map +1 -1
- package/dist/esm/createComponent.mjs +4 -3
- package/dist/esm/createComponent.mjs.map +1 -1
- package/dist/esm/createComponent.native.js +10 -8
- package/dist/esm/createComponent.native.js.map +1 -1
- package/dist/esm/eventHandling.mjs +1 -1
- package/dist/esm/eventHandling.mjs.map +1 -1
- package/dist/esm/eventHandling.native.js +12 -36
- package/dist/esm/eventHandling.native.js.map +1 -1
- package/dist/esm/helpers/getSplitStyles.mjs +5 -0
- package/dist/esm/helpers/getSplitStyles.mjs.map +1 -1
- package/dist/esm/helpers/getSplitStyles.native.js +9 -0
- package/dist/esm/helpers/getSplitStyles.native.js.map +1 -1
- package/dist/esm/helpers/mainThreadPressEvents.native.js +20 -3
- package/dist/esm/helpers/mainThreadPressEvents.native.js.map +1 -1
- package/dist/esm/helpers/pointerEvents.native.js +26 -20
- package/dist/esm/helpers/pointerEvents.native.js.map +1 -1
- package/dist/esm/helpers/propMapper.native.js +40 -15
- package/dist/esm/helpers/propMapper.native.js.map +1 -1
- package/dist/esm/hooks/useComponentState.mjs +26 -2
- package/dist/esm/hooks/useComponentState.mjs.map +1 -1
- package/dist/esm/hooks/useComponentState.native.js +26 -2
- package/dist/esm/hooks/useComponentState.native.js.map +1 -1
- package/dist/esm/hooks/useMedia.mjs +108 -43
- package/dist/esm/hooks/useMedia.mjs.map +1 -1
- package/dist/esm/hooks/useMedia.native.js +150 -61
- package/dist/esm/hooks/useMedia.native.js.map +1 -1
- package/dist/esm/hooks/useTheme.mjs +17 -4
- package/dist/esm/hooks/useTheme.mjs.map +1 -1
- package/dist/esm/hooks/useTheme.native.js +18 -4
- package/dist/esm/hooks/useTheme.native.js.map +1 -1
- package/dist/esm/hooks/useThemeState.mjs +133 -68
- package/dist/esm/hooks/useThemeState.mjs.map +1 -1
- package/dist/esm/hooks/useThemeState.native.js +145 -71
- package/dist/esm/hooks/useThemeState.native.js.map +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +3 -3
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -3
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/views/Theme.mjs +1 -1
- package/dist/esm/views/Theme.mjs.map +1 -1
- package/dist/esm/views/Theme.native.js +1 -1
- package/dist/esm/views/Theme.native.js.map +1 -1
- package/package.json +13 -13
- package/src/createComponent.tsx +18 -7
- package/src/eventHandling.native.ts +28 -40
- package/src/eventHandling.ts +2 -1
- package/src/helpers/getSplitStyles.tsx +33 -0
- package/src/helpers/mainThreadPressEvents.native.ts +25 -3
- package/src/helpers/pointerEvents.native.ts +26 -19
- package/src/helpers/propMapper.ts +23 -0
- package/src/hooks/useComponentState.ts +39 -2
- package/src/hooks/useMedia.tsx +147 -42
- package/src/hooks/useTheme.tsx +27 -5
- package/src/hooks/useThemeState.ts +263 -137
- package/src/index.ts +7 -1
- package/src/types.tsx +53 -5
- package/src/views/Theme.tsx +4 -1
- package/types/createComponent.d.ts.map +1 -1
- package/types/eventHandling.d.ts +1 -1
- package/types/eventHandling.d.ts.map +1 -1
- package/types/eventHandling.native.d.ts +2 -2
- package/types/eventHandling.native.d.ts.map +1 -1
- package/types/helpers/getSplitStyles.d.ts.map +1 -1
- package/types/helpers/mainThreadPressEvents.native.d.ts.map +1 -1
- package/types/helpers/pointerEvents.native.d.ts.map +1 -1
- package/types/helpers/propMapper.d.ts.map +1 -1
- package/types/hooks/useComponentState.d.ts +1 -1
- package/types/hooks/useComponentState.d.ts.map +1 -1
- package/types/hooks/useMedia.d.ts.map +1 -1
- package/types/hooks/useTheme.d.ts +1 -1
- package/types/hooks/useTheme.d.ts.map +1 -1
- package/types/hooks/useThemeState.d.ts +1 -1
- package/types/hooks/useThemeState.d.ts.map +1 -1
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/types.d.ts +3 -1
- package/types/types.d.ts.map +1 -1
- package/types/views/Theme.d.ts.map +1 -1
package/src/eventHandling.ts
CHANGED
|
@@ -153,6 +153,15 @@ function isValidStyleKey(
|
|
|
153
153
|
return key in validStyles ? true : accept && key in accept
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function shouldSkipNativeHoverProp(key: string, isMedia: false | boolean | string) {
|
|
157
|
+
if (process.env.TAMAGUI_TARGET !== 'native') return false
|
|
158
|
+
if (key === 'hoverStyle') return true
|
|
159
|
+
if (isMedia === 'group') {
|
|
160
|
+
return getGroupPropParts(key.slice(1)).pseudo === 'hover'
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
|
|
156
165
|
export const getSplitStyles: StyleSplitter = (
|
|
157
166
|
props,
|
|
158
167
|
staticConfig,
|
|
@@ -305,6 +314,18 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
305
314
|
continue
|
|
306
315
|
}
|
|
307
316
|
|
|
317
|
+
// native: data-* attributes never become native props (they're stripped
|
|
318
|
+
// further down anyway), and the compiler-emitted data-disable-theme/-media
|
|
319
|
+
// flags are already consumed in createComponent. skip them before any per-prop
|
|
320
|
+
// work so they don't pay the isValidStyleKey + handling cost on the hot path.
|
|
321
|
+
if (
|
|
322
|
+
process.env.TAMAGUI_TARGET === 'native' &&
|
|
323
|
+
keyInit[0] === 'd' &&
|
|
324
|
+
keyInit.startsWith('data-')
|
|
325
|
+
) {
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
|
|
308
329
|
if (
|
|
309
330
|
process.env.NODE_ENV === 'development' &&
|
|
310
331
|
(debug === 'profile' || (globalThis as any).time)
|
|
@@ -481,6 +502,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
481
502
|
|
|
482
503
|
const isStyleProp = isValidStyleKeyInit || isMediaOrPseudo || (isVariant && !noExpand)
|
|
483
504
|
|
|
505
|
+
if (shouldSkipNativeHoverProp(keyInit, isMedia)) {
|
|
506
|
+
continue
|
|
507
|
+
}
|
|
508
|
+
|
|
484
509
|
if (isStyleProp && (asChild === 'except-style' || asChild === 'except-style-web')) {
|
|
485
510
|
continue
|
|
486
511
|
}
|
|
@@ -625,6 +650,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
625
650
|
key = normalizeGroupKey(key, groupContext)
|
|
626
651
|
}
|
|
627
652
|
|
|
653
|
+
if (shouldSkipNativeHoverProp(key, isMedia)) {
|
|
654
|
+
return
|
|
655
|
+
}
|
|
656
|
+
|
|
628
657
|
if (
|
|
629
658
|
inlineProps?.has(key) ||
|
|
630
659
|
(process.env.IS_STATIC === 'is_static' && inlineWhenUnflattened?.has(key))
|
|
@@ -948,6 +977,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
948
977
|
const groupPseudoKey = groupInfo.pseudo
|
|
949
978
|
const groupMediaKey = groupInfo.media
|
|
950
979
|
|
|
980
|
+
if (process.env.TAMAGUI_TARGET === 'native' && groupPseudoKey === 'hover') {
|
|
981
|
+
return
|
|
982
|
+
}
|
|
983
|
+
|
|
951
984
|
if (!groupState) {
|
|
952
985
|
if (process.env.NODE_ENV === 'development' && debug) {
|
|
953
986
|
log(`No parent with group prop, skipping styles: ${groupName}`)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* long press, cancellation, and min press duration.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { unstable_hasExternalPressOwnership } from '@tamagui/native'
|
|
9
10
|
import { useRef } from 'react'
|
|
10
11
|
|
|
11
12
|
type PressState =
|
|
@@ -20,6 +21,7 @@ interface PressRef {
|
|
|
20
21
|
pressOutTimer: ReturnType<typeof setTimeout> | null
|
|
21
22
|
longPressTimer: ReturnType<typeof setTimeout> | null
|
|
22
23
|
activateTime: number
|
|
24
|
+
blockedByExternalOwnership: boolean
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
const DEFAULT_LONG_PRESS_DELAY = 500
|
|
@@ -39,6 +41,7 @@ export function useMainThreadPressEvents(
|
|
|
39
41
|
pressOutTimer: null,
|
|
40
42
|
longPressTimer: null,
|
|
41
43
|
activateTime: 0,
|
|
44
|
+
blockedByExternalOwnership: false,
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
|
|
@@ -92,12 +95,22 @@ export function useMainThreadPressEvents(
|
|
|
92
95
|
const userTerminationRequest = viewProps.onResponderTerminationRequest
|
|
93
96
|
const userMove = viewProps.onResponderMove
|
|
94
97
|
|
|
95
|
-
viewProps.onStartShouldSetResponder = (e: any) =>
|
|
96
|
-
|
|
98
|
+
viewProps.onStartShouldSetResponder = (e: any) => {
|
|
99
|
+
if (userStartShouldSet?.(e)) return true
|
|
100
|
+
return !events.disabled && !unstable_hasExternalPressOwnership()
|
|
101
|
+
}
|
|
97
102
|
|
|
98
103
|
viewProps.onResponderGrant = (e: any) => {
|
|
99
|
-
userGrant?.(e)
|
|
100
104
|
cleanup()
|
|
105
|
+
|
|
106
|
+
if (unstable_hasExternalPressOwnership()) {
|
|
107
|
+
ref.current.state = 'idle'
|
|
108
|
+
ref.current.blockedByExternalOwnership = true
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
userGrant?.(e)
|
|
113
|
+
ref.current.blockedByExternalOwnership = false
|
|
101
114
|
ref.current.state = 'pressing'
|
|
102
115
|
|
|
103
116
|
if (delayPressIn > 0) {
|
|
@@ -117,6 +130,13 @@ export function useMainThreadPressEvents(
|
|
|
117
130
|
}
|
|
118
131
|
|
|
119
132
|
viewProps.onResponderRelease = (e: any) => {
|
|
133
|
+
if (ref.current.blockedByExternalOwnership || unstable_hasExternalPressOwnership()) {
|
|
134
|
+
cleanup()
|
|
135
|
+
ref.current.blockedByExternalOwnership = false
|
|
136
|
+
ref.current.state = 'idle'
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
userRelease?.(e)
|
|
121
141
|
const wasLongPressed = ref.current.state === 'longPressed'
|
|
122
142
|
cleanup()
|
|
@@ -132,6 +152,7 @@ export function useMainThreadPressEvents(
|
|
|
132
152
|
|
|
133
153
|
deactivate(e)
|
|
134
154
|
ref.current.state = 'idle'
|
|
155
|
+
ref.current.blockedByExternalOwnership = false
|
|
135
156
|
}
|
|
136
157
|
|
|
137
158
|
viewProps.onResponderTerminate = (e: any) => {
|
|
@@ -141,6 +162,7 @@ export function useMainThreadPressEvents(
|
|
|
141
162
|
deactivate(e)
|
|
142
163
|
}
|
|
143
164
|
ref.current.state = 'idle'
|
|
165
|
+
ref.current.blockedByExternalOwnership = false
|
|
144
166
|
}
|
|
145
167
|
|
|
146
168
|
viewProps.onResponderTerminationRequest = (e: any) => {
|
|
@@ -25,13 +25,20 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
25
25
|
onPointerEnter ||
|
|
26
26
|
onPointerLeave
|
|
27
27
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// single ref-bag (was three separate useRef slots) — one hook slot per
|
|
29
|
+
// component instead of three. isInside = pointer within bounds (enter/leave),
|
|
30
|
+
// layout = measured bounds, isCaptured = setPointerCapture state for moves.
|
|
31
|
+
const ref = useRef<{
|
|
32
|
+
isInside: boolean
|
|
33
|
+
layout: { width: number; height: number }
|
|
34
|
+
isCaptured: boolean
|
|
35
|
+
} | null>(null)
|
|
36
|
+
if (!ref.current) {
|
|
37
|
+
ref.current = { isInside: false, layout: { width: 0, height: 0 }, isCaptured: false }
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
if (!hasPointerEvents) return
|
|
41
|
+
const bag = ref.current
|
|
35
42
|
|
|
36
43
|
// create normalized event with setPointerCapture support
|
|
37
44
|
const createNormalizedEvent = (e: any) => {
|
|
@@ -49,10 +56,10 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
49
56
|
nativeEvent: touch,
|
|
50
57
|
target: {
|
|
51
58
|
setPointerCapture: (_pointerId: number) => {
|
|
52
|
-
|
|
59
|
+
bag.isCaptured = true
|
|
53
60
|
},
|
|
54
61
|
releasePointerCapture: (_pointerId: number) => {
|
|
55
|
-
|
|
62
|
+
bag.isCaptured = false
|
|
56
63
|
},
|
|
57
64
|
},
|
|
58
65
|
}
|
|
@@ -69,7 +76,7 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
69
76
|
// pointer up
|
|
70
77
|
if (onPointerUp) {
|
|
71
78
|
viewProps.onTouchEnd = composeEventHandlers(viewProps.onTouchEnd, (e: any) => {
|
|
72
|
-
|
|
79
|
+
bag.isCaptured = false // auto-release on up
|
|
73
80
|
onPointerUp(createNormalizedEvent(e))
|
|
74
81
|
})
|
|
75
82
|
}
|
|
@@ -78,12 +85,12 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
78
85
|
if (onPointerMove) {
|
|
79
86
|
viewProps.onTouchMove = composeEventHandlers(viewProps.onTouchMove, (e: any) => {
|
|
80
87
|
const { locationX, locationY } = e.nativeEvent
|
|
81
|
-
const { width, height } =
|
|
88
|
+
const { width, height } = bag.layout
|
|
82
89
|
const isInBounds =
|
|
83
90
|
locationX >= 0 && locationX <= width && locationY >= 0 && locationY <= height
|
|
84
91
|
|
|
85
92
|
// fire if captured OR in bounds (matches web behavior)
|
|
86
|
-
if (
|
|
93
|
+
if (bag.isCaptured || isInBounds) {
|
|
87
94
|
onPointerMove(createNormalizedEvent(e))
|
|
88
95
|
}
|
|
89
96
|
})
|
|
@@ -92,7 +99,7 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
92
99
|
// pointer cancel
|
|
93
100
|
if (onPointerCancel) {
|
|
94
101
|
viewProps.onTouchCancel = composeEventHandlers(viewProps.onTouchCancel, (e: any) => {
|
|
95
|
-
|
|
102
|
+
bag.isCaptured = false
|
|
96
103
|
onPointerCancel(createNormalizedEvent(e))
|
|
97
104
|
})
|
|
98
105
|
}
|
|
@@ -101,7 +108,7 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
101
108
|
if (onPointerEnter || onPointerLeave || onPointerMove) {
|
|
102
109
|
// track layout for bounds checking
|
|
103
110
|
viewProps.onLayout = composeEventHandlers(viewProps.onLayout, (e: any) => {
|
|
104
|
-
|
|
111
|
+
bag.layout = {
|
|
105
112
|
width: e.nativeEvent.layout.width,
|
|
106
113
|
height: e.nativeEvent.layout.height,
|
|
107
114
|
}
|
|
@@ -112,9 +119,9 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
112
119
|
if (onPointerEnter) {
|
|
113
120
|
viewProps.onTouchStart = composeEventHandlers(viewProps.onTouchStart, (e: any) => {
|
|
114
121
|
const { locationX, locationY } = e.nativeEvent
|
|
115
|
-
const { width, height } =
|
|
122
|
+
const { width, height } = bag.layout
|
|
116
123
|
if (locationX >= 0 && locationX <= width && locationY >= 0 && locationY <= height) {
|
|
117
|
-
|
|
124
|
+
bag.isInside = true
|
|
118
125
|
onPointerEnter(createNormalizedEvent(e))
|
|
119
126
|
}
|
|
120
127
|
})
|
|
@@ -124,18 +131,18 @@ export function usePointerEvents(props: any, viewProps: any) {
|
|
|
124
131
|
if (onPointerLeave) {
|
|
125
132
|
viewProps.onTouchMove = composeEventHandlers(viewProps.onTouchMove, (e: any) => {
|
|
126
133
|
const { locationX, locationY } = e.nativeEvent
|
|
127
|
-
const { width, height } =
|
|
134
|
+
const { width, height } = bag.layout
|
|
128
135
|
const isInside =
|
|
129
136
|
locationX >= 0 && locationX <= width && locationY >= 0 && locationY <= height
|
|
130
|
-
if (
|
|
131
|
-
|
|
137
|
+
if (bag.isInside && !isInside) {
|
|
138
|
+
bag.isInside = false
|
|
132
139
|
onPointerLeave(createNormalizedEvent(e))
|
|
133
140
|
}
|
|
134
141
|
})
|
|
135
142
|
|
|
136
143
|
viewProps.onTouchEnd = composeEventHandlers(viewProps.onTouchEnd, (e: any) => {
|
|
137
|
-
if (
|
|
138
|
-
|
|
144
|
+
if (bag.isInside) {
|
|
145
|
+
bag.isInside = false
|
|
139
146
|
onPointerLeave(createNormalizedEvent(e))
|
|
140
147
|
}
|
|
141
148
|
})
|
|
@@ -43,6 +43,29 @@ export const propMapper: PropMapper = (key, value, styleState, disabled, map) =>
|
|
|
43
43
|
const { conf, styleProps, staticConfig } = styleState
|
|
44
44
|
const { variants } = staticConfig
|
|
45
45
|
|
|
46
|
+
// "unset" is a CSS-wide keyword: valid CSS on web, but React Native
|
|
47
|
+
// style props reject it (e.g. aspectRatio throws "must be a number, a
|
|
48
|
+
// ratio string or `auto`"). On native, clear anything an earlier prop or
|
|
49
|
+
// styled default already merged for this key — matching web, where unset
|
|
50
|
+
// resets toward initial — then drop the value so RN never sees it.
|
|
51
|
+
if (process.env.TAMAGUI_TARGET === 'native' && value === 'unset') {
|
|
52
|
+
const expandedKey =
|
|
53
|
+
(!styleProps.disableExpandShorthands && conf.shorthands[key]) || key
|
|
54
|
+
const expanded = styleProps.noExpand
|
|
55
|
+
? null
|
|
56
|
+
: expandStyle(expandedKey, value, conf.settings.styleCompat || 'web')
|
|
57
|
+
if (styleState.style) {
|
|
58
|
+
if (expanded) {
|
|
59
|
+
for (const [nkey] of expanded) {
|
|
60
|
+
delete styleState.style[nkey]
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
delete styleState.style[expandedKey]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
if (!styleProps.noExpand) {
|
|
47
70
|
if (variants && key in variants) {
|
|
48
71
|
const variantValue = resolveVariants(key, value, styleProps, styleState, '')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getPlatformDriver, isServer, isWeb } from '@tamagui/constants'
|
|
2
|
-
import {
|
|
2
|
+
import { mergeIfNotShallowEqual } from '@tamagui/is-equal-shallow'
|
|
3
3
|
import { useDidFinishSSR, useIsClientOnly } from '@tamagui/use-did-finish-ssr'
|
|
4
4
|
import { useRef, useState } from 'react'
|
|
5
5
|
import { getSetting } from '../config'
|
|
@@ -176,7 +176,44 @@ export const useComponentState = (
|
|
|
176
176
|
|
|
177
177
|
const groupName = props.group as any as string | undefined
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
// hoisted shallow-set closure: created once per component instance and
|
|
180
|
+
// reused every render. drops the useCallback hook that useCreateShallowSetState
|
|
181
|
+
// would otherwise add. setState from useState is stable per instance so we
|
|
182
|
+
// can safely capture it. debug is read off stateRef at call time.
|
|
183
|
+
//
|
|
184
|
+
// important: this lives on `baseSetStateShallow`, not `setStateShallow`.
|
|
185
|
+
// createComponent's avoidReRenders path overwrites `stateRef.current.setStateShallow`
|
|
186
|
+
// with an emitter wrapper and captures this base as its real-re-render escape hatch.
|
|
187
|
+
// if the base shared the `setStateShallow` field, on the 2nd+ render this hook would
|
|
188
|
+
// read back the wrapper, the wrapper's escape hatch would point at itself, and a real
|
|
189
|
+
// re-render (e.g. unmounted 'should-enter' -> false) would never reach React, leaving
|
|
190
|
+
// enter animations stuck at opacity 0.
|
|
191
|
+
if (!stateRef.current.baseSetStateShallow) {
|
|
192
|
+
const r = stateRef.current
|
|
193
|
+
r.baseSetStateShallow = (stateOrGetState: any) => {
|
|
194
|
+
setState((prev: any) => {
|
|
195
|
+
const next =
|
|
196
|
+
typeof stateOrGetState === 'function' ? stateOrGetState(prev) : stateOrGetState
|
|
197
|
+
const update = mergeIfNotShallowEqual(prev, next)
|
|
198
|
+
if (process.env.NODE_ENV === 'development') {
|
|
199
|
+
const dbg = (r as any).__debug
|
|
200
|
+
if (dbg && update !== prev) {
|
|
201
|
+
console.groupCollapsed(`setStateShallow CHANGE`, '=>', update)
|
|
202
|
+
console.info(`previously`, prev)
|
|
203
|
+
console.trace()
|
|
204
|
+
console.groupEnd()
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return update
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (process.env.NODE_ENV === 'development') {
|
|
212
|
+
;(stateRef.current as any).__debug = props.debug
|
|
213
|
+
}
|
|
214
|
+
const setStateShallow = stateRef.current.baseSetStateShallow!
|
|
215
|
+
if (process.env.NODE_ENV === 'development' && globalThis.time)
|
|
216
|
+
globalThis.time`state-useCreateShallowSetState`
|
|
180
217
|
|
|
181
218
|
// set enter/exit variants onto our new props object
|
|
182
219
|
if (presenceState && isAnimated && isHydrated && staticConfig.variants) {
|
package/src/hooks/useMedia.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isServer, isWeb } from '@tamagui/constants'
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useReducer, useRef } from 'react'
|
|
3
3
|
import { getSetting } from '../config'
|
|
4
4
|
import { resetMediaStyleCache } from '../helpers/createMediaStyle'
|
|
5
5
|
import { matchMedia } from '../helpers/matchMedia'
|
|
@@ -66,6 +66,8 @@ export const configureMedia = (config: TamaguiInternalConfig) => {
|
|
|
66
66
|
mediaVersion++
|
|
67
67
|
// reset cached media style prefixes/selectors so they get recalculated with new key order
|
|
68
68
|
resetMediaStyleCache()
|
|
69
|
+
// touch-tracker getter object depends on the current media key set
|
|
70
|
+
resetMediaTouchTracker()
|
|
69
71
|
for (const key in media) {
|
|
70
72
|
getMedia()[key] = mediaQueryDefaultActive?.[key] || false
|
|
71
73
|
mediaKeys.add(`$${key}`)
|
|
@@ -136,6 +138,47 @@ type MediaState = {
|
|
|
136
138
|
|
|
137
139
|
const States = new WeakMap<any, MediaState>()
|
|
138
140
|
|
|
141
|
+
// shared "touch tracker" prototype: one object whose enumerable getter
|
|
142
|
+
// properties are pre-defined for every configured media key. Hermes inlines
|
|
143
|
+
// getter calls; the old `new Proxy(state, { get })` path forced an interpreted
|
|
144
|
+
// trap on every access — the dominant per-component cost in benchmarks. Each
|
|
145
|
+
// component owns just an Object.create(proto) with a Symbol-keyed slot
|
|
146
|
+
// pointing at its tracking set + current snapshot.
|
|
147
|
+
type MediaRefSlot = {
|
|
148
|
+
proxyTarget: MediaQueryState
|
|
149
|
+
keys: Set<string>
|
|
150
|
+
}
|
|
151
|
+
let touchTrackerProto: object | null = null
|
|
152
|
+
const refSlot = Symbol('mediaRefSlot')
|
|
153
|
+
|
|
154
|
+
function buildTouchTrackerProto(): object {
|
|
155
|
+
const proto: PropertyDescriptorMap = {}
|
|
156
|
+
for (const fullKey of mediaKeys) {
|
|
157
|
+
const key = fullKey[0] === '$' ? fullKey.slice(1) : fullKey
|
|
158
|
+
proto[key] = {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
configurable: true,
|
|
161
|
+
get(this: { [refSlot]: MediaRefSlot }) {
|
|
162
|
+
const slot = this[refSlot]
|
|
163
|
+
if (!disableMediaTouch) {
|
|
164
|
+
slot.keys.add(key)
|
|
165
|
+
}
|
|
166
|
+
return slot.proxyTarget[key]
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return Object.create(null, proto)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getTouchTrackerProto(): object {
|
|
174
|
+
if (!touchTrackerProto) touchTrackerProto = buildTouchTrackerProto()
|
|
175
|
+
return touchTrackerProto
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function resetMediaTouchTracker() {
|
|
179
|
+
touchTrackerProto = null
|
|
180
|
+
}
|
|
181
|
+
|
|
139
182
|
export function setMediaShouldUpdate(
|
|
140
183
|
ref: any,
|
|
141
184
|
enabled?: boolean,
|
|
@@ -165,38 +208,46 @@ export function useMedia(
|
|
|
165
208
|
): UseMediaState {
|
|
166
209
|
'use no memo'
|
|
167
210
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const internalRef = useRef<{
|
|
211
|
+
type MediaRef = {
|
|
171
212
|
keys: Set<string>
|
|
172
213
|
lastState: MediaQueryState
|
|
173
214
|
pendingState?: MediaQueryState
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
internalRef.current.pendingState = undefined
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const { keys } = internalRef.current
|
|
189
|
-
|
|
190
|
-
// clear each render to track only rendered touched keys
|
|
191
|
-
if (keys.size) {
|
|
192
|
-
keys.clear()
|
|
215
|
+
renderVersion: number
|
|
216
|
+
unsubscribe?: () => void
|
|
217
|
+
// stable per-component closures + reusable Proxy. allocating new ones each
|
|
218
|
+
// render (via useSyncExternalStore + `new Proxy(state, ...)`) was a real
|
|
219
|
+
// per-component-per-render cost; we hold one Proxy whose target is swapped
|
|
220
|
+
// by mutating `proxyTarget` and re-reading it in the get trap.
|
|
221
|
+
proxyTarget: MediaQueryState
|
|
222
|
+
proxy: UseMediaState
|
|
223
|
+
getSnapshot: () => MediaQueryState
|
|
224
|
+
componentContext?: ComponentContextI
|
|
225
|
+
debug?: DebugProp
|
|
193
226
|
}
|
|
194
227
|
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
()
|
|
198
|
-
|
|
199
|
-
|
|
228
|
+
const internalRef = useRef<MediaRef | null>(null)
|
|
229
|
+
if (!internalRef.current) {
|
|
230
|
+
const initial = getMedia()
|
|
231
|
+
const r: MediaRef = {
|
|
232
|
+
keys: new Set<string>(),
|
|
233
|
+
lastState: initial,
|
|
234
|
+
renderVersion: 0,
|
|
235
|
+
proxyTarget: initial,
|
|
236
|
+
proxy: undefined as unknown as UseMediaState,
|
|
237
|
+
getSnapshot: undefined as unknown as () => MediaQueryState,
|
|
238
|
+
componentContext,
|
|
239
|
+
debug,
|
|
240
|
+
}
|
|
241
|
+
// proxy → Object.create(getterProto) with a Symbol slot. Per-key get is a
|
|
242
|
+
// monomorphic getter call (Hermes-fast) instead of a Proxy trap.
|
|
243
|
+
const tracker = Object.create(getTouchTrackerProto())
|
|
244
|
+
tracker[refSlot] = { proxyTarget: initial, keys: r.keys } as MediaRefSlot
|
|
245
|
+
r.proxy = tracker as UseMediaState
|
|
246
|
+
r.getSnapshot = () => {
|
|
247
|
+
const curKeys = r.componentContext
|
|
248
|
+
? States.get(r.componentContext)?.keys || r.keys
|
|
249
|
+
: r.keys
|
|
250
|
+
const { lastState, pendingState } = r
|
|
200
251
|
|
|
201
252
|
if (!curKeys.size) {
|
|
202
253
|
return lastState
|
|
@@ -205,38 +256,92 @@ export function useMedia(
|
|
|
205
256
|
const ms = getMedia()
|
|
206
257
|
for (const key of curKeys) {
|
|
207
258
|
if (ms[key] !== (pendingState || lastState)[key]) {
|
|
208
|
-
if (process.env.NODE_ENV === 'development' && debug) {
|
|
259
|
+
if (process.env.NODE_ENV === 'development' && r.debug) {
|
|
209
260
|
console.warn(`useMedia() ✍️`, key, lastState[key], '=>', ms[key])
|
|
210
261
|
}
|
|
211
262
|
|
|
212
263
|
// in emitter mode (no-rerender) avoid changing state, instead emit
|
|
213
|
-
if (componentContext?.mediaEmit) {
|
|
214
|
-
componentContext.mediaEmit(ms)
|
|
215
|
-
|
|
264
|
+
if (r.componentContext?.mediaEmit) {
|
|
265
|
+
r.componentContext.mediaEmit(ms)
|
|
266
|
+
r.pendingState = ms
|
|
216
267
|
return lastState
|
|
217
268
|
}
|
|
218
269
|
|
|
219
|
-
|
|
270
|
+
r.lastState = ms
|
|
220
271
|
|
|
221
272
|
return ms
|
|
222
273
|
}
|
|
223
274
|
}
|
|
224
275
|
|
|
225
276
|
return lastState
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
277
|
+
}
|
|
278
|
+
internalRef.current = r
|
|
279
|
+
} else {
|
|
280
|
+
// refresh per-render inputs the closures read through the ref
|
|
281
|
+
internalRef.current.componentContext = componentContext
|
|
282
|
+
internalRef.current.debug = debug
|
|
283
|
+
}
|
|
229
284
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
285
|
+
const ref = internalRef.current
|
|
286
|
+
ref.renderVersion++
|
|
287
|
+
|
|
288
|
+
// reset on next render
|
|
289
|
+
if (ref.pendingState) {
|
|
290
|
+
ref.lastState = ref.pendingState
|
|
291
|
+
ref.pendingState = undefined
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// clear each render to track only rendered touched keys
|
|
295
|
+
if (ref.keys.size) {
|
|
296
|
+
ref.keys.clear()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// manual subscription (same shape as useThemeStateSubscribed): same
|
|
300
|
+
// granular bailout via getSnapshot returning the same MediaQueryState ref
|
|
301
|
+
// when none of the component's touched keys changed, but fewer
|
|
302
|
+
// React-internal hook slots on Hermes than useSyncExternalStore.
|
|
303
|
+
const [, forceUpdate] = useReducer(incReducer, 0)
|
|
304
|
+
const state = isServer ? initState : ref.getSnapshot()
|
|
305
|
+
ref.proxyTarget = state
|
|
306
|
+
;(ref.proxy as any)[refSlot].proxyTarget = state
|
|
307
|
+
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
const renderVersion = ref.renderVersion
|
|
310
|
+
const shouldSubscribe =
|
|
311
|
+
!ref.componentContext || !!States.get(ref.componentContext)?.enabled
|
|
312
|
+
|
|
313
|
+
if (shouldSubscribe) {
|
|
314
|
+
if (!ref.unsubscribe) {
|
|
315
|
+
ref.unsubscribe = subscribe(() => {
|
|
316
|
+
const next = ref.getSnapshot()
|
|
317
|
+
if (next !== ref.proxyTarget) {
|
|
318
|
+
ref.proxyTarget = next
|
|
319
|
+
;(ref.proxy as any)[refSlot].proxyTarget = next
|
|
320
|
+
forceUpdate()
|
|
321
|
+
}
|
|
322
|
+
})
|
|
234
323
|
}
|
|
235
|
-
|
|
236
|
-
|
|
324
|
+
} else if (ref.unsubscribe) {
|
|
325
|
+
ref.unsubscribe()
|
|
326
|
+
ref.unsubscribe = undefined
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return () => {
|
|
330
|
+
// react runs passive cleanup before the next effect as well as on unmount.
|
|
331
|
+
// a newer render bumps renderVersion before that cleanup, so equality here
|
|
332
|
+
// means this is the final unmount cleanup.
|
|
333
|
+
if (ref.renderVersion === renderVersion) {
|
|
334
|
+
ref.unsubscribe?.()
|
|
335
|
+
ref.unsubscribe = undefined
|
|
336
|
+
}
|
|
337
|
+
}
|
|
237
338
|
})
|
|
339
|
+
|
|
340
|
+
return ref.proxy
|
|
238
341
|
}
|
|
239
342
|
|
|
343
|
+
const incReducer = (c: number): number => c + 1
|
|
344
|
+
|
|
240
345
|
const getServerSnapshot = () => initState
|
|
241
346
|
|
|
242
347
|
let disableMediaTouch = false
|
package/src/hooks/useTheme.tsx
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { useRef } from 'react'
|
|
1
|
+
import { useRef, type MutableRefObject } from 'react'
|
|
2
2
|
import type { ThemeParsed, ThemeState, UseThemeWithStateProps } from '../types'
|
|
3
3
|
import { getThemeProxied, type ThemeProxied } from './getThemeProxied'
|
|
4
4
|
import { useThemeState } from './useThemeState'
|
|
5
5
|
|
|
6
6
|
const EMPTY = {}
|
|
7
7
|
|
|
8
|
+
type KeysBag = {
|
|
9
|
+
keys: MutableRefObject<Set<string> | null>
|
|
10
|
+
schemeKeys: MutableRefObject<Set<string> | null>
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
export const useTheme = () => {
|
|
9
14
|
'use no memo'
|
|
10
15
|
|
|
@@ -20,13 +25,30 @@ export type ThemeWithState = [ThemeParsed, ThemeState]
|
|
|
20
25
|
*/
|
|
21
26
|
export const useThemeWithState = (
|
|
22
27
|
props: UseThemeWithStateProps,
|
|
23
|
-
isRoot = false
|
|
28
|
+
isRoot = false,
|
|
29
|
+
// <Theme> components push their themeState.id into ThemeStateContext,
|
|
30
|
+
// so their descendants subscribe under this id; <Theme> needs to schedule
|
|
31
|
+
// descendant updates when its propsKey changes. Leaf styled components
|
|
32
|
+
// (createComponent, useProps, useTheme, animation hooks) do NOT push a
|
|
33
|
+
// context — listenersByParent[componentId] is always empty — so the
|
|
34
|
+
// schedule-update effect is a no-op for them and can be skipped to save
|
|
35
|
+
// one hook slot per styled component on mount. Theme.tsx passes true.
|
|
36
|
+
forThemeView = false
|
|
24
37
|
): ThemeWithState => {
|
|
25
38
|
'use no memo'
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
// single useRef holding both keys + schemeKeys; saves one hook slot per
|
|
41
|
+
// component vs separate useRef calls. these still look like MutableRefObject
|
|
42
|
+
// to getThemeProxied which only reads/writes `.current`.
|
|
43
|
+
const bag = useRef<KeysBag | null>(null)
|
|
44
|
+
if (!bag.current) {
|
|
45
|
+
bag.current = {
|
|
46
|
+
keys: { current: null },
|
|
47
|
+
schemeKeys: { current: null },
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const { keys, schemeKeys } = bag.current
|
|
51
|
+
const themeState = useThemeState(props, isRoot, keys, schemeKeys, forThemeView)
|
|
30
52
|
|
|
31
53
|
if (process.env.NODE_ENV === 'development') {
|
|
32
54
|
if (!props.passThrough && !themeState?.theme) {
|