@tamagui/core 1.0.2 → 1.0.4
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/config.js +1 -1
- package/dist/cjs/config.js.map +2 -2
- package/dist/cjs/createComponent.js +51 -95
- package/dist/cjs/createComponent.js.map +2 -2
- package/dist/cjs/createTamagui.js +7 -4
- package/dist/cjs/createTamagui.js.map +2 -2
- package/dist/cjs/createVariable.js +1 -1
- package/dist/cjs/createVariable.js.map +2 -2
- package/dist/cjs/createVariables.js +12 -3
- package/dist/cjs/createVariables.js.map +2 -2
- package/dist/cjs/helpers/ThemeManager.js +12 -2
- package/dist/cjs/helpers/ThemeManager.js.map +2 -2
- package/dist/cjs/helpers/getSize.js +9 -5
- package/dist/cjs/helpers/getSize.js.map +2 -2
- package/dist/cjs/helpers/getSplitStyles.js +39 -45
- package/dist/cjs/helpers/getSplitStyles.js.map +2 -2
- package/dist/cjs/helpers/getStylesAtomic.js +32 -26
- package/dist/cjs/helpers/getStylesAtomic.js.map +2 -2
- package/dist/cjs/helpers/getStylesAtomic.native.js +6 -2
- package/dist/cjs/helpers/getStylesAtomic.native.js.map +2 -2
- package/dist/cjs/helpers/insertStyleRule.js +12 -1
- package/dist/cjs/helpers/insertStyleRule.js.map +2 -2
- package/dist/cjs/helpers/normalizeColor.js +8 -7
- package/dist/cjs/helpers/normalizeColor.js.map +2 -2
- package/dist/cjs/hooks/useMedia.js +37 -36
- package/dist/cjs/hooks/useMedia.js.map +2 -2
- package/dist/cjs/hooks/useTheme.js +28 -42
- package/dist/cjs/hooks/useTheme.js.map +3 -3
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/styled.js +4 -1
- package/dist/cjs/styled.js.map +2 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/views/Slot.js +1 -33
- package/dist/cjs/views/Slot.js.map +3 -3
- package/dist/cjs/views/Stack.test.js +4 -6
- package/dist/cjs/views/Stack.test.js.map +2 -2
- package/dist/cjs/views/Theme.js +45 -41
- package/dist/cjs/views/Theme.js.map +2 -2
- package/dist/cjs/views/ThemeProvider.js +1 -1
- package/dist/cjs/views/ThemeProvider.js.map +2 -2
- package/dist/esm/config.js +1 -1
- package/dist/esm/config.js.map +2 -2
- package/dist/esm/createComponent.js +53 -97
- package/dist/esm/createComponent.js.map +2 -2
- package/dist/esm/createTamagui.js +8 -5
- package/dist/esm/createTamagui.js.map +2 -2
- package/dist/esm/createVariable.js +1 -1
- package/dist/esm/createVariable.js.map +2 -2
- package/dist/esm/createVariables.js +10 -2
- package/dist/esm/createVariables.js.map +2 -2
- package/dist/esm/helpers/ThemeManager.js +13 -3
- package/dist/esm/helpers/ThemeManager.js.map +2 -2
- package/dist/esm/helpers/getSize.js +9 -5
- package/dist/esm/helpers/getSize.js.map +2 -2
- package/dist/esm/helpers/getSplitStyles.js +39 -45
- package/dist/esm/helpers/getSplitStyles.js.map +2 -2
- package/dist/esm/helpers/getStylesAtomic.js +31 -26
- package/dist/esm/helpers/getStylesAtomic.js.map +2 -2
- package/dist/esm/helpers/getStylesAtomic.native.js +4 -1
- package/dist/esm/helpers/getStylesAtomic.native.js.map +2 -2
- package/dist/esm/helpers/insertStyleRule.js +12 -1
- package/dist/esm/helpers/insertStyleRule.js.map +2 -2
- package/dist/esm/helpers/normalizeColor.js +8 -7
- package/dist/esm/helpers/normalizeColor.js.map +2 -2
- package/dist/esm/hooks/useMedia.js +37 -37
- package/dist/esm/hooks/useMedia.js.map +2 -2
- package/dist/esm/hooks/useTheme.js +25 -39
- package/dist/esm/hooks/useTheme.js.map +3 -3
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/styled.js +4 -1
- package/dist/esm/styled.js.map +2 -2
- package/dist/esm/views/Slot.js +1 -33
- package/dist/esm/views/Slot.js.map +3 -3
- package/dist/esm/views/Stack.test.js +1 -3
- package/dist/esm/views/Stack.test.js.map +2 -2
- package/dist/esm/views/Theme.js +44 -40
- package/dist/esm/views/Theme.js.map +2 -2
- package/dist/esm/views/ThemeProvider.js +1 -1
- package/dist/esm/views/ThemeProvider.js.map +2 -2
- package/package.json +11 -11
- package/reset.css +4 -0
- package/src/__snapshots__/createTokens.test.ts.snap +90 -195
- package/src/config.ts +1 -1
- package/src/createComponent.tsx +72 -152
- package/src/createTamagui.ts +8 -5
- package/src/createVariable.ts +2 -1
- package/src/createVariables.ts +14 -1
- package/src/helpers/ThemeManager.tsx +13 -3
- package/src/helpers/getSplitStyles.tsx +43 -53
- package/src/helpers/getStylesAtomic.native.ts +2 -0
- package/src/helpers/getStylesAtomic.ts +36 -31
- package/src/helpers/insertStyleRule.tsx +12 -2
- package/src/helpers/normalizeColor.ts +8 -7
- package/src/hooks/useMedia.tsx +53 -48
- package/src/hooks/useTheme.tsx +37 -56
- package/src/index.ts +0 -1
- package/src/styled.tsx +4 -1
- package/src/types.tsx +7 -3
- package/src/views/Slot.tsx +3 -59
- package/src/views/Stack.test.tsx +1 -4
- package/src/views/Theme.tsx +60 -54
- package/src/views/ThemeProvider.tsx +1 -1
- package/types/createVariables.d.ts +1 -0
- package/types/helpers/ThemeManager.d.ts +1 -1
- package/types/helpers/getSplitStyles.d.ts +2 -3
- package/types/helpers/getStylesAtomic.d.ts +1 -0
- package/types/helpers/getStylesAtomic.native.d.ts +1 -0
- package/types/hooks/useMedia.d.ts +8 -4
- package/types/hooks/useTheme.d.ts +16 -2
- package/types/index.d.ts +0 -1
- package/types/types.d.ts +3 -2
- package/types/views/Slot.d.ts +5 -4
- package/types/views/Theme.d.ts +5 -2
- package/src/helpers/cache.ts +0 -13
- package/src/helpers/getSize.ts +0 -23
|
@@ -58,8 +58,7 @@ import {
|
|
|
58
58
|
import { pseudoDescriptors } from './pseudoDescriptors'
|
|
59
59
|
|
|
60
60
|
type GetStyleResult = {
|
|
61
|
-
pseudos
|
|
62
|
-
medias: Record<MediaQueryKey, ViewStyle>
|
|
61
|
+
pseudos?: PseudoStyles
|
|
63
62
|
style: ViewStyle
|
|
64
63
|
classNames: ClassNamesObject
|
|
65
64
|
rulesToInsert: RulesToInsert
|
|
@@ -222,6 +221,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
222
221
|
return cache.get(props)
|
|
223
222
|
}
|
|
224
223
|
|
|
224
|
+
if (props.debug) {
|
|
225
|
+
console.log('props', props)
|
|
226
|
+
}
|
|
227
|
+
|
|
225
228
|
conf = conf || getConfig()
|
|
226
229
|
const { shorthands } = conf
|
|
227
230
|
const {
|
|
@@ -235,14 +238,13 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
235
238
|
} = staticConfig
|
|
236
239
|
const validStyleProps = staticConfig.isText ? stylePropsText : validStyles
|
|
237
240
|
const viewProps: GetStyleResult['viewProps'] = {}
|
|
238
|
-
|
|
241
|
+
let pseudos: PseudoStyles | null = null
|
|
239
242
|
let psuedosUsed: Record<string, number> | null = null
|
|
240
|
-
const medias: Record<MediaQueryKey, ViewStyle> = {}
|
|
241
243
|
const mediaState = state.mediaState || globalMediaState
|
|
242
244
|
const usedKeys: Record<string, number> = {}
|
|
243
245
|
const propKeys = Object.keys(props)
|
|
244
246
|
let space: SpaceTokens | null = props.space
|
|
245
|
-
let hasMedia: boolean |
|
|
247
|
+
let hasMedia: boolean | string[] = false
|
|
246
248
|
|
|
247
249
|
const shouldDoClasses =
|
|
248
250
|
staticConfig.acceptsClassName && (isWeb || IS_STATIC) && !state.noClassNames
|
|
@@ -617,6 +619,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
617
619
|
}
|
|
618
620
|
|
|
619
621
|
if (!shouldDoClasses || IS_STATIC) {
|
|
622
|
+
pseudos ||= {}
|
|
620
623
|
pseudos[key] ||= {}
|
|
621
624
|
Object.assign(pseudos[key], pseudoStyleObject)
|
|
622
625
|
}
|
|
@@ -675,6 +678,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
675
678
|
const curImportance = psuedosUsed[importance] || 0
|
|
676
679
|
if (importance >= curImportance) {
|
|
677
680
|
psuedosUsed[pkey] = importance
|
|
681
|
+
pseudos ||= {}
|
|
678
682
|
pseudos[key] ||= {}
|
|
679
683
|
pseudos[key][pkey] = val
|
|
680
684
|
// eslint-disable-next-line no-console
|
|
@@ -707,29 +711,37 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
707
711
|
const mediaKeyShort = key.slice(1)
|
|
708
712
|
|
|
709
713
|
if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
|
|
714
|
+
// prettier-ignore
|
|
710
715
|
// eslint-disable-next-line no-console
|
|
711
|
-
console.log(` 📺 ${key}`,
|
|
712
|
-
key,
|
|
713
|
-
mediaStyle,
|
|
714
|
-
props,
|
|
715
|
-
shouldDoClasses,
|
|
716
|
-
mediaState: { ...mediaState },
|
|
717
|
-
})
|
|
716
|
+
console.log(` 📺 ${key}`, { key, mediaStyle, props, shouldDoClasses })
|
|
718
717
|
}
|
|
719
718
|
|
|
720
719
|
if ('space' in mediaStyle) {
|
|
721
|
-
hasMedia
|
|
720
|
+
if (!Array.isArray(hasMedia)) {
|
|
721
|
+
hasMedia = []
|
|
722
|
+
}
|
|
723
|
+
hasMedia.push(mediaKeyShort)
|
|
722
724
|
}
|
|
723
725
|
|
|
724
726
|
if (shouldDoClasses) {
|
|
725
727
|
if ('space' in mediaStyle) {
|
|
726
|
-
const val = valInit.space
|
|
727
728
|
delete mediaStyle['space']
|
|
728
|
-
if (
|
|
729
|
-
|
|
730
|
-
getMediaImportanceIfMoreImportant(
|
|
731
|
-
|
|
732
|
-
|
|
729
|
+
if (mediaState[mediaKeyShort]) {
|
|
730
|
+
const val = valInit.space
|
|
731
|
+
const importance = getMediaImportanceIfMoreImportant(
|
|
732
|
+
mediaKeyShort,
|
|
733
|
+
'space',
|
|
734
|
+
usedKeys
|
|
735
|
+
)
|
|
736
|
+
if (importance) {
|
|
737
|
+
space = val
|
|
738
|
+
usedKeys['space'] = importance
|
|
739
|
+
if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
|
|
740
|
+
console.log(
|
|
741
|
+
`Found more important space for current media ${mediaKeyShort}: ${val} (importance: ${importance})`
|
|
742
|
+
)
|
|
743
|
+
}
|
|
744
|
+
}
|
|
733
745
|
}
|
|
734
746
|
}
|
|
735
747
|
const mediaStyles = getStylesAtomic(mediaStyle)
|
|
@@ -879,7 +891,6 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
879
891
|
fontFamily,
|
|
880
892
|
viewProps,
|
|
881
893
|
style,
|
|
882
|
-
medias,
|
|
883
894
|
pseudos,
|
|
884
895
|
classNames,
|
|
885
896
|
rulesToInsert,
|
|
@@ -893,15 +904,8 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
893
904
|
if (isDevTools) {
|
|
894
905
|
// eslint-disable-next-line no-console
|
|
895
906
|
console.groupCollapsed(' 🔹 styles =>')
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
viewProps,
|
|
899
|
-
state,
|
|
900
|
-
rulesToInsert,
|
|
901
|
-
parentSplitStyles,
|
|
902
|
-
flatTransforms,
|
|
903
|
-
result,
|
|
904
|
-
}
|
|
907
|
+
// prettier-ignore
|
|
908
|
+
const logs = { transforms, viewProps, state, rulesToInsert, parentSplitStyles, flatTransforms, result }
|
|
905
909
|
for (const key in { ...result, ...logs }) {
|
|
906
910
|
// eslint-disable-next-line no-console
|
|
907
911
|
console.log(key, logs[key])
|
|
@@ -926,35 +930,21 @@ function mergeClassName(
|
|
|
926
930
|
if (process.env.TAMAGUI_TARGET === 'web') {
|
|
927
931
|
// empty classnames passed by compiler sometimes
|
|
928
932
|
if (!val) return
|
|
929
|
-
if (val.startsWith('_transform-')) {
|
|
930
|
-
const
|
|
933
|
+
if (val[0] === '_' && val.startsWith('_transform-')) {
|
|
934
|
+
const ns: TransformNamespaceKey = isMediaOrPseudo ? key : 'transform'
|
|
931
935
|
let transform = insertedTransforms[val]
|
|
932
|
-
if (isClient) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
transform = insertedTransforms[val]
|
|
938
|
-
if (process.env.NODE_ENV === 'development') {
|
|
939
|
-
if (!transform) {
|
|
940
|
-
// eslint-disable-next-line no-console
|
|
941
|
-
console.warn('no transform found', { insertedTransforms, val })
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
if (!transform) {
|
|
946
|
-
if (isWeb && val[0] !== '_') {
|
|
947
|
-
// runtime insert
|
|
948
|
-
transform = val
|
|
949
|
-
}
|
|
936
|
+
if (isClient && !transform) {
|
|
937
|
+
scanAllSheets() // HMR or loaded a new chunk
|
|
938
|
+
transform = insertedTransforms[val]
|
|
939
|
+
if (!transform && isWeb && val[0] !== '_') {
|
|
940
|
+
transform = val // runtime insert
|
|
950
941
|
}
|
|
951
942
|
}
|
|
952
|
-
transforms[
|
|
953
|
-
|
|
954
|
-
transforms[namespace][0] += identifier
|
|
943
|
+
transforms[ns] ||= ['', '']
|
|
944
|
+
transforms[ns][0] += val.replace('_transform', '')
|
|
955
945
|
// ssr doesn't need to do anything just make the right classname
|
|
956
946
|
if (transform) {
|
|
957
|
-
transforms[
|
|
947
|
+
transforms[ns][1] += transform
|
|
958
948
|
}
|
|
959
949
|
} else {
|
|
960
950
|
classNames[key] = val
|
|
@@ -8,7 +8,6 @@ import type { TextStyle, ViewStyle } from 'react-native'
|
|
|
8
8
|
|
|
9
9
|
import { getConfig } from '../config'
|
|
10
10
|
import { TamaguiInternalConfig } from '../types'
|
|
11
|
-
import { Style, cache } from './cache'
|
|
12
11
|
import { defaultOffset } from './expandStyles'
|
|
13
12
|
import { normalizeValueWithProperty } from './normalizeValueWithProperty.js'
|
|
14
13
|
import { PseudoDescriptor, pseudoDescriptors } from './pseudoDescriptors'
|
|
@@ -48,17 +47,31 @@ export function getStylesAtomic(stylesIn: ViewStyleWithPseudos) {
|
|
|
48
47
|
return res
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
const cache = new Map()
|
|
51
|
+
|
|
52
|
+
export const startClearStyleCacheInterval = () => {
|
|
53
|
+
setInterval(() => {
|
|
54
|
+
cache.clear()
|
|
55
|
+
}, 10_000)
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
export function getAtomicStyle(
|
|
52
59
|
style: ViewOrTextStyle,
|
|
53
60
|
pseudo?: PseudoDescriptor
|
|
54
61
|
): StyleObject[] {
|
|
62
|
+
if (!style) return []
|
|
63
|
+
const key = JSON.stringify(style) + (pseudo ? JSON.stringify(pseudo) : '')
|
|
64
|
+
if (cache.has(key)) {
|
|
65
|
+
return cache.get(key)
|
|
66
|
+
}
|
|
55
67
|
if (process.env.NODE_ENV === 'development') {
|
|
56
68
|
if (!style || typeof style !== 'object') {
|
|
57
69
|
throw new Error(`Wrong style type: "${typeof style}": ${style}`)
|
|
58
70
|
}
|
|
59
71
|
}
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
const out = generateAtomicStyles(style, pseudo)
|
|
73
|
+
cache.set(key, out)
|
|
74
|
+
return out
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
let conf: TamaguiInternalConfig
|
|
@@ -96,35 +109,27 @@ const generateAtomicStyles = (
|
|
|
96
109
|
const out: StyleObject[] = []
|
|
97
110
|
for (const key in style) {
|
|
98
111
|
const value = style[key]
|
|
99
|
-
if (value
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
property: key,
|
|
117
|
-
pseudo: pseudo?.name as any,
|
|
118
|
-
value,
|
|
119
|
-
identifier,
|
|
120
|
-
rules,
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
cache.set(uid, value, styleObject)
|
|
124
|
-
out.push(styleObject)
|
|
125
|
-
}
|
|
112
|
+
if (value == null || value == undefined) continue
|
|
113
|
+
const hash = presetHashes[value]
|
|
114
|
+
? value
|
|
115
|
+
: typeof value === 'string'
|
|
116
|
+
? simpleHash(value)
|
|
117
|
+
: `${value}`.replace('.', 'dot')
|
|
118
|
+
|
|
119
|
+
const pseudoPrefix = pseudo ? `0${pseudo.name}-` : ''
|
|
120
|
+
const shortProp = conf.inverseShorthands[key] || key
|
|
121
|
+
const identifier = `_${shortProp}-${pseudoPrefix}${hash}`
|
|
122
|
+
const rules = createAtomicRules(identifier, key, value, pseudo)
|
|
123
|
+
const styleObject: StyleObject = {
|
|
124
|
+
property: key,
|
|
125
|
+
pseudo: pseudo?.name as any,
|
|
126
|
+
value,
|
|
127
|
+
identifier,
|
|
128
|
+
rules,
|
|
126
129
|
}
|
|
130
|
+
out.push(styleObject)
|
|
127
131
|
}
|
|
132
|
+
|
|
128
133
|
return out
|
|
129
134
|
}
|
|
130
135
|
|
|
@@ -166,7 +171,7 @@ export function styleToCSS(style: Record<string, any>) {
|
|
|
166
171
|
}
|
|
167
172
|
}
|
|
168
173
|
|
|
169
|
-
function createDeclarationBlock(style:
|
|
174
|
+
function createDeclarationBlock(style: Record<string, any>, important = false) {
|
|
170
175
|
let next = ''
|
|
171
176
|
for (const key in style) {
|
|
172
177
|
const prop = hyphenateStyleName(key)
|
|
@@ -134,7 +134,6 @@ function getTamaguiSelector(
|
|
|
134
134
|
): readonly [string, CSSStyleRule] | null {
|
|
135
135
|
if (rule instanceof CSSStyleRule) {
|
|
136
136
|
const text = rule.selectorText
|
|
137
|
-
// .startsWith('._')
|
|
138
137
|
if (text[0] === '.' && text[1] === '_') {
|
|
139
138
|
return [text.slice(1), rule]
|
|
140
139
|
}
|
|
@@ -185,7 +184,18 @@ export function insertStyleRules(rulesToInsert: RulesToInsert) {
|
|
|
185
184
|
updateRules(identifier, rules)
|
|
186
185
|
if (sheet) {
|
|
187
186
|
for (const rule of rules) {
|
|
188
|
-
|
|
187
|
+
try {
|
|
188
|
+
sheet.insertRule(rule, sheet.cssRules.length)
|
|
189
|
+
} catch (err) {
|
|
190
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
191
|
+
console.groupCollapsed(
|
|
192
|
+
`Error inserting rule into CSSStyleSheet: ${String(err)}`
|
|
193
|
+
)
|
|
194
|
+
console.log({ rule, rulesToInsert })
|
|
195
|
+
console.trace()
|
|
196
|
+
console.groupEnd()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isWeb } from '@tamagui/constants'
|
|
1
2
|
import normalizeCSSColor from 'normalize-css-color'
|
|
2
3
|
|
|
3
4
|
export const normalizeColor = (color?: number | string | null, opacity?: number) => {
|
|
@@ -6,7 +7,7 @@ export const normalizeColor = (color?: number | string | null, opacity?: number)
|
|
|
6
7
|
}
|
|
7
8
|
if (
|
|
8
9
|
color[0] === '$' ||
|
|
9
|
-
(
|
|
10
|
+
(isWeb &&
|
|
10
11
|
typeof color === 'string' &&
|
|
11
12
|
(webColors[color] || color[0] === '_' || color.startsWith('var(')))
|
|
12
13
|
) {
|
|
@@ -30,12 +31,12 @@ const webColors = {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export function colorToRGBA(color: string | number) {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
const r = (
|
|
36
|
-
const g = (
|
|
37
|
-
const b =
|
|
38
|
-
const a = ((
|
|
34
|
+
const ci = processColor(color)
|
|
35
|
+
if (ci != null) {
|
|
36
|
+
const r = (ci >> 16) & 255
|
|
37
|
+
const g = (ci >> 8) & 255
|
|
38
|
+
const b = ci & 255
|
|
39
|
+
const a = ((ci >> 24) & 255) / 255
|
|
39
40
|
return [r, g, b, a] as const
|
|
40
41
|
}
|
|
41
42
|
return null
|
package/src/hooks/useMedia.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useSyncExternalStore } from 'react'
|
|
1
|
+
import { startTransition, useEffect, useMemo, useSyncExternalStore } from 'react'
|
|
2
2
|
|
|
3
3
|
import { getConfig } from '../config'
|
|
4
4
|
import { createProxy } from '../helpers/createProxy'
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '../types'
|
|
13
13
|
import { useSafeRef } from './useSafeRef'
|
|
14
14
|
|
|
15
|
-
export
|
|
15
|
+
export let mediaState: MediaQueryState =
|
|
16
16
|
// development only safeguard
|
|
17
17
|
process.env.NODE_ENV === 'development'
|
|
18
18
|
? createProxy(
|
|
@@ -109,7 +109,7 @@ function setupMediaListeners() {
|
|
|
109
109
|
function update() {
|
|
110
110
|
const next = !!getMatch().matches
|
|
111
111
|
if (next === mediaState[key]) return
|
|
112
|
-
mediaState[key]
|
|
112
|
+
mediaState = { ...mediaState, [key]: next }
|
|
113
113
|
updateCurrentState()
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -123,12 +123,17 @@ export function useMediaListeners(config: TamaguiInternalConfig) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const currentStateListeners = new Set<any>()
|
|
126
|
+
let isFlushing = false
|
|
127
|
+
|
|
126
128
|
function updateCurrentState() {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
if (isFlushing) return
|
|
130
|
+
isFlushing = true
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
startTransition(() => {
|
|
133
|
+
currentStateListeners.forEach((cb) => cb(mediaState))
|
|
134
|
+
})
|
|
135
|
+
isFlushing = false
|
|
136
|
+
}, 0)
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
type MediaKeysState = {
|
|
@@ -139,61 +144,61 @@ type UseMediaState = {
|
|
|
139
144
|
[key in MediaQueryKey]: boolean
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
type UpdateState = {
|
|
148
|
+
enabled: boolean
|
|
149
|
+
keys: MediaQueryKey[]
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
const empty = {}
|
|
149
|
-
let initialUseState: UseMediaInternalState
|
|
152
|
+
const shouldUpdate = new WeakMap<any, UpdateState>()
|
|
150
153
|
|
|
151
|
-
export function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
export function setMediaShouldUpdate(ref: any, props: UpdateState) {
|
|
155
|
+
return shouldUpdate.set(ref, props)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
type UseMediaInternalState = {
|
|
159
|
+
prev: MediaKeysState
|
|
160
|
+
touched?: Set<string>
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function subscribe(subscriber: any) {
|
|
164
|
+
currentStateListeners.add(subscriber)
|
|
165
|
+
return () => currentStateListeners.delete(subscriber)
|
|
166
|
+
}
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
export function useMedia(uid?: any, debug?: any): UseMediaState {
|
|
169
|
+
const internal = useSafeRef<UseMediaInternalState>(undefined as any)
|
|
170
|
+
if (!internal.current) {
|
|
171
|
+
internal.current = {
|
|
172
|
+
prev: initState,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
159
175
|
const state = useSyncExternalStore<MediaQueryState>(
|
|
160
176
|
subscribe,
|
|
161
177
|
() => {
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let didUpdate = false
|
|
169
|
-
|
|
170
|
-
// ensure we keep the minimal object of only used keys
|
|
171
|
-
const next = {}
|
|
172
|
-
|
|
173
|
-
for (const key in curState.next) {
|
|
174
|
-
const val = mediaState[key]
|
|
175
|
-
if (curState.prev[key] !== val) {
|
|
176
|
-
didUpdate = true
|
|
177
|
-
}
|
|
178
|
-
next[key] = val
|
|
178
|
+
const { touched, prev } = internal.current
|
|
179
|
+
const componentState = uid ? shouldUpdate.get(uid) : undefined
|
|
180
|
+
if (componentState?.enabled === false) {
|
|
181
|
+
return prev
|
|
179
182
|
}
|
|
183
|
+
const testKeys =
|
|
184
|
+
componentState?.keys ?? (componentState?.enabled && touched ? [...touched] : null)
|
|
180
185
|
|
|
181
|
-
if (
|
|
182
|
-
return
|
|
186
|
+
if (testKeys?.every((key) => mediaState[key] === prev[key])) {
|
|
187
|
+
return prev
|
|
183
188
|
}
|
|
184
189
|
|
|
185
|
-
|
|
186
|
-
return
|
|
190
|
+
internal.current.prev = mediaState
|
|
191
|
+
return mediaState
|
|
187
192
|
},
|
|
188
193
|
() => initState
|
|
189
194
|
)
|
|
190
195
|
|
|
191
196
|
return useMemo(() => {
|
|
192
|
-
return new Proxy(state
|
|
193
|
-
get(_, key
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
return new Proxy(state, {
|
|
198
|
+
get(_, key) {
|
|
199
|
+
if (typeof key === 'string') {
|
|
200
|
+
internal.current.touched ||= new Set()
|
|
201
|
+
internal.current.touched.add(key)
|
|
197
202
|
}
|
|
198
203
|
return Reflect.get(state, key)
|
|
199
204
|
},
|
|
@@ -205,7 +210,7 @@ export function useMedia(): UseMediaState {
|
|
|
205
210
|
* Useful for more complex components that need access to the currently active props,
|
|
206
211
|
* accounting for the currently active media queries.
|
|
207
212
|
*
|
|
208
|
-
* Use sparingly,
|
|
213
|
+
* Use sparingly, it will loop props and trigger re-render on all media queries.
|
|
209
214
|
*
|
|
210
215
|
* */
|
|
211
216
|
export function useMediaPropsActive<A extends Object>(
|
package/src/hooks/useTheme.tsx
CHANGED
|
@@ -39,31 +39,36 @@ interface UseThemeState {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export const useTheme = (props: ThemeProps = emptyProps): ThemeParsed => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
42
|
+
return (isRSC ? null : useThemeWithState(props)?.theme) || getDefaultThemeProxied()
|
|
43
|
+
}
|
|
45
44
|
|
|
45
|
+
export const useThemeWithState = (props: ThemeProps) => {
|
|
46
46
|
const state = useServerRef() as React.MutableRefObject<UseThemeState>
|
|
47
47
|
if (isClient && !state.current) {
|
|
48
48
|
state.current = {
|
|
49
49
|
keys: new Set(),
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
// clear at start of every render to track usages
|
|
52
54
|
state.current?.keys.clear()
|
|
53
55
|
|
|
54
|
-
const
|
|
56
|
+
const changedTheme = useChangeThemeEffect(
|
|
55
57
|
props,
|
|
56
58
|
false,
|
|
57
|
-
isClient
|
|
59
|
+
isClient
|
|
60
|
+
? () => {
|
|
61
|
+
return props.shouldUpdate?.() ?? state.current.keys.size === 0
|
|
62
|
+
}
|
|
63
|
+
: undefined
|
|
58
64
|
)
|
|
59
65
|
|
|
66
|
+
const { themeManager, isNewTheme, theme, name, className } = changedTheme
|
|
67
|
+
|
|
60
68
|
if (process.env.NODE_ENV === 'development') {
|
|
61
69
|
// ensure we aren't creating too many ThemeManagers
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
isNewTheme &&
|
|
65
|
-
className === themeManager?.parentManager?.state.className
|
|
66
|
-
) {
|
|
70
|
+
// prettier-ignore
|
|
71
|
+
if (isWeb && isNewTheme && className === themeManager?.parentManager?.state.className) {
|
|
67
72
|
console.error('Should always change, duplicating ThemeMananger bug', themeManager)
|
|
68
73
|
}
|
|
69
74
|
if (props.debug === 'verbose') {
|
|
@@ -76,38 +81,29 @@ export const useTheme = (props: ThemeProps = emptyProps): ThemeParsed => {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
if (!theme) {
|
|
84
|
+
if (!changedTheme.theme) {
|
|
80
85
|
if (process.env.NODE_ENV === 'development') {
|
|
81
86
|
console.warn('No theme found', name, props, themeManager)
|
|
82
87
|
}
|
|
83
|
-
return
|
|
88
|
+
return null
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
return getThemeProxied(
|
|
88
|
-
isNewTheme,
|
|
89
|
-
theme,
|
|
90
|
-
name,
|
|
91
|
-
className,
|
|
92
|
-
themeManager,
|
|
93
|
-
state,
|
|
94
|
-
})
|
|
91
|
+
const proxiedTheme = useMemo(() => {
|
|
92
|
+
return getThemeProxied(changedTheme as any)
|
|
95
93
|
}, [theme, isNewTheme, name, className, themeManager])
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
...changedTheme,
|
|
97
|
+
theme: proxiedTheme,
|
|
98
|
+
}
|
|
96
99
|
}
|
|
97
100
|
|
|
98
|
-
function getThemeProxied({
|
|
101
|
+
export function getThemeProxied({
|
|
99
102
|
theme,
|
|
100
|
-
name,
|
|
101
|
-
className,
|
|
102
103
|
themeManager,
|
|
103
|
-
isNewTheme,
|
|
104
104
|
state,
|
|
105
|
-
}: {
|
|
106
|
-
theme:
|
|
107
|
-
name: string
|
|
108
|
-
className?: string
|
|
109
|
-
themeManager?: ThemeManager | null
|
|
110
|
-
isNewTheme?: boolean
|
|
105
|
+
}: Partial<ChangedThemeResponse> & {
|
|
106
|
+
theme: ThemeParsed
|
|
111
107
|
state?: React.RefObject<UseThemeState>
|
|
112
108
|
}) {
|
|
113
109
|
return createProxy(theme, {
|
|
@@ -121,26 +117,19 @@ function getThemeProxied({
|
|
|
121
117
|
get(_, key) {
|
|
122
118
|
if (key === GetThemeUnwrapped) {
|
|
123
119
|
return theme
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return isNewTheme
|
|
128
|
-
} else if (key === 'name') {
|
|
129
|
-
return name
|
|
130
|
-
} else if (key === 'className') {
|
|
131
|
-
return className
|
|
132
|
-
} else if (
|
|
133
|
-
!name ||
|
|
120
|
+
}
|
|
121
|
+
if (
|
|
122
|
+
key === 'undefined' ||
|
|
134
123
|
key === '__proto__' ||
|
|
135
|
-
|
|
136
|
-
key
|
|
124
|
+
key === '$typeof' ||
|
|
125
|
+
typeof key !== 'string' ||
|
|
126
|
+
!themeManager
|
|
137
127
|
) {
|
|
138
128
|
return Reflect.get(_, key)
|
|
139
129
|
}
|
|
140
130
|
// auto convert variables to plain
|
|
141
|
-
if (key[0] === '$')
|
|
142
|
-
|
|
143
|
-
return theme[key]
|
|
131
|
+
if (key[0] === '$') {
|
|
132
|
+
key = key.slice(1)
|
|
144
133
|
}
|
|
145
134
|
const val = themeManager.getValue(key)
|
|
146
135
|
if (val && state) {
|
|
@@ -150,7 +139,7 @@ function getThemeProxied({
|
|
|
150
139
|
if (subkey === 'val') {
|
|
151
140
|
state.current!.keys.add(key as any)
|
|
152
141
|
}
|
|
153
|
-
return Reflect.get(val, subkey)
|
|
142
|
+
return Reflect.get(val as any, subkey)
|
|
154
143
|
},
|
|
155
144
|
})
|
|
156
145
|
}
|
|
@@ -160,14 +149,6 @@ function getThemeProxied({
|
|
|
160
149
|
})
|
|
161
150
|
}
|
|
162
151
|
|
|
163
|
-
const GetThemeManager = Symbol()
|
|
164
|
-
const GetIsNewTheme = Symbol()
|
|
165
|
-
|
|
166
|
-
export const getThemeManager = (theme: any): ThemeManager | undefined =>
|
|
167
|
-
theme?.[GetThemeManager]
|
|
168
|
-
export const getThemeIsNewTheme = (theme: any): ThemeManager | undefined =>
|
|
169
|
-
theme?.[GetIsNewTheme]
|
|
170
|
-
|
|
171
152
|
export const activeThemeManagers = new Set<ThemeManager>()
|
|
172
153
|
|
|
173
154
|
export const useChangeThemeEffect = (
|
|
@@ -191,7 +172,7 @@ export const useChangeThemeEffect = (
|
|
|
191
172
|
} = props
|
|
192
173
|
|
|
193
174
|
if (disable) {
|
|
194
|
-
if (!parentManager) throw
|
|
175
|
+
if (!parentManager) throw `❌`
|
|
195
176
|
return {
|
|
196
177
|
...parentManager.state,
|
|
197
178
|
isNewTheme: false,
|
package/src/index.ts
CHANGED
|
@@ -24,7 +24,6 @@ export * from './helpers/useShallowSetState'
|
|
|
24
24
|
export * from './helpers/getVariantExtras'
|
|
25
25
|
export * from './helpers/getAnimationDriver'
|
|
26
26
|
export * from './helpers/getExpandedShorthands'
|
|
27
|
-
export * from './helpers/getSize'
|
|
28
27
|
export * from './helpers/getSplitStyles'
|
|
29
28
|
export * from './helpers/getStylesAtomic'
|
|
30
29
|
export * from './helpers/pseudoDescriptors'
|