Skip to content

Commit 5fbda47

Browse files
authored
refactor: migrate css preview to new style engine (#4353)
Preview now managed by new style engine. ![Screenshot 2024-10-29 at 12 05 25](https://github.com/user-attachments/assets/3017247e-2fc6-4572-bb19-8309067a570d)
1 parent 4fb2338 commit 5fbda47

File tree

6 files changed

+158
-118
lines changed

6 files changed

+158
-118
lines changed

apps/builder/app/builder/features/navigator/css-preview.tsx

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
1+
import { computed } from "nanostores";
2+
import { useStore } from "@nanostores/react";
13
import {
24
ScrollArea,
35
css,
46
textVariants,
57
theme,
68
} from "@webstudio-is/design-system";
7-
// @todo feature should not import another feature
8-
import {
9-
type StyleInfo,
10-
useStyleInfo,
11-
} from "~/builder/features/style-panel/shared/style-info";
129
import {
1310
generateStyleMap,
1411
hyphenateProperty,
1512
mergeStyles,
1613
} from "@webstudio-is/css-engine";
1714
import type { StyleMap, StyleProperty } from "@webstudio-is/css-engine";
1815
import { CollapsibleSection } from "~/builder/shared/collapsible-section";
19-
import { useMemo } from "react";
20-
import { captureError } from "@webstudio-is/error-utils";
2116
import { highlightCss } from "~/builder/shared/code-highlight";
17+
import type { ComputedStyleDecl } from "~/shared/style-object-model";
18+
import { $selectedInstanceSelector } from "~/shared/nano-states";
19+
import { $definedComputedStyles } from "~/builder/features/style-panel/shared/model";
2220

2321
const preStyle = css(textVariants.mono, {
2422
margin: 0,
@@ -27,43 +25,40 @@ const preStyle = css(textVariants.mono, {
2725
cursor: "text",
2826
});
2927

30-
// - Compiles a CSS string from the style info
28+
// - Compiles a CSS string from the style engine
3129
// - Groups by category and separates categories with comments
32-
const getCssText = (currentStyle: StyleInfo) => {
30+
const getCssText = (
31+
computedStyles: ComputedStyleDecl[],
32+
instanceId: string
33+
) => {
3334
const sourceStyles: StyleMap = new Map();
3435
const inheritedStyles: StyleMap = new Map();
3536
const cascadedStyles: StyleMap = new Map();
3637
const presetStyles: StyleMap = new Map();
3738

3839
// Aggregate styles by category so we can group them when rendering.
39-
let property: StyleProperty;
40-
for (property in currentStyle) {
41-
const value = currentStyle[property];
42-
property = hyphenateProperty(property) as StyleProperty;
43-
if (value === undefined) {
44-
continue;
45-
}
46-
if (value.local ?? value.nextSource?.value ?? value.previousSource?.value) {
47-
sourceStyles.set(property, value.value);
48-
continue;
40+
for (const styleDecl of computedStyles) {
41+
const property = hyphenateProperty(styleDecl.property) as StyleProperty;
42+
let group;
43+
if (
44+
styleDecl.source.name === "local" ||
45+
styleDecl.source.name === "overwritten"
46+
) {
47+
group = sourceStyles;
4948
}
50-
if (value.preset) {
51-
presetStyles.set(property, value.value);
52-
continue;
49+
if (styleDecl.source.name === "remote") {
50+
group = cascadedStyles;
5351
}
54-
if (value.cascaded?.value) {
55-
cascadedStyles.set(property, value.value);
56-
continue;
52+
if (styleDecl.source.name === "preset") {
53+
group = presetStyles;
5754
}
58-
if (value.inherited?.value) {
59-
inheritedStyles.set(property, value.value);
60-
continue;
55+
if (group) {
56+
if (styleDecl.source.instanceId === instanceId) {
57+
group.set(property, styleDecl.cascadedValue);
58+
} else {
59+
inheritedStyles.set(property, styleDecl.cascadedValue);
60+
}
6161
}
62-
if (value.value) {
63-
// Doesn't need handling
64-
continue;
65-
}
66-
captureError(new Error("Unknown style source"), value);
6762
}
6863

6964
const result: Array<string> = [];
@@ -84,25 +79,23 @@ const getCssText = (currentStyle: StyleInfo) => {
8479
return result.join("\n");
8580
};
8681

87-
const useHighlightedCss = () => {
88-
const currentStyle = useStyleInfo();
89-
90-
return useMemo(() => {
91-
if (Object.keys(currentStyle).length === 0) {
82+
const $highlightedCss = computed(
83+
[$selectedInstanceSelector, $definedComputedStyles],
84+
(instanceSelector, computedStyles) => {
85+
if (instanceSelector === undefined) {
9286
return;
9387
}
94-
const cssText = getCssText(currentStyle);
88+
const [instanceId] = instanceSelector;
89+
const cssText = getCssText(computedStyles, instanceId);
9590
return highlightCss(cssText);
96-
}, [currentStyle]);
97-
};
91+
}
92+
);
9893

9994
export const CssPreview = () => {
100-
const code = useHighlightedCss();
101-
95+
const code = useStore($highlightedCss);
10296
if (code === undefined) {
10397
return null;
10498
}
105-
10699
return (
107100
<CollapsibleSection label="CSS Preview" fullWidth>
108101
<ScrollArea css={{ padding: theme.panel.padding }}>

apps/builder/app/builder/features/settings-panel/props-section/props-section.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export const PropsSection = (props: PropsSectionProps) => {
162162

163163
return (
164164
<>
165-
<Row css={{ py: theme.spacing[3] }}>
165+
<Row css={{ py: theme.panel.paddingBlock }}>
166166
{logic.systemProps.map((item) => renderProperty(props, item))}
167167
</Row>
168168

apps/builder/app/builder/features/style-panel/sections/advanced/advanced.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ import { PropertyInfo } from "../../property-label";
6767
import { sections } from "../sections";
6868
import { ColorPopover } from "../../shared/color-picker";
6969
import {
70+
$instances,
71+
$registeredComponentMetas,
7072
$selectedInstanceSelector,
7173
$styles,
7274
$styleSourceSelections,
@@ -377,11 +379,20 @@ const initialProperties = new Set<StyleProperty>([
377379
const $advancedProperties = computed(
378380
[
379381
$selectedInstanceSelector,
382+
$instances,
383+
$registeredComponentMetas,
380384
$styleSourceSelections,
381385
$matchingBreakpoints,
382386
$styles,
383387
],
384-
(instanceSelector, styleSourceSelections, matchingBreakpoints, styles) => {
388+
(
389+
instanceSelector,
390+
instances,
391+
metas,
392+
styleSourceSelections,
393+
matchingBreakpoints,
394+
styles
395+
) => {
385396
if (instanceSelector === undefined) {
386397
return [];
387398
}
@@ -393,6 +404,8 @@ const $advancedProperties = computed(
393404
instanceSelector;
394405
const definedStyles = getDefinedStyles({
395406
instanceSelector: instanceAndRootSelector,
407+
instances,
408+
metas,
396409
matchingBreakpoints,
397410
styleSourceSelections,
398411
styles,

apps/builder/app/builder/features/style-panel/shared/model.tsx

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import {
1010
type VarValue,
1111
} from "@webstudio-is/css-engine";
1212
import {
13+
Instances,
1314
ROOT_INSTANCE_ID,
1415
Styles,
1516
StyleSourceSelections,
1617
type Breakpoint,
1718
type Instance,
1819
type StyleDecl,
1920
} from "@webstudio-is/sdk";
20-
import { rootComponent } from "@webstudio-is/react-sdk";
21+
import { rootComponent, WsComponentMeta } from "@webstudio-is/react-sdk";
2122
import {
2223
$breakpoints,
2324
$instances,
@@ -96,20 +97,36 @@ export const $matchingBreakpoints = computed(
9697

9798
export const getDefinedStyles = ({
9899
instanceSelector,
100+
instances,
101+
metas,
99102
matchingBreakpoints: matchingBreakpointsArray,
100103
styleSourceSelections,
101104
styles,
102105
}: {
103106
instanceSelector: InstanceSelector;
107+
instances: Instances;
108+
metas: Map<string, WsComponentMeta>;
104109
matchingBreakpoints: Breakpoint["id"][];
105110
styleSourceSelections: StyleSourceSelections;
106111
styles: Styles;
107112
}) => {
108-
const definedStyles = new Set<StyleDecl>();
113+
const definedStyles = new Set<{
114+
property: StyleProperty;
115+
listed?: boolean;
116+
}>();
109117
const inheritedStyleSources = new Set();
110118
const instanceStyleSources = new Set();
111119
const matchingBreakpoints = new Set(matchingBreakpointsArray);
112120
for (const instanceId of instanceSelector) {
121+
const instance = instances.get(instanceId);
122+
const meta = instance?.component
123+
? metas.get(instance.component)
124+
: undefined;
125+
for (const presetStyles of Object.values(meta?.presetStyle ?? {})) {
126+
for (const styleDecl of presetStyles) {
127+
definedStyles.add(styleDecl);
128+
}
129+
}
113130
const styleSources = styleSourceSelections.get(instanceId)?.values;
114131
if (styleSources) {
115132
for (const styleSourceId of styleSources) {
@@ -143,23 +160,43 @@ export const getDefinedStyles = ({
143160
return definedStyles;
144161
};
145162

163+
const $instanceAndRootSelector = computed(
164+
$selectedInstanceSelector,
165+
(instanceSelector) => {
166+
if (instanceSelector === undefined) {
167+
return;
168+
}
169+
if (instanceSelector[0] === ROOT_INSTANCE_ID) {
170+
return instanceSelector;
171+
}
172+
return [...instanceSelector, ROOT_INSTANCE_ID];
173+
}
174+
);
175+
146176
export const $definedStyles = computed(
147177
[
148-
$selectedInstanceSelector,
178+
$instanceAndRootSelector,
179+
$instances,
180+
$registeredComponentMetas,
149181
$styleSourceSelections,
150182
$matchingBreakpoints,
151183
$styles,
152184
],
153-
(instanceSelector, styleSourceSelections, matchingBreakpoints, styles) => {
185+
(
186+
instanceSelector,
187+
instances,
188+
metas,
189+
styleSourceSelections,
190+
matchingBreakpoints,
191+
styles
192+
) => {
154193
if (instanceSelector === undefined) {
155194
return new Set<StyleDecl>();
156195
}
157-
const instanceAndRootSelector =
158-
instanceSelector[0] === ROOT_INSTANCE_ID
159-
? instanceSelector
160-
: [...instanceSelector, ROOT_INSTANCE_ID];
161196
return getDefinedStyles({
162-
instanceSelector: instanceAndRootSelector,
197+
instanceSelector,
198+
instances,
199+
metas,
163200
matchingBreakpoints,
164201
styleSourceSelections,
165202
styles,
@@ -198,46 +235,47 @@ const $model = computed(
198235
}
199236
);
200237

201-
const $instanceAndRootSelector = computed(
202-
$selectedInstanceSelector,
203-
(instanceSelector) => {
204-
if (instanceSelector === undefined) {
205-
return;
206-
}
207-
if (instanceSelector[0] === ROOT_INSTANCE_ID) {
208-
return instanceSelector;
209-
}
210-
return [...instanceSelector, ROOT_INSTANCE_ID];
211-
}
212-
);
213-
214-
export const $availableVariables = computed(
238+
export const $definedComputedStyles = computed(
215239
[
216240
$definedStyles,
217241
$model,
218242
$instanceAndRootSelector,
219243
$selectedOrLastStyleSourceSelector,
220244
],
221245
(definedStyles, model, instanceSelector, styleSourceSelector) => {
222-
const availableVariables = new Map<string, VarValue>();
246+
const computedStyles = new Map<string, ComputedStyleDecl>();
223247
for (const { property } of definedStyles) {
224-
if (property.startsWith("--")) {
225-
const { computedValue } = getComputedStyleDecl({
226-
model,
227-
instanceSelector,
228-
styleSourceId: styleSourceSelector?.styleSourceId,
229-
state: styleSourceSelector?.state,
230-
property,
231-
});
232-
// deduplicate by property name
233-
availableVariables.set(property, {
248+
// deduplicate by property name
249+
if (computedStyles.has(property)) {
250+
continue;
251+
}
252+
const computedStyleDecl = getComputedStyleDecl({
253+
model,
254+
instanceSelector,
255+
styleSourceId: styleSourceSelector?.styleSourceId,
256+
state: styleSourceSelector?.state,
257+
property,
258+
});
259+
computedStyles.set(property, computedStyleDecl);
260+
}
261+
return Array.from(computedStyles.values());
262+
}
263+
);
264+
265+
export const $availableVariables = computed(
266+
$definedComputedStyles,
267+
(computedStyles) => {
268+
const availableVariables: VarValue[] = [];
269+
for (const styleDecl of computedStyles) {
270+
if (styleDecl.property.startsWith("--")) {
271+
availableVariables.push({
234272
type: "var",
235-
value: property.slice(2),
236-
fallback: toVarFallback(computedValue),
273+
value: styleDecl.property.slice(2),
274+
fallback: toVarFallback(styleDecl.computedValue),
237275
});
238276
}
239277
}
240-
return Array.from(availableVariables.values());
278+
return availableVariables;
241279
}
242280
);
243281

apps/builder/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"dependencies": {
1919
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
2020
"@codemirror/autocomplete": "^6.18.1",
21-
"@codemirror/commands": "^6.7.0",
21+
"@codemirror/commands": "^6.7.1",
2222
"@codemirror/lang-css": "^6.3.0",
2323
"@codemirror/lang-html": "^6.4.9",
2424
"@codemirror/lang-javascript": "^6.2.2",
@@ -36,7 +36,7 @@
3636
"@lexical/react": "^0.16.0",
3737
"@lexical/selection": "^0.16.0",
3838
"@lexical/utils": "^0.16.0",
39-
"@lezer/common": "^1.2.2",
39+
"@lezer/common": "^1.2.3",
4040
"@lezer/css": "^1.1.9",
4141
"@lezer/highlight": "^1.2.1",
4242
"@nanostores/react": "^0.7.1",
@@ -60,7 +60,6 @@
6060
"@webstudio-is/dashboard": "workspace:*",
6161
"@webstudio-is/design-system": "workspace:*",
6262
"@webstudio-is/domain": "workspace:*",
63-
"@webstudio-is/error-utils": "workspace:*",
6463
"@webstudio-is/feature-flags": "workspace:*",
6564
"@webstudio-is/fonts": "workspace:*",
6665
"@webstudio-is/http-client": "workspace:*",

0 commit comments

Comments
 (0)