@mgcrea/react-native-tailwind 0.4.0 → 0.5.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/README.md +443 -13
- package/dist/babel/index.cjs +804 -274
- package/dist/babel/index.d.ts +2 -1
- package/dist/babel/index.ts +319 -16
- package/dist/components/Pressable.d.ts +32 -0
- package/dist/components/Pressable.js +1 -0
- package/dist/components/TextInput.d.ts +56 -0
- package/dist/components/TextInput.js +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +1 -1
- package/dist/parser/aspectRatio.d.ts +16 -0
- package/dist/parser/aspectRatio.js +1 -0
- package/dist/parser/aspectRatio.test.d.ts +1 -0
- package/dist/parser/aspectRatio.test.js +1 -0
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.d.ts +1 -0
- package/dist/parser/borders.test.js +1 -0
- package/dist/parser/colors.d.ts +1 -0
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.d.ts +1 -0
- package/dist/parser/colors.test.js +1 -0
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.d.ts +2 -0
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.d.ts +1 -0
- package/dist/parser/layout.test.js +1 -0
- package/dist/parser/modifiers.d.ts +47 -0
- package/dist/parser/modifiers.js +1 -0
- package/dist/parser/modifiers.test.d.ts +1 -0
- package/dist/parser/modifiers.test.js +1 -0
- package/dist/parser/shadows.d.ts +26 -0
- package/dist/parser/shadows.js +1 -0
- package/dist/parser/shadows.test.d.ts +1 -0
- package/dist/parser/shadows.test.js +1 -0
- package/dist/parser/sizing.test.d.ts +1 -0
- package/dist/parser/sizing.test.js +1 -0
- package/dist/parser/spacing.d.ts +1 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.d.ts +1 -0
- package/dist/parser/spacing.test.js +1 -0
- package/dist/parser/typography.d.ts +2 -1
- package/dist/parser/typography.js +1 -1
- package/dist/parser/typography.test.d.ts +1 -0
- package/dist/parser/typography.test.js +1 -0
- package/dist/types.d.ts +5 -2
- package/package.json +7 -6
- package/src/babel/index.ts +319 -16
- package/src/components/Pressable.tsx +46 -0
- package/src/components/TextInput.tsx +90 -0
- package/src/index.ts +20 -2
- package/src/parser/aspectRatio.test.ts +191 -0
- package/src/parser/aspectRatio.ts +73 -0
- package/src/parser/borders.test.ts +329 -0
- package/src/parser/borders.ts +187 -108
- package/src/parser/colors.test.ts +335 -0
- package/src/parser/colors.ts +117 -6
- package/src/parser/index.ts +13 -2
- package/src/parser/layout.test.ts +459 -0
- package/src/parser/layout.ts +128 -0
- package/src/parser/modifiers.test.ts +375 -0
- package/src/parser/modifiers.ts +104 -0
- package/src/parser/shadows.test.ts +201 -0
- package/src/parser/shadows.ts +133 -0
- package/src/parser/sizing.test.ts +256 -0
- package/src/parser/spacing.test.ts +226 -0
- package/src/parser/spacing.ts +93 -138
- package/src/parser/typography.test.ts +221 -0
- package/src/parser/typography.ts +143 -112
- package/src/types.ts +2 -2
- package/dist/react-native.d.js +0 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { COLORS, parseColor } from "./colors";
|
|
3
|
+
|
|
4
|
+
describe("COLORS", () => {
|
|
5
|
+
it("should export complete color palette", () => {
|
|
6
|
+
expect(COLORS).toMatchSnapshot();
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe("parseColor - background colors", () => {
|
|
11
|
+
it("should parse background colors with preset values", () => {
|
|
12
|
+
expect(parseColor("bg-blue-500")).toEqual({ backgroundColor: "#3B82F6" });
|
|
13
|
+
expect(parseColor("bg-red-500")).toEqual({ backgroundColor: "#EF4444" });
|
|
14
|
+
expect(parseColor("bg-green-500")).toEqual({ backgroundColor: "#22C55E" });
|
|
15
|
+
expect(parseColor("bg-gray-300")).toEqual({ backgroundColor: "#D1D5DB" });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should parse background colors with basic values", () => {
|
|
19
|
+
expect(parseColor("bg-white")).toEqual({ backgroundColor: "#FFFFFF" });
|
|
20
|
+
expect(parseColor("bg-black")).toEqual({ backgroundColor: "#000000" });
|
|
21
|
+
expect(parseColor("bg-transparent")).toEqual({ backgroundColor: "transparent" });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should parse background colors with arbitrary 6-digit hex values", () => {
|
|
25
|
+
expect(parseColor("bg-[#ff0000]")).toEqual({ backgroundColor: "#ff0000" });
|
|
26
|
+
expect(parseColor("bg-[#3B82F6]")).toEqual({ backgroundColor: "#3B82F6" });
|
|
27
|
+
expect(parseColor("bg-[#000000]")).toEqual({ backgroundColor: "#000000" });
|
|
28
|
+
expect(parseColor("bg-[#FFFFFF]")).toEqual({ backgroundColor: "#FFFFFF" });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should parse background colors with arbitrary 3-digit hex values", () => {
|
|
32
|
+
expect(parseColor("bg-[#f00]")).toEqual({ backgroundColor: "#ff0000" });
|
|
33
|
+
expect(parseColor("bg-[#abc]")).toEqual({ backgroundColor: "#aabbcc" });
|
|
34
|
+
expect(parseColor("bg-[#123]")).toEqual({ backgroundColor: "#112233" });
|
|
35
|
+
expect(parseColor("bg-[#FFF]")).toEqual({ backgroundColor: "#FFFFFF" });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should parse background colors with arbitrary 8-digit hex values (with alpha)", () => {
|
|
39
|
+
expect(parseColor("bg-[#ff0000aa]")).toEqual({ backgroundColor: "#ff0000aa" });
|
|
40
|
+
expect(parseColor("bg-[#3B82F680]")).toEqual({ backgroundColor: "#3B82F680" });
|
|
41
|
+
expect(parseColor("bg-[#00000000]")).toEqual({ backgroundColor: "#00000000" });
|
|
42
|
+
expect(parseColor("bg-[#FFFFFFFF]")).toEqual({ backgroundColor: "#FFFFFFFF" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should handle case-insensitive hex values", () => {
|
|
46
|
+
expect(parseColor("bg-[#FF0000]")).toEqual({ backgroundColor: "#FF0000" });
|
|
47
|
+
expect(parseColor("bg-[#ff0000]")).toEqual({ backgroundColor: "#ff0000" });
|
|
48
|
+
expect(parseColor("bg-[#Ff0000]")).toEqual({ backgroundColor: "#Ff0000" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should prefer arbitrary values over preset colors", () => {
|
|
52
|
+
// If someone creates a custom color named "[#ff0000]", the arbitrary value should take precedence
|
|
53
|
+
const customColors = { "[#ff0000]": "#00ff00" };
|
|
54
|
+
expect(parseColor("bg-[#ff0000]", customColors)).toEqual({ backgroundColor: "#ff0000" });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("parseColor - text colors", () => {
|
|
59
|
+
it("should parse text colors with preset values", () => {
|
|
60
|
+
expect(parseColor("text-blue-500")).toEqual({ color: "#3B82F6" });
|
|
61
|
+
expect(parseColor("text-red-500")).toEqual({ color: "#EF4444" });
|
|
62
|
+
expect(parseColor("text-green-500")).toEqual({ color: "#22C55E" });
|
|
63
|
+
expect(parseColor("text-gray-700")).toEqual({ color: "#374151" });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should parse text colors with basic values", () => {
|
|
67
|
+
expect(parseColor("text-white")).toEqual({ color: "#FFFFFF" });
|
|
68
|
+
expect(parseColor("text-black")).toEqual({ color: "#000000" });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should parse text colors with arbitrary 6-digit hex values", () => {
|
|
72
|
+
expect(parseColor("text-[#ff0000]")).toEqual({ color: "#ff0000" });
|
|
73
|
+
expect(parseColor("text-[#3B82F6]")).toEqual({ color: "#3B82F6" });
|
|
74
|
+
expect(parseColor("text-[#333333]")).toEqual({ color: "#333333" });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should parse text colors with arbitrary 3-digit hex values", () => {
|
|
78
|
+
expect(parseColor("text-[#f00]")).toEqual({ color: "#ff0000" });
|
|
79
|
+
expect(parseColor("text-[#abc]")).toEqual({ color: "#aabbcc" });
|
|
80
|
+
expect(parseColor("text-[#000]")).toEqual({ color: "#000000" });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should parse text colors with arbitrary 8-digit hex values (with alpha)", () => {
|
|
84
|
+
expect(parseColor("text-[#ff0000aa]")).toEqual({ color: "#ff0000aa" });
|
|
85
|
+
expect(parseColor("text-[#00000080]")).toEqual({ color: "#00000080" });
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("parseColor - border colors", () => {
|
|
90
|
+
it("should parse border colors with preset values", () => {
|
|
91
|
+
expect(parseColor("border-blue-500")).toEqual({ borderColor: "#3B82F6" });
|
|
92
|
+
expect(parseColor("border-red-500")).toEqual({ borderColor: "#EF4444" });
|
|
93
|
+
expect(parseColor("border-green-500")).toEqual({ borderColor: "#22C55E" });
|
|
94
|
+
expect(parseColor("border-gray-200")).toEqual({ borderColor: "#E5E7EB" });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should parse border colors with basic values", () => {
|
|
98
|
+
expect(parseColor("border-white")).toEqual({ borderColor: "#FFFFFF" });
|
|
99
|
+
expect(parseColor("border-black")).toEqual({ borderColor: "#000000" });
|
|
100
|
+
expect(parseColor("border-transparent")).toEqual({ borderColor: "transparent" });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should parse border colors with arbitrary 6-digit hex values", () => {
|
|
104
|
+
expect(parseColor("border-[#ff0000]")).toEqual({ borderColor: "#ff0000" });
|
|
105
|
+
expect(parseColor("border-[#3B82F6]")).toEqual({ borderColor: "#3B82F6" });
|
|
106
|
+
expect(parseColor("border-[#cccccc]")).toEqual({ borderColor: "#cccccc" });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should parse border colors with arbitrary 3-digit hex values", () => {
|
|
110
|
+
expect(parseColor("border-[#f00]")).toEqual({ borderColor: "#ff0000" });
|
|
111
|
+
expect(parseColor("border-[#abc]")).toEqual({ borderColor: "#aabbcc" });
|
|
112
|
+
expect(parseColor("border-[#999]")).toEqual({ borderColor: "#999999" });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should parse border colors with arbitrary 8-digit hex values (with alpha)", () => {
|
|
116
|
+
expect(parseColor("border-[#ff0000aa]")).toEqual({ borderColor: "#ff0000aa" });
|
|
117
|
+
expect(parseColor("border-[#0000FF50]")).toEqual({ borderColor: "#0000FF50" });
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should not match border width classes", () => {
|
|
121
|
+
expect(parseColor("border-0")).toBeNull();
|
|
122
|
+
expect(parseColor("border-2")).toBeNull();
|
|
123
|
+
expect(parseColor("border-4")).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("parseColor - custom colors", () => {
|
|
128
|
+
const customColors = {
|
|
129
|
+
"brand-primary": "#FF6B6B",
|
|
130
|
+
"brand-secondary": "#4ECDC4",
|
|
131
|
+
accent: "#FFE66D",
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
it("should support custom background colors", () => {
|
|
135
|
+
expect(parseColor("bg-brand-primary", customColors)).toEqual({ backgroundColor: "#FF6B6B" });
|
|
136
|
+
expect(parseColor("bg-brand-secondary", customColors)).toEqual({ backgroundColor: "#4ECDC4" });
|
|
137
|
+
expect(parseColor("bg-accent", customColors)).toEqual({ backgroundColor: "#FFE66D" });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should support custom text colors", () => {
|
|
141
|
+
expect(parseColor("text-brand-primary", customColors)).toEqual({ color: "#FF6B6B" });
|
|
142
|
+
expect(parseColor("text-brand-secondary", customColors)).toEqual({ color: "#4ECDC4" });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should support custom border colors", () => {
|
|
146
|
+
expect(parseColor("border-brand-primary", customColors)).toEqual({ borderColor: "#FF6B6B" });
|
|
147
|
+
expect(parseColor("border-accent", customColors)).toEqual({ borderColor: "#FFE66D" });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should allow custom colors to override preset colors", () => {
|
|
151
|
+
const overrideColors = { "blue-500": "#FF0000" };
|
|
152
|
+
expect(parseColor("bg-blue-500", overrideColors)).toEqual({ backgroundColor: "#FF0000" });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should fallback to preset colors when custom color not found", () => {
|
|
156
|
+
expect(parseColor("bg-red-500", customColors)).toEqual({ backgroundColor: "#EF4444" });
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("parseColor - edge cases", () => {
|
|
161
|
+
it("should return null for invalid classes", () => {
|
|
162
|
+
expect(parseColor("invalid")).toBeNull();
|
|
163
|
+
expect(parseColor("bg")).toBeNull();
|
|
164
|
+
expect(parseColor("text")).toBeNull();
|
|
165
|
+
expect(parseColor("border")).toBeNull();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should return null for invalid color values", () => {
|
|
169
|
+
expect(parseColor("bg-invalid")).toBeNull();
|
|
170
|
+
expect(parseColor("text-notacolor")).toBeNull();
|
|
171
|
+
expect(parseColor("border-xyz")).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should return null for invalid arbitrary hex values", () => {
|
|
175
|
+
expect(parseColor("bg-[#ff]")).toBeNull(); // Too short (2 digits)
|
|
176
|
+
expect(parseColor("bg-[#ffff]")).toBeNull(); // Invalid length (4 digits)
|
|
177
|
+
expect(parseColor("bg-[#fffff]")).toBeNull(); // Invalid length (5 digits)
|
|
178
|
+
expect(parseColor("bg-[#fffffff]")).toBeNull(); // Invalid length (7 digits)
|
|
179
|
+
expect(parseColor("bg-[#fffffffff]")).toBeNull(); // Too long (9 digits)
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should return null for malformed arbitrary values", () => {
|
|
183
|
+
expect(parseColor("bg-[#ff0000")).toBeNull(); // Missing closing bracket
|
|
184
|
+
expect(parseColor("bg-#ff0000]")).toBeNull(); // Missing opening bracket
|
|
185
|
+
expect(parseColor("bg-[]")).toBeNull(); // Empty brackets
|
|
186
|
+
expect(parseColor("bg-[ff0000]")).toBeNull(); // Missing # symbol
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should return null for non-hex arbitrary values", () => {
|
|
190
|
+
expect(parseColor("bg-[#gggggg]")).toBeNull(); // Invalid hex characters
|
|
191
|
+
expect(parseColor("bg-[#zzzzzz]")).toBeNull(); // Invalid hex characters
|
|
192
|
+
expect(parseColor("bg-[rgb(255,0,0)]")).toBeNull(); // RGB format not supported
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should not match partial class names", () => {
|
|
196
|
+
expect(parseColor("background-blue-500")).toBeNull();
|
|
197
|
+
expect(parseColor("textcolor-red-500")).toBeNull();
|
|
198
|
+
expect(parseColor("border-color-blue-500")).toBeNull();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should handle all color scale variants", () => {
|
|
202
|
+
const scales = ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900"];
|
|
203
|
+
scales.forEach((scale) => {
|
|
204
|
+
expect(parseColor(`bg-blue-${scale}`)).toBeTruthy();
|
|
205
|
+
expect(parseColor(`text-red-${scale}`)).toBeTruthy();
|
|
206
|
+
expect(parseColor(`border-green-${scale}`)).toBeTruthy();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("parseColor - comprehensive coverage", () => {
|
|
212
|
+
it("should parse all color types with same preset color", () => {
|
|
213
|
+
expect(parseColor("bg-blue-500")).toEqual({ backgroundColor: "#3B82F6" });
|
|
214
|
+
expect(parseColor("text-blue-500")).toEqual({ color: "#3B82F6" });
|
|
215
|
+
expect(parseColor("border-blue-500")).toEqual({ borderColor: "#3B82F6" });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("should parse all color types with same arbitrary hex", () => {
|
|
219
|
+
expect(parseColor("bg-[#ff0000]")).toEqual({ backgroundColor: "#ff0000" });
|
|
220
|
+
expect(parseColor("text-[#ff0000]")).toEqual({ color: "#ff0000" });
|
|
221
|
+
expect(parseColor("border-[#ff0000]")).toEqual({ borderColor: "#ff0000" });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should handle all color families", () => {
|
|
225
|
+
const families = ["gray", "red", "blue", "green", "yellow", "purple", "pink", "orange", "indigo"];
|
|
226
|
+
families.forEach((family) => {
|
|
227
|
+
expect(parseColor(`bg-${family}-500`)).toBeTruthy();
|
|
228
|
+
expect(parseColor(`text-${family}-500`)).toBeTruthy();
|
|
229
|
+
expect(parseColor(`border-${family}-500`)).toBeTruthy();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("should handle arbitrary values with mixed case", () => {
|
|
234
|
+
expect(parseColor("bg-[#AbCdEf]")).toEqual({ backgroundColor: "#AbCdEf" });
|
|
235
|
+
expect(parseColor("text-[#aBcDeF]")).toEqual({ color: "#aBcDeF" });
|
|
236
|
+
expect(parseColor("border-[#ABCDEF]")).toEqual({ borderColor: "#ABCDEF" });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should expand 3-digit hex consistently across all color types", () => {
|
|
240
|
+
expect(parseColor("bg-[#abc]")).toEqual({ backgroundColor: "#aabbcc" });
|
|
241
|
+
expect(parseColor("text-[#abc]")).toEqual({ color: "#aabbcc" });
|
|
242
|
+
expect(parseColor("border-[#abc]")).toEqual({ borderColor: "#aabbcc" });
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("should handle alpha channel consistently across all color types", () => {
|
|
246
|
+
expect(parseColor("bg-[#ff0000aa]")).toEqual({ backgroundColor: "#ff0000aa" });
|
|
247
|
+
expect(parseColor("text-[#ff0000aa]")).toEqual({ color: "#ff0000aa" });
|
|
248
|
+
expect(parseColor("border-[#ff0000aa]")).toEqual({ borderColor: "#ff0000aa" });
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe("parseColor - opacity modifiers", () => {
|
|
253
|
+
it("should parse background colors with opacity modifiers", () => {
|
|
254
|
+
expect(parseColor("bg-black/50")).toEqual({ backgroundColor: "#00000080" });
|
|
255
|
+
expect(parseColor("bg-white/50")).toEqual({ backgroundColor: "#FFFFFF80" });
|
|
256
|
+
expect(parseColor("bg-blue-500/80")).toEqual({ backgroundColor: "#3B82F6CC" });
|
|
257
|
+
expect(parseColor("bg-red-500/30")).toEqual({ backgroundColor: "#EF44444D" });
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("should parse text colors with opacity modifiers", () => {
|
|
261
|
+
expect(parseColor("text-black/80")).toEqual({ color: "#000000CC" });
|
|
262
|
+
expect(parseColor("text-white/90")).toEqual({ color: "#FFFFFFE6" });
|
|
263
|
+
expect(parseColor("text-gray-900/70")).toEqual({ color: "#111827B3" });
|
|
264
|
+
expect(parseColor("text-blue-500/50")).toEqual({ color: "#3B82F680" });
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("should parse border colors with opacity modifiers", () => {
|
|
268
|
+
expect(parseColor("border-black/25")).toEqual({ borderColor: "#00000040" });
|
|
269
|
+
expect(parseColor("border-red-500/60")).toEqual({ borderColor: "#EF444499" });
|
|
270
|
+
expect(parseColor("border-gray-200/40")).toEqual({ borderColor: "#E5E7EB66" });
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should handle opacity modifier with arbitrary hex colors", () => {
|
|
274
|
+
expect(parseColor("bg-[#ff0000]/50")).toEqual({ backgroundColor: "#FF000080" });
|
|
275
|
+
expect(parseColor("text-[#3B82F6]/80")).toEqual({ color: "#3B82F6CC" });
|
|
276
|
+
expect(parseColor("border-[#abc]/60")).toEqual({ borderColor: "#AABBCC99" });
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should handle opacity modifier with custom colors", () => {
|
|
280
|
+
const customColors = { "brand-primary": "#FF6B6B" };
|
|
281
|
+
expect(parseColor("bg-brand-primary/50", customColors)).toEqual({ backgroundColor: "#FF6B6B80" });
|
|
282
|
+
expect(parseColor("text-brand-primary/75", customColors)).toEqual({ color: "#FF6B6BBF" });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("should handle opacity 0 (fully transparent)", () => {
|
|
286
|
+
expect(parseColor("bg-black/0")).toEqual({ backgroundColor: "#00000000" });
|
|
287
|
+
expect(parseColor("text-red-500/0")).toEqual({ color: "#EF444400" });
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should handle opacity 100 (fully opaque)", () => {
|
|
291
|
+
expect(parseColor("bg-black/100")).toEqual({ backgroundColor: "#000000FF" });
|
|
292
|
+
expect(parseColor("text-blue-500/100")).toEqual({ color: "#3B82F6FF" });
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("should handle transparent color with opacity modifier", () => {
|
|
296
|
+
// Transparent with opacity should remain transparent
|
|
297
|
+
expect(parseColor("bg-transparent/50")).toEqual({ backgroundColor: "transparent" });
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should convert opacity percentage to correct hex values", () => {
|
|
301
|
+
// Test key opacity values
|
|
302
|
+
expect(parseColor("bg-black/0")).toEqual({ backgroundColor: "#00000000" }); // 0%
|
|
303
|
+
expect(parseColor("bg-black/10")).toEqual({ backgroundColor: "#0000001A" }); // ~10%
|
|
304
|
+
expect(parseColor("bg-black/25")).toEqual({ backgroundColor: "#00000040" }); // 25%
|
|
305
|
+
expect(parseColor("bg-black/50")).toEqual({ backgroundColor: "#00000080" }); // 50%
|
|
306
|
+
expect(parseColor("bg-black/75")).toEqual({ backgroundColor: "#000000BF" }); // 75%
|
|
307
|
+
expect(parseColor("bg-black/100")).toEqual({ backgroundColor: "#000000FF" }); // 100%
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should return null for invalid opacity values", () => {
|
|
311
|
+
expect(parseColor("bg-black/101")).toBeNull(); // > 100
|
|
312
|
+
expect(parseColor("bg-black/-1")).toBeNull(); // < 0
|
|
313
|
+
expect(parseColor("bg-black/150")).toBeNull(); // Way over 100
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("should return null for malformed opacity syntax", () => {
|
|
317
|
+
expect(parseColor("bg-black/")).toBeNull(); // Missing opacity value
|
|
318
|
+
expect(parseColor("bg-black/abc")).toBeNull(); // Non-numeric opacity
|
|
319
|
+
expect(parseColor("bg-black/50/")).toBeNull(); // Extra slash
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should handle opacity with 3-digit hex expansion", () => {
|
|
323
|
+
expect(parseColor("bg-[#f00]/50")).toEqual({ backgroundColor: "#FF000080" });
|
|
324
|
+
expect(parseColor("text-[#abc]/75")).toEqual({ color: "#AABBCCBF" });
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("should work with all color families", () => {
|
|
328
|
+
const families = ["gray", "red", "blue", "green", "yellow", "purple", "pink", "orange", "indigo"];
|
|
329
|
+
families.forEach((family) => {
|
|
330
|
+
expect(parseColor(`bg-${family}-500/50`)).toBeTruthy();
|
|
331
|
+
expect(parseColor(`text-${family}-500/50`)).toBeTruthy();
|
|
332
|
+
expect(parseColor(`border-${family}-500/50`)).toBeTruthy();
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|
package/src/parser/colors.ts
CHANGED
|
@@ -120,8 +120,75 @@ export const COLORS: Record<string, string> = {
|
|
|
120
120
|
transparent: "transparent",
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Apply opacity to hex color by appending alpha channel
|
|
125
|
+
* @param hex - Hex color string (e.g., "#ff0000", "#f00", or "transparent")
|
|
126
|
+
* @param opacity - Opacity value 0-100 (e.g., 50 for 50%)
|
|
127
|
+
* @returns 8-digit hex with alpha (e.g., "#FF000080") or rgba for special colors
|
|
128
|
+
*/
|
|
129
|
+
function applyOpacity(hex: string, opacity: number): string {
|
|
130
|
+
// Handle transparent specially
|
|
131
|
+
if (hex === "transparent") {
|
|
132
|
+
return "transparent";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Remove # if present
|
|
136
|
+
const cleanHex = hex.replace(/^#/, "");
|
|
137
|
+
|
|
138
|
+
// Expand 3-digit hex to 6-digit: #abc -> #aabbcc
|
|
139
|
+
const fullHex =
|
|
140
|
+
cleanHex.length === 3
|
|
141
|
+
? cleanHex
|
|
142
|
+
.split("")
|
|
143
|
+
.map((char) => char + char)
|
|
144
|
+
.join("")
|
|
145
|
+
: cleanHex;
|
|
146
|
+
|
|
147
|
+
// Convert opacity percentage (0-100) to hex (00-FF)
|
|
148
|
+
const alpha = Math.round((opacity / 100) * 255);
|
|
149
|
+
const alphaHex = alpha.toString(16).padStart(2, "0").toUpperCase();
|
|
150
|
+
|
|
151
|
+
// Return 8-digit hex: #RRGGBBAA
|
|
152
|
+
return `#${fullHex.toUpperCase()}${alphaHex}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Parse arbitrary color value: [#ff0000], [#f00], [#FF0000AA]
|
|
157
|
+
* Supports 3-digit, 6-digit, and 8-digit (with alpha) hex colors
|
|
158
|
+
* Returns hex string if valid, null otherwise
|
|
159
|
+
*/
|
|
160
|
+
function parseArbitraryColor(value: string): string | null {
|
|
161
|
+
// Match: [#rgb], [#rrggbb], or [#rrggbbaa]
|
|
162
|
+
const hexMatch = value.match(/^\[#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\]$/);
|
|
163
|
+
if (hexMatch) {
|
|
164
|
+
const hex = hexMatch[1];
|
|
165
|
+
// Expand 3-digit hex to 6-digit: #abc -> #aabbcc
|
|
166
|
+
if (hex.length === 3) {
|
|
167
|
+
const expanded = hex
|
|
168
|
+
.split("")
|
|
169
|
+
.map((char) => char + char)
|
|
170
|
+
.join("");
|
|
171
|
+
return `#${expanded}`;
|
|
172
|
+
}
|
|
173
|
+
return `#${hex}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Warn about unsupported formats
|
|
177
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
178
|
+
if (process.env.NODE_ENV !== "production") {
|
|
179
|
+
console.warn(
|
|
180
|
+
`[react-native-tailwind] Unsupported arbitrary color value: ${value}. Only hex colors are supported (e.g., [#ff0000], [#f00], or [#ff0000aa]).`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
123
189
|
/**
|
|
124
190
|
* Parse color classes (background, text, border)
|
|
191
|
+
* Supports opacity modifier: bg-blue-500/50, text-black/80, border-red-500/30
|
|
125
192
|
*/
|
|
126
193
|
export function parseColor(cls: string, customColors?: Record<string, string>): StyleObject | null {
|
|
127
194
|
// Helper to get color with custom override (custom colors take precedence)
|
|
@@ -129,28 +196,72 @@ export function parseColor(cls: string, customColors?: Record<string, string>):
|
|
|
129
196
|
return customColors?.[key] ?? COLORS[key];
|
|
130
197
|
};
|
|
131
198
|
|
|
132
|
-
//
|
|
199
|
+
// Helper to parse color with optional opacity modifier
|
|
200
|
+
const parseColorWithOpacity = (colorKey: string): string | null => {
|
|
201
|
+
// Check for opacity modifier: blue-500/50
|
|
202
|
+
const opacityMatch = colorKey.match(/^(.+)\/(\d+)$/);
|
|
203
|
+
if (opacityMatch) {
|
|
204
|
+
const baseColorKey = opacityMatch[1];
|
|
205
|
+
const opacity = Number.parseInt(opacityMatch[2], 10);
|
|
206
|
+
|
|
207
|
+
// Validate opacity range (0-100)
|
|
208
|
+
if (opacity < 0 || opacity > 100) {
|
|
209
|
+
if (process.env.NODE_ENV !== "production") {
|
|
210
|
+
console.warn(
|
|
211
|
+
`[react-native-tailwind] Invalid opacity value: ${opacity}. Opacity must be between 0 and 100.`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Try arbitrary color first: bg-[#ff0000]/50
|
|
218
|
+
const arbitraryColor = parseArbitraryColor(baseColorKey);
|
|
219
|
+
if (arbitraryColor !== null) {
|
|
220
|
+
return applyOpacity(arbitraryColor, opacity);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Try preset/custom colors: bg-blue-500/50
|
|
224
|
+
const color = getColor(baseColorKey);
|
|
225
|
+
if (color) {
|
|
226
|
+
return applyOpacity(color, opacity);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// No opacity modifier - try normal color parsing
|
|
233
|
+
// Try arbitrary value first
|
|
234
|
+
const arbitraryColor = parseArbitraryColor(colorKey);
|
|
235
|
+
if (arbitraryColor !== null) {
|
|
236
|
+
return arbitraryColor;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Try preset/custom colors
|
|
240
|
+
return getColor(colorKey) ?? null;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Background color: bg-blue-500, bg-blue-500/50, bg-[#ff0000]/80
|
|
133
244
|
if (cls.startsWith("bg-")) {
|
|
134
245
|
const colorKey = cls.substring(3);
|
|
135
|
-
const color =
|
|
246
|
+
const color = parseColorWithOpacity(colorKey);
|
|
136
247
|
if (color) {
|
|
137
248
|
return { backgroundColor: color };
|
|
138
249
|
}
|
|
139
250
|
}
|
|
140
251
|
|
|
141
|
-
// Text color: text-blue-500
|
|
252
|
+
// Text color: text-blue-500, text-blue-500/50, text-[#ff0000]/80
|
|
142
253
|
if (cls.startsWith("text-")) {
|
|
143
254
|
const colorKey = cls.substring(5);
|
|
144
|
-
const color =
|
|
255
|
+
const color = parseColorWithOpacity(colorKey);
|
|
145
256
|
if (color) {
|
|
146
257
|
return { color: color };
|
|
147
258
|
}
|
|
148
259
|
}
|
|
149
260
|
|
|
150
|
-
// Border color: border-blue-500
|
|
261
|
+
// Border color: border-blue-500, border-blue-500/50, border-[#ff0000]/80
|
|
151
262
|
if (cls.startsWith("border-") && !cls.match(/^border-[0-9]/)) {
|
|
152
263
|
const colorKey = cls.substring(7);
|
|
153
|
-
const color =
|
|
264
|
+
const color = parseColorWithOpacity(colorKey);
|
|
154
265
|
if (color) {
|
|
155
266
|
return { borderColor: color };
|
|
156
267
|
}
|
package/src/parser/index.ts
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { StyleObject } from "../types";
|
|
7
|
+
import { parseAspectRatio } from "./aspectRatio";
|
|
7
8
|
import { parseBorder } from "./borders";
|
|
8
9
|
import { parseColor } from "./colors";
|
|
9
10
|
import { parseLayout } from "./layout";
|
|
11
|
+
import { parseShadow } from "./shadows";
|
|
10
12
|
import { parseSizing } from "./sizing";
|
|
11
13
|
import { parseSpacing } from "./spacing";
|
|
12
14
|
import { parseTypography } from "./typography";
|
|
@@ -37,14 +39,17 @@ export function parseClassName(className: string, customColors?: Record<string,
|
|
|
37
39
|
*/
|
|
38
40
|
export function parseClass(cls: string, customColors?: Record<string, string>): StyleObject {
|
|
39
41
|
// Try each parser in order
|
|
42
|
+
// Note: parseBorder must come before parseColor to avoid border-[3px] being parsed as a color
|
|
40
43
|
// parseColor gets custom colors, others don't need it
|
|
41
|
-
const parsers:
|
|
44
|
+
const parsers: ((cls: string) => StyleObject | null)[] = [
|
|
42
45
|
parseSpacing,
|
|
46
|
+
parseBorder,
|
|
43
47
|
(cls: string) => parseColor(cls, customColors),
|
|
44
48
|
parseLayout,
|
|
45
49
|
parseTypography,
|
|
46
|
-
parseBorder,
|
|
47
50
|
parseSizing,
|
|
51
|
+
parseShadow,
|
|
52
|
+
parseAspectRatio,
|
|
48
53
|
];
|
|
49
54
|
|
|
50
55
|
for (const parser of parsers) {
|
|
@@ -63,9 +68,15 @@ export function parseClass(cls: string, customColors?: Record<string, string>):
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// Re-export parsers for testing/advanced usage
|
|
71
|
+
export { parseAspectRatio } from "./aspectRatio";
|
|
66
72
|
export { parseBorder } from "./borders";
|
|
67
73
|
export { parseColor } from "./colors";
|
|
68
74
|
export { parseLayout } from "./layout";
|
|
75
|
+
export { parseShadow } from "./shadows";
|
|
69
76
|
export { parseSizing } from "./sizing";
|
|
70
77
|
export { parseSpacing } from "./spacing";
|
|
71
78
|
export { parseTypography } from "./typography";
|
|
79
|
+
|
|
80
|
+
// Re-export modifier utilities
|
|
81
|
+
export { hasModifier, parseModifier, splitModifierClasses } from "./modifiers";
|
|
82
|
+
export type { ModifierType, ParsedModifier } from "./modifiers";
|