diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.test.ts b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.test.ts index f02878abf456..c3c9b5a754bc 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.test.ts +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect } from "@jest/globals"; import { extractRotatePropertiesFromTransform, extractSkewPropertiesFromTransform, - extractTransformOriginValues, + extractTransformOrPerspectiveOriginValues, } from "./transform-extractors"; import { parseCssValue } from "@webstudio-is/css-data"; import type { TupleValue } from "@webstudio-is/css-engine"; @@ -108,10 +108,10 @@ describe("extractSkewPropertiesFromTransform", () => { }); }); -describe("extractTransformOriginValues", () => { +describe("extractTransformOrPerspectiveOriginValues", () => { test("parses transform-origin and returns the individual properties from the value", () => { expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "center") as TupleValue ) ).toEqual({ @@ -121,7 +121,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "top") as TupleValue ) ).toEqual({ @@ -131,7 +131,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "right") as TupleValue ) ).toEqual({ @@ -141,7 +141,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "45px") as TupleValue ) ).toEqual({ @@ -151,7 +151,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "20px 40px") as TupleValue ) ).toEqual({ @@ -161,7 +161,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "10px 20px 30px") as TupleValue ) ).toEqual({ @@ -171,7 +171,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "left top 30px") as TupleValue ) ).toEqual({ @@ -181,7 +181,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "bottom right 60px") as TupleValue ) ).toEqual({ @@ -191,7 +191,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "left 50% 60px") as TupleValue ) ).toEqual({ @@ -201,7 +201,7 @@ describe("extractTransformOriginValues", () => { }); expect( - extractTransformOriginValues( + extractTransformOrPerspectiveOriginValues( parseCssValue("transformOrigin", "50% bottom 60px") as TupleValue ) ).toEqual({ diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.ts b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.ts index 8a1f50061e7b..28da7b5d16f8 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.ts +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-extractors.ts @@ -59,7 +59,11 @@ const isValidTransformOriginValue = ( return value.type === "unit" || value.type === "keyword"; }; -export const extractTransformOriginValues = ( +// https://developer.mozilla.org/en-US/docs/Web/CSS/perspective-origin#syntax +// https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin#syntax +// Both transform and perspective origin shares the same syntax for their values. +// The only difference is `transform-origin` can have a 3rd value for z-axis. +export const extractTransformOrPerspectiveOriginValues = ( value: TupleValue ): { x: KeywordValue | UnitValue; @@ -71,7 +75,6 @@ export const extractTransformOriginValues = ( let z: UnitValue | undefined; // https://www.w3.org/TR/css-transforms-1/#transform-origin-property - // https://github.com/mdn/content/issues/35411 // If only one value is specified, the second value is assumed to be center. if (value.value.length === 1 && value.value[0].type === "unit") { x = value.value[0]; diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-origin.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-origin.tsx index 9c2c9fdb8bc6..c575eb660356 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-origin.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-origin.tsx @@ -16,7 +16,7 @@ import { import { PropertyName } from "../../shared/property-name"; import { styleConfigByName } from "../../shared/configs"; import { useMemo } from "react"; -import { extractTransformOriginValues } from "./transform-extractors"; +import { extractTransformOrPerspectiveOriginValues } from "./transform-extractors"; import { CssValueInputContainer } from "../../shared/css-value-input"; import type { StyleUpdateOptions } from "../../shared/use-style-data"; @@ -62,7 +62,7 @@ export const TransformOrigin = (props: SectionProps) => { return; } - return extractTransformOriginValues(value); + return extractTransformOrPerspectiveOriginValues(value); }, [value]); const xInfo = useMemo(() => calculateBackgroundPosition(origin?.x), [origin]); diff --git a/packages/css-data/src/parse-css-value.test.ts b/packages/css-data/src/parse-css-value.test.ts index f4cff2dbf536..4492ba09aa37 100644 --- a/packages/css-data/src/parse-css-value.test.ts +++ b/packages/css-data/src/parse-css-value.test.ts @@ -957,3 +957,44 @@ test("parse transform-origin", () => { value: "top left right", }); }); + +test("parse perspective-origin", () => { + expect(parseCssValue("perspectiveOrigin", "center")).toEqual({ + type: "tuple", + value: [{ type: "keyword", value: "center" }], + }); + + expect(parseCssValue("perspectiveOrigin", "bottom right")).toEqual({ + type: "tuple", + value: [ + { type: "keyword", value: "bottom" }, + { type: "keyword", value: "right" }, + ], + }); + + expect(parseCssValue("perspectiveOrigin", "bottom 55%")).toEqual({ + type: "invalid", + value: "bottom 55%", + }); + + expect(parseCssValue("perspectiveOrigin", "75% bottom")).toEqual({ + type: "tuple", + value: [ + { type: "unit", value: 75, unit: "%" }, + { type: "keyword", value: "bottom" }, + ], + }); + + expect(parseCssValue("perspectiveOrigin", "-175%")).toEqual({ + type: "tuple", + value: [{ type: "unit", value: -175, unit: "%" }], + }); + + expect(parseCssValue("perspectiveOrigin", "50% 50%")).toEqual({ + type: "tuple", + value: [ + { type: "unit", value: 50, unit: "%" }, + { type: "unit", value: 50, unit: "%" }, + ], + }); +}); diff --git a/packages/css-data/src/parse-css-value.ts b/packages/css-data/src/parse-css-value.ts index 3dafcdc007a6..0c1228a06c15 100644 --- a/packages/css-data/src/parse-css-value.ts +++ b/packages/css-data/src/parse-css-value.ts @@ -144,6 +144,7 @@ const tupleProps = new Set([ "filter", "backdropFilter", "transformOrigin", + "perspectiveOrigin", ]); const availableUnits = new Set(Object.values(units).flat());