Skip to content

Commit

Permalink
experimental: parse transform css property into tuples (#3704)
Browse files Browse the repository at this point in the history
## Description

Parse `transform` value into tuples. We need to use shorthand transform
property for `skew` and `rotate` to support the UX.

- **skew** doesn't have individual property yet. So need to use this as
backup.
- **rotate** doesn support setting individual `x, y, z` properties on
the long hand property like in the design. So, we are can use transforms
as backup and allow to set individual `rotateX`, `rotateY` and `rotateZ`
values.


## Code Review

- [ ] hi @TrySound , I need you to do
  - detailed review (read every line)
  

## Before requesting a review

- [x] made a self-review


## Before merging

- [x] tested locally and on preview environment (preview dev login:
5de6)
- [x] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [x] added tests
  • Loading branch information
JayaKrishnaNamburu authored Jul 11, 2024
1 parent 3c32053 commit 6abceec
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -2744,7 +2744,7 @@ describe("Styles", () => {
outline: 0 none currentColor
}
x {
transform: translate3d(7px,74px,16px)
transform: translate3d(7px, 74px, 16px)
}
x:hover {
background-color: rgba(199, 16, 16, 1)
Expand Down
83 changes: 83 additions & 0 deletions packages/css-data/src/parse-css-value.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,86 @@ test("parse unknown properties as unparsed", () => {
}
);
});

test("parse transform property as tuple", () => {
expect(
parseCssValue("transform", "rotateX(45deg) rotateY(30deg) rotateZ(60deg)")
).toEqual({
type: "tuple",
value: [
{
type: "function",
name: "rotateX",
args: {
type: "tuple",
value: [{ type: "unit", value: 45, unit: "deg" }],
},
},
{
type: "function",
name: "rotateY",
args: {
type: "tuple",
value: [{ type: "unit", value: 30, unit: "deg" }],
},
},
{
type: "function",
name: "rotateZ",
args: {
type: "tuple",
value: [{ type: "unit", value: 60, unit: "deg" }],
},
},
],
});

expect(parseCssValue("transform", "skew(30deg, 20deg)")).toEqual({
type: "tuple",
value: [
{
type: "function",
name: "skew",
args: {
type: "layers",
value: [
{ type: "unit", value: 30, unit: "deg" },
{ type: "unit", value: 20, unit: "deg" },
],
},
},
],
});

expect(
parseCssValue("transform", "translate3d(-100px, 50px, -150px)")
).toEqual({
type: "tuple",
value: [
{
type: "function",
name: "translate3d",
args: {
type: "layers",
value: [
{ type: "unit", value: -100, unit: "px" },
{ type: "unit", value: 50, unit: "px" },
{ type: "unit", value: -150, unit: "px" },
],
},
},
],
});
});

test("parses transform values and returns invalid for invalid values", () => {
expect(parseCssValue("transform", "scale(1.5, 50px)")).toEqual({
type: "invalid",
value: "scale(1.5, 50px)",
});

expect(parseCssValue("transform", "matrix(1, 0.5, -0.5, 1, 100)")).toEqual({
type: "invalid",
value: "matrix(1, 0.5, -0.5, 1, 100)",
});
});
5 changes: 5 additions & 0 deletions packages/css-data/src/parse-css-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { keywordValues } from "./__generated__/keyword-values";
import { units } from "./__generated__/units";
import { parseFilter, parseShadow } from "./property-parsers";
import { parseTransform } from "./property-parsers/transform";

export const cssTryParseValue = (input: string) => {
try {
Expand Down Expand Up @@ -155,6 +156,10 @@ export const parseCssValue = (
return invalidValue;
}

if (property === "transform") {
return parseTransform(input);
}

if (property === "filter" || property === "backdropFilter") {
return parseFilter(property, input);
}
Expand Down
1 change: 1 addition & 0 deletions packages/css-data/src/property-parsers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./transition";
export * from "./transition-property-extractor";
export * from "./shadow-properties-extractor";
export * from "./filter";
export * from "./transform";
95 changes: 95 additions & 0 deletions packages/css-data/src/property-parsers/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as csstree from "css-tree";
import type {
InvalidValue,
LayersValue,
TupleValue,
TupleValueItem,
Unit,
} from "@webstudio-is/css-engine";
import { cssTryParseValue } from "../parse-css-value";

export const parseTransform = (input: string): TupleValue | InvalidValue => {
let tokenStream = input.trim();
tokenStream = tokenStream.endsWith(";")
? tokenStream.slice(0, -1)
: tokenStream;

const cleanupKeywords = ["transform:"];

for (const cleanupKeyword of cleanupKeywords) {
tokenStream = tokenStream.startsWith(cleanupKeyword)
? tokenStream.slice(cleanupKeyword.length).trim()
: tokenStream;
}

const cssAst = cssTryParseValue(tokenStream);
if (cssAst === undefined) {
return {
type: "invalid",
value: input,
};
}

const parsed = csstree.lexer.matchProperty("transform", cssAst);
if (parsed.error) {
return {
type: "invalid",
value: input,
};
}

const transformValue: TupleValueItem[] = [];

csstree.walk(cssAst, (node) => {
if (node.type === "Value") {
for (const child of node.children) {
if (child.type === "Function") {
const isCommaSeparatedArgs = child.children.some(
(arg) => arg.type === "Operator" && arg.value === ","
);

const args: TupleValue | LayersValue = isCommaSeparatedArgs
? { type: "layers", value: [] }
: { type: "tuple", value: [] };

for (const arg of child.children) {
if (arg.type === "Number") {
args.value.push({
type: "unit",
value: Number(arg.value),
unit: "number",
});
}

if (arg.type === "Dimension") {
args.value.push({
type: "unit",
value: Number(arg.value),
unit: arg.unit as Unit,
});
}

if (arg.type === "Percentage") {
args.value.push({
type: "unit",
value: Number(arg.value),
unit: "%",
});
}
}

transformValue.push({
type: "function",
name: child.name,
args,
});
}
}
}
});

return {
type: "tuple",
value: transformValue,
};
};

0 comments on commit 6abceec

Please sign in to comment.