@niibase/uniwind 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1197 -0
- package/dist/common/components/web/ActivityIndicator.js +3 -1
- package/dist/common/components/web/Button.js +3 -1
- package/dist/common/components/web/FlatList.js +3 -1
- package/dist/common/components/web/Image.js +3 -1
- package/dist/common/components/web/ImageBackground.js +3 -1
- package/dist/common/components/web/KeyboardAvoidingView.js +3 -1
- package/dist/common/components/web/Modal.js +3 -1
- package/dist/common/components/web/Pressable.js +3 -1
- package/dist/common/components/web/RefreshControl.js +3 -1
- package/dist/common/components/web/SafeAreaView.js +3 -1
- package/dist/common/components/web/ScrollView.js +3 -1
- package/dist/common/components/web/SectionList.js +3 -1
- package/dist/common/components/web/Switch.js +3 -1
- package/dist/common/components/web/Text.js +3 -1
- package/dist/common/components/web/TextInput.js +3 -1
- package/dist/common/components/web/TouchableHighlight.js +3 -1
- package/dist/common/components/web/TouchableOpacity.js +3 -1
- package/dist/common/components/web/TouchableWithoutFeedback.js +3 -1
- package/dist/common/components/web/View.js +2 -0
- package/dist/common/components/web/VirtualizedList.js +3 -1
- package/dist/common/components/web/generateDataSet.js +18 -0
- package/dist/common/core/config/config.common.js +6 -3
- package/dist/common/core/web/cssListener.js +58 -6
- package/dist/common/core/web/getWebStyles.js +20 -18
- package/dist/common/css/extraUtilities.js +19 -0
- package/dist/common/css/index.js +2 -3
- package/dist/common/css/insets.js +2 -2
- package/dist/common/css/overwrite.js +2 -2
- package/dist/common/css/variants.js +2 -2
- package/dist/metro/metro-transformer.cjs +41 -7
- package/dist/metro/metro-transformer.mjs +41 -7
- package/dist/module/components/web/ActivityIndicator.js +3 -1
- package/dist/module/components/web/Button.js +3 -1
- package/dist/module/components/web/FlatList.js +3 -1
- package/dist/module/components/web/Image.js +3 -1
- package/dist/module/components/web/ImageBackground.js +3 -1
- package/dist/module/components/web/KeyboardAvoidingView.js +3 -1
- package/dist/module/components/web/Modal.js +3 -1
- package/dist/module/components/web/Pressable.js +3 -1
- package/dist/module/components/web/RefreshControl.js +3 -1
- package/dist/module/components/web/SafeAreaView.js +3 -1
- package/dist/module/components/web/ScrollView.js +3 -1
- package/dist/module/components/web/SectionList.js +3 -1
- package/dist/module/components/web/Switch.js +3 -1
- package/dist/module/components/web/Text.js +3 -1
- package/dist/module/components/web/TextInput.js +3 -1
- package/dist/module/components/web/TouchableHighlight.js +3 -1
- package/dist/module/components/web/TouchableOpacity.js +3 -1
- package/dist/module/components/web/TouchableWithoutFeedback.js +3 -1
- package/dist/module/components/web/View.js +2 -0
- package/dist/module/components/web/VirtualizedList.js +3 -1
- package/dist/module/components/web/generateDataSet.d.ts +32 -0
- package/dist/module/components/web/generateDataSet.js +9 -0
- package/dist/module/core/config/config.common.d.ts +2 -1
- package/dist/module/core/config/config.common.js +6 -3
- package/dist/module/core/web/cssListener.d.ts +6 -1
- package/dist/module/core/web/cssListener.js +58 -6
- package/dist/module/core/web/getWebStyles.js +21 -18
- package/dist/module/css/extraUtilities.d.ts +1 -0
- package/dist/module/css/extraUtilities.js +20 -0
- package/dist/module/css/index.js +8 -8
- package/dist/module/css/insets.d.ts +1 -1
- package/dist/module/css/insets.js +2 -1
- package/dist/module/css/overwrite.d.ts +1 -1
- package/dist/module/css/overwrite.js +1 -1
- package/dist/module/css/variants.d.ts +1 -1
- package/dist/module/css/variants.js +2 -1
- package/dist/module/index.d.ts +1 -0
- package/dist/shared/{uniwind.DTMo4epG.cjs → uniwind.BZyFsest.cjs} +28 -6
- package/dist/shared/{uniwind.BWb5KNML.mjs → uniwind.C-rHhHOg.mjs} +28 -6
- package/dist/vite/index.cjs +1 -1
- package/dist/vite/index.mjs +1 -1
- package/package.json +4 -2
- package/src/components/web/ActivityIndicator.tsx +2 -0
- package/src/components/web/Button.tsx +2 -0
- package/src/components/web/FlatList.tsx +2 -0
- package/src/components/web/Image.tsx +2 -0
- package/src/components/web/ImageBackground.tsx +2 -0
- package/src/components/web/KeyboardAvoidingView.tsx +2 -0
- package/src/components/web/Modal.tsx +2 -0
- package/src/components/web/Pressable.tsx +2 -0
- package/src/components/web/RefreshControl.tsx +2 -0
- package/src/components/web/SafeAreaView.tsx +2 -0
- package/src/components/web/ScrollView.tsx +2 -0
- package/src/components/web/SectionList.tsx +2 -0
- package/src/components/web/Switch.tsx +2 -0
- package/src/components/web/Text.tsx +2 -0
- package/src/components/web/TextInput.tsx +2 -0
- package/src/components/web/TouchableHighlight.tsx +2 -0
- package/src/components/web/TouchableOpacity.tsx +2 -0
- package/src/components/web/TouchableWithoutFeedback.tsx +2 -0
- package/src/components/web/View.tsx +2 -0
- package/src/components/web/VirtualizedList.tsx +2 -0
- package/src/components/web/generateDataSet.ts +52 -0
- package/src/components/web/rnw.ts +1 -1
- package/src/core/config/config.common.ts +7 -3
- package/src/core/web/cssListener.ts +73 -6
- package/src/core/web/getWebStyles.ts +26 -25
- package/src/css/extraUtilities.ts +26 -0
- package/src/css/index.ts +8 -8
- package/src/css/insets.ts +3 -1
- package/src/css/overwrite.ts +1 -1
- package/src/css/variants.ts +3 -1
- package/src/index.ts +1 -0
- package/src/metro/processor/css.ts +35 -0
- package/src/metro/utils/serialize.ts +13 -8
- package/uniwind.css +8 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SafeAreaView as RNSafeAreaView, ViewProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const SafeAreaView = copyComponentProperties(RNSafeAreaView, (props: ViewProps) => {
|
|
@@ -7,6 +8,7 @@ export const SafeAreaView = copyComponentProperties(RNSafeAreaView, (props: View
|
|
|
7
8
|
<RNSafeAreaView
|
|
8
9
|
{...props}
|
|
9
10
|
style={[toRNWClassName(props.className), props.style]}
|
|
11
|
+
dataSet={generateDataSet(props)}
|
|
10
12
|
/>
|
|
11
13
|
)
|
|
12
14
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ScrollView as RNScrollView, ScrollViewProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const ScrollView = copyComponentProperties(RNScrollView, (props: ScrollViewProps) => {
|
|
@@ -8,6 +9,7 @@ export const ScrollView = copyComponentProperties(RNScrollView, (props: ScrollVi
|
|
|
8
9
|
{...props}
|
|
9
10
|
style={[toRNWClassName(props.className), props.style]}
|
|
10
11
|
contentContainerStyle={[toRNWClassName(props.contentContainerClassName), props.contentContainerStyle]}
|
|
12
|
+
dataSet={generateDataSet(props)}
|
|
11
13
|
/>
|
|
12
14
|
)
|
|
13
15
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SectionList as RNSectionList, SectionListProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const SectionList = copyComponentProperties(RNSectionList, (props: SectionListProps<unknown, unknown>) => {
|
|
@@ -10,6 +11,7 @@ export const SectionList = copyComponentProperties(RNSectionList, (props: Sectio
|
|
|
10
11
|
contentContainerStyle={[toRNWClassName(props.contentContainerClassName), props.contentContainerStyle]}
|
|
11
12
|
ListFooterComponentStyle={[toRNWClassName(props.ListFooterComponentClassName), props.ListFooterComponentStyle]}
|
|
12
13
|
ListHeaderComponentStyle={[toRNWClassName(props.ListHeaderComponentClassName), props.ListHeaderComponentStyle]}
|
|
14
|
+
dataSet={generateDataSet(props)}
|
|
13
15
|
/>
|
|
14
16
|
)
|
|
15
17
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Switch as RNSwitch, SwitchProps } from 'react-native'
|
|
2
2
|
import { useUniwindAccent } from '../../hooks'
|
|
3
3
|
import { copyComponentProperties } from '../utils'
|
|
4
|
+
import { generateDataSet } from './generateDataSet'
|
|
4
5
|
import { toRNWClassName } from './rnw'
|
|
5
6
|
|
|
6
7
|
export const Switch = copyComponentProperties(RNSwitch, (props: SwitchProps) => {
|
|
@@ -14,6 +15,7 @@ export const Switch = copyComponentProperties(RNSwitch, (props: SwitchProps) =>
|
|
|
14
15
|
style={[toRNWClassName(props.className), props.style]}
|
|
15
16
|
thumbColor={props.thumbColor ?? thumbColor}
|
|
16
17
|
trackColor={{ true: props.trackColor?.true ?? trackColorOn, false: props.trackColor?.false ?? trackColorOff }}
|
|
18
|
+
dataSet={generateDataSet(props)}
|
|
17
19
|
/>
|
|
18
20
|
)
|
|
19
21
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Text as RNText, TextProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const Text = copyComponentProperties(RNText, (props: TextProps) => {
|
|
@@ -7,6 +8,7 @@ export const Text = copyComponentProperties(RNText, (props: TextProps) => {
|
|
|
7
8
|
<RNText
|
|
8
9
|
{...props}
|
|
9
10
|
style={[toRNWClassName(props.className), props.style]}
|
|
11
|
+
dataSet={generateDataSet(props)}
|
|
10
12
|
/>
|
|
11
13
|
)
|
|
12
14
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TextInput as RNTextInput, TextInputProps } from 'react-native'
|
|
2
2
|
import { useUniwindAccent } from '../../hooks'
|
|
3
3
|
import { copyComponentProperties } from '../utils'
|
|
4
|
+
import { generateDataSet } from './generateDataSet'
|
|
4
5
|
import { toRNWClassName } from './rnw'
|
|
5
6
|
|
|
6
7
|
export const TextInput = copyComponentProperties(RNTextInput, (props: TextInputProps) => {
|
|
@@ -11,6 +12,7 @@ export const TextInput = copyComponentProperties(RNTextInput, (props: TextInputP
|
|
|
11
12
|
{...props}
|
|
12
13
|
style={[toRNWClassName(props.className), props.style]}
|
|
13
14
|
placeholderTextColor={props.placeholderTextColor ?? placeholderTextColor}
|
|
15
|
+
dataSet={generateDataSet(props)}
|
|
14
16
|
/>
|
|
15
17
|
)
|
|
16
18
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TouchableHighlight as RNTouchableHighlight, TouchableHighlightProps } from 'react-native'
|
|
2
2
|
import { useUniwindAccent } from '../../hooks'
|
|
3
3
|
import { copyComponentProperties } from '../utils'
|
|
4
|
+
import { generateDataSet } from './generateDataSet'
|
|
4
5
|
import { toRNWClassName } from './rnw'
|
|
5
6
|
|
|
6
7
|
export const TouchableHighlight = copyComponentProperties(RNTouchableHighlight, (props: TouchableHighlightProps) => {
|
|
@@ -11,6 +12,7 @@ export const TouchableHighlight = copyComponentProperties(RNTouchableHighlight,
|
|
|
11
12
|
{...props}
|
|
12
13
|
style={[toRNWClassName(props.className), props.style]}
|
|
13
14
|
underlayColor={props.underlayColor ?? underlayColor}
|
|
15
|
+
dataSet={generateDataSet(props)}
|
|
14
16
|
/>
|
|
15
17
|
)
|
|
16
18
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TouchableOpacity as RNTouchableOpacity, TouchableOpacityProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const TouchableOpacity = copyComponentProperties(RNTouchableOpacity, (props: TouchableOpacityProps) => {
|
|
@@ -7,6 +8,7 @@ export const TouchableOpacity = copyComponentProperties(RNTouchableOpacity, (pro
|
|
|
7
8
|
<RNTouchableOpacity
|
|
8
9
|
{...props}
|
|
9
10
|
style={[toRNWClassName(props.className), props.style]}
|
|
11
|
+
dataSet={generateDataSet(props)}
|
|
10
12
|
/>
|
|
11
13
|
)
|
|
12
14
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TouchableWithoutFeedback as RNTouchableWithoutFeedback, TouchableWithoutFeedbackProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const TouchableWithoutFeedback = copyComponentProperties(RNTouchableWithoutFeedback, (props: TouchableWithoutFeedbackProps) => {
|
|
@@ -7,6 +8,7 @@ export const TouchableWithoutFeedback = copyComponentProperties(RNTouchableWitho
|
|
|
7
8
|
<RNTouchableWithoutFeedback
|
|
8
9
|
{...props}
|
|
9
10
|
style={[toRNWClassName(props.className), props.style]}
|
|
11
|
+
dataSet={generateDataSet(props)}
|
|
10
12
|
/>
|
|
11
13
|
)
|
|
12
14
|
})
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { View as RNView, ViewProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const View = copyComponentProperties(RNView, (props: ViewProps) => {
|
|
6
7
|
return (
|
|
7
8
|
<RNView
|
|
8
9
|
{...props}
|
|
10
|
+
dataSet={generateDataSet(props)}
|
|
9
11
|
style={[toRNWClassName(props.className), props.style]}
|
|
10
12
|
/>
|
|
11
13
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { VirtualizedList as RNVirtualizedList, VirtualizedListProps } from 'react-native'
|
|
2
2
|
import { copyComponentProperties } from '../utils'
|
|
3
|
+
import { generateDataSet } from './generateDataSet'
|
|
3
4
|
import { toRNWClassName } from './rnw'
|
|
4
5
|
|
|
5
6
|
export const VirtualizedList = copyComponentProperties(RNVirtualizedList, (props: VirtualizedListProps<unknown>) => {
|
|
@@ -10,6 +11,7 @@ export const VirtualizedList = copyComponentProperties(RNVirtualizedList, (props
|
|
|
10
11
|
contentContainerStyle={[toRNWClassName(props.contentContainerClassName), props.contentContainerStyle]}
|
|
11
12
|
ListFooterComponentStyle={[toRNWClassName(props.ListFooterComponentClassName), props.ListFooterComponentStyle]}
|
|
12
13
|
ListHeaderComponentStyle={[toRNWClassName(props.ListHeaderComponentClassName), props.ListHeaderComponentStyle]}
|
|
14
|
+
dataSet={generateDataSet(props)}
|
|
13
15
|
/>
|
|
14
16
|
)
|
|
15
17
|
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const generateDataSet = (props: Record<PropertyKey, any>) => {
|
|
2
|
+
const dataSet: DataSet = props.dataSet !== undefined ? { ...props.dataSet } : {}
|
|
3
|
+
|
|
4
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
5
|
+
if (key.startsWith('data-')) {
|
|
6
|
+
// Remove data- prefix
|
|
7
|
+
dataSet[key.slice(5)] = value
|
|
8
|
+
}
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
return dataSet
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type DataSet = Record<string, string | boolean>
|
|
15
|
+
|
|
16
|
+
declare module 'react-native' {
|
|
17
|
+
interface SwitchProps {
|
|
18
|
+
dataSet?: DataSet
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TextProps {
|
|
22
|
+
dataSet?: DataSet
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface TouchableWithoutFeedbackProps {
|
|
26
|
+
dataSet?: DataSet
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ViewProps {
|
|
30
|
+
dataSet?: DataSet
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PressableProps {
|
|
34
|
+
dataSet?: DataSet
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface TextInputProps {
|
|
38
|
+
dataSet?: DataSet
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ImagePropsBase {
|
|
42
|
+
dataSet?: DataSet
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface InputAccessoryViewProps {
|
|
46
|
+
dataSet?: DataSet
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ButtonProps {
|
|
50
|
+
dataSet?: DataSet
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -13,7 +13,7 @@ const RN_VERSION = Platform.constants?.reactNativeVersion?.minor ?? 0
|
|
|
13
13
|
const UNSPECIFIED_THEME = RN_VERSION >= 82 ? 'unspecified' : undefined
|
|
14
14
|
|
|
15
15
|
export class UniwindConfigBuilder {
|
|
16
|
-
protected
|
|
16
|
+
protected _themes = ['light', 'dark']
|
|
17
17
|
#hasAdaptiveThemes = true
|
|
18
18
|
#currentTheme = this.colorScheme as ThemeName
|
|
19
19
|
|
|
@@ -33,6 +33,10 @@ export class UniwindConfigBuilder {
|
|
|
33
33
|
})
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
get themes() {
|
|
37
|
+
return this._themes as Array<ThemeName>
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
get hasAdaptiveThemes() {
|
|
37
41
|
return this.#hasAdaptiveThemes
|
|
38
42
|
}
|
|
@@ -77,7 +81,7 @@ export class UniwindConfigBuilder {
|
|
|
77
81
|
return
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
if (!this.
|
|
84
|
+
if (!this._themes.includes(theme)) {
|
|
81
85
|
throw new Error(`Uniwind: You're trying to setTheme to '${theme}', but it was not registered.`)
|
|
82
86
|
}
|
|
83
87
|
|
|
@@ -116,7 +120,7 @@ export class UniwindConfigBuilder {
|
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
protected __reinit(_: GenerateStyleSheetsCallback, themes: Array<string>) {
|
|
119
|
-
this.
|
|
123
|
+
this._themes = themes
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
protected onThemeChange() {
|
|
@@ -2,9 +2,10 @@ import { StyleDependency } from '../../types'
|
|
|
2
2
|
import { UniwindListener } from '../listener'
|
|
3
3
|
|
|
4
4
|
class CSSListenerBuilder {
|
|
5
|
+
activeRules = new Set<CSSStyleRule>()
|
|
5
6
|
private classNameMediaQueryListeners = new Map<string, MediaQueryList>()
|
|
6
7
|
private listeners = new Map<MediaQueryList, Set<VoidFunction>>()
|
|
7
|
-
private
|
|
8
|
+
private registeredRulesMediaQueries = new Map<string, MediaQueryList>()
|
|
8
9
|
private processedStyleSheets = new WeakSet<CSSStyleSheet>()
|
|
9
10
|
private pendingInitialization: number | undefined = undefined
|
|
10
11
|
|
|
@@ -15,6 +16,22 @@ class CSSListenerBuilder {
|
|
|
15
16
|
|
|
16
17
|
const observer = new MutationObserver(mutations => {
|
|
17
18
|
for (const mutation of mutations) {
|
|
19
|
+
if (mutation.type === 'attributes') {
|
|
20
|
+
const el = mutation.target as HTMLLinkElement | HTMLStyleElement
|
|
21
|
+
|
|
22
|
+
if (!('sheet' in el)) {
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const sheet = el.sheet
|
|
27
|
+
|
|
28
|
+
if (sheet) {
|
|
29
|
+
this.processedStyleSheets.delete(sheet)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.scheduleInitialization()
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
if (mutation.type === 'childList') {
|
|
19
36
|
this.scheduleInitialization()
|
|
20
37
|
}
|
|
@@ -83,8 +100,19 @@ class CSSListenerBuilder {
|
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
|
|
103
|
+
private pruneStaleRules() {
|
|
104
|
+
const activeSheets = new Set(Array.from(document.styleSheets))
|
|
105
|
+
|
|
106
|
+
for (const rule of this.activeRules) {
|
|
107
|
+
if (!rule.parentStyleSheet || !activeSheets.has(rule.parentStyleSheet)) {
|
|
108
|
+
this.activeRules.delete(rule)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
private initialize() {
|
|
87
114
|
this.pendingInitialization = undefined
|
|
115
|
+
this.pruneStaleRules()
|
|
88
116
|
|
|
89
117
|
for (const sheet of Array.from(document.styleSheets)) {
|
|
90
118
|
// Skip already processed stylesheets
|
|
@@ -121,6 +149,10 @@ class CSSListenerBuilder {
|
|
|
121
149
|
return rule.constructor.name === 'CSSMediaRule'
|
|
122
150
|
}
|
|
123
151
|
|
|
152
|
+
private isSupportsRule(rule: CSSRule): rule is CSSSupportsRule {
|
|
153
|
+
return rule.constructor.name === 'CSSSupportsRule'
|
|
154
|
+
}
|
|
155
|
+
|
|
124
156
|
private collectParentMediaQueries(rule: CSSRule, acc = [] as Array<CSSMediaRule>): Array<CSSMediaRule> {
|
|
125
157
|
const { parentRule } = rule
|
|
126
158
|
|
|
@@ -144,13 +176,25 @@ class CSSListenerBuilder {
|
|
|
144
176
|
if (this.isStyleRule(rule)) {
|
|
145
177
|
const mediaQueries = this.collectParentMediaQueries(rule)
|
|
146
178
|
|
|
179
|
+
this.activeRules.add(rule)
|
|
180
|
+
|
|
147
181
|
if (mediaQueries.length > 0) {
|
|
148
|
-
this.addMediaQuery(mediaQueries, rule
|
|
182
|
+
this.addMediaQuery(mediaQueries, rule)
|
|
149
183
|
}
|
|
150
184
|
|
|
151
185
|
continue
|
|
152
186
|
}
|
|
153
187
|
|
|
188
|
+
if (this.isSupportsRule(rule)) {
|
|
189
|
+
if (!CSS.supports(rule.conditionText)) {
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.addMediaQueriesDeep(rule.cssRules)
|
|
194
|
+
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
|
|
154
198
|
if ('cssRules' in rule && rule.cssRules instanceof CSSRuleList) {
|
|
155
199
|
this.addMediaQueriesDeep(rule.cssRules)
|
|
156
200
|
|
|
@@ -159,27 +203,50 @@ class CSSListenerBuilder {
|
|
|
159
203
|
}
|
|
160
204
|
}
|
|
161
205
|
|
|
162
|
-
private addMediaQuery(mediaQueries: Array<CSSMediaRule>,
|
|
206
|
+
private addMediaQuery(mediaQueries: Array<CSSMediaRule>, rule: CSSStyleRule) {
|
|
207
|
+
const className = rule.selectorText
|
|
163
208
|
const rules = mediaQueries.map(mediaQuery => mediaQuery.conditionText).sort().join(' and ')
|
|
164
209
|
const parsedClassName = className.replace('.', '').replace('\\', '')
|
|
165
|
-
const cachedMediaQueryList = this.
|
|
210
|
+
const cachedMediaQueryList = this.registeredRulesMediaQueries.get(rules)
|
|
166
211
|
|
|
167
212
|
if (cachedMediaQueryList) {
|
|
168
213
|
this.classNameMediaQueryListeners.set(parsedClassName, cachedMediaQueryList)
|
|
214
|
+
this.toggleRule(cachedMediaQueryList, rule)
|
|
215
|
+
|
|
216
|
+
cachedMediaQueryList.addEventListener('change', () => {
|
|
217
|
+
this.toggleRule(cachedMediaQueryList, rule)
|
|
218
|
+
})
|
|
169
219
|
|
|
170
220
|
return
|
|
171
221
|
}
|
|
172
222
|
|
|
173
223
|
const mediaQueryList = window.matchMedia(rules)
|
|
174
224
|
|
|
175
|
-
this.
|
|
225
|
+
this.toggleRule(mediaQueryList, rule)
|
|
226
|
+
this.registeredRulesMediaQueries.set(rules, mediaQueryList)
|
|
176
227
|
this.listeners.set(mediaQueryList, new Set())
|
|
177
228
|
this.classNameMediaQueryListeners.set(parsedClassName, mediaQueryList)
|
|
178
229
|
|
|
179
230
|
mediaQueryList.addEventListener('change', () => {
|
|
180
|
-
this.listeners.get(mediaQueryList)!.forEach(listener =>
|
|
231
|
+
this.listeners.get(mediaQueryList)!.forEach(listener => {
|
|
232
|
+
listener()
|
|
233
|
+
})
|
|
234
|
+
this.toggleRule(mediaQueryList, rule)
|
|
181
235
|
})
|
|
182
236
|
}
|
|
237
|
+
|
|
238
|
+
private isRuleLive(rule: CSSStyleRule) {
|
|
239
|
+
const sheet = rule.parentStyleSheet
|
|
240
|
+
return sheet !== null && Array.from(document.styleSheets).includes(sheet)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private toggleRule(mqList: MediaQueryList, rule: CSSStyleRule) {
|
|
244
|
+
if (mqList.matches && this.isRuleLive(rule)) {
|
|
245
|
+
this.activeRules.add(rule)
|
|
246
|
+
} else {
|
|
247
|
+
this.activeRules.delete(rule)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
183
250
|
}
|
|
184
251
|
|
|
185
252
|
export const CSSListener = new CSSListenerBuilder()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RNStyle, UniwindContextType } from '../types'
|
|
2
|
+
import { CSSListener } from './cssListener'
|
|
2
3
|
import { parseCSSValue } from './parseCSSValue'
|
|
3
4
|
|
|
4
5
|
const dummyParent = typeof document !== 'undefined'
|
|
@@ -15,40 +16,40 @@ if (dummyParent && dummy) {
|
|
|
15
16
|
dummyParent.appendChild(dummy)
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const getActiveStylesForClass = (className: string) => {
|
|
20
|
+
const extractedStyles = {} as Record<string, string>
|
|
21
|
+
|
|
19
22
|
if (!dummy) {
|
|
20
|
-
return
|
|
23
|
+
return extractedStyles
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
const classNames = className.split(/\s+/).filter(Boolean)
|
|
23
27
|
const computedStyles = window.getComputedStyle(dummy)
|
|
24
|
-
const styles = {} as CSSStyleDeclaration
|
|
25
|
-
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
27
|
-
for (let i = 0; i < computedStyles.length; i++) {
|
|
28
|
-
// Typescript is unable to infer it properly
|
|
29
|
-
const prop = computedStyles[i] as any
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
CSSListener.activeRules.forEach(rule => {
|
|
30
|
+
const selector = rule.selectorText
|
|
31
|
+
const mightMatch = classNames.some((cls) => selector.includes(`.${CSS.escape(cls)}`))
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const initialStyles = typeof document !== 'undefined'
|
|
38
|
-
? getComputedStyles()
|
|
39
|
-
: {} as CSSStyleDeclaration
|
|
40
|
-
|
|
41
|
-
const getObjectDifference = <T extends object>(obj1: T, obj2: T): T => {
|
|
42
|
-
const diff = {} as T
|
|
43
|
-
const keys = Object.keys(obj2) as Array<keyof T>
|
|
33
|
+
if (!mightMatch) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// element.matches() throws errors if it sees pseudo-elements like ::before
|
|
38
|
+
// So we strip them out safely just for the matching test
|
|
39
|
+
const safeSelector = selector.replace(/::[a-z-]+/gi, '')
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (safeSelector !== '' && dummy.matches(safeSelector)) {
|
|
43
|
+
for (const propertyName of rule.style) {
|
|
44
|
+
extractedStyles[propertyName] = computedStyles.getPropertyValue(propertyName)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Failsafe for unparseable selectors
|
|
48
49
|
}
|
|
49
50
|
})
|
|
50
51
|
|
|
51
|
-
return
|
|
52
|
+
return extractedStyles
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export const getWebStyles = (className: string | undefined, uniwindContext: UniwindContextType): RNStyle => {
|
|
@@ -68,7 +69,7 @@ export const getWebStyles = (className: string | undefined, uniwindContext: Uniw
|
|
|
68
69
|
|
|
69
70
|
dummy.className = className
|
|
70
71
|
|
|
71
|
-
const computedStyles =
|
|
72
|
+
const computedStyles = getActiveStylesForClass(className)
|
|
72
73
|
|
|
73
74
|
return Object.fromEntries(
|
|
74
75
|
Object.entries(computedStyles)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RNStyle } from '../core/types'
|
|
2
|
+
|
|
3
|
+
const toKebabCase = (str: string) => str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)
|
|
4
|
+
|
|
5
|
+
const generateExtraUtilities = (map: Record<string, RNStyle>) => {
|
|
6
|
+
return Object.entries(map)
|
|
7
|
+
.map(([name, style]) =>
|
|
8
|
+
[
|
|
9
|
+
`@utility ${name} {`,
|
|
10
|
+
...Object.entries(style).map(([key, value]) => ` ${toKebabCase(key)}: ${value};`),
|
|
11
|
+
`}`,
|
|
12
|
+
'',
|
|
13
|
+
].join('\n')
|
|
14
|
+
).join('\n')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const EXTRA_UTILITIES_MAP = {
|
|
18
|
+
'border-continuous': {
|
|
19
|
+
borderCurve: 'continuous',
|
|
20
|
+
},
|
|
21
|
+
'border-circular': {
|
|
22
|
+
borderCurve: 'circular',
|
|
23
|
+
},
|
|
24
|
+
} satisfies Record<string, RNStyle>
|
|
25
|
+
|
|
26
|
+
export const EXTRA_UTILITIES_CSS = generateExtraUtilities(EXTRA_UTILITIES_MAP)
|
package/src/css/index.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { EXTRA_UTILITIES_CSS } from './extraUtilities'
|
|
4
|
+
import { INSETS_CSS } from './insets'
|
|
5
|
+
import { OVERWRITE_CSS } from './overwrite'
|
|
5
6
|
import { generateCSSForThemes } from './themes'
|
|
6
|
-
import {
|
|
7
|
+
import { VARIANTS_CSS } from './variants'
|
|
7
8
|
|
|
8
9
|
const dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname
|
|
9
10
|
|
|
10
11
|
export const buildCSS = async (themes: Array<string>, input: string) => {
|
|
11
|
-
const variants = generateCSSForVariants()
|
|
12
|
-
const insets = generateCSSForInsets()
|
|
13
12
|
const themesCSS = await generateCSSForThemes(themes, input)
|
|
14
13
|
const cssFilePath = path.join(dirname, '../../uniwind.css')
|
|
15
14
|
const oldCSSFile = fs.existsSync(cssFilePath)
|
|
@@ -17,9 +16,10 @@ export const buildCSS = async (themes: Array<string>, input: string) => {
|
|
|
17
16
|
: ''
|
|
18
17
|
|
|
19
18
|
const newCssFile = [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
VARIANTS_CSS,
|
|
20
|
+
INSETS_CSS,
|
|
21
|
+
OVERWRITE_CSS,
|
|
22
|
+
EXTRA_UTILITIES_CSS,
|
|
23
23
|
themesCSS,
|
|
24
24
|
].join('\n')
|
|
25
25
|
|
package/src/css/insets.ts
CHANGED
|
@@ -9,7 +9,7 @@ type TypeName = (typeof types)[number]
|
|
|
9
9
|
type SafeAreaType = (typeof safeAreaTypes)[number]
|
|
10
10
|
type Inset = 'top' | 'bottom' | 'left' | 'right'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const generateCSSForInsets = () => {
|
|
13
13
|
let css = `@utility h-screen-safe {
|
|
14
14
|
height: calc(100vh - (env(safe-area-inset-top) + env(safe-area-inset-bottom)));
|
|
15
15
|
}\n\n`
|
|
@@ -104,3 +104,5 @@ export const generateCSSForInsets = () => {
|
|
|
104
104
|
// Remove the last newline character
|
|
105
105
|
return css.slice(0, -1)
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
export const INSETS_CSS = generateCSSForInsets()
|
package/src/css/overwrite.ts
CHANGED
package/src/css/variants.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const variants = ['ios', 'android', 'web', 'native', 'tv', 'android-tv', 'apple-tv']
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const generateCSSForVariants = () => {
|
|
4
4
|
let css = ''
|
|
5
5
|
|
|
6
6
|
variants.forEach(variant => {
|
|
@@ -9,3 +9,5 @@ export const generateCSSForVariants = () => {
|
|
|
9
9
|
|
|
10
10
|
return css
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
export const VARIANTS_CSS = generateCSSForVariants()
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './components/ScopedTheme'
|
|
2
2
|
export { Uniwind } from './core'
|
|
3
|
+
export type { ThemeName } from './core/types'
|
|
3
4
|
export { withUniwind } from './hoc'
|
|
4
5
|
export type { ApplyUniwind, ApplyUniwindOptions } from './hoc/types'
|
|
5
6
|
export { useCSSVariable, useResolveClassNames, useUniwind } from './hooks'
|
|
@@ -434,6 +434,14 @@ export class CSS {
|
|
|
434
434
|
return this.processValue(declarationValue[0]!)
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
if (this.isTransformArray(declarationValue)) {
|
|
438
|
+
return declarationValue.flatMap(value => {
|
|
439
|
+
const result = this.processValue(value)
|
|
440
|
+
|
|
441
|
+
return Array.isArray(result) ? result : [result]
|
|
442
|
+
})
|
|
443
|
+
}
|
|
444
|
+
|
|
437
445
|
return this.addComaBetweenTokens(declarationValue).reduce<string | number>((acc, value, index, array) => {
|
|
438
446
|
if (typeof value === 'object') {
|
|
439
447
|
const nextValue = array.at(index + 1)
|
|
@@ -580,6 +588,33 @@ export class CSS {
|
|
|
580
588
|
return undefined
|
|
581
589
|
}
|
|
582
590
|
|
|
591
|
+
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
592
|
+
private static readonly TRANSFORM_TYPES = new Set([
|
|
593
|
+
'translate',
|
|
594
|
+
'translateX',
|
|
595
|
+
'translateY',
|
|
596
|
+
'translateZ',
|
|
597
|
+
'rotate',
|
|
598
|
+
'rotateX',
|
|
599
|
+
'rotateY',
|
|
600
|
+
'rotateZ',
|
|
601
|
+
'scale',
|
|
602
|
+
'scaleX',
|
|
603
|
+
'scaleY',
|
|
604
|
+
'scaleZ',
|
|
605
|
+
'skew',
|
|
606
|
+
'skewX',
|
|
607
|
+
'skewY',
|
|
608
|
+
'matrix',
|
|
609
|
+
'perspective',
|
|
610
|
+
])
|
|
611
|
+
|
|
612
|
+
private isTransformArray(values: Array<any>) {
|
|
613
|
+
return values.every(
|
|
614
|
+
value => typeof value === 'object' && value !== null && 'type' in value && CSS.TRANSFORM_TYPES.has(value.type),
|
|
615
|
+
)
|
|
616
|
+
}
|
|
617
|
+
|
|
583
618
|
private isOverflow(value: any): value is { x: OverflowKeyword; y: OverflowKeyword } {
|
|
584
619
|
return typeof value === 'object' && 'x' in value && ['hidden', 'visible'].includes(value.x)
|
|
585
620
|
}
|