@niibase/uniwind 1.4.0 → 1.4.1
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/common/core/native/runtime.js +11 -1
- package/dist/common/css-visitor/function-visitor.js +9 -0
- package/dist/common/css-visitor/rule-visitor.js +49 -5
- package/dist/metro/metro-transformer.cjs +4 -1
- package/dist/metro/metro-transformer.mjs +4 -1
- package/dist/module/core/native/runtime.js +5 -2
- package/dist/module/core/types.d.ts +1 -0
- package/dist/module/css-visitor/function-visitor.d.ts +1 -0
- package/dist/module/css-visitor/function-visitor.js +3 -0
- package/dist/module/css-visitor/rule-visitor.d.ts +3 -1
- package/dist/module/css-visitor/rule-visitor.js +54 -5
- package/dist/shared/{uniwind.D-ahjOrG.mjs → uniwind.B_j3NcHy.mjs} +57 -5
- package/dist/shared/{uniwind.CyACT0sD.cjs → uniwind.D7C2Zt-r.cjs} +57 -5
- package/dist/vite/index.cjs +1 -1
- package/dist/vite/index.mjs +1 -1
- package/package.json +3 -2
- package/readme.md +4 -12
- package/src/core/native/runtime.ts +5 -1
- package/src/core/types.ts +1 -0
- package/src/css-visitor/function-visitor.ts +4 -0
- package/src/css-visitor/rule-visitor.ts +68 -9
- package/src/metro/processor/functions.ts +4 -0
|
@@ -34,6 +34,16 @@ const UniwindRuntime = exports.UniwindRuntime = {
|
|
|
34
34
|
steps: _reactNativeReanimated.steps,
|
|
35
35
|
linear: _reactNativeReanimated.linear,
|
|
36
36
|
lightDark: () => "",
|
|
37
|
-
parseColor: _nativeUtils.parseColor
|
|
37
|
+
parseColor: _nativeUtils.parseColor,
|
|
38
|
+
platformSelect: (android, ios, other) => {
|
|
39
|
+
return _reactNative.Platform.select(Boolean(other) ? {
|
|
40
|
+
android,
|
|
41
|
+
ios,
|
|
42
|
+
default: other
|
|
43
|
+
} : {
|
|
44
|
+
android,
|
|
45
|
+
default: ios
|
|
46
|
+
});
|
|
47
|
+
}
|
|
38
48
|
};
|
|
39
49
|
UniwindRuntime.lightDark = _nativeUtils.lightDark.bind(UniwindRuntime);
|
|
@@ -13,6 +13,15 @@ const ONE_PX = {
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
class FunctionVisitor {
|
|
16
|
+
platformSelect(fn) {
|
|
17
|
+
return fn.arguments.at(0) ?? {
|
|
18
|
+
type: "token",
|
|
19
|
+
value: {
|
|
20
|
+
type: "ident",
|
|
21
|
+
value: "initial"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
16
25
|
pixelRatio(fn) {
|
|
17
26
|
return {
|
|
18
27
|
type: "function",
|
|
@@ -16,9 +16,8 @@ class RuleVisitor {
|
|
|
16
16
|
};
|
|
17
17
|
style = styleRule => {
|
|
18
18
|
const firstSelector = styleRule.value.selectors.at(0)?.at(0);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return this.processThemeStyle(styleRule, secondSelector);
|
|
19
|
+
if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
|
|
20
|
+
return this.removeNulls(this.processThemeRoot(styleRule));
|
|
22
21
|
}
|
|
23
22
|
if (firstSelector?.type === "class") {
|
|
24
23
|
return this.processClassStyle(styleRule, firstSelector);
|
|
@@ -29,14 +28,44 @@ class RuleVisitor {
|
|
|
29
28
|
this.processedClassNames.clear();
|
|
30
29
|
this.processedVariables.clear();
|
|
31
30
|
}
|
|
31
|
+
processThemeRoot(styleRule) {
|
|
32
|
+
const themeScopedRules = styleRule.value.rules?.filter(rule => {
|
|
33
|
+
if (rule.type !== "style") {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const firstSelector = rule.value.selectors.at(0)?.at(0);
|
|
37
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
38
|
+
return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
|
|
39
|
+
}) ?? [];
|
|
40
|
+
const nonThemeRules = styleRule.value.rules?.filter(rule => !themeScopedRules.includes(rule));
|
|
41
|
+
const processedThemeScopedRules = themeScopedRules.map(rule => {
|
|
42
|
+
if (rule.type !== "style") {
|
|
43
|
+
return rule;
|
|
44
|
+
}
|
|
45
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
46
|
+
if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
|
|
47
|
+
return this.processThemeStyle(rule, secondSelector);
|
|
48
|
+
}
|
|
49
|
+
return rule;
|
|
50
|
+
});
|
|
51
|
+
return [{
|
|
52
|
+
type: "style",
|
|
53
|
+
value: {
|
|
54
|
+
loc: styleRule.value.loc,
|
|
55
|
+
selectors: styleRule.value.selectors,
|
|
56
|
+
rules: nonThemeRules,
|
|
57
|
+
declarations: styleRule.value.declarations
|
|
58
|
+
}
|
|
59
|
+
}, ...processedThemeScopedRules];
|
|
60
|
+
}
|
|
32
61
|
processThemeStyle(styleRule, secondSelector) {
|
|
33
62
|
const whereSelector = secondSelector.selectors.at(0)?.at(0);
|
|
34
63
|
if (whereSelector?.type !== "class") {
|
|
35
|
-
return;
|
|
64
|
+
return styleRule;
|
|
36
65
|
}
|
|
37
66
|
const selectedVariant = this.themes.find(theme => whereSelector.name === theme);
|
|
38
67
|
if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
|
|
39
|
-
return;
|
|
68
|
+
return styleRule;
|
|
40
69
|
}
|
|
41
70
|
this.processedVariables.add(selectedVariant);
|
|
42
71
|
return {
|
|
@@ -74,5 +103,20 @@ class RuleVisitor {
|
|
|
74
103
|
}
|
|
75
104
|
};
|
|
76
105
|
}
|
|
106
|
+
// Fixes lightningcss serialization bug
|
|
107
|
+
removeNulls(value) {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
return value.map(v => this.removeNulls(v));
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === "object" && value !== null) {
|
|
112
|
+
return Object.fromEntries(Object.entries(value).filter(([_, value2]) => {
|
|
113
|
+
if (value2 === null) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}).map(([key, value2]) => [key, this.removeNulls(value2)]));
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
77
121
|
}
|
|
78
122
|
exports.RuleVisitor = RuleVisitor;
|
|
@@ -7,7 +7,7 @@ const node = require('@tailwindcss/node');
|
|
|
7
7
|
const oxide = require('@tailwindcss/oxide');
|
|
8
8
|
const lightningcss = require('lightningcss');
|
|
9
9
|
const types = require('../shared/uniwind.BZIuaszw.cjs');
|
|
10
|
-
const stringifyThemes = require('../shared/uniwind.
|
|
10
|
+
const stringifyThemes = require('../shared/uniwind.D7C2Zt-r.cjs');
|
|
11
11
|
const culori = require('culori');
|
|
12
12
|
|
|
13
13
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
@@ -1118,6 +1118,9 @@ class Functions {
|
|
|
1118
1118
|
if (fn.name === "hairlineWidth") {
|
|
1119
1119
|
return "rt.hairlineWidth";
|
|
1120
1120
|
}
|
|
1121
|
+
if (fn.name === "platformSelect") {
|
|
1122
|
+
return `rt.platformSelect(${this.Processor.CSS.processValue(fn.arguments)})`;
|
|
1123
|
+
}
|
|
1121
1124
|
if (fn.name === "pixelRatio") {
|
|
1122
1125
|
if (fn.arguments.length === 0) {
|
|
1123
1126
|
return "rt.pixelRatio(1)";
|
|
@@ -5,7 +5,7 @@ import { compile } from '@tailwindcss/node';
|
|
|
5
5
|
import { Scanner } from '@tailwindcss/oxide';
|
|
6
6
|
import { transform as transform$1 } from 'lightningcss';
|
|
7
7
|
import { P as Platform } from '../shared/uniwind.CyoRUwOj.mjs';
|
|
8
|
-
import { L as Logger, U as UniwindCSSVisitor, s as stringifyThemes, b as buildDtsFile, a as buildCSS } from '../shared/uniwind.
|
|
8
|
+
import { L as Logger, U as UniwindCSSVisitor, s as stringifyThemes, b as buildDtsFile, a as buildCSS } from '../shared/uniwind.B_j3NcHy.mjs';
|
|
9
9
|
import { converter, parse, formatHex, formatHex8 } from 'culori';
|
|
10
10
|
|
|
11
11
|
const parseStringValue = (value) => {
|
|
@@ -1111,6 +1111,9 @@ class Functions {
|
|
|
1111
1111
|
if (fn.name === "hairlineWidth") {
|
|
1112
1112
|
return "rt.hairlineWidth";
|
|
1113
1113
|
}
|
|
1114
|
+
if (fn.name === "platformSelect") {
|
|
1115
|
+
return `rt.platformSelect(${this.Processor.CSS.processValue(fn.arguments)})`;
|
|
1116
|
+
}
|
|
1114
1117
|
if (fn.name === "pixelRatio") {
|
|
1115
1118
|
if (fn.arguments.length === 0) {
|
|
1116
1119
|
return "rt.pixelRatio(1)";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Appearance, Dimensions, I18nManager, PixelRatio, StyleSheet } from "react-native";
|
|
1
|
+
import { Appearance, Dimensions, I18nManager, PixelRatio, Platform, StyleSheet } from "react-native";
|
|
2
2
|
import { cubicBezier, linear, steps } from "react-native-reanimated";
|
|
3
3
|
import { initialWindowMetrics } from "react-native-safe-area-context";
|
|
4
4
|
import { ColorScheme, Orientation } from "../../types.js";
|
|
@@ -23,6 +23,9 @@ export const UniwindRuntime = {
|
|
|
23
23
|
steps,
|
|
24
24
|
linear,
|
|
25
25
|
lightDark: () => "",
|
|
26
|
-
parseColor
|
|
26
|
+
parseColor,
|
|
27
|
+
platformSelect: (android, ios, other) => {
|
|
28
|
+
return Platform.select(Boolean(other) ? { android, ios, default: other } : { android, default: ios });
|
|
29
|
+
}
|
|
27
30
|
};
|
|
28
31
|
UniwindRuntime.lightDark = lightDark.bind(UniwindRuntime);
|
|
@@ -57,6 +57,7 @@ export type UniwindRuntime = {
|
|
|
57
57
|
linear: (...points: ControlPoint[]) => LinearEasing;
|
|
58
58
|
lightDark: (light: string, dark: string) => string;
|
|
59
59
|
parseColor: (type: string, color: string) => string;
|
|
60
|
+
platformSelect: (android: string, ios: string, other?: string) => string;
|
|
60
61
|
};
|
|
61
62
|
export type RNStyle = ViewStyle & TextStyle & ImageStyle & {
|
|
62
63
|
accentColor?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Function as LightningCSSFunction, TokenOrValue } from 'lightningcss';
|
|
2
2
|
export declare class FunctionVisitor {
|
|
3
3
|
[name: string]: (fn: LightningCSSFunction) => TokenOrValue;
|
|
4
|
+
platformSelect(fn: LightningCSSFunction): TokenOrValue;
|
|
4
5
|
pixelRatio(fn: LightningCSSFunction): TokenOrValue;
|
|
5
6
|
fontScale(fn: LightningCSSFunction): TokenOrValue;
|
|
6
7
|
hairlineWidth(): TokenOrValue;
|
|
@@ -3,6 +3,9 @@ const ONE_PX = {
|
|
|
3
3
|
value: { type: "dimension", unit: "px", value: 1 }
|
|
4
4
|
};
|
|
5
5
|
export class FunctionVisitor {
|
|
6
|
+
platformSelect(fn) {
|
|
7
|
+
return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
|
|
8
|
+
}
|
|
6
9
|
pixelRatio(fn) {
|
|
7
10
|
return {
|
|
8
11
|
type: "function",
|
|
@@ -16,9 +16,11 @@ export declare class RuleVisitor implements LightningRuleVisitors {
|
|
|
16
16
|
}>) => void;
|
|
17
17
|
style: (styleRule: Extract<LightningRuleVisitor, {
|
|
18
18
|
type: "style";
|
|
19
|
-
}>) => void | ReturnedRule;
|
|
19
|
+
}>) => void | ReturnedRule | ReturnedRule[];
|
|
20
20
|
cleanup(): void;
|
|
21
|
+
private processThemeRoot;
|
|
21
22
|
private processThemeStyle;
|
|
22
23
|
private processClassStyle;
|
|
24
|
+
private removeNulls;
|
|
23
25
|
}
|
|
24
26
|
export {};
|
|
@@ -10,9 +10,8 @@ export class RuleVisitor {
|
|
|
10
10
|
};
|
|
11
11
|
style = (styleRule) => {
|
|
12
12
|
const firstSelector = styleRule.value.selectors.at(0)?.at(0);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return this.processThemeStyle(styleRule, secondSelector);
|
|
13
|
+
if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
|
|
14
|
+
return this.removeNulls(this.processThemeRoot(styleRule));
|
|
16
15
|
}
|
|
17
16
|
if (firstSelector?.type === "class") {
|
|
18
17
|
return this.processClassStyle(styleRule, firstSelector);
|
|
@@ -23,14 +22,47 @@ export class RuleVisitor {
|
|
|
23
22
|
this.processedClassNames.clear();
|
|
24
23
|
this.processedVariables.clear();
|
|
25
24
|
}
|
|
25
|
+
processThemeRoot(styleRule) {
|
|
26
|
+
const themeScopedRules = styleRule.value.rules?.filter((rule) => {
|
|
27
|
+
if (rule.type !== "style") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const firstSelector = rule.value.selectors.at(0)?.at(0);
|
|
31
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
32
|
+
return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
|
|
33
|
+
}) ?? [];
|
|
34
|
+
const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
|
|
35
|
+
const processedThemeScopedRules = themeScopedRules.map((rule) => {
|
|
36
|
+
if (rule.type !== "style") {
|
|
37
|
+
return rule;
|
|
38
|
+
}
|
|
39
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
40
|
+
if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
|
|
41
|
+
return this.processThemeStyle(rule, secondSelector);
|
|
42
|
+
}
|
|
43
|
+
return rule;
|
|
44
|
+
});
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
type: "style",
|
|
48
|
+
value: {
|
|
49
|
+
loc: styleRule.value.loc,
|
|
50
|
+
selectors: styleRule.value.selectors,
|
|
51
|
+
rules: nonThemeRules,
|
|
52
|
+
declarations: styleRule.value.declarations
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
...processedThemeScopedRules
|
|
56
|
+
];
|
|
57
|
+
}
|
|
26
58
|
processThemeStyle(styleRule, secondSelector) {
|
|
27
59
|
const whereSelector = secondSelector.selectors.at(0)?.at(0);
|
|
28
60
|
if (whereSelector?.type !== "class") {
|
|
29
|
-
return;
|
|
61
|
+
return styleRule;
|
|
30
62
|
}
|
|
31
63
|
const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
|
|
32
64
|
if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
|
|
33
|
-
return;
|
|
65
|
+
return styleRule;
|
|
34
66
|
}
|
|
35
67
|
this.processedVariables.add(selectedVariant);
|
|
36
68
|
return {
|
|
@@ -59,4 +91,21 @@ export class RuleVisitor {
|
|
|
59
91
|
}
|
|
60
92
|
};
|
|
61
93
|
}
|
|
94
|
+
// Fixes lightningcss serialization bug
|
|
95
|
+
removeNulls(value) {
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.map((v) => this.removeNulls(v));
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === "object" && value !== null) {
|
|
100
|
+
return Object.fromEntries(
|
|
101
|
+
Object.entries(value).filter(([_, value2]) => {
|
|
102
|
+
if (value2 === null) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}).map(([key, value2]) => [key, this.removeNulls(value2)])
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
62
111
|
}
|
|
@@ -44,6 +44,9 @@ const ONE_PX = {
|
|
|
44
44
|
value: { type: "dimension", unit: "px", value: 1 }
|
|
45
45
|
};
|
|
46
46
|
class FunctionVisitor {
|
|
47
|
+
platformSelect(fn) {
|
|
48
|
+
return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
|
|
49
|
+
}
|
|
47
50
|
pixelRatio(fn) {
|
|
48
51
|
return {
|
|
49
52
|
type: "function",
|
|
@@ -90,9 +93,8 @@ class RuleVisitor {
|
|
|
90
93
|
};
|
|
91
94
|
style = (styleRule) => {
|
|
92
95
|
const firstSelector = styleRule.value.selectors.at(0)?.at(0);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return this.processThemeStyle(styleRule, secondSelector);
|
|
96
|
+
if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
|
|
97
|
+
return this.removeNulls(this.processThemeRoot(styleRule));
|
|
96
98
|
}
|
|
97
99
|
if (firstSelector?.type === "class") {
|
|
98
100
|
return this.processClassStyle(styleRule, firstSelector);
|
|
@@ -103,14 +105,47 @@ class RuleVisitor {
|
|
|
103
105
|
this.processedClassNames.clear();
|
|
104
106
|
this.processedVariables.clear();
|
|
105
107
|
}
|
|
108
|
+
processThemeRoot(styleRule) {
|
|
109
|
+
const themeScopedRules = styleRule.value.rules?.filter((rule) => {
|
|
110
|
+
if (rule.type !== "style") {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const firstSelector = rule.value.selectors.at(0)?.at(0);
|
|
114
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
115
|
+
return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
|
|
116
|
+
}) ?? [];
|
|
117
|
+
const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
|
|
118
|
+
const processedThemeScopedRules = themeScopedRules.map((rule) => {
|
|
119
|
+
if (rule.type !== "style") {
|
|
120
|
+
return rule;
|
|
121
|
+
}
|
|
122
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
123
|
+
if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
|
|
124
|
+
return this.processThemeStyle(rule, secondSelector);
|
|
125
|
+
}
|
|
126
|
+
return rule;
|
|
127
|
+
});
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
type: "style",
|
|
131
|
+
value: {
|
|
132
|
+
loc: styleRule.value.loc,
|
|
133
|
+
selectors: styleRule.value.selectors,
|
|
134
|
+
rules: nonThemeRules,
|
|
135
|
+
declarations: styleRule.value.declarations
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
...processedThemeScopedRules
|
|
139
|
+
];
|
|
140
|
+
}
|
|
106
141
|
processThemeStyle(styleRule, secondSelector) {
|
|
107
142
|
const whereSelector = secondSelector.selectors.at(0)?.at(0);
|
|
108
143
|
if (whereSelector?.type !== "class") {
|
|
109
|
-
return;
|
|
144
|
+
return styleRule;
|
|
110
145
|
}
|
|
111
146
|
const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
|
|
112
147
|
if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
|
|
113
|
-
return;
|
|
148
|
+
return styleRule;
|
|
114
149
|
}
|
|
115
150
|
this.processedVariables.add(selectedVariant);
|
|
116
151
|
return {
|
|
@@ -139,6 +174,23 @@ class RuleVisitor {
|
|
|
139
174
|
}
|
|
140
175
|
};
|
|
141
176
|
}
|
|
177
|
+
// Fixes lightningcss serialization bug
|
|
178
|
+
removeNulls(value) {
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
return value.map((v) => this.removeNulls(v));
|
|
181
|
+
}
|
|
182
|
+
if (typeof value === "object" && value !== null) {
|
|
183
|
+
return Object.fromEntries(
|
|
184
|
+
Object.entries(value).filter(([_, value2]) => {
|
|
185
|
+
if (value2 === null) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}).map(([key, value2]) => [key, this.removeNulls(value2)])
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
142
194
|
}
|
|
143
195
|
|
|
144
196
|
class UniwindCSSVisitor {
|
|
@@ -51,6 +51,9 @@ const ONE_PX = {
|
|
|
51
51
|
value: { type: "dimension", unit: "px", value: 1 }
|
|
52
52
|
};
|
|
53
53
|
class FunctionVisitor {
|
|
54
|
+
platformSelect(fn) {
|
|
55
|
+
return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
|
|
56
|
+
}
|
|
54
57
|
pixelRatio(fn) {
|
|
55
58
|
return {
|
|
56
59
|
type: "function",
|
|
@@ -97,9 +100,8 @@ class RuleVisitor {
|
|
|
97
100
|
};
|
|
98
101
|
style = (styleRule) => {
|
|
99
102
|
const firstSelector = styleRule.value.selectors.at(0)?.at(0);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return this.processThemeStyle(styleRule, secondSelector);
|
|
103
|
+
if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
|
|
104
|
+
return this.removeNulls(this.processThemeRoot(styleRule));
|
|
103
105
|
}
|
|
104
106
|
if (firstSelector?.type === "class") {
|
|
105
107
|
return this.processClassStyle(styleRule, firstSelector);
|
|
@@ -110,14 +112,47 @@ class RuleVisitor {
|
|
|
110
112
|
this.processedClassNames.clear();
|
|
111
113
|
this.processedVariables.clear();
|
|
112
114
|
}
|
|
115
|
+
processThemeRoot(styleRule) {
|
|
116
|
+
const themeScopedRules = styleRule.value.rules?.filter((rule) => {
|
|
117
|
+
if (rule.type !== "style") {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const firstSelector = rule.value.selectors.at(0)?.at(0);
|
|
121
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
122
|
+
return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
|
|
123
|
+
}) ?? [];
|
|
124
|
+
const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
|
|
125
|
+
const processedThemeScopedRules = themeScopedRules.map((rule) => {
|
|
126
|
+
if (rule.type !== "style") {
|
|
127
|
+
return rule;
|
|
128
|
+
}
|
|
129
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1);
|
|
130
|
+
if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
|
|
131
|
+
return this.processThemeStyle(rule, secondSelector);
|
|
132
|
+
}
|
|
133
|
+
return rule;
|
|
134
|
+
});
|
|
135
|
+
return [
|
|
136
|
+
{
|
|
137
|
+
type: "style",
|
|
138
|
+
value: {
|
|
139
|
+
loc: styleRule.value.loc,
|
|
140
|
+
selectors: styleRule.value.selectors,
|
|
141
|
+
rules: nonThemeRules,
|
|
142
|
+
declarations: styleRule.value.declarations
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
...processedThemeScopedRules
|
|
146
|
+
];
|
|
147
|
+
}
|
|
113
148
|
processThemeStyle(styleRule, secondSelector) {
|
|
114
149
|
const whereSelector = secondSelector.selectors.at(0)?.at(0);
|
|
115
150
|
if (whereSelector?.type !== "class") {
|
|
116
|
-
return;
|
|
151
|
+
return styleRule;
|
|
117
152
|
}
|
|
118
153
|
const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
|
|
119
154
|
if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
|
|
120
|
-
return;
|
|
155
|
+
return styleRule;
|
|
121
156
|
}
|
|
122
157
|
this.processedVariables.add(selectedVariant);
|
|
123
158
|
return {
|
|
@@ -146,6 +181,23 @@ class RuleVisitor {
|
|
|
146
181
|
}
|
|
147
182
|
};
|
|
148
183
|
}
|
|
184
|
+
// Fixes lightningcss serialization bug
|
|
185
|
+
removeNulls(value) {
|
|
186
|
+
if (Array.isArray(value)) {
|
|
187
|
+
return value.map((v) => this.removeNulls(v));
|
|
188
|
+
}
|
|
189
|
+
if (typeof value === "object" && value !== null) {
|
|
190
|
+
return Object.fromEntries(
|
|
191
|
+
Object.entries(value).filter(([_, value2]) => {
|
|
192
|
+
if (value2 === null) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}).map(([key, value2]) => [key, this.removeNulls(value2)])
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
149
201
|
}
|
|
150
202
|
|
|
151
203
|
class UniwindCSSVisitor {
|
package/dist/vite/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const node = require('@tailwindcss/node');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const common = require('../shared/uniwind.nl8746mK.cjs');
|
|
6
|
-
const stringifyThemes = require('../shared/uniwind.
|
|
6
|
+
const stringifyThemes = require('../shared/uniwind.D7C2Zt-r.cjs');
|
|
7
7
|
require('fs');
|
|
8
8
|
require('lightningcss');
|
|
9
9
|
|
package/dist/vite/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizePath } from '@tailwindcss/node';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { u as uniq, n as name } from '../shared/uniwind.F-0-Rr--.mjs';
|
|
4
|
-
import { s as stringifyThemes, U as UniwindCSSVisitor, a as buildCSS, b as buildDtsFile } from '../shared/uniwind.
|
|
4
|
+
import { s as stringifyThemes, U as UniwindCSSVisitor, a as buildCSS, b as buildDtsFile } from '../shared/uniwind.B_j3NcHy.mjs';
|
|
5
5
|
import 'fs';
|
|
6
6
|
import 'lightningcss';
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@niibase/uniwind",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"description": "The fastest Tailwind bindings for React Native with Reanimated 4 support",
|
|
6
6
|
"homepage": "https://uniwind.dev",
|
|
7
7
|
"author": "Unistack",
|
|
8
8
|
"type": "module",
|
|
9
|
-
"repository": "https://github.com/divineniiquaye/uniwind",
|
|
9
|
+
"repository": "https://github.com/divineniiquaye/uniwind-oss",
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"scripts": {
|
|
12
12
|
"precommit": "bun lint",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"keywords": [
|
|
26
26
|
"unistyles",
|
|
27
27
|
"react-native-unistyles",
|
|
28
|
+
"uniwind",
|
|
28
29
|
"react-native",
|
|
29
30
|
"react",
|
|
30
31
|
"native",
|
package/readme.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
[](https://www.npmjs.com/package/uniwind)
|
|
16
16
|
[](https://opensource.org/licenses/MIT)
|
|
17
17
|
|
|
18
|
-
> **Note:** This is a fork of uniwind that
|
|
18
|
+
> **Note:** This is a fork of uniwind that uses **react-native-reanimated** (v4.0.0+) to provide CSS-like animations and transitions using Tailwind classes. Updates are frequently released with original uniwind package with versions in-sync.
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
bun add @niibase/uniwind tailwindcss react-native-safe-area-context react-native-reanimated
|
|
25
25
|
|
|
26
26
|
# Or install from the original package
|
|
27
|
-
bun add uniwind tailwindcss react-native-safe-area-context
|
|
27
|
+
bun add uniwind tailwindcss react-native-safe-area-context
|
|
28
28
|
|
|
29
29
|
# Or install @niibase/uniwind as alias for uniwind
|
|
30
30
|
bun add uniwind@npm:@niibase/uniwind tailwindcss react-native-safe-area-context react-native-reanimated
|
|
@@ -41,8 +41,8 @@ Then follow [installation guides](https://docs.uniwind.dev/quickstart)
|
|
|
41
41
|
|
|
42
42
|
## Features
|
|
43
43
|
|
|
44
|
-
- ⚛️ Out‑of‑the‑box `className` bindings for every React Native component
|
|
45
|
-
- ⚡ Styles are computed at build time for maximum performance
|
|
44
|
+
- ⚛️ Out‑of‑the‑box `className` bindings for every React Native component
|
|
45
|
+
- ⚡ Styles are computed at build time for maximum performance
|
|
46
46
|
- 🎬 **CSS animations and transitions** powered by Reanimated 4
|
|
47
47
|
- 🌙 Dark mode and 🎨 fully customizable themes
|
|
48
48
|
- 🧩 Pseudo‑classes support — `focus`, `active`, `disabled`, and more
|
|
@@ -50,14 +50,6 @@ Then follow [installation guides](https://docs.uniwind.dev/quickstart)
|
|
|
50
50
|
- 🧰 Use custom CSS properties directly in React Native
|
|
51
51
|
- 🔥 And [much more](https://docs.uniwind.dev/api/use-uniwind)
|
|
52
52
|
|
|
53
|
-
## Roadmap
|
|
54
|
-
|
|
55
|
-
- [x] **Reanimated 4 support** - Full compatibility with React Native Reanimated v4 ✅
|
|
56
|
-
- [ ] **Babel support to extend beyond React Native components** - Enable className support for custom components and third-party libraries
|
|
57
|
-
- [ ] **Unistyles (future)** - Integration with Unistyles for enhanced styling capabilities
|
|
58
|
-
|
|
59
|
-
> **Note:** Once Babel support is completed, the likelihood of adding Unistyles support is high.
|
|
60
|
-
|
|
61
53
|
## Contributing
|
|
62
54
|
|
|
63
55
|
Contributions are welcome!
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Appearance, Dimensions, I18nManager, PixelRatio, StyleSheet } from 'react-native'
|
|
1
|
+
import { Appearance, Dimensions, I18nManager, PixelRatio, Platform, StyleSheet } from 'react-native'
|
|
2
2
|
import { cubicBezier, linear, steps } from 'react-native-reanimated'
|
|
3
3
|
import { initialWindowMetrics } from 'react-native-safe-area-context'
|
|
4
4
|
import { ColorScheme, Orientation } from '../../types'
|
|
@@ -27,6 +27,10 @@ export const UniwindRuntime = {
|
|
|
27
27
|
linear,
|
|
28
28
|
lightDark: () => '',
|
|
29
29
|
parseColor,
|
|
30
|
+
platformSelect: (android: string, ios: string, other?: string) => {
|
|
31
|
+
// eslint-disable-next-line no-extra-boolean-cast
|
|
32
|
+
return Platform.select(Boolean(other) ? { android, ios, default: other } : { android, default: ios })
|
|
33
|
+
},
|
|
30
34
|
} as UniwindRuntimeType
|
|
31
35
|
|
|
32
36
|
UniwindRuntime.lightDark = lightDark.bind(UniwindRuntime)
|
package/src/core/types.ts
CHANGED
|
@@ -62,6 +62,7 @@ export type UniwindRuntime = {
|
|
|
62
62
|
linear: (...points: ControlPoint[]) => LinearEasing
|
|
63
63
|
lightDark: (light: string, dark: string) => string
|
|
64
64
|
parseColor: (type: string, color: string) => string
|
|
65
|
+
platformSelect: (android: string, ios: string, other?: string) => string
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export type RNStyle = ViewStyle & TextStyle & ImageStyle & {
|
|
@@ -8,6 +8,10 @@ const ONE_PX = {
|
|
|
8
8
|
export class FunctionVisitor {
|
|
9
9
|
[name: string]: (fn: LightningCSSFunction) => TokenOrValue
|
|
10
10
|
|
|
11
|
+
platformSelect(fn: LightningCSSFunction): TokenOrValue {
|
|
12
|
+
return fn.arguments.at(0) ?? { type: 'token', value: { type: 'ident', value: 'initial' } }
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
pixelRatio(fn: LightningCSSFunction): TokenOrValue {
|
|
12
16
|
return {
|
|
13
17
|
type: 'function',
|
|
@@ -20,13 +20,9 @@ export class RuleVisitor implements LightningRuleVisitors {
|
|
|
20
20
|
|
|
21
21
|
style = (styleRule: Extract<LightningRuleVisitor, { type: 'style' }>) => {
|
|
22
22
|
const firstSelector = styleRule.value.selectors.at(0)?.at(0)
|
|
23
|
-
const secondSelector = styleRule.value.selectors.at(0)?.at(1)
|
|
24
23
|
|
|
25
|
-
if (
|
|
26
|
-
this.
|
|
27
|
-
&& secondSelector.kind === 'where'
|
|
28
|
-
) {
|
|
29
|
-
return this.processThemeStyle(styleRule, secondSelector)
|
|
24
|
+
if (this.currentLayerName === 'theme' && firstSelector?.type === 'pseudo-class' && firstSelector.kind === 'root') {
|
|
25
|
+
return this.removeNulls(this.processThemeRoot(styleRule)) as Array<ReturnedRule>
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
if (firstSelector?.type === 'class') {
|
|
@@ -40,20 +36,60 @@ export class RuleVisitor implements LightningRuleVisitors {
|
|
|
40
36
|
this.processedVariables.clear()
|
|
41
37
|
}
|
|
42
38
|
|
|
39
|
+
private processThemeRoot(styleRule: Extract<LightningRuleVisitor, { type: 'style' }>): Array<ReturnedRule> {
|
|
40
|
+
const themeScopedRules = styleRule.value.rules?.filter(rule => {
|
|
41
|
+
if (rule.type !== 'style') {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const firstSelector = rule.value.selectors.at(0)?.at(0)
|
|
46
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1)
|
|
47
|
+
|
|
48
|
+
return firstSelector?.type === 'nesting' && secondSelector?.type === 'pseudo-class' && secondSelector.kind === 'where'
|
|
49
|
+
}) ?? []
|
|
50
|
+
const nonThemeRules = styleRule.value.rules?.filter(rule => !themeScopedRules.includes(rule))
|
|
51
|
+
const processedThemeScopedRules = themeScopedRules.map(rule => {
|
|
52
|
+
if (rule.type !== 'style') {
|
|
53
|
+
return rule
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const secondSelector = rule.value.selectors.at(0)?.at(1)
|
|
57
|
+
|
|
58
|
+
if (secondSelector?.type === 'pseudo-class' && secondSelector.kind === 'where') {
|
|
59
|
+
return this.processThemeStyle(rule, secondSelector)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return rule
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
{
|
|
67
|
+
type: 'style',
|
|
68
|
+
value: {
|
|
69
|
+
loc: styleRule.value.loc,
|
|
70
|
+
selectors: styleRule.value.selectors,
|
|
71
|
+
rules: nonThemeRules,
|
|
72
|
+
declarations: styleRule.value.declarations,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
...processedThemeScopedRules,
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
43
79
|
private processThemeStyle(
|
|
44
80
|
styleRule: Extract<LightningRuleVisitor, { type: 'style' }>,
|
|
45
81
|
secondSelector: Extract<SelectorComponent, { type: 'pseudo-class'; kind: 'where' }>,
|
|
46
|
-
): ReturnedRule
|
|
82
|
+
): ReturnedRule {
|
|
47
83
|
const whereSelector = secondSelector.selectors.at(0)?.at(0)
|
|
48
84
|
|
|
49
85
|
if (whereSelector?.type !== 'class') {
|
|
50
|
-
return
|
|
86
|
+
return styleRule
|
|
51
87
|
}
|
|
52
88
|
|
|
53
89
|
const selectedVariant = this.themes.find(theme => whereSelector.name === theme)
|
|
54
90
|
|
|
55
91
|
if (selectedVariant === undefined || this.processedVariables.has(selectedVariant)) {
|
|
56
|
-
return
|
|
92
|
+
return styleRule
|
|
57
93
|
}
|
|
58
94
|
|
|
59
95
|
this.processedVariables.add(selectedVariant)
|
|
@@ -93,4 +129,27 @@ export class RuleVisitor implements LightningRuleVisitors {
|
|
|
93
129
|
},
|
|
94
130
|
}
|
|
95
131
|
}
|
|
132
|
+
|
|
133
|
+
// Fixes lightningcss serialization bug
|
|
134
|
+
private removeNulls(value: unknown): unknown {
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
return value.map(v => this.removeNulls(v))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof value === 'object' && value !== null) {
|
|
140
|
+
return Object.fromEntries(
|
|
141
|
+
Object.entries(value)
|
|
142
|
+
.filter(([_, value]) => {
|
|
143
|
+
if (value === null) {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return true
|
|
148
|
+
})
|
|
149
|
+
.map(([key, value]) => [key, this.removeNulls(value)]),
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return value
|
|
154
|
+
}
|
|
96
155
|
}
|
|
@@ -162,6 +162,10 @@ export class Functions {
|
|
|
162
162
|
return 'rt.hairlineWidth'
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
if (fn.name === 'platformSelect') {
|
|
166
|
+
return `rt.platformSelect(${this.Processor.CSS.processValue(fn.arguments)})`
|
|
167
|
+
}
|
|
168
|
+
|
|
165
169
|
if (fn.name === 'pixelRatio') {
|
|
166
170
|
if (fn.arguments.length === 0) {
|
|
167
171
|
return 'rt.pixelRatio(1)'
|