Skip to content

Commit 6624fe5

Browse files
committed
feat: filter (#37)
1 parent 5fe1744 commit 6624fe5

File tree

4 files changed

+301
-26
lines changed

4 files changed

+301
-26
lines changed

src/__tests__/vendor/tailwind.test.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,118 @@ test("box-shadow", () => {
111111
testID,
112112
});
113113
});
114+
115+
test("filter", () => {
116+
const compiled = registerCSS(`
117+
:root, :host {
118+
--drop-shadow-md: 0 3px 3px rgb(0 0 0 / 0.12);
119+
}
120+
121+
.brightness-50 {
122+
--tw-brightness: brightness(50%);
123+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
124+
}
125+
126+
.drop-shadow-md {
127+
--tw-drop-shadow-size: drop-shadow(0 3px 3px var(--tw-drop-shadow-color, rgb(0 0 0 / 0.12)));
128+
--tw-drop-shadow: drop-shadow(var(--drop-shadow-md));
129+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
130+
}
131+
`);
132+
133+
expect(compiled).toStrictEqual({
134+
s: [
135+
[
136+
"brightness-50",
137+
[
138+
{
139+
d: [
140+
[
141+
[
142+
[{}, "var", "tw-blur", 1],
143+
[{}, "var", "tw-brightness", 1],
144+
[{}, "var", "tw-contrast", 1],
145+
[{}, "var", "tw-grayscale", 1],
146+
[{}, "var", "tw-hue-rotate", 1],
147+
[{}, "var", "tw-invert", 1],
148+
[{}, "var", "tw-saturate", 1],
149+
[{}, "var", "tw-sepia", 1],
150+
[{}, "var", "tw-drop-shadow", 1],
151+
],
152+
"filter",
153+
],
154+
],
155+
s: [2, 1],
156+
v: [["tw-brightness", [{}, "brightness", "50%"]]],
157+
},
158+
],
159+
],
160+
[
161+
"drop-shadow-md",
162+
[
163+
{
164+
d: [
165+
[
166+
[
167+
[{}, "var", "tw-blur", 1],
168+
[{}, "var", "tw-brightness", 1],
169+
[{}, "var", "tw-contrast", 1],
170+
[{}, "var", "tw-grayscale", 1],
171+
[{}, "var", "tw-hue-rotate", 1],
172+
[{}, "var", "tw-invert", 1],
173+
[{}, "var", "tw-saturate", 1],
174+
[{}, "var", "tw-sepia", 1],
175+
[{}, "var", "tw-drop-shadow", 1],
176+
],
177+
"filter",
178+
],
179+
],
180+
s: [3, 1],
181+
v: [
182+
[
183+
"tw-drop-shadow-size",
184+
[
185+
{},
186+
"drop-shadow",
187+
[
188+
0,
189+
3,
190+
3,
191+
[{}, "var", ["tw-drop-shadow-color", "#0000001f"], 1],
192+
],
193+
],
194+
],
195+
[
196+
"tw-drop-shadow",
197+
[{}, "drop-shadow", [{}, "var", "drop-shadow-md", 1]],
198+
],
199+
],
200+
},
201+
],
202+
],
203+
],
204+
vr: [["drop-shadow-md", [[0, 3, 3, "#0000001f"]]]],
205+
});
206+
207+
render(<View testID={testID} className="brightness-50 drop-shadow-md" />);
208+
const component = screen.getByTestId(testID);
209+
210+
expect(component.type).toBe("View");
211+
expect(component.props).toStrictEqual({
212+
children: undefined,
213+
style: {
214+
filter: [
215+
{ brightness: "50%" },
216+
{
217+
dropShadow: {
218+
blurRadius: 3,
219+
color: "#0000001f",
220+
offsetX: 0,
221+
offsetY: 3,
222+
},
223+
},
224+
],
225+
},
226+
testID,
227+
});
228+
});

