Skip to content

Commit

Permalink
experimental: autocomplete variables in backgrounds code (#4281)
Browse files Browse the repository at this point in the history
Ref #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)
  • Loading branch information
TrySound authored Oct 14, 2024
1 parent 0e71255 commit fc14133
Show file tree
Hide file tree
Showing 23 changed files with 437 additions and 407 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -44,7 +44,7 @@ export const Section = () => {
onAdd={() => {
addRepeatedStyleItem(
[styleDecl],
parseCssFragment(initialBackdropFilter, "backdropFilter")
parseCssFragment(initialBackdropFilter, ["backdropFilter"])
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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") {
Expand Down Expand Up @@ -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 (
<CssValueInputContainer
property="backgroundImage"
styleSource="default"
getOptions={() => $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);
Expand Down Expand Up @@ -275,16 +215,6 @@ export const BackgroundContent = ({ index }: { index: number }) => {
</FloatingPanelProvider>
</>
)}
{imageGradientToggle === "gradient" && (
<>
<PropertyInlineLabel
label="Gradient"
description={propertyDescriptions.backgroundImage}
properties={["backgroundImage"]}
/>
<GradientControl index={index} />
</>
)}

<PropertyInlineLabel
label="Clip"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import type {
InvalidValue,
LayersValue,
StyleValue,
import {
toValue,
type InvalidValue,
type StyleValue,
} from "@webstudio-is/css-engine";
import { parseCssValue } from "@webstudio-is/css-data";
import {
Flex,
Label,
Text,
TextArea,
textVariants,
theme,
Tooltip,
} from "@webstudio-is/design-system";
import { Flex, Label, Text, theme, Tooltip } from "@webstudio-is/design-system";
import { useEffect, useRef, useState } from "react";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { InfoCircleIcon } from "@webstudio-is/icons";
import { setProperty } from "../../shared/use-style-data";
import { useComputedStyleDecl } from "../../shared/model";
import {
editRepeatedStyleItem,
getRepeatedStyleItem,
setRepeatedStyleItem,
} from "../../shared/repeated-style";
import { parseCssFragment, CssFragmentEditor } from "../../shared/css-fragment";

type IntermediateValue = {
type: "intermediate";
Expand All @@ -33,18 +24,17 @@ const isTransparent = (color: StyleValue) =>
color.type === "keyword" && color.value === "transparent";

export const BackgroundGradient = ({ index }: { index: number }) => {
const textAreaRef = useRef<HTMLTextAreaElement>(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({
Expand All @@ -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;
}
Expand All @@ -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, {
Expand All @@ -107,7 +91,7 @@ export const BackgroundGradient = ({ index }: { index: number }) => {
editRepeatedStyleItem(
[styleDecl],
index,
new Map([["backgroundImage", layers]])
new Map([["backgroundImage", backgroundImage]])
);
};

Expand Down Expand Up @@ -153,15 +137,9 @@ export const BackgroundGradient = ({ index }: { index: number }) => {
</Tooltip>
</Flex>
</Label>
<TextArea
ref={textAreaRef}
css={{ ...textVariants.mono }}
rows={2}
autoGrow
maxRows={4}
name="description"
<CssFragmentEditor
invalid={intermediateValue?.type === "invalid"}
value={textAreaValue ?? ""}
color={intermediateValue?.type === "invalid" ? "error" : undefined}
onChange={handleChange}
onBlur={handleOnComplete}
onKeyDown={(event) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useStore } from "@nanostores/react";
import { $assets } from "~/shared/nano-states";
import type { StyleUpdateOptions } from "../../shared/use-style-data";
import { InfoCircleIcon } from "@webstudio-is/icons";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";
import { useComputedStyleDecl } from "../../shared/model";
import {
getRepeatedStyleItem,
Expand Down Expand Up @@ -90,7 +90,7 @@ export const BackgroundImage = ({ index }: { index: number }) => {
value: value,
});

const parsed = parseCssFragment(value, "background");
const parsed = parseCssFragment(value, ["backgroundImage", "background"]);
const newValue = parsed.get("backgroundImage");

if (newValue === undefined || newValue?.type === "invalid") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
RepeatedStyle,
} from "../../shared/repeated-style";
import { useComputedStyles } from "../../shared/model";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";
import { BackgroundContent } from "./background-content";
import {
getBackgroundLabel,
Expand All @@ -34,7 +34,7 @@ export const Section = () => {
description="Add one or more backgrounds to the instance such as a color, image, or gradient."
properties={properties}
onAdd={() => {
addRepeatedStyleItem(styles, parseCssFragment("none", "background"));
addRepeatedStyleItem(styles, parseCssFragment("none", ["background"]));
}}
>
<Flex gap={1} direction="column">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
editRepeatedStyleItem,
RepeatedStyle,
} from "../../shared/repeated-style";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";

export const properties = ["boxShadow"] satisfies [
StyleProperty,
Expand Down Expand Up @@ -67,7 +67,7 @@ export const Section = () => {
onAdd={() => {
addRepeatedStyleItem(
[styleDecl],
parseCssFragment(initialBoxShadow, "boxShadow")
parseCssFragment(initialBoxShadow, ["boxShadow"])
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
editRepeatedStyleItem,
RepeatedStyle,
} from "../../shared/repeated-style";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";
import { useComputedStyleDecl } from "../../shared/model";
import { humanizeString } from "~/shared/string-utils";

Expand Down Expand Up @@ -44,7 +44,7 @@ export const Section = () => {
onAdd={() => {
addRepeatedStyleItem(
[styleDecl],
parseCssFragment(initialFilter, "filter")
parseCssFragment(initialFilter, ["filter"])
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
editRepeatedStyleItem,
RepeatedStyle,
} from "../../shared/repeated-style";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";
import { useComputedStyleDecl } from "../../shared/model";

export const properties = ["textShadow"] satisfies [
Expand Down Expand Up @@ -54,7 +54,7 @@ export const Section = () => {
onAdd={() => {
addRepeatedStyleItem(
[styleDecl],
parseCssFragment(initialTextShadow, "textShadow")
parseCssFragment(initialTextShadow, ["textShadow"])
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { properties, propertyDescriptions } from "@webstudio-is/css-data";
import type { StyleUpdateOptions } from "../../shared/use-style-data";
import { type IntermediateStyleValue } from "../../shared/css-value-input";
import { CssValueInputContainer } from "../../shared/css-value-input";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";
import { PropertyInlineLabel } from "../../property-label";
import { TransitionProperty } from "./transition-property";
import { TransitionTiming } from "./transition-timing";
Expand Down Expand Up @@ -82,7 +82,7 @@ export const TransitionContent = ({ index }: { index: number }) => {
editRepeatedStyleItem(
styles,
index,
parseCssFragment(intermediateValue.value, "transition")
parseCssFragment(intermediateValue.value, ["transition"])
);
};

Expand Down Expand Up @@ -111,7 +111,7 @@ export const TransitionContent = ({ index }: { index: number }) => {
editRepeatedStyleItem(
styles,
index,
parseCssFragment(shorthand, "transition"),
parseCssFragment(shorthand, ["transition"]),
options
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
type TransitionProperty,
} from "./transition-utils";
import { TransitionContent } from "./transition-content";
import { parseCssFragment } from "../../shared/parse-css-fragment";
import { parseCssFragment } from "../../shared/css-fragment";

export { transitionLongHandProperties as properties };

Expand Down Expand Up @@ -116,10 +116,9 @@ export const Section = () => {
setIsOpen(true);
addRepeatedStyleItem(
styles,
parseCssFragment(
"opacity 200ms ease 0ms normal",
"transition"
)
parseCssFragment("opacity 200ms ease 0ms normal", [
"transition",
])
);
}}
/>
Expand Down
Loading

0 comments on commit fc14133

Please sign in to comment.