From ce1f9026cdf7ee49c8ea8bc0baa0ae6e21f905be Mon Sep 17 00:00:00 2001 From: Jaya Krishna Namburu Date: Wed, 8 May 2024 17:55:03 +0200 Subject: [PATCH] feat: Add support for `text-shadow` property in style-panel (#3318) ## Description This PR adds support for using backdrop-filter property in the style-panel. Here are more details on the property to test it around. https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow ## Steps for reproduction 1. Add an element to the canvas. 2. Click on the "+" icon in the style panel next to the `Text Shadows` section. 3. Edit the default filter that is added. 4. Swap the layers and publish the project. ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [x] made a self-review - [x] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [x] tested locally and on preview environment (preview dev login: 5de6) --- .../backdrop-filter/backdrop-filter.tsx | 9 +- .../sections/box-shadows/box-shadows.tsx | 36 ++- .../sections/filter/filter-content.tsx | 5 +- .../sections/filter/filter-layer.tsx | 1 + .../style-panel/sections/filter/filter.tsx | 7 +- .../features/style-panel/sections/sections.ts | 2 + .../sections/text-shadows/text-shadows.tsx | 111 +++++++++ .../transitions/transition-content.tsx | 19 +- .../shadow-content.tsx} | 218 ++++++++---------- .../shadow-layer.tsx} | 27 ++- .../style-panel/style-layers-list.tsx | 2 + 11 files changed, 286 insertions(+), 151 deletions(-) create mode 100644 apps/builder/app/builder/features/style-panel/sections/text-shadows/text-shadows.tsx rename apps/builder/app/builder/features/style-panel/{sections/box-shadows/box-shadow-content.tsx => shared/shadow-content.tsx} (70%) rename apps/builder/app/builder/features/style-panel/{sections/box-shadows/box-shadow-layer.tsx => shared/shadow-layer.tsx} (83%) 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 17226abed734..6bf2084128c7 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 @@ -91,14 +91,19 @@ export const Section = (props: SectionProps) => { label={label} tooltip={ {label} backdrop-filter Applies graphical effects like blur or color shift to - the area behind an element + the area behind an element, for example: +
+
+ + {INITIAL_BACKDROP_FILTER} +
} diff --git a/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadows.tsx b/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadows.tsx index 6283cb31d6de..ce7456dde83e 100644 --- a/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadows.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadows.tsx @@ -2,8 +2,10 @@ import { SectionTitle, SectionTitleButton, SectionTitleLabel, + Tooltip, + Text, } from "@webstudio-is/design-system"; -import { PlusIcon } from "@webstudio-is/icons"; +import { InfoCircleIcon, PlusIcon } from "@webstudio-is/icons"; import type { LayersValue, StyleProperty, @@ -16,9 +18,9 @@ import { PropertyName } from "../../shared/property-name"; import { getStyleSource } from "../../shared/style-info"; import type { SectionProps } from "../shared/section"; import { LayersList } from "../../style-layers-list"; -import { BoxShadowLayer } from "./box-shadow-layer"; import { addLayer } from "../../style-layer-utils"; import { parseShadow } from "@webstudio-is/css-data"; +import { ShadowLayer } from "../../shared/shadow-layer"; export const properties = ["boxShadow"] satisfies Array; @@ -60,7 +62,7 @@ export const Section = (props: SectionProps) => { } > { property={property} layers={value} {...props} - renderLayer={(layersProps) => ( - - )} + renderLayer={(layersProps) => { + return ( + + Paste a box-shadow CSS code without the property name, + for example: +
+
+ {INITIAL_BOX_SHADOW} + + } + > + +
+ } + /> + ); + }} /> )} diff --git a/apps/builder/app/builder/features/style-panel/sections/filter/filter-content.tsx b/apps/builder/app/builder/features/style-panel/sections/filter/filter-content.tsx index 6ce4df8a61cd..42e683decd84 100644 --- a/apps/builder/app/builder/features/style-panel/sections/filter/filter-content.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/filter/filter-content.tsx @@ -1,6 +1,7 @@ import type { InvalidValue, LayersValue, + StyleProperty, TupleValue, } from "@webstudio-is/css-engine"; import { @@ -18,6 +19,7 @@ import type { DeleteProperty } from "../../shared/use-style-data"; type FilterContentProps = { index: number; filter: string; + property: StyleProperty; onEditLayer: (index: number, layers: LayersValue | TupleValue) => void; deleteProperty: DeleteProperty; tooltip: JSX.Element; @@ -26,6 +28,7 @@ type FilterContentProps = { export const FilterSectionContent = ({ index, filter, + property, onEditLayer, deleteProperty, tooltip, @@ -101,7 +104,7 @@ export const FilterSectionContent = ({ return; } - deleteProperty("filter", { isEphemeral: true }); + deleteProperty(property, { isEphemeral: true }); setIntermediateValue(undefined); event.preventDefault(); } diff --git a/apps/builder/app/builder/features/style-panel/sections/filter/filter-layer.tsx b/apps/builder/app/builder/features/style-panel/sections/filter/filter-layer.tsx index cab1e36dd9c0..2601398babbd 100644 --- a/apps/builder/app/builder/features/style-panel/sections/filter/filter-layer.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/filter/filter-layer.tsx @@ -19,6 +19,7 @@ export const FilterLayer = (props: LayerProps) => { title={label} content={ { label={label} tooltip={ {label} filter Applies graphical effects like blur or color shift to an - element + element, for example: +
+
+ {INITIAL_FILTER}
} diff --git a/apps/builder/app/builder/features/style-panel/sections/sections.ts b/apps/builder/app/builder/features/style-panel/sections/sections.ts index e6a6d657a465..04b1dfbc5edb 100644 --- a/apps/builder/app/builder/features/style-panel/sections/sections.ts +++ b/apps/builder/app/builder/features/style-panel/sections/sections.ts @@ -13,6 +13,7 @@ import * as filter from "./filter/filter"; import * as transitions from "./transitions/transitions"; import * as outline from "./outline/outline"; import * as advanced from "./advanced/advanced"; +import * as textShadows from "./text-shadows/text-shadows"; import * as backdropFilter from "./backdrop-filter/backdrop-filter"; import type { StyleProperty } from "@webstudio-is/css-engine"; import type { SectionProps } from "./shared/section"; @@ -31,6 +32,7 @@ export const sections = new Map< ["size", size], ["position", position], ["typography", typography], + ["textShadows", textShadows], ["backgrounds", backgrounds], ["borders", borders], ["boxShadows", boxShadows], diff --git a/apps/builder/app/builder/features/style-panel/sections/text-shadows/text-shadows.tsx b/apps/builder/app/builder/features/style-panel/sections/text-shadows/text-shadows.tsx new file mode 100644 index 000000000000..80868f201cda --- /dev/null +++ b/apps/builder/app/builder/features/style-panel/sections/text-shadows/text-shadows.tsx @@ -0,0 +1,111 @@ +import { CollapsibleSectionRoot } from "~/builder/shared/collapsible-section"; +import type { SectionProps } from "../shared/section"; +import type { + LayersValue, + StyleProperty, + TupleValue, +} from "@webstudio-is/css-engine"; +import { useState } from "react"; +import { + SectionTitle, + SectionTitleButton, + SectionTitleLabel, + Tooltip, + Text, +} from "@webstudio-is/design-system"; +import { InfoCircleIcon, PlusIcon } from "@webstudio-is/icons"; +import { addLayer } from "../../style-layer-utils"; +import { parseShadow } from "@webstudio-is/css-data"; +import { getDots } from "../../shared/collapsible-section"; +import { PropertyName } from "../../shared/property-name"; +import { getStyleSource } from "../../shared/style-info"; +import { LayersList } from "../../style-layers-list"; +import { ShadowLayer } from "../../shared/shadow-layer"; + +export const properties = ["textShadow"] satisfies Array; + +const property: StyleProperty = properties[0]; +const label = "Text Shadows"; +const INITIAL_TEXT_SHADOW = "0px 2px 5px rgba(0, 0, 0, 0.2)"; + +export const Section = (props: SectionProps) => { + const { currentStyle, createBatchUpdate, deleteProperty } = props; + const [isOpen, setIsOpen] = useState(false); + const value = currentStyle[property]?.value; + const sectionStyleSource = + value?.type === "unparsed" || value?.type === "guaranteedInvalid" + ? undefined + : getStyleSource(currentStyle[property]); + + return ( + } + onClick={() => { + addLayer( + property, + parseShadow("textShadow", INITIAL_TEXT_SHADOW), + currentStyle, + createBatchUpdate + ); + setIsOpen(true); + }} + /> + } + > + + {label} + + } + onReset={() => deleteProperty(property)} + /> + + } + > + {value?.type === "layers" && value.value.length > 0 && ( + + property={property} + layers={value} + {...props} + renderLayer={(layersProps) => ( + + Paste a text-shadow CSS code without the property name, + for example: +
+
+ {INITIAL_TEXT_SHADOW} + + } + > + +
+ } + /> + )} + /> + )} + + ); +}; diff --git a/apps/builder/app/builder/features/style-panel/sections/transitions/transition-content.tsx b/apps/builder/app/builder/features/style-panel/sections/transitions/transition-content.tsx index b75f6a43b47c..cc6074b8acfc 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transitions/transition-content.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transitions/transition-content.tsx @@ -123,6 +123,7 @@ export const TransitionContent = ({ Duration @@ -130,10 +131,8 @@ export const TransitionContent = ({ transition-duration - Sets the length of time a -
- transition animation should take -
to complete. + Sets the length of time a transition animation should take to + complete.
} @@ -161,6 +160,7 @@ export const TransitionContent = ({ Delay @@ -168,9 +168,7 @@ export const TransitionContent = ({ transition-delay - Specify the duration to wait -
- before the transition begins. + Specify the duration to wait before the transition begins.
} @@ -218,14 +216,11 @@ export const TransitionContent = ({ variant="wrapped" content={ - Paste CSS code for a transition -
- or part of a transition, for -
+ Paste CSS code for a transition or part of a transition, for example:

- opacity 200ms ease; + opacity 200ms ease
} > diff --git a/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadow-content.tsx b/apps/builder/app/builder/features/style-panel/shared/shadow-content.tsx similarity index 70% rename from apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadow-content.tsx rename to apps/builder/app/builder/features/style-panel/shared/shadow-content.tsx index 5af08575e298..0794d9187cac 100644 --- a/apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadow-content.tsx +++ b/apps/builder/app/builder/features/style-panel/shared/shadow-content.tsx @@ -3,6 +3,7 @@ import { type InvalidValue, type LayersValue, type RgbValue, + type StyleProperty, type StyleValue, type TupleValue, type UnitValue, @@ -25,17 +26,13 @@ import { ToggleGroupButton, Tooltip, } from "@webstudio-is/design-system"; -import { - InfoCircleIcon, - ShadowInsetIcon, - ShadowNormalIcon, -} from "@webstudio-is/icons"; +import { ShadowInsetIcon, ShadowNormalIcon } from "@webstudio-is/icons"; import { useMemo, useState } from "react"; -import type { IntermediateStyleValue } from "../../shared/css-value-input"; -import { CssValueInputContainer } from "../../shared/css-value-input"; -import { toPascalCase } from "../../shared/keyword-utils"; -import { ColorControl } from "../../controls"; -import type { DeleteProperty, SetProperty } from "../../shared/use-style-data"; +import type { IntermediateStyleValue } from "../shared/css-value-input"; +import { CssValueInputContainer } from "../shared/css-value-input"; +import { toPascalCase } from "../shared/keyword-utils"; +import { ColorControl } from "../controls"; +import type { DeleteProperty, SetProperty } from "../shared/use-style-data"; /* When it comes to checking and validating individual CSS properties for the box-shadow, @@ -61,10 +58,12 @@ import type { DeleteProperty, SetProperty } from "../../shared/use-style-data"; https://www.w3.org/TR/css-backgrounds-3/#propdef-border-top-width */ -type BoxShadowContentProps = { +type ShadowContentProps = { index: number; layer: TupleValue; + property: StyleProperty; shadow: string; + tooltip: JSX.Element; onEditLayer: (index: number, layers: LayersValue) => void; deleteProperty: DeleteProperty; }; @@ -86,13 +85,15 @@ const boxShadowInsetValues = [ { value: "inset", Icon: ShadowInsetIcon }, ] as const; -export const BoxShadowContent = ({ +export const ShadowContent = ({ layer, index, + property, + tooltip, shadow, onEditLayer, deleteProperty, -}: BoxShadowContentProps) => { +}: ShadowContentProps) => { const [intermediateValue, setIntermediateValue] = useState< IntermediateStyleValue | InvalidValue | undefined >(); @@ -161,16 +162,14 @@ export const BoxShadowContent = ({ > X Offset offset-x - Sets the horizontal offset of the -
- shadow. Negative values place -
- the shadow to the left. + Sets the horizontal offset of the shadow. Negative values + place the shadow to the left.
} @@ -197,16 +196,14 @@ export const BoxShadowContent = ({ Blur Radius blur-radius - The larger this value, the bigger -
- the blur, so the shadow becomes -
- bigger and lighter. + The larger this value, the bigger the blur, so the shadow + becomes bigger and lighter.
} @@ -233,15 +230,13 @@ export const BoxShadowContent = ({ Y Offset offset-y - Sets the vertical offset of the -
- shadow. Negative values place -
+ Sets the vertical offset of the shadow. Negative values place the shadow above.
@@ -267,43 +262,41 @@ export const BoxShadowContent = ({ /> - - - Spread Radius - spread-radius - - Positive values will cause the -
- shadow to expand and grow -
- bigger, negative values will cause -
- the shadow to shrink. -
-
- } - > - - - + + Spread Radius + spread-radius + + Positive values will cause the shadow to expand and grow + bigger, negative values will cause the shadow to shrink. + + + } + > + + + handlePropertyChange({ spread: value })} - deleteProperty={() => - handlePropertyChange({ - spread: spread ?? undefined, - }) - } - /> - + property="outlineOffset" + styleSource="local" + keywords={[]} + value={spread ?? { type: "unit", value: 0, unit: "px" }} + setValue={(value) => handlePropertyChange({ spread: value })} + deleteProperty={() => + handlePropertyChange({ + spread: spread ?? undefined, + }) + } + /> + + ) : null} Color @@ -346,47 +340,47 @@ export const BoxShadowContent = ({ /> - - - Inset - inset - - Changes the shadow from -
- an outer shadow (outset) to an -
- inner shadow (inset). -
-
- } - > - - - { - if (value === "inset") { - handlePropertyChange({ - inset: { type: "keyword", value: "inset" }, - }); - } else { - handlePropertyChange({ inset: undefined }); + {property === "boxShadow" ? ( + + + Inset + inset + + Changes the shadow from an outer shadow (outset) to an inner + shadow (inset). + + } - }} - > - {boxShadowInsetValues.map(({ value, Icon }) => ( - - - - - - ))} - - + > + + + { + if (value === "inset") { + handlePropertyChange({ + inset: { type: "keyword", value: "inset" }, + }); + } else { + handlePropertyChange({ inset: undefined }); + } + }} + > + {boxShadowInsetValues.map(({ value, Icon }) => ( + + + + + + ))} + + + ) : null}
@@ -403,23 +397,7 @@ export const BoxShadowContent = ({