From fc14133f281f7e645150a5e0854e650b722dfca7 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Mon, 14 Oct 2024 17:34:51 +0300 Subject: [PATCH] experimental: autocomplete variables in backgrounds code (#4281) Ref https://github.com/webstudio-is/webstudio/issues/3399 Using dedicated inputs to autocomplete variables makes UI more complex. Though we already have "Code" fields which can be used for the same purpose. Here added css fragment editor to background gradient. ![Screenshot 2024-10-14 at 15 56 54](https://github.com/user-attachments/assets/2c41f3e7-9f79-46c3-92be-870f69ac515d) --- .../backdrop-filter/backdrop-filter.tsx | 4 +- .../backgrounds/background-content.tsx | 74 +---------- .../backgrounds/background-gradient.tsx | 66 ++++------ .../sections/backgrounds/background-image.tsx | 4 +- .../sections/backgrounds/backgrounds.tsx | 4 +- .../sections/box-shadows/box-shadows.tsx | 4 +- .../style-panel/sections/filter/filter.tsx | 4 +- .../sections/text-shadows/text-shadows.tsx | 4 +- .../transitions/transition-content.tsx | 6 +- .../sections/transitions/transitions.tsx | 9 +- .../style-panel/shared/css-fragment.test.ts | 75 ++++++++++++ .../style-panel/shared/css-fragment.tsx | 109 +++++++++++++++++ .../style-panel/shared/filter-content.tsx | 4 +- .../shared/parse-css-fragment.test.ts | 75 ------------ .../style-panel/shared/parse-css-fragment.ts | 14 --- .../style-panel/shared/repeated-style.test.ts | 115 +++++++++++++----- .../style-panel/shared/repeated-style.tsx | 41 +++++-- .../style-panel/shared/shadow-content.tsx | 9 +- .../app/builder/shared/code-editor-base.tsx | 47 +++++++ .../app/builder/shared/code-editor.tsx | 52 +------- .../app/builder/shared/expression-editor.tsx | 52 +------- apps/builder/package.json | 1 + pnpm-lock.yaml | 71 ++++++----- 23 files changed, 437 insertions(+), 407 deletions(-) create mode 100644 apps/builder/app/builder/features/style-panel/shared/css-fragment.test.ts create mode 100644 apps/builder/app/builder/features/style-panel/shared/css-fragment.tsx delete mode 100644 apps/builder/app/builder/features/style-panel/shared/parse-css-fragment.test.ts delete mode 100644 apps/builder/app/builder/features/style-panel/shared/parse-css-fragment.ts diff --git a/apps/builder/app/builder/features/style-panel/sections/backdrop-filter/backdrop-filter.tsx b/apps/builder/app/builder/features/style-panel/sections/backdrop-filter/backdrop-filter.tsx index 7efe65f88c18..64eba8fc50b7 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backdrop-filter/backdrop-filter.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backdrop-filter/backdrop-filter.tsx @@ -8,7 +8,7 @@ import { InfoCircleIcon } from "@webstudio-is/icons"; import { humanizeString } from "~/shared/string-utils"; import { RepeatedStyleSection } from "../../shared/style-section"; import { FilterSectionContent } from "../../shared/filter-content"; -import { parseCssFragment } from "../../shared/parse-css-fragment"; +import { parseCssFragment } from "../../shared/css-fragment"; import { addRepeatedStyleItem, editRepeatedStyleItem, @@ -44,7 +44,7 @@ export const Section = () => { onAdd={() => { addRepeatedStyleItem( [styleDecl], - parseCssFragment(initialBackdropFilter, "backdropFilter") + parseCssFragment(initialBackdropFilter, ["backdropFilter"]) ); }} > diff --git a/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.tsx b/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.tsx index ea72a517442c..9cdc0d4ad21b 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backgrounds/background-content.tsx @@ -11,13 +11,7 @@ import { RepeatRowIcon, CrossSmallIcon, } from "@webstudio-is/icons"; -import { - LayersValue, - type StyleProperty, - type StyleValue, - toValue, - UnparsedValue, -} from "@webstudio-is/css-engine"; +import { type StyleValue, toValue } from "@webstudio-is/css-engine"; import { theme, Flex, @@ -35,17 +29,11 @@ import { BackgroundImage } from "./background-image"; import { BackgroundPosition } from "./background-position"; import { PropertyInlineLabel } from "../../property-label"; import { ToggleGroupTooltip } from "../../controls/toggle-group/toggle-group-control"; -import { $availableVariables, useComputedStyleDecl } from "../../shared/model"; +import { useComputedStyleDecl } from "../../shared/model"; import { getRepeatedStyleItem, setRepeatedStyleItem, } from "../../shared/repeated-style"; -import { CssValueInputContainer } from "../../shared/css-value-input"; -import { - setProperty, - type StyleUpdateOptions, -} from "../../shared/use-style-data"; -import type { ComputedStyleDecl } from "~/shared/style-object-model"; const detectImageOrGradientToggle = (styleValue?: StyleValue) => { if (styleValue?.type === "image") { @@ -73,54 +61,6 @@ const Spacer = styled("div", { height: theme.spacing[5], }); -const setGradientProperty = ( - styleDecl: ComputedStyleDecl, - index: number, - newItem: StyleValue, - options?: StyleUpdateOptions -) => { - const property = styleDecl.property as StyleProperty; - let items: StyleValue[] = []; - if (styleDecl.cascadedValue.type === "var") { - items = [styleDecl.cascadedValue]; - } - if (styleDecl.cascadedValue.type === "layers") { - items = styleDecl.cascadedValue.value; - } - const unpackedItem = newItem.type === "layers" ? newItem.value[0] : newItem; - if (items.length === 1 && unpackedItem.type === "var") { - setProperty(property)(unpackedItem, options); - } else { - const newValue = { type: "layers", value: items } as LayersValue; - newValue.value[index] = newItem as UnparsedValue; - setProperty(property)(newValue, options); - } -}; - -const GradientControl = ({ index }: { index: number }) => { - const styleDecl = useComputedStyleDecl("backgroundImage"); - const value = - styleDecl.cascadedValue.type === "var" - ? styleDecl.cascadedValue - : getRepeatedStyleItem(styleDecl, index); - return ( - $availableVariables.get()} - value={value} - setValue={(newValue, options) => { - setGradientProperty(styleDecl, index, newValue, options); - }} - deleteProperty={() => { - if (value) { - setGradientProperty(styleDecl, index, value); - } - }} - /> - ); -}; - const BackgroundRepeat = ({ index }: { index: number }) => { const styleDecl = useComputedStyleDecl("backgroundRepeat"); const value = getRepeatedStyleItem(styleDecl, index); @@ -275,16 +215,6 @@ export const BackgroundContent = ({ index }: { index: number }) => { )} - {imageGradientToggle === "gradient" && ( - <> - - - - )} color.type === "keyword" && color.value === "transparent"; export const BackgroundGradient = ({ index }: { index: number }) => { - const textAreaRef = useRef(null); - const property = "backgroundImage"; - const styleDecl = useComputedStyleDecl(property); - const styleValue = getRepeatedStyleItem(styleDecl, index); + const styleDecl = useComputedStyleDecl("backgroundImage"); + let styleValue = styleDecl.cascadedValue; + if (styleValue.type === "layers") { + styleValue = styleValue.value[index]; + } const [intermediateValue, setIntermediateValue] = useState< IntermediateValue | InvalidValue | undefined >(undefined); - const textAreaValue = - intermediateValue?.value ?? - (styleValue?.type === "unparsed" ? styleValue.value : undefined); + const textAreaValue = intermediateValue?.value ?? toValue(styleValue); const handleChange = (value: string) => { setIntermediateValue({ @@ -55,9 +45,9 @@ export const BackgroundGradient = ({ index }: { index: number }) => { // This doesn't have the same behavior as CssValueInput. // However, it's great to see the immediate results when making gradient changes, // especially until we have a better gradient tool. - const newValue = parseCssValue(property, value); + const newValue = parseCssValue("backgroundImage", value); - if (newValue.type === "unparsed") { + if (newValue.type === "unparsed" || newValue.type === "var") { setRepeatedStyleItem(styleDecl, index, newValue, { isEphemeral: true }); return; } @@ -76,21 +66,15 @@ export const BackgroundGradient = ({ index }: { index: number }) => { return; } - const parsed = parseCssFragment(intermediateValue.value, "background"); + const parsed = parseCssFragment(intermediateValue.value, [ + "backgroundImage", + "background", + ]); const backgroundImage = parsed.get("backgroundImage"); const backgroundColor = parsed.get("backgroundColor"); - const layers: LayersValue = - backgroundImage?.type === "layers" - ? backgroundImage - : { type: "layers", value: [] }; - const [firstLayer] = layers.value; // set invalid state - if ( - backgroundColor?.type === "invalid" || - layers.value.length === 0 || - firstLayer.type === "invalid" - ) { + if (backgroundColor?.type === "invalid" || backgroundImage === undefined) { setIntermediateValue({ type: "invalid", value: intermediateValue.value }); if (styleValue) { setRepeatedStyleItem(styleDecl, index, styleValue, { @@ -107,7 +91,7 @@ export const BackgroundGradient = ({ index }: { index: number }) => { editRepeatedStyleItem( [styleDecl], index, - new Map([["backgroundImage", layers]]) + new Map([["backgroundImage", backgroundImage]]) ); }; @@ -153,15 +137,9 @@ export const BackgroundGradient = ({ index }: { index: number }) => { -