From 71efee6193822183e9a37b9c1df742572fea8432 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 16 Feb 2025 10:50:35 +0000 Subject: [PATCH 01/13] accidentally commited --- .../style-panel/shared/css-value-input/css-value-input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/shared/css-value-input/css-value-input.tsx b/apps/builder/app/builder/features/style-panel/shared/css-value-input/css-value-input.tsx index cd9b120c8a67..1d4805b86f80 100644 --- a/apps/builder/app/builder/features/style-panel/shared/css-value-input/css-value-input.tsx +++ b/apps/builder/app/builder/features/style-panel/shared/css-value-input/css-value-input.tsx @@ -820,7 +820,7 @@ export const CssValueInput = ({ // - allows to close the menu // - prevents baspace from deleting the value AFTER its already reseted to default, e.g. we get "aut" instead of "auto" event.preventDefault(); - //closeMenu(); + closeMenu(); onReset(); } }; From 042a4f673b94775dd18d89e0a5f2d3d752a50a5b Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 16 Feb 2025 12:06:22 +0000 Subject: [PATCH 02/13] drive-by fix for better interface --- .../features/navigator/css-preview.tsx | 2 +- .../sections/advanced/copy-paste-menu.tsx | 4 ++-- packages/css-engine/src/core/rules.ts | 22 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/builder/app/builder/features/navigator/css-preview.tsx b/apps/builder/app/builder/features/navigator/css-preview.tsx index d1afaf3cfff7..023e5ab41b7e 100644 --- a/apps/builder/app/builder/features/navigator/css-preview.tsx +++ b/apps/builder/app/builder/features/navigator/css-preview.tsx @@ -65,7 +65,7 @@ const getCssText = ( return; } result.push(`/* ${comment} */`); - result.push(generateStyleMap({ style: mergeStyles(style) })); + result.push(generateStyleMap(mergeStyles(style))); }; add("Style Sources", sourceStyles); diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/copy-paste-menu.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/copy-paste-menu.tsx index 99bf799797b6..3c27bfd521ff 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/copy-paste-menu.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/copy-paste-menu.tsx @@ -45,7 +45,7 @@ export const CopyPasteMenu = ({ } } - const css = generateStyleMap({ style: mergeStyles(currentStyleMap) }); + const css = generateStyleMap(mergeStyles(currentStyleMap)); navigator.clipboard.writeText(css); }; @@ -59,7 +59,7 @@ export const CopyPasteMenu = ({ return; } const style = new Map([[property, value]]); - const css = generateStyleMap({ style }); + const css = generateStyleMap(style); navigator.clipboard.writeText(css); }; diff --git a/packages/css-engine/src/core/rules.ts b/packages/css-engine/src/core/rules.ts index 93e938324914..06143315476b 100644 --- a/packages/css-engine/src/core/rules.ts +++ b/packages/css-engine/src/core/rules.ts @@ -52,15 +52,16 @@ const mergeDeclarations = (declarations: Iterable) => { export type StyleMap = Map; -export const generateStyleMap = ({ - style, - indent = 0, - transformValue, -}: { - style: StyleMap; - indent?: number; - transformValue?: TransformValue; -}) => { +export const generateStyleMap = ( + style: StyleMap, + { + indent = 0, + transformValue, + }: { + indent?: number; + transformValue?: TransformValue; + } = {} +) => { const spaces = " ".repeat(indent); let lines = ""; for (const [property, value] of style) { @@ -269,8 +270,7 @@ export class NestingRule { leftSelector.localeCompare(rightSelector) ) .map(([selector, style]) => { - const content = generateStyleMap({ - style: prefixStyles(style), + const content = generateStyleMap(prefixStyles(style), { indent: indent + 2, transformValue, }); From d2e0468107a88eca9da738cf1e642968c79a4e39 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 16 Feb 2025 14:28:03 +0000 Subject: [PATCH 03/13] ignore blur on dropdown click --- .../sections/advanced/add-styles-input.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx index c057a88f038f..9cd044d385cb 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx @@ -1,5 +1,11 @@ import { lexer } from "css-tree"; -import { forwardRef, useRef, useState, type KeyboardEvent } from "react"; +import { + forwardRef, + useRef, + useState, + type FocusEvent, + type KeyboardEvent, +} from "react"; import { matchSorter } from "match-sorter"; import { Box, @@ -207,6 +213,17 @@ export const AddStylesInput = forwardRef< handleDelete, ]); + const handleBlur = composeEventHandlers([ + inputProps.onBlur, + () => { + // When user clicks on a combobox item, input will receive blur event, + // but we don't want that to be handled upstream because input may get hidden without click getting handled. + if (combobox.isOpen === false) { + onBlur(); + } + }, + ]); + return (
@@ -215,10 +232,7 @@ export const AddStylesInput = forwardRef< {...inputProps} autoFocus onFocus={onFocus} - onBlur={(event) => { - inputProps.onBlur(event); - onBlur(); - }} + onBlur={handleBlur} inputRef={forwardedRef} onKeyDown={handleKeyDown} placeholder="Add styles" From b54f095a494c1c6a21584ba55d31db6a2d8779c5 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 16 Feb 2025 18:40:57 +0000 Subject: [PATCH 04/13] rewrite parseing and add tests --- ...d-styles-input.tsx => add-style-input.tsx} | 40 +++---- .../sections/advanced/advanced.tsx | 31 +++--- .../advanced/parse-style-input.test.ts | 102 ++++++++++++++++++ .../sections/advanced/parse-style-input.ts | 56 ++++++++++ 4 files changed, 185 insertions(+), 44 deletions(-) rename apps/builder/app/builder/features/style-panel/sections/advanced/{add-styles-input.tsx => add-style-input.tsx} (90%) create mode 100644 apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts create mode 100644 apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx similarity index 90% rename from apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx rename to apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx index 9cd044d385cb..f5e9bb80651e 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-styles-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx @@ -1,11 +1,4 @@ -import { lexer } from "css-tree"; -import { - forwardRef, - useRef, - useState, - type FocusEvent, - type KeyboardEvent, -} from "react"; +import { forwardRef, useRef, useState, type KeyboardEvent } from "react"; import { matchSorter } from "match-sorter"; import { Box, @@ -30,11 +23,13 @@ import { } from "@webstudio-is/css-data"; import { cssWideKeywords, + generateStyleMap, hyphenateProperty, type StyleProperty, } from "@webstudio-is/css-engine"; import { deleteProperty, setProperty } from "../../shared/use-style-data"; import { composeEventHandlers } from "~/shared/event-utils"; +import { parseStyleInput } from "./parse-style-input"; type SearchItem = { property: string; label: string; value?: string }; @@ -94,25 +89,14 @@ const matchOrSuggestToCreate = ( // Limit the array to 100 elements matched.length = Math.min(matched.length, 100); - const property = search.trim(); - if ( - property.startsWith("--") && - lexer.match("", property).matched - ) { - matched.unshift({ - property, - label: `Create "${property}"`, - }); - } - // When there is no match we suggest to create a custom property. - if ( - matched.length === 0 && - lexer.match("", `--${property}`).matched - ) { - matched.unshift({ - property: `--${property}`, - label: `--${property}: unset;`, - }); + if (matched.length === 0) { + const parsedStyles = parseStyleInput(search); + for (const style of parsedStyles) { + matched.push({ + property: style.property, + label: `Create "${generateStyleMap(new Map([[style.property, style.value]]))}"`, + }); + } } return matched; @@ -128,7 +112,7 @@ const matchOrSuggestToCreate = ( * paste css declarations * */ -export const AddStylesInput = forwardRef< +export const AddStyleInput = forwardRef< HTMLInputElement, { onClose: () => void; diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/advanced.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/advanced.tsx index d64ba9a95bf1..ca01dc6b7e72 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/advanced.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/advanced.tsx @@ -26,7 +26,7 @@ import { theme, Tooltip, } from "@webstudio-is/design-system"; -import { parseCss, propertyDescriptions } from "@webstudio-is/css-data"; +import { propertyDescriptions } from "@webstudio-is/css-data"; import { hyphenateProperty, toValue, @@ -55,7 +55,8 @@ import { useClientSupports } from "~/shared/client-supports"; import { CopyPasteMenu, propertyContainerAttribute } from "./copy-paste-menu"; import { $advancedStyles } from "./stores"; import { $settings } from "~/builder/shared/client-settings"; -import { AddStylesInput } from "./add-styles-input"; +import { AddStyleInput } from "./add-style-input"; +import { parseStyleInput } from "./parse-style-input"; // Only here to keep the same section module interface export const properties = []; @@ -97,13 +98,8 @@ const AdvancedStyleSection = (props: { ); }; -const insertStyles = (text: string) => { - let parsedStyles = parseCss(`selector{${text}}`); - if (parsedStyles.length === 0) { - // Try a single property without a value. - parsedStyles = parseCss(`selector{${text}: unset}`); - } - +const insertStyles = (css: string) => { + const parsedStyles = parseStyleInput(css); if (parsedStyles.length === 0) { return []; } @@ -386,9 +382,10 @@ export const Section = () => { setRecentProperties( Array.from(new Set([...recentProperties, ...insertedProperties])) ); + return styles; }; - const handleShowAddStylesInput = () => { + const handleShowAddStyleInput = () => { setIsAdding(true); // User can click twice on the add button, so we need to focus the input on the second click after autoFocus isn't working. addPropertyInputRef.current?.focus(); @@ -434,7 +431,7 @@ export const Section = () => { { autoFocus={isLast} onChangeComplete={(event) => { if (event.type === "enter") { - handleShowAddStylesInput(); + handleShowAddStyleInput(); } }} onReset={() => { @@ -482,15 +479,17 @@ export const Section = () => { { overflow: "hidden", height: 0 } } > - { - setIsAdding(false); - handleInsertStyles(cssText); + const styles = handleInsertStyles(cssText); + if (styles.length > 0) { + setIsAdding(false); + } }} onClose={handleAbortAddStyles} onFocus={() => { if (isAdding === false) { - handleShowAddStylesInput(); + handleShowAddStyleInput(); } }} onBlur={() => { diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts new file mode 100644 index 000000000000..61722c08b40c --- /dev/null +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts @@ -0,0 +1,102 @@ +import { describe, test, expect } from "vitest"; +import { parseStyleInput } from "./parse-style-input"; + +describe("parseStyleInput", () => { + test("parses custom property", () => { + const result = parseStyleInput("--custom-color"); + expect(result).toEqual([ + { + selector: "selector", + property: "--custom-color", + value: { type: "unset", value: "" }, + }, + ]); + }); + + test("parses regular property", () => { + const result = parseStyleInput("color"); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "unset", value: "" }, + }, + ]); + }); + + test("trims whitespace", () => { + const result = parseStyleInput(" color "); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "unset", value: "" }, + }, + ]); + }); + + test("handles invalid regular property", () => { + const result = parseStyleInput("notapro perty"); + expect(result).toEqual([]); + }); + + test("converts unknown property to custom property assuming user forgot to add --", () => { + const result = parseStyleInput("notaproperty"); + expect(result).toEqual([ + { + selector: "selector", + property: "--notaproperty", + value: { type: "unset", value: "" }, + }, + ]); + }); + + test("parses single property-value pair", () => { + const result = parseStyleInput("color: red"); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "keyword", value: "red" }, + }, + ]); + }); + + test("parses multiple property-value pairs", () => { + const result = parseStyleInput("color: red; display: block"); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "keyword", value: "red" }, + }, + { + selector: "selector", + property: "display", + value: { type: "keyword", value: "block" }, + }, + ]); + }); + + test("parses custom property with value", () => { + const result = parseStyleInput("--custom-color: red"); + expect(result).toEqual([ + { + selector: "selector", + property: "--custom-color", + value: { type: "unparsed", value: "red" }, + }, + ]); + }); + + test("handles malformed style block", () => { + const result = parseStyleInput("color: red; invalid;"); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "keyword", value: "red" }, + }, + ]); + }); +}); diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts new file mode 100644 index 000000000000..fb16476ce61e --- /dev/null +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts @@ -0,0 +1,56 @@ +import { + properties, + parseCss, + type ParsedStyleDecl, +} from "@webstudio-is/css-data"; +import { type StyleProperty } from "@webstudio-is/css-engine"; +import { camelCase } from "change-case"; +import { lexer } from "css-tree"; + +/** + * Does several attempts to parse: + * - Custom property "--foo" + * - Known regular property "color" + * - Custom property without -- (user forgot to add) + * - Custom property and value: --foo: red + * - Property and value: color: red + * - Multiple properties: color: red; background: blue + */ +export const parseStyleInput = (css: string): Array => { + css = css.trim(); + // Is it a custom property "--foo"? + if (css.startsWith("--") && lexer.match("", css).matched) { + return [ + { + selector: "selector", + property: css as StyleProperty, + value: { type: "unset", value: "" }, + }, + ]; + } + + // Is it a known regular property? + const camelCasedProperty = camelCase(css); + if (camelCasedProperty in properties) { + return [ + { + selector: "selector", + property: css as StyleProperty, + value: { type: "unset", value: "" }, + }, + ]; + } + + // Is it a custom property "--foo"? + if (lexer.match("", `--${css}`).matched) { + return [ + { + selector: "selector", + property: `--${css}`, + value: { type: "unset", value: "" }, + }, + ]; + } + + return parseCss(`selector{${css}}`); +}; From 3834fd7a83781ed018ea2934a57ee6c59fafe8aa Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 17 Feb 2025 14:04:08 +0000 Subject: [PATCH 05/13] prefix unknown property with -- when user pasted somethingunknown: red --- .../sections/advanced/parse-style-input.test.ts | 16 ++++++++++++++++ .../sections/advanced/parse-style-input.ts | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts index 61722c08b40c..7922b5fa1881 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts @@ -78,6 +78,22 @@ describe("parseStyleInput", () => { ]); }); + test("parses multiple property-value pairs, one is invalid", () => { + const result = parseStyleInput("color: red; somethinginvalid: block"); + expect(result).toEqual([ + { + selector: "selector", + property: "color", + value: { type: "keyword", value: "red" }, + }, + { + selector: "selector", + property: "--somethinginvalid", + value: { type: "unparsed", value: "block" }, + }, + ]); + }); + test("parses custom property with value", () => { const result = parseStyleInput("--custom-color: red"); expect(result).toEqual([ diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts index fb16476ce61e..7c6b91506a44 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts @@ -6,6 +6,7 @@ import { import { type StyleProperty } from "@webstudio-is/css-engine"; import { camelCase } from "change-case"; import { lexer } from "css-tree"; +import { styleConfigByName } from "../../shared/configs"; /** * Does several attempts to parse: @@ -52,5 +53,17 @@ export const parseStyleInput = (css: string): Array => { ]; } - return parseCss(`selector{${css}}`); + const styles = parseCss(`selector{${css}}`); + + for (const style of styles) { + // somethingunknown: red; -> --somethingunknown: red; + if ( + style.value.type === "unparsed" && + style.property.startsWith("--") === false + ) { + style.property = `--${style.property}`; + } + } + + return styles; }; From 0d3b819cd1870160e2a59ad6b8b3067744634339 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 17 Feb 2025 14:04:17 +0000 Subject: [PATCH 06/13] lint --- .../features/style-panel/sections/advanced/parse-style-input.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts index 7c6b91506a44..0f908b9eae0c 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts @@ -6,7 +6,6 @@ import { import { type StyleProperty } from "@webstudio-is/css-engine"; import { camelCase } from "change-case"; import { lexer } from "css-tree"; -import { styleConfigByName } from "../../shared/configs"; /** * Does several attempts to parse: From 4a7d3b4069bee5d6783374d8aefaa0ce4f23fa9f Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 17 Feb 2025 15:19:50 +0000 Subject: [PATCH 07/13] fix selecting from dropdown entry with value --- .../features/style-panel/sections/advanced/add-style-input.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx index f5e9bb80651e..93d5773a39fe 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx @@ -25,6 +25,7 @@ import { cssWideKeywords, generateStyleMap, hyphenateProperty, + toValue, type StyleProperty, } from "@webstudio-is/css-engine"; import { deleteProperty, setProperty } from "../../shared/use-style-data"; @@ -94,6 +95,7 @@ const matchOrSuggestToCreate = ( for (const style of parsedStyles) { matched.push({ property: style.property, + value: toValue(style.value), label: `Create "${generateStyleMap(new Map([[style.property, style.value]]))}"`, }); } From da66e02e5ed1634ada13d7e53ea2668aad1f50a4 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 17 Feb 2025 20:08:28 +0000 Subject: [PATCH 08/13] tests --- .../style-panel/sections/advanced/parse-style-input.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts index 7922b5fa1881..075f9e8a3a06 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.test.ts @@ -35,7 +35,7 @@ describe("parseStyleInput", () => { ]); }); - test("handles invalid regular property", () => { + test("handles unparsable regular property", () => { const result = parseStyleInput("notapro perty"); expect(result).toEqual([]); }); From 8d06a1d519fabadabb6dce4940ed6a8f6a13b113 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 18 Feb 2025 10:42:28 +0000 Subject: [PATCH 09/13] suggest shorthand user entered --- .../style-panel/sections/advanced/add-style-input.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx index 93d5773a39fe..e4ffaf36a569 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx @@ -92,6 +92,15 @@ const matchOrSuggestToCreate = ( if (matched.length === 0) { const parsedStyles = parseStyleInput(search); + // When parsedStyles is more than one, user entered a shorthand. + // We will suggest to insert their shorthand first. + if (parsedStyles.length > 1) { + matched.push({ + property: search, + label: `Create "${search}"`, + }); + } + // Now we will suggest to insert each longhand separately. for (const style of parsedStyles) { matched.push({ property: style.property, From 8a95bd516762427916c88ce286dcb33ea8674a37 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 18 Feb 2025 10:55:21 +0000 Subject: [PATCH 10/13] fix entering gap:2px --- .../style-panel/sections/advanced/add-style-input.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx index e4ffaf36a569..d2b5ed5a4a40 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx @@ -148,7 +148,11 @@ export const AddStyleInput = forwardRef< onChange: (value) => setItem({ property: value ?? "", label: value ?? "" }), onItemSelect: (item) => { clear(); - onSubmit(`${item.property}: ${item.value ?? "unset"}`); + // When there is no value, it is either a property or a declaration(s) + if (item.value === undefined) { + return onSubmit(item.property); + } + onSubmit(`${item.property}: ${item.value}`); }, onItemHighlight: (item) => { const previousHighlightedItem = highlightedItemRef.current; From bd6b4f767ae9c25dd2878517c8025df5152240d0 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 18 Feb 2025 11:02:55 +0000 Subject: [PATCH 11/13] better comment --- .../style-panel/sections/advanced/add-style-input.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx index d2b5ed5a4a40..e97fc858bfba 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/add-style-input.tsx @@ -148,7 +148,10 @@ export const AddStyleInput = forwardRef< onChange: (value) => setItem({ property: value ?? "", label: value ?? "" }), onItemSelect: (item) => { clear(); - // When there is no value, it is either a property or a declaration(s) + // When there is no value, property can be: + // - property without value: gap + // - declaration with value: gap: 10px + // - block: gap: 10px; margin: 20px; if (item.value === undefined) { return onSubmit(item.property); } From b74fa8db2786ac9f9a1b5374e6efc932050dff96 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 18 Feb 2025 11:18:51 +0000 Subject: [PATCH 12/13] turn unknown property into a custom property --- .../style-panel/sections/advanced/parse-style-input.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts index 0f908b9eae0c..e6452146e205 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts @@ -57,8 +57,9 @@ export const parseStyleInput = (css: string): Array => { for (const style of styles) { // somethingunknown: red; -> --somethingunknown: red; if ( - style.value.type === "unparsed" && - style.property.startsWith("--") === false + style.value.type === "invalid" || + (style.value.type === "unparsed" && + style.property.startsWith("--") === false) ) { style.property = `--${style.property}`; } From 87c917e185ea2ff57a3cb2cb2fb5f48eca814f87 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Tue, 18 Feb 2025 11:22:18 +0000 Subject: [PATCH 13/13] add note --- .../features/style-panel/sections/advanced/parse-style-input.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts index e6452146e205..01cabcdcf54a 100644 --- a/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts +++ b/apps/builder/app/builder/features/style-panel/sections/advanced/parse-style-input.ts @@ -57,6 +57,8 @@ export const parseStyleInput = (css: string): Array => { for (const style of styles) { // somethingunknown: red; -> --somethingunknown: red; if ( + // Note: currently in tests it returns unparsed, but in the client it returns invalid, + // because we use native APIs when available in parseCss. style.value.type === "invalid" || (style.value.type === "unparsed" && style.property.startsWith("--") === false)