Skip to content

Commit e805693

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/typescript-eslint-8.45.0
2 parents 620e723 + bb13c29 commit e805693

File tree

12 files changed

+330
-38
lines changed

12 files changed

+330
-38
lines changed

example/src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Text, View } from "react-native";
1+
import { Button, Text, View } from "react-native";
22

33
import { StatusBar } from "expo-status-bar";
44

@@ -10,6 +10,7 @@ export default function App() {
1010
<Text className="text-red-800 text-2xl font-bold animate-bounce">
1111
Hello world!!!
1212
</Text>
13+
<Button title="test2" className="text-red-500" />
1314
<StatusBar style="auto" />
1415
</View>
1516
);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { render, screen } from "@testing-library/react-native";
2+
import { View } from "react-native-css/components/View";
3+
import { registerCSS, testID } from "react-native-css/jest";
4+
5+
test("color-mix() - keyword", () => {
6+
registerCSS(
7+
`.test {
8+
--bg: red;
9+
@supports (color: color-mix(in lab, red, red)) {
10+
background-color: color-mix(in oklab, var(--bg) 50%, transparent);
11+
}
12+
}
13+
`,
14+
{
15+
inlineVariables: false,
16+
},
17+
);
18+
19+
render(<View testID={testID} className="test" />);
20+
const component = screen.getByTestId(testID);
21+
22+
expect(component.props.style).toStrictEqual({
23+
backgroundColor: "rgba(255, 0, 0, 0.5)",
24+
});
25+
});
26+
27+
test("color-mix() - oklch", () => {
28+
registerCSS(
29+
`.test {
30+
--bg: oklch(0.577 0.245 27.325);
31+
@supports (color: color-mix(in lab, red, red)) {
32+
background-color: color-mix(in oklab, var(--bg) 50%, transparent);
33+
}
34+
}
35+
`,
36+
{
37+
inlineVariables: false,
38+
},
39+
);
40+
41+
render(<View testID={testID} className="test" />);
42+
const component = screen.getByTestId(testID);
43+
44+
expect(component.props.style).toStrictEqual({
45+
backgroundColor: "rgba(231, 0, 11, 0.5)",
46+
});
47+
});

src/__tests__/native/container-queries.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ test("container query width", () => {
107107
},
108108
});
109109

110-
expect(parent.props.style).toStrictEqual([{ width: 200 }, { width: 500 }]);
110+
expect(parent.props.style).toStrictEqual({ width: 500 });
111111