src/compiler/declarations.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ const parsers: {
142142
"container-type": parseContainerType,
143143
"display": parseDisplay,
144144
"fill": parseSVGPaint,
145+
"filter": parseFilter,
145146
"flex": parseFlex,
146147
"flex-basis": parseLengthPercentageOrAutoDeclaration,
147148
"flex-direction": ({ value }) => value,
@@ -993,19 +994,28 @@ export function parseUnparsed(
993994
case "translateY":
994995
tokenOrValue.value.name = `@${tokenOrValue.value.name}`;
995996
return unparsedFunction(tokenOrValue, builder);
996-
case "platformColor":
997-
case "pixelSizeForLayoutSize":
998-
case "roundToNearestPixel":
999-
case "pixelScale":
997+
case "brightness":
998+
case "contrast":
999+
case "cubic-bezier":
1000+
case "drop-shadow":
10001001
case "fontScale":
1001-
case "shadow":
1002-
case "rgb":
1003-
case "rgba":
1002+
case "grayscale":
10041003
case "hsl":
10051004
case "hsla":
1005+
case "hue-rotate":
1006+
case "invert":
10061007
case "linear-gradient":
1008+
case "opacity":
1009+
case "pixelScale":
1010+
case "pixelSizeForLayoutSize":
1011+
case "platformColor":
10071012
case "radial-gradient":
1008-
case "cubic-bezier":
1013+
case "rgb":
1014+
case "rgba":
1015+
case "roundToNearestPixel":
1016+
case "saturate":
1017+
case "sepia":
1018+
case "shadow":
10091019
case "steps":
10101020
return unparsedFunction(tokenOrValue, builder);
10111021
case "hairlineWidth":
@@ -2768,6 +2778,40 @@ function parseGradientItem(
27682778
}
27692779
}
27702780

2781+
function parseFilter(
2782+
declaration: DeclarationType<"filter">,
2783+
builder: StylesheetBuilder,
2784+
) {
2785+
if (declaration.value.type === "none") {
2786+
return "unset";
2787+
}
2788+
2789+
return declaration.value.value
2790+
.map((value) => {
2791+
switch (value.type) {
2792+
case "opacity":
2793+
case "blur":
2794+
case "brightness":
2795+
case "contrast":
2796+
case "grayscale":
2797+
case "invert":
2798+
case "saturate":
2799+
case "sepia":
2800+
return {
2801+
[value.type]: parseLength(value.value, builder),
2802+
} as unknown as StyleDescriptor;
2803+
case "hue-rotate":
2804+
return {
2805+
[value.type]: parseAngle(value.value, builder),
2806+
} as unknown as StyleDescriptor;
2807+
case "drop-shadow":
2808+
case "url":
2809+
return;
2810+
}
2811+
})
2812+
.filter((value) => value !== undefined);
2813+
}
2814+
27712815
const namedColors = new Set([
27722816
"aliceblue",
27732817
"antiquewhite",

src/runtime/native/styles/filters.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { isStyleDescriptorArray } from "../../utils";
2+
import type { StyleFunctionResolver } from "./resolve";
3+
import { shorthandHandler } from "./shorthand";
4+
5+
export const blur: StyleFunctionResolver = (resolveValue, value) => {
6+
const args = resolveValue(value[2]);
7+
return {
8+
blur: (Array.isArray(args) ? args[0] : args) as unknown,
9+
};
10+
};
11+
12+
export const brightness: StyleFunctionResolver = (resolveValue, value) => {
13+
const args = resolveValue(value[2]);
14+
return {
15+
brightness: (Array.isArray(args) ? args[0] : args) as unknown,
16+
};
17+
};
18+
19+
export const contrast: StyleFunctionResolver = (resolveValue, value) => {
20+
const args = resolveValue(value[2]);
21+
return {
22+
contrast: (Array.isArray(args) ? args[0] : args) as unknown,
23+
};
24+
};
25+
26+
export const grayscale: StyleFunctionResolver = (resolveValue, value) => {
27+
const args = resolveValue(value[2]);
28+
return {
29+
grayscale: (Array.isArray(args) ? args[0] : args) as unknown,
30+
};
31+
};
32+
33+
export const hueRotate: StyleFunctionResolver = (resolveValue, value) => {
34+
const args = resolveValue(value[2]);
35+
return {
36+
hueRotate: (Array.isArray(args) ? args[0] : args) as unknown,
37+
};
38+
};
39+
40+
export const invert: StyleFunctionResolver = (resolveValue, value) => {
41+
const args = resolveValue(value[2]);
42+
return {
43+
invert: (Array.isArray(args) ? args[0] : args) as unknown,
44+
};
45+
};
46+
47+
export const sepia: StyleFunctionResolver = (resolveValue, value) => {
48+
const args = resolveValue(value[2]);
49+
return {
50+
sepia: (Array.isArray(args) ? args[0] : args) as unknown,
51+
};
52+
};
53+
54+
export const saturate: StyleFunctionResolver = (resolveValue, value) => {
55+
const args = resolveValue(value[2]);
56+
return {
57+
saturate: (Array.isArray(args) ? args[0] : args) as unknown,
58+
};
59+
};
60+
61+
export const opacity: StyleFunctionResolver = (resolveValue, value) => {
62+
const args = resolveValue(value[2]);
63+
return {
64+
hueRotate: (Array.isArray(args) ? args[0] : args) as unknown,
65+
};
66+
};
67+
68+
const color = ["color", "string"] as const;
69+
const offsetX = ["offsetX", "number"] as const;
70+
const offsetY = ["offsetY", "number"] as const;
71+
const blurRadius = ["blurRadius", "number"] as const;
72+
73+
const handler = shorthandHandler(
74+
[
75+
[offsetX, offsetY, blurRadius, color],
76+
[color, offsetX, offsetY],
77+
[color, offsetX, offsetY, blurRadius],
78+
[offsetX, offsetY, color],
79+
[offsetX, offsetY, blurRadius, color],
80+
],
81+
[],
82+
"object",
83+
);
84+
85+
export const dropShadow: StyleFunctionResolver = (
86+
resolveValue,
87+
func,
88+
get,
89+
options,
90+
) => {
91+
const args = resolveValue(func[2]);
92+
93+
return isStyleDescriptorArray(args)
94+
? {
95+
dropShadow: handler(resolveValue, args, get, options),
96+
}
97+
: undefined;
98+
};

src/runtime/native/styles/resolve.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable */
2+
23
import type {
34
InlineVariable,
45
StyleDescriptor,
@@ -12,6 +13,18 @@ import { boxShadow } from "./box-shadow";
1213
import { calc } from "./calc";
1314
import type { calculateProps } from "./calculate-props";
1415
import { transformKeys } from "./defaults";
16+
import {
17+
blur,
18+
brightness,
19+
contrast,
20+
dropShadow,
21+
grayscale,
22+
hueRotate,
23+
invert,
24+
opacity,
25+
saturate,
26+
sepia,
27+
} from "./filters";
1528
import {
1629
fontScale,
1730
hairlineWidth,
@@ -44,31 +57,36 @@ export type StyleResolver = (
4457
options: ResolveValueOptions,
4558
) => unknown;
4659

47-
const shorthands: Record<`@${string}`, StyleFunctionResolver | StyleResolver> =
48-
{
49-
"@animation": animation,
50-
"@textShadow": textShadow,
51-
"@transform": transform,
52-
"@boxShadow": boxShadow,
53-
"@border": border,
54-
};
55-
5660
const functions: Record<string, StyleFunctionResolver> = {
61+
"@animation": animation,
62+
"@border": border,
63+
"@boxShadow": boxShadow,
64+
"@textShadow": textShadow,
65+
"@transform": transform,
66+
"animationName": animation,
67+
"cubic-bezier": timingFunctionResolver,
68+
"steps": timingFunctionResolver,
69+
"hue-rotate": hueRotate,
70+
"drop-shadow": dropShadow,
71+
blur,
72+
brightness,
5773
calc,
74+
contrast,
5875
em,
59-
vw,
60-
vh,
61-
rem,
62-
platformColor,
76+
fontScale,
77+
grayscale,
6378
hairlineWidth,
79+
invert,
80+
opacity,
6481
pixelRatio,
65-
fontScale,
6682
pixelSizeForLayoutSize,
83+
platformColor,
84+
rem,
6785
roundToNearestPixel,
68-
"animationName": animation,
69-
"cubic-bezier": timingFunctionResolver,
70-
"steps": timingFunctionResolver,
71-
...shorthands,
86+
saturate,
87+
sepia,
88+
vh,
89+
vw,
7290
};
7391

7492
export type ResolveValueOptions = {

0 commit comments

Comments
 (0)