@tamagui/core 1.0.1-beta.167 → 1.0.1-beta.169
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/addTheme.js.map +1 -1
- package/dist/cjs/config.js.map +1 -1
- package/dist/cjs/constants/platform.js +4 -4
- package/dist/cjs/constants/platform.js.map +2 -2
- package/dist/cjs/constants/platform.native.js +3 -3
- package/dist/cjs/constants/platform.native.js.map +2 -2
- package/dist/cjs/createComponent.js +39 -17
- package/dist/cjs/createComponent.js.map +2 -2
- package/dist/cjs/createTamagui.js +1 -4
- package/dist/cjs/createTamagui.js.map +2 -2
- package/dist/cjs/helpers/ThemeManager.js.map +1 -1
- package/dist/cjs/helpers/getSplitStyles.js +18 -10
- package/dist/cjs/helpers/getSplitStyles.js.map +3 -3
- package/dist/cjs/helpers/getThemeCSSRules.js.map +1 -1
- package/dist/cjs/hooks/useIsSSR.js +11 -7
- package/dist/cjs/hooks/useIsSSR.js.map +2 -2
- package/dist/cjs/hooks/useIsTouchDevice.js +1 -1
- package/dist/cjs/hooks/useIsTouchDevice.js.map +2 -2
- package/dist/cjs/hooks/useMedia.js +51 -29
- package/dist/cjs/hooks/useMedia.js.map +2 -2
- package/dist/cjs/hooks/useTheme.js +1 -1
- package/dist/cjs/hooks/useTheme.js.map +2 -2
- package/dist/cjs/styled.js +11 -8
- package/dist/cjs/styled.js.map +2 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/updateTheme.js.map +2 -2
- package/dist/cjs/views/TamaguiProvider.js +2 -2
- package/dist/cjs/views/TamaguiProvider.js.map +2 -2
- package/dist/esm/addTheme.js.map +1 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/constants/platform.js +3 -3
- package/dist/esm/constants/platform.js.map +2 -2
- package/dist/esm/constants/platform.native.js +2 -2
- package/dist/esm/constants/platform.native.js.map +2 -2
- package/dist/esm/createComponent.js +43 -18
- package/dist/esm/createComponent.js.map +2 -2
- package/dist/esm/createTamagui.js +1 -4
- package/dist/esm/createTamagui.js.map +2 -2
- package/dist/esm/helpers/ThemeManager.js.map +1 -1
- package/dist/esm/helpers/getSplitStyles.js +18 -10
- package/dist/esm/helpers/getSplitStyles.js.map +2 -2
- package/dist/esm/helpers/getThemeCSSRules.js.map +1 -1
- package/dist/esm/hooks/useIsSSR.js +12 -8
- package/dist/esm/hooks/useIsSSR.js.map +2 -2
- package/dist/esm/hooks/useIsTouchDevice.js +2 -2
- package/dist/esm/hooks/useIsTouchDevice.js.map +2 -2
- package/dist/esm/hooks/useMedia.js +51 -30
- package/dist/esm/hooks/useMedia.js.map +2 -2
- package/dist/esm/hooks/useTheme.js +2 -2
- package/dist/esm/hooks/useTheme.js.map +2 -2
- package/dist/esm/styled.js +11 -8
- package/dist/esm/styled.js.map +2 -2
- package/dist/esm/updateTheme.js.map +2 -2
- package/dist/esm/views/TamaguiProvider.js +3 -3
- package/dist/esm/views/TamaguiProvider.js.map +2 -2
- package/package.json +10 -10
- package/src/addTheme.ts +3 -3
- package/src/config.ts +1 -1
- package/src/constants/platform.native.ts +1 -1
- package/src/constants/platform.ts +2 -2
- package/src/createComponent.tsx +51 -23
- package/src/createTamagui.ts +7 -10
- package/src/helpers/ThemeManager.tsx +4 -4
- package/src/helpers/getSplitStyles.tsx +27 -13
- package/src/helpers/getThemeCSSRules.ts +2 -2
- package/src/hooks/useIsSSR.tsx +13 -8
- package/src/hooks/useIsTouchDevice.tsx +2 -2
- package/src/hooks/useMedia.tsx +72 -39
- package/src/hooks/useTheme.tsx +6 -7
- package/src/styled.tsx +14 -8
- package/src/types.tsx +41 -12
- package/src/updateTheme.ts +2 -2
- package/src/views/TamaguiProvider.tsx +3 -4
- package/types/addTheme.d.ts +2 -2
- package/types/config.d.ts +18 -2
- package/types/constants/platform.d.ts +1 -1
- package/types/constants/platform.native.d.ts +1 -1
- package/types/createVariable.d.ts +4 -0
- package/types/helpers/ThemeManager.d.ts +5 -5
- package/types/helpers/calc.d.ts +5 -0
- package/types/helpers/expandStyle.d.ts +4 -0
- package/types/helpers/expandStyles.d.ts +7 -0
- package/types/helpers/getSplitStyles.d.ts +3 -3
- package/types/helpers/getStylesAtomic.d.ts +4 -0
- package/types/helpers/getThemeCSSRules.d.ts +2 -2
- package/types/helpers/mergeProps.d.ts +13 -0
- package/types/helpers/normalizeValueWithProperty.d.ts +4 -0
- package/types/hooks/useIsSSR.d.ts +3 -1
- package/types/hooks/useMedia.d.ts +8 -5
- package/types/hooks/useTheme.d.ts +4 -4
- package/types/insertFont.d.ts +3 -0
- package/types/styled.d.ts +1 -0
- package/types/types.d.ts +111 -11
- package/types/updateTheme.d.ts +2 -2
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { stylePropsText, stylePropsTransform, validPseudoKeys, validStyles } from '@tamagui/helpers'
|
|
2
|
-
import { useInsertionEffect
|
|
2
|
+
import { useInsertionEffect } from 'react'
|
|
3
3
|
import type { ViewStyle } from 'react-native'
|
|
4
4
|
|
|
5
5
|
import { getConfig } from '../config'
|
|
6
6
|
import { isDevTools } from '../constants/isDevTools'
|
|
7
7
|
import { isClient, isRSC, isWeb, useIsomorphicLayoutEffect } from '../constants/platform'
|
|
8
|
-
import {
|
|
8
|
+
import { mediaState as globalMediaState, mediaQueryConfig } from '../hooks/useMedia'
|
|
9
9
|
import type {
|
|
10
10
|
DebugProp,
|
|
11
11
|
MediaQueryKey,
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
StackProps,
|
|
16
16
|
StaticConfigParsed,
|
|
17
17
|
TamaguiInternalConfig,
|
|
18
|
-
|
|
18
|
+
ThemeParsed,
|
|
19
19
|
} from '../types'
|
|
20
20
|
import { FontLanguageProps, LanguageContextType } from '../views/FontLanguage.types'
|
|
21
21
|
import { createMediaStyle } from './createMediaStyle'
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
updateInserted,
|
|
31
31
|
updateRules,
|
|
32
32
|
} from './insertStyleRule'
|
|
33
|
+
import { mergeProps } from './mergeProps'
|
|
33
34
|
import { mergeTransform } from './mergeTransform'
|
|
34
35
|
import { normalizeValueWithProperty } from './normalizeValueWithProperty.js'
|
|
35
36
|
import { pseudoDescriptors } from './pseudoDescriptors'
|
|
@@ -66,7 +67,7 @@ let conf: TamaguiInternalConfig
|
|
|
66
67
|
type StyleSplitter = (
|
|
67
68
|
props: { [key: string]: any },
|
|
68
69
|
staticConfig: StaticConfigParsed,
|
|
69
|
-
theme:
|
|
70
|
+
theme: ThemeParsed,
|
|
70
71
|
state: SplitStyleState,
|
|
71
72
|
defaultClassNames?: any,
|
|
72
73
|
languageContext?: LanguageContextType,
|
|
@@ -114,10 +115,14 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
114
115
|
const viewProps: StackProps = {}
|
|
115
116
|
const pseudos: PseudoStyles = {}
|
|
116
117
|
const medias: Record<MediaQueryKey, ViewStyle> = {}
|
|
118
|
+
const mediaState = state.mediaState || globalMediaState
|
|
117
119
|
const usedKeys = new Set<string>()
|
|
118
120
|
const propKeys = Object.keys(props)
|
|
119
121
|
const shouldDoClasses =
|
|
120
|
-
|
|
122
|
+
staticConfig.acceptsClassName &&
|
|
123
|
+
(isWeb || process.env.IS_STATIC === 'is_static') &&
|
|
124
|
+
!state.noClassNames &&
|
|
125
|
+
!props.animation
|
|
121
126
|
const len = propKeys.length
|
|
122
127
|
const rulesToInsert: RulesToInsert = []
|
|
123
128
|
const style: ViewStyle = {}
|
|
@@ -126,7 +131,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
126
131
|
// parity with react-native-web
|
|
127
132
|
style.display = 'inline-flex' as any
|
|
128
133
|
}
|
|
129
|
-
|
|
134
|
+
const classNames: ClassNamesObject = {}
|
|
130
135
|
// we need to gather these specific to each media query / pseudo
|
|
131
136
|
// value is [hash, val], so ["-jnjad-asdnjk", "scaleX(1) rotate(10deg)"]
|
|
132
137
|
const transforms: Record<TransformNamespaceKey, [string, string]> = {}
|
|
@@ -346,10 +351,18 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
346
351
|
// media
|
|
347
352
|
if (isMedia) {
|
|
348
353
|
const mediaKey = key
|
|
349
|
-
mediaKeys.push(key)
|
|
350
354
|
const mediaKeyShort = mediaKey.slice(1)
|
|
351
355
|
|
|
356
|
+
mediaKeys.push(mediaKeyShort)
|
|
357
|
+
|
|
352
358
|
if (!mediaQueryConfig[mediaKeyShort]) {
|
|
359
|
+
if (process.env.NODE_ENV === 'development' && debug) {
|
|
360
|
+
// eslint-disable-next-line no-console
|
|
361
|
+
console.warn(
|
|
362
|
+
`Warning: using a $ prefixed prop that doesn't match to a media query, may be an error`,
|
|
363
|
+
key
|
|
364
|
+
)
|
|
365
|
+
}
|
|
353
366
|
if (!usedKeys.has(key)) {
|
|
354
367
|
// this isn't a media key, pass through
|
|
355
368
|
viewProps[key] = val
|
|
@@ -376,7 +389,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
376
389
|
if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
|
|
377
390
|
// prettier-ignore
|
|
378
391
|
// eslint-disable-next-line no-console
|
|
379
|
-
console.log(
|
|
392
|
+
console.log(` 📺 ${mediaKey}`, mediaState[mediaKeyShort], { mediaKey, mediaStyle, props, shouldDoClasses, mediaState: { ...mediaState } })
|
|
380
393
|
}
|
|
381
394
|
|
|
382
395
|
if (shouldDoClasses) {
|
|
@@ -391,7 +404,7 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
391
404
|
}
|
|
392
405
|
}
|
|
393
406
|
} else {
|
|
394
|
-
if (mediaState[
|
|
407
|
+
if (mediaState[mediaKeyShort]) {
|
|
395
408
|
if (process.env.NODE_ENV === 'development' && debug) {
|
|
396
409
|
// eslint-disable-next-line no-console
|
|
397
410
|
console.log('apply media style', mediaKey, mediaState)
|
|
@@ -447,9 +460,10 @@ export const getSplitStyles: StyleSplitter = (
|
|
|
447
460
|
|
|
448
461
|
// add in defaults if not set:
|
|
449
462
|
if (defaultClassNames) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
463
|
+
for (const key in defaultClassNames) {
|
|
464
|
+
if (!(key in classNames)) {
|
|
465
|
+
classNames[key] = defaultClassNames[key]
|
|
466
|
+
}
|
|
453
467
|
}
|
|
454
468
|
}
|
|
455
469
|
|
|
@@ -646,7 +660,7 @@ export const getSubStyle = (
|
|
|
646
660
|
subKey: string,
|
|
647
661
|
styleIn: Object,
|
|
648
662
|
staticConfig: StaticConfigParsed,
|
|
649
|
-
theme:
|
|
663
|
+
theme: ThemeParsed,
|
|
650
664
|
props: any,
|
|
651
665
|
state: SplitStyleState,
|
|
652
666
|
conf: TamaguiInternalConfig,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { THEME_CLASSNAME_PREFIX } from '../constants/constants'
|
|
2
2
|
import { Variable, variableToString } from '../createVariable'
|
|
3
|
-
import { CreateTamaguiProps,
|
|
3
|
+
import { CreateTamaguiProps, ThemeParsed } from '../types'
|
|
4
4
|
import { tokensValueToVariable } from './registerCSSVariable'
|
|
5
5
|
|
|
6
6
|
export function getThemeCSSRules({
|
|
@@ -11,7 +11,7 @@ export function getThemeCSSRules({
|
|
|
11
11
|
}: {
|
|
12
12
|
config: CreateTamaguiProps
|
|
13
13
|
themeName: string
|
|
14
|
-
theme:
|
|
14
|
+
theme: ThemeParsed
|
|
15
15
|
names: string[]
|
|
16
16
|
}) {
|
|
17
17
|
const cssRuleSets: string[] = []
|
package/src/hooks/useIsSSR.tsx
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { isServer, isWeb, useIsomorphicLayoutEffect } from '../constants/platform'
|
|
4
4
|
import { startTransition } from '../helpers/startTransition'
|
|
5
5
|
|
|
6
6
|
// because any change before first useEffect causes hydration / mismatch issues
|
|
7
7
|
|
|
8
|
-
export const useIsSSR = () => {
|
|
8
|
+
export const useIsSSR = (props?: { immediate?: boolean }) => {
|
|
9
9
|
const [val, setVal] = useState(true)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if (isWeb && !isServer) {
|
|
12
|
+
useIsomorphicLayoutEffect(() => {
|
|
13
|
+
// could also have a global tamagui `config.immediateSSRTransition`
|
|
14
|
+
if (props?.immediate) {
|
|
14
15
|
setVal(false)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
} else {
|
|
17
|
+
startTransition(() => {
|
|
18
|
+
setVal(false)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}, [])
|
|
22
|
+
}
|
|
18
23
|
|
|
19
24
|
return val
|
|
20
25
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { isServer, isTouchable, isWeb, useIsomorphicLayoutEffect } from '../constants/platform'
|
|
4
4
|
|
|
5
5
|
// ssr friendly
|
|
6
6
|
|
|
7
7
|
export const useIsTouchDevice = () => {
|
|
8
8
|
const [touchOnly, setTouchOnly] = useState(false)
|
|
9
9
|
|
|
10
|
-
if (isWeb && !
|
|
10
|
+
if (isWeb && !isServer) {
|
|
11
11
|
// only ever false on web SSR (env), so run effect conditionally
|
|
12
12
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
13
13
|
useIsomorphicLayoutEffect(() => {
|
package/src/hooks/useMedia.tsx
CHANGED
|
@@ -1,26 +1,47 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react'
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { useIsomorphicLayoutEffect } from '../constants/platform'
|
|
4
4
|
import { matchMedia } from '../helpers/matchMedia'
|
|
5
5
|
import {
|
|
6
|
-
ConfigureMediaQueryOptions,
|
|
7
6
|
MediaQueries,
|
|
8
7
|
MediaQueryKey,
|
|
9
8
|
MediaQueryObject,
|
|
10
9
|
MediaQueryState,
|
|
10
|
+
TamaguiInternalConfig,
|
|
11
11
|
} from '../types'
|
|
12
12
|
import { useSafeRef } from './useSafeRef'
|
|
13
13
|
|
|
14
|
-
export const mediaState: MediaQueryState =
|
|
15
|
-
|
|
14
|
+
export const mediaState: MediaQueryState =
|
|
15
|
+
// development time safeguard
|
|
16
|
+
process.env.NODE_ENV === 'development'
|
|
17
|
+
? new Proxy(
|
|
18
|
+
{},
|
|
19
|
+
{
|
|
20
|
+
get(target, key) {
|
|
21
|
+
if (typeof key === 'string' && key[0] === '$') {
|
|
22
|
+
throw new Error(`Access of mediaState should never include $`)
|
|
23
|
+
}
|
|
24
|
+
return Reflect.get(target, key)
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
: ({} as any)
|
|
29
|
+
|
|
30
|
+
type MediaListener = (next: boolean) => void
|
|
31
|
+
|
|
32
|
+
const mediaQueryListeners: { [key: string]: Set<MediaListener> } = {}
|
|
16
33
|
|
|
17
|
-
export function addMediaQueryListener(key: MediaQueryKey, cb:
|
|
34
|
+
export function addMediaQueryListener(key: MediaQueryKey, cb: MediaListener) {
|
|
35
|
+
if (process.env.NODE_ENV === 'development' && key[0] === '$') {
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.warn(`Warning, listening to media queries shouldn't use the "$" prefix`)
|
|
38
|
+
}
|
|
18
39
|
mediaQueryListeners[key] = mediaQueryListeners[key] || new Set()
|
|
19
40
|
mediaQueryListeners[key].add(cb)
|
|
20
41
|
return () => removeMediaQueryListener(key, cb)
|
|
21
42
|
}
|
|
22
43
|
|
|
23
|
-
export function removeMediaQueryListener(key: MediaQueryKey, cb:
|
|
44
|
+
export function removeMediaQueryListener(key: MediaQueryKey, cb: MediaListener) {
|
|
24
45
|
mediaQueryListeners[key]?.delete(cb)
|
|
25
46
|
}
|
|
26
47
|
|
|
@@ -35,37 +56,40 @@ const dispose = new Set<Function>()
|
|
|
35
56
|
// for SSR capture it at time of startup
|
|
36
57
|
let initialMediaState: MediaQueryState | null
|
|
37
58
|
|
|
38
|
-
export const configureMedia = ({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// support hot reload
|
|
45
|
-
if (initialMediaState) {
|
|
46
|
-
if (process.env.NODE_ENV === 'development') {
|
|
47
|
-
if (JSON.stringify(queries) === JSON.stringify(mediaQueryConfig)) {
|
|
48
|
-
// hmr avoid update
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
setupMediaListeners()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
Object.assign(mediaQueryConfig, queries)
|
|
56
|
-
// start in the initial state
|
|
57
|
-
for (const key in queries) {
|
|
58
|
-
mediaState[key] = defaultActive[key] || false
|
|
59
|
+
export const configureMedia = (config: TamaguiInternalConfig) => {
|
|
60
|
+
const { media, mediaQueryDefaultActive } = config
|
|
61
|
+
if (!media) return
|
|
62
|
+
for (const key in media) {
|
|
63
|
+
mediaState[key] = mediaQueryDefaultActive?.[key] || false
|
|
59
64
|
}
|
|
65
|
+
Object.assign(mediaQueryConfig, media)
|
|
60
66
|
initialMediaState = { ...mediaState }
|
|
67
|
+
if (config.disableSSR) {
|
|
68
|
+
setupMediaListeners()
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
export const getInitialMediaState = () => initialMediaState
|
|
73
|
+
|
|
63
74
|
function unlisten() {
|
|
64
75
|
dispose.forEach((cb) => cb())
|
|
65
76
|
dispose.clear()
|
|
66
77
|
}
|
|
67
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Note: This should *not* set the state on the first render!
|
|
81
|
+
* Because to avoid hydration issues SSR must match the server
|
|
82
|
+
* *and then* re-render with the actual media query state.
|
|
83
|
+
*/
|
|
84
|
+
let configuredKey = ''
|
|
68
85
|
function setupMediaListeners() {
|
|
86
|
+
// avoid setting up more than once per config
|
|
87
|
+
const nextKey = JSON.stringify(mediaQueryConfig)
|
|
88
|
+
if (nextKey === configuredKey) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
configuredKey = nextKey
|
|
92
|
+
|
|
69
93
|
// hmr, undo existing before re-binding
|
|
70
94
|
unlisten()
|
|
71
95
|
|
|
@@ -79,6 +103,7 @@ function setupMediaListeners() {
|
|
|
79
103
|
// react native needs these deprecated apis for now
|
|
80
104
|
match.addListener(update)
|
|
81
105
|
dispose.add(() => match.removeListener(update))
|
|
106
|
+
|
|
82
107
|
update()
|
|
83
108
|
|
|
84
109
|
function update() {
|
|
@@ -87,16 +112,18 @@ function setupMediaListeners() {
|
|
|
87
112
|
mediaState[key] = next
|
|
88
113
|
const listeners = mediaQueryListeners[key]
|
|
89
114
|
if (listeners?.size) {
|
|
90
|
-
|
|
91
|
-
cb()
|
|
92
|
-
}
|
|
115
|
+
listeners.forEach((cb) => cb(next))
|
|
93
116
|
}
|
|
94
117
|
}
|
|
95
118
|
}
|
|
96
119
|
}
|
|
97
120
|
|
|
98
|
-
export function useMediaQueryListeners() {
|
|
99
|
-
|
|
121
|
+
export function useMediaQueryListeners(config: TamaguiInternalConfig) {
|
|
122
|
+
if (config.disableSSR) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
100
127
|
setupMediaListeners()
|
|
101
128
|
return unlisten
|
|
102
129
|
}, [])
|
|
@@ -105,7 +132,7 @@ export function useMediaQueryListeners() {
|
|
|
105
132
|
export function useMedia(): {
|
|
106
133
|
[key in MediaQueryKey]: boolean
|
|
107
134
|
} {
|
|
108
|
-
const [state, setState] = useState(initialMediaState
|
|
135
|
+
const [state, setState] = useState(initialMediaState || {})
|
|
109
136
|
const keys = useSafeRef({} as Record<string, boolean>)
|
|
110
137
|
|
|
111
138
|
function updateState() {
|
|
@@ -120,13 +147,12 @@ export function useMedia(): {
|
|
|
120
147
|
}
|
|
121
148
|
|
|
122
149
|
useIsomorphicLayoutEffect(() => {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
150
|
+
const disposes: Function[] = Object.keys(keys.current).map((key) =>
|
|
151
|
+
addMediaQueryListener(key, updateState)
|
|
152
|
+
)
|
|
127
153
|
updateState()
|
|
128
154
|
return () => {
|
|
129
|
-
|
|
155
|
+
disposes.forEach((cb) => cb())
|
|
130
156
|
}
|
|
131
157
|
})
|
|
132
158
|
|
|
@@ -134,8 +160,15 @@ export function useMedia(): {
|
|
|
134
160
|
() =>
|
|
135
161
|
new Proxy(state, {
|
|
136
162
|
get(_, key: string) {
|
|
163
|
+
if (process.env.NODE_ENV === 'development') {
|
|
164
|
+
if (!initialMediaState) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`To use useMedia() you must pass in media configuration to createTamagui, none was found.`
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
137
170
|
if (!keys.current[key]) {
|
|
138
|
-
keys.current
|
|
171
|
+
keys.current[key] = true
|
|
139
172
|
}
|
|
140
173
|
return Reflect.get(state, key)
|
|
141
174
|
},
|
package/src/hooks/useTheme.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import React, { useContext, useLayoutEffect, useMemo, useRef, useState } from 'r
|
|
|
3
3
|
|
|
4
4
|
import { getConfig } from '../config'
|
|
5
5
|
import { isDevTools } from '../constants/isDevTools'
|
|
6
|
-
import { isRSC,
|
|
6
|
+
import { isRSC, isServer, useIsomorphicLayoutEffect } from '../constants/platform'
|
|
7
7
|
import { ThemeContext } from '../contexts/ThemeContext'
|
|
8
8
|
import { areEqualSets } from '../helpers/areEqualSets'
|
|
9
9
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ThemeManagerContext,
|
|
13
13
|
emptyManager,
|
|
14
14
|
} from '../helpers/ThemeManager'
|
|
15
|
-
import { ThemeName,
|
|
15
|
+
import { ThemeName, ThemeParsed } from '../types'
|
|
16
16
|
import { GetThemeUnwrapped } from './getThemeUnwrapped'
|
|
17
17
|
import { useConstant } from './useConstant'
|
|
18
18
|
import { useServerRef } from './useServerHooks'
|
|
@@ -38,7 +38,7 @@ export const useTheme = (
|
|
|
38
38
|
componentName?: string,
|
|
39
39
|
props?: ThemeProps,
|
|
40
40
|
forceUpdate?: any
|
|
41
|
-
):
|
|
41
|
+
): ThemeParsed => {
|
|
42
42
|
// TODO this can use useChangeThemeEffect almost ready
|
|
43
43
|
if (isRSC) {
|
|
44
44
|
const config = getConfig()
|
|
@@ -195,10 +195,10 @@ export const useChangeThemeEffect = (
|
|
|
195
195
|
forceUpdateProp?: any,
|
|
196
196
|
uuid?: Object
|
|
197
197
|
): {
|
|
198
|
-
themes: Record<string,
|
|
198
|
+
themes: Record<string, ThemeParsed>
|
|
199
199
|
themeManager: ThemeManager | null
|
|
200
200
|
name: string
|
|
201
|
-
theme:
|
|
201
|
+
theme: ThemeParsed | null
|
|
202
202
|
className?: string
|
|
203
203
|
} => {
|
|
204
204
|
const { themes } = getConfig()
|
|
@@ -236,10 +236,9 @@ export const useChangeThemeEffect = (
|
|
|
236
236
|
return new ThemeManager(next.name, next.className, next.theme, parentManager, reset)
|
|
237
237
|
})
|
|
238
238
|
|
|
239
|
-
if (!
|
|
239
|
+
if (!isServer) {
|
|
240
240
|
useLayoutEffect(() => {
|
|
241
241
|
themeManager.update(next)
|
|
242
|
-
|
|
243
242
|
activeThemeManagers.add(themeManager)
|
|
244
243
|
|
|
245
244
|
const disposeParentOnChange = parentManager.onChangeTheme(() => {
|
package/src/styled.tsx
CHANGED
|
@@ -48,6 +48,7 @@ export function styled<
|
|
|
48
48
|
variants?: Variants | undefined
|
|
49
49
|
// thought i had this typed, but can't get it linked
|
|
50
50
|
defaultVariants?: { [key: string]: any }
|
|
51
|
+
acceptsClassName?: boolean
|
|
51
52
|
},
|
|
52
53
|
staticExtractionOptions?: Partial<StaticConfig>
|
|
53
54
|
) {
|
|
@@ -73,7 +74,13 @@ export function styled<
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
if (options) {
|
|
76
|
-
const {
|
|
77
|
+
const {
|
|
78
|
+
variants,
|
|
79
|
+
name,
|
|
80
|
+
defaultVariants,
|
|
81
|
+
acceptsClassName: acceptsClassNameProp,
|
|
82
|
+
...defaultProps
|
|
83
|
+
} = options
|
|
77
84
|
if (defaultVariants) {
|
|
78
85
|
Object.assign(defaultProps, defaultVariants)
|
|
79
86
|
}
|
|
@@ -84,12 +91,7 @@ export function styled<
|
|
|
84
91
|
RNComponents.add(Component)
|
|
85
92
|
isReactNativeWeb = true
|
|
86
93
|
} else {
|
|
87
|
-
|
|
88
|
-
// eslint-disable-next-line no-console
|
|
89
|
-
console.warn(
|
|
90
|
-
`Invalid styled() component: Tamagui only accepts react-native Text, View, Image, TextInput, or another styled Tamagui view`
|
|
91
|
-
)
|
|
92
|
-
}
|
|
94
|
+
// assuming some sort of non-tamagui non-rn view, will always use style={}
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
const reactNativeWebComponent = isReactNativeWeb
|
|
@@ -107,6 +109,8 @@ export function styled<
|
|
|
107
109
|
isInput || staticExtractionOptions?.isText || parentStaticConfig?.isText || Comp === Text
|
|
108
110
|
)
|
|
109
111
|
|
|
112
|
+
const acceptsClassName = acceptsClassNameProp ?? (isTamagui || isReactNativeWeb)
|
|
113
|
+
|
|
110
114
|
const conf: Partial<StaticConfig> = {
|
|
111
115
|
...staticExtractionOptions,
|
|
112
116
|
...(!isTamagui && {
|
|
@@ -125,10 +129,12 @@ export function styled<
|
|
|
125
129
|
isInput,
|
|
126
130
|
isText,
|
|
127
131
|
isImage,
|
|
132
|
+
acceptsClassName,
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
// TODO compiler doesn't have logic to include children, de-opt (see EnsureFlexed for test usage)
|
|
131
|
-
|
|
136
|
+
// bail on non className views as well
|
|
137
|
+
if (defaultProps.children || !acceptsClassName) {
|
|
132
138
|
conf.neverFlatten = true
|
|
133
139
|
}
|
|
134
140
|
|
package/src/types.tsx
CHANGED
|
@@ -162,7 +162,12 @@ export type CreateTamaguiConfig<
|
|
|
162
162
|
fonts: RemoveLanguagePostfixes<F>
|
|
163
163
|
fontLanguages: GetLanguagePostfixes<F> extends never ? string[] : GetLanguagePostfixes<F>[]
|
|
164
164
|
tokens: A
|
|
165
|
-
|
|
165
|
+
// parsed
|
|
166
|
+
themes: {
|
|
167
|
+
[Name in keyof B]: {
|
|
168
|
+
[Key in keyof B[Name]]: Variable
|
|
169
|
+
}
|
|
170
|
+
}
|
|
166
171
|
shorthands: C
|
|
167
172
|
media: D
|
|
168
173
|
animations: AnimationDriver<E>
|
|
@@ -230,13 +235,16 @@ export type GenericTamaguiConfig = CreateTamaguiConfig<
|
|
|
230
235
|
>
|
|
231
236
|
|
|
232
237
|
// since TamaguiConfig will be re-declared, these will all be typed globally
|
|
233
|
-
export type
|
|
238
|
+
export type ThemeDefinition = TamaguiConfig['themes'][keyof TamaguiConfig['themes']]
|
|
239
|
+
export type ThemeKeys = keyof ThemeDefinition
|
|
240
|
+
export type ThemeParsed = {
|
|
241
|
+
[key in ThemeKeys]: Variable
|
|
242
|
+
}
|
|
234
243
|
export type Tokens = TamaguiConfig['tokens']
|
|
235
244
|
export type Shorthands = TamaguiConfig['shorthands']
|
|
236
245
|
export type Media = TamaguiConfig['media']
|
|
237
246
|
export type Themes = TamaguiConfig['themes']
|
|
238
247
|
export type ThemeName = GetAltThemeNames<keyof Themes>
|
|
239
|
-
export type ThemeKeys = keyof ThemeObject
|
|
240
248
|
export type ThemeTokens = `$${ThemeKeys}`
|
|
241
249
|
export type AnimationKeys = Omit<GetAnimationKeys<TamaguiConfig>, number>
|
|
242
250
|
export type FontLanguages = ArrayIntersection<TamaguiConfig['fontLanguages']>
|
|
@@ -264,6 +272,26 @@ export type CreateTamaguiProps = {
|
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
defaultTheme?: string
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* *Advanced use case* For all CSS extracted views, this has no effect.
|
|
278
|
+
*
|
|
279
|
+
* For SSR compatibility on the web, Tamagui will render once with the settings
|
|
280
|
+
* from `mediaQueryDefaultActive` set for all media queries. Then, it will render
|
|
281
|
+
* again after the initial render using the proper media query values. This is so that
|
|
282
|
+
* hydration will match perfectly with the server.
|
|
283
|
+
*
|
|
284
|
+
* Setting disableSSR will avoid this second render by setting the media query state
|
|
285
|
+
* to the actual browser dimensions on initial load. This is only useful for client-only
|
|
286
|
+
* apps.
|
|
287
|
+
*
|
|
288
|
+
*/
|
|
289
|
+
disableSSR?: boolean
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Disable inserting a theme class in the DOM or context, allowing you to manually place it higher.
|
|
293
|
+
* For custom use cases like integration with next-theme.
|
|
294
|
+
*/
|
|
267
295
|
disableRootThemeClass?: boolean
|
|
268
296
|
|
|
269
297
|
defaultProps?: Record<string, any> & {
|
|
@@ -314,9 +342,6 @@ export type TamaguiInternalConfig<
|
|
|
314
342
|
getCSS: () => string
|
|
315
343
|
parsed: boolean
|
|
316
344
|
inverseShorthands: Record<string, string>
|
|
317
|
-
|
|
318
|
-
// just passed in from CreateTamaguiProps
|
|
319
|
-
themeClassNameOnRoot?: boolean
|
|
320
345
|
}
|
|
321
346
|
|
|
322
347
|
export type GetAnimationKeys<A extends GenericTamaguiConfig> = keyof A['animations']['animations']
|
|
@@ -352,10 +377,6 @@ export type MediaProps<A> = {
|
|
|
352
377
|
export type MediaQueries = {
|
|
353
378
|
[key in MediaQueryKey]: MediaQueryObject
|
|
354
379
|
}
|
|
355
|
-
export type ConfigureMediaQueryOptions = {
|
|
356
|
-
queries?: MediaQueries
|
|
357
|
-
defaultActive?: Record<MediaQueryKey, boolean>
|
|
358
|
-
}
|
|
359
380
|
|
|
360
381
|
export type TransformStyleProps = {
|
|
361
382
|
x?: number
|
|
@@ -423,7 +444,7 @@ export type SizeTokens = GetTokenString<keyof Tokens['size']> | number
|
|
|
423
444
|
export type SpaceTokens = GetTokenString<keyof Tokens['space']> | number | boolean
|
|
424
445
|
export type ColorTokens =
|
|
425
446
|
| GetTokenString<keyof Tokens['color']>
|
|
426
|
-
| GetTokenString<keyof
|
|
447
|
+
| GetTokenString<keyof ThemeParsed>
|
|
427
448
|
| CSSColorNames
|
|
428
449
|
export type ZIndexTokens = GetTokenString<keyof Tokens['zIndex']> | number
|
|
429
450
|
export type RadiusTokens = GetTokenString<keyof Tokens['radius']> | number
|
|
@@ -640,7 +661,7 @@ export type TamaguiProviderProps = Partial<Omit<ThemeProviderProps, 'children'>>
|
|
|
640
661
|
export type PropMapper = (
|
|
641
662
|
key: string,
|
|
642
663
|
value: any,
|
|
643
|
-
theme:
|
|
664
|
+
theme: ThemeParsed,
|
|
644
665
|
props: Record<string, any>,
|
|
645
666
|
state: Partial<SplitStyleState>,
|
|
646
667
|
languageContext?: FontLanguageProps,
|
|
@@ -773,6 +794,13 @@ export type StaticConfig = {
|
|
|
773
794
|
* Used insternally to attach default props to names
|
|
774
795
|
*/
|
|
775
796
|
parentNames?: string[]
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* By default if styled() doesn't recognize a parent Tamagui compoent or specific react-native views,
|
|
800
|
+
* it will assume the passed in component only accepts style={} for react-native compatibility.
|
|
801
|
+
* Setting `acceptsClassName: true` indicates Tamagui can pass in className props.
|
|
802
|
+
*/
|
|
803
|
+
acceptsClassName?: boolean
|
|
776
804
|
}
|
|
777
805
|
|
|
778
806
|
/**
|
|
@@ -1183,6 +1211,7 @@ export type TamaguiComponentState = {
|
|
|
1183
1211
|
pressIn: boolean
|
|
1184
1212
|
focus: boolean
|
|
1185
1213
|
mounted: boolean
|
|
1214
|
+
mediaState?: null | Record<string, boolean>
|
|
1186
1215
|
animation?: null | {
|
|
1187
1216
|
style?: any
|
|
1188
1217
|
avoidClasses?: boolean
|
package/src/updateTheme.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { addTheme } from './addTheme'
|
|
2
2
|
import { activeThemeManagers } from './hooks/useTheme'
|
|
3
|
-
import {
|
|
3
|
+
import { ThemeDefinition } from './types'
|
|
4
4
|
|
|
5
|
-
export function updateTheme({ name, theme }: { name: string; theme:
|
|
5
|
+
export function updateTheme({ name, theme }: { name: string; theme: ThemeDefinition }) {
|
|
6
6
|
const next = addTheme({ name, theme, insertCSS: true, update: true })
|
|
7
7
|
|
|
8
8
|
if (process.env.TAMAGUI_TARGET === 'native') {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { useForceUpdate } from '@tamagui/use-force-update'
|
|
2
1
|
import * as React from 'react'
|
|
3
2
|
|
|
4
|
-
import { isClient, isRSC,
|
|
3
|
+
import { isClient, isRSC, isServer } from '../constants/platform'
|
|
5
4
|
import { ButtonInsideButtonContext } from '../contexts/ButtonInsideButtonContext'
|
|
6
5
|
import { TextAncestorProvider } from '../contexts/TextAncestorContext'
|
|
7
6
|
import { useMediaQueryListeners } from '../hooks/useMedia'
|
|
@@ -22,8 +21,8 @@ export function TamaguiProvider({
|
|
|
22
21
|
)
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
if (!
|
|
26
|
-
useMediaQueryListeners()
|
|
24
|
+
if (!isServer) {
|
|
25
|
+
useMediaQueryListeners(config)
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
if (isClient) {
|
package/types/addTheme.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ThemeDefinition } from './types';
|
|
2
2
|
export declare function addTheme({ name: themeName, theme: themeIn, insertCSS, update, }: {
|
|
3
3
|
name: string;
|
|
4
|
-
theme:
|
|
4
|
+
theme: ThemeDefinition;
|
|
5
5
|
insertCSS?: boolean;
|
|
6
6
|
update?: boolean;
|
|
7
7
|
}): {
|