112112
expect(child.props.style).toStrictEqual({
113113
color: "#00f",

src/__tests__/native/specificity.test.tsx

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ test("inline styles", () => {
1616
/>,
1717
).getByTestId(testID);
1818

19-
expect(component.props.style).toStrictEqual([
20-
{ backgroundColor: "#f00" },
21-
{ backgroundColor: "blue" },
22-
]);
19+
expect(component.props.style).toStrictEqual({ backgroundColor: "blue" });
2320
});
2421

2522
test("specificity order", () => {
@@ -60,10 +57,7 @@ test("important - requires sorting", () => {
6057
<Text testID={testID} className="blue red" />,
6158
).getByTestId(testID);
6259

63-
expect(component.props.style).toStrictEqual([
64-
{ color: "#f00" },
65-
{ color: "#00f" },
66-
]);
60+
expect(component.props.style).toStrictEqual({ color: "#00f" });
6761
});
6862

6963
test("important - inline", () => {
@@ -79,10 +73,7 @@ test("important - inline", () => {
7973
/>,
8074
).getByTestId(testID);
8175

82-
expect(component.props.style).toStrictEqual([
83-
{ backgroundColor: "red" },
84-
{ backgroundColor: "#00f" },
85-
]);
76+
expect(component.props.style).toStrictEqual({ backgroundColor: "#00f" });
8677
});
8778

8879
test("important - modifiers", () => {
@@ -96,17 +87,11 @@ test("important - modifiers", () => {
9687
<Text testID={testID} className="blue red" />,
9788
).getByTestId(testID);
9889

99-
expect(component.props.style).toStrictEqual([
100-
{ color: "#f00" },
101-
{ color: "#00f" },
102-
]);
90+
expect(component.props.style).toStrictEqual({ color: "#00f" });
10391

10492
fireEvent(component, "hoverIn");
10593

106-
expect(component.props.style).toStrictEqual([
107-
{ color: "#008000" },
108-
{ color: "#00f" },
109-
]);
94+
expect(component.props.style).toStrictEqual({ color: "#00f" });
11095
});
11196

11297
test("passThrough - inline", () => {

src/compiler/declarations.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,8 @@ export function parseUnparsed(
12281228
builder,
12291229
property,
12301230
);
1231+
case "color-mix":
1232+
return parseColorMix(tokenOrValue.value.arguments, builder, property);
12311233
default: {
12321234
builder.addWarning("value", `${tokenOrValue.value.name}()`);
12331235
return;
@@ -2633,6 +2635,100 @@ export function parseCalcFn(
26332635
return;
26342636
}
26352637

2638+
export function parseColorMix(
2639+
tokens: TokenOrValue[],
2640+
builder: StylesheetBuilder,
2641+
property: string,
2642+
): StyleDescriptor {
2643+
const [inToken, whitespace, colorSpace, comma, ...rest] = tokens;
2644+
if (
2645+
typeof inToken !== "object" ||
2646+
inToken.type !== "token" ||
2647+
inToken.value.type !== "ident" ||
2648+
inToken.value.value !== "in"
2649+
) {
2650+
return;
2651+
}
2652+
2653+
if (
2654+
typeof whitespace !== "object" ||
2655+
whitespace.type !== "token" ||
2656+
whitespace.value.type !== "white-space"
2657+
) {
2658+
return;
2659+
}
2660+
2661+
if (
2662+
typeof comma !== "object" ||
2663+
comma.type !== "token" ||
2664+
comma.value.type !== "comma"
2665+
) {
2666+
return;
2667+
}
2668+
2669+
const colorSpaceArg = parseUnparsed(colorSpace, builder, property);
2670+
if (typeof colorSpaceArg !== "string") {
2671+
return;
2672+
}
2673+
2674+
let nextToken = rest.shift();
2675+
2676+
const leftColorArg = parseUnparsed(nextToken, builder, property);
2677+
2678+
if (!leftColorArg) {
2679+
return;
2680+
}
2681+
2682+
nextToken = rest.shift();
2683+
2684+
let leftColorPercentage: StyleDescriptor | undefined;
2685+
if (nextToken?.type !== "token" || nextToken.value.type !== "comma") {
2686+
leftColorPercentage = parseUnparsed(nextToken, builder, property);
2687+
nextToken = rest.shift();
2688+
}
2689+
2690+
if (
2691+
typeof nextToken !== "object" ||
2692+
nextToken.type !== "token" ||
2693+
nextToken.value.type !== "comma"
2694+
) {
2695+
return;
2696+
}
2697+
2698+
nextToken = rest.shift();
2699+
2700+
const rightColorArg = parseUnparsed(nextToken, builder, property);
2701+
2702+
if (rightColorArg === "transparent") {
2703+
// Ignore the rest, treat as single color with alpha
2704+
return [{}, "colorMix", [colorSpaceArg, leftColorArg, leftColorPercentage]];
2705+
}
2706+
2707+
nextToken = rest.shift();
2708+
let rightColorPercentage: StyleDescriptor | undefined;
2709+
if (nextToken?.type !== "token" || nextToken.value.type !== "comma") {
2710+
rightColorPercentage = parseUnparsed(nextToken, builder, property);
2711+
nextToken = rest.shift();
2712+
}
2713+
2714+
// We should have expired all tokens now
2715+
if (nextToken) {
2716+
return;
2717+
}
2718+
2719+
return [
2720+
{},
2721+
"colorMix",
2722+
[
2723+
colorSpaceArg,
2724+
leftColorArg,
2725+
leftColorPercentage,
2726+
rightColorArg,
2727+
rightColorPercentage,
2728+
],
2729+
];
2730+
}
2731+
26362732
export function parseCalcArguments(
26372733
[...args]: TokenOrValue[],
26382734
builder: StylesheetBuilder,

src/compiler/lightningcss-loader.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-require-imports */
12
export function lightningcssLoader() {
23
let lightningcssPath: string | undefined;
34

@@ -28,11 +29,29 @@ export function lightningcssLoader() {
2829
);
2930
}
3031

31-
// eslint-disable-next-line @typescript-eslint/no-require-imports
3232
const { transform: lightningcss, Features } = require(
3333
lightningcssPath,
3434
) as typeof import("lightningcss");
3535

36+
try {
37+
const lightningcssPackageJSONPath = require.resolve("../../package.json", {
38+
paths: [lightningcssPath],
39+
});
40+
41+
const packageJSON = require(lightningcssPackageJSONPath) as Record<
42+
string,
43+
unknown
44+
>;
45+
46+
if (packageJSON.version === "1.30.2") {
47+
throw new Error(
48+
"[react-native-css] lightningcss version 1.30.2 has a critical bug that breaks compilation. Please pin the version of lightningcss to 1.30.1; or try upgrading.",
49+
);
50+
}
51+
} catch {
52+
// Intentionally left empty
53+
}
54+
3655
return {
3756
lightningcss,
3857
Features,

src/compiler/supports.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ export function supportsConditionValid(condition: SupportsCondition): boolean {
2323
const declarations: Record<string, string[]> = {
2424
// We don't actually support this, but its needed for Tailwind CSS
2525
"-moz-orient": ["inline"],
26+
// Special text used by TailwindCSS. We should probably change this to all color-mix
27+
"color": ["color-mix(in lab, red, red)"],
2628
};

src/native/react/useNativeCss.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,29 +172,36 @@ export function useNativeCss(
172172
export function mappingToConfig(mapping: StyledConfiguration<any>) {
173173
return Object.entries(mapping).flatMap(([key, value]): Config => {
174174
if (value === true) {
175-
return { source: key, target: key };
175+
return {
176+
source: key,
177+
target: key,
178+
};
176179
} else if (value === false) {
177180
return { source: key, target: false };
178181
} else if (typeof value === "string") {
179182
return { source: key, target: value.split(".") };
180183
} else if (typeof value === "object") {
184+
const nativeStyleMapping = value.nativeStyleMapping as
185+
| Record<string, string>
186+
| undefined;
187+
181188
if (Array.isArray(value)) {
182-
return { source: key, target: value };
189+
return { source: key, target: value, nativeStyleMapping };
183190
}
184191

185192
if ("target" in value) {
186193
if (value.target === false) {
187-
return { source: key, target: false };
194+
return { source: key, target: false, nativeStyleMapping };
188195
} else if (typeof value.target === "string") {
189196
const target = value.target.split(".");
190197

191198
if (target.length === 1) {
192-
return { source: key, target: target[0]! };
199+
return { source: key, target: target[0]!, nativeStyleMapping };
193200
} else {
194-
return { source: key, target };
201+
return { source: key, target, nativeStyleMapping };
195202
}
196203
} else if (Array.isArray(value.target)) {
197-
return { source: key, target: value.target };
204+
return { source: key, target: value.target, nativeStyleMapping };
198205
}
199206
}
200207
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
2+
import type { PlainColorObject } from "colorjs.io";
3+
import {
4+
ColorSpace,
5+
to as convert,
6+
mix,
7+
OKLab,
8+
P3,
9+
parse,
10+
sRGB,
11+
type ColorConstructor,
12+
} from "colorjs.io/fn";
13+
14+
import type { StyleFunctionResolver } from "../resolve";
15+
16+
ColorSpace.register(sRGB);
17+
ColorSpace.register(P3);
18+
ColorSpace.register(OKLab);
19+
20+
export const colorMix: StyleFunctionResolver = (resolveValue, value) => {
21+
const args = resolveValue(value[2]);
22+
23+
if (!Array.isArray(args) || args.length < 3) {
24+
return;
25+
}
26+
27+
try {
28+
const space = args.shift();
29+
30+
let left: ColorConstructor | PlainColorObject = parse(
31+
args.shift() as string,
32+
);
33+
34+
let next = args.shift();
35+
36+
if (typeof next === "string" && next.endsWith("%")) {
37+
left.alpha = parseFloat(next) / 100;
38+
next = args.shift();
39+
}
40+
41+
if (next === undefined) {
42+
if (left.spaceId !== "srgb") {
43+
left = convert(left, "srgb");
44+
}
45+
46+
return `rgba(${(left.coords[0] ?? 0) * 255}, ${(left.coords[1] ?? 0) * 255}, ${(left.coords[2] ?? 0) * 255}, ${left.alpha})`;
47+
}
48+
49+
if (typeof next !== "string") {
50+
return;
51+
}
52+
const right = parse(next);
53+
54+
next = args.shift();
55+
if (next && typeof next === "string" && next.endsWith("%")) {
56+
right.alpha = parseFloat(next) / 100;
57+
}
58+
59+
const result = mix(left, right, {
60+
space,
61+
outputSpace: "srgb",
62+
});
63+
64+
return `rgba(${(result.coords[0] ?? 0) * 255}, ${(result.coords[1] ?? 0) * 255}, ${(result.coords[2] ?? 0) * 255}, ${result.alpha})`;
65+
} catch {
66+
return;
67+
}
68+
};

src/native/styles/functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from "./numeric-functions";
55
export * from "./platform-functions";
66
export * from "./string-functions";
77
export * from "./transform-functions";
8+
export * from "./color-mix";

0 commit comments

Comments
 (0)