create a css rule when previewStyle was triggered before a css rule was created

when first action user does is not actually saving a style but uses any of the preview logic
This commit is contained in:
Oleg Isonen committed 2022-11-16 18:30:17 +01:00
1 parent 005baac2ed
commit 1ae993edbb
1 file changed
+76 -58
+76 -58
View File
@@ -15,56 +15,10 @@ import {
validStaticValueTypes,
} from "@webstudio-is/react-sdk";
import { globalCss } from "@webstudio-is/design-system";
import { useEffect, useRef } from "react";
import { useEffect } from "react";
import { createCssEngine, type StyleRule } from "@webstudio-is/css-engine";
import { useIsomorphicLayoutEffect } from "react-use";
const setCssVar = (id: string, property: string, value?: StyleValue) => {
const customProperty = `--${toVarNamespace(id, property)}`;
if (value === undefined) {
document.body.style.removeProperty(customProperty);
return;
}
document.body.style.setProperty(customProperty, toValue(value));
};
const useUpdateStyle = () => {
const [selectedInstance] = useSelectedInstance();
useSubscribe("updateStyle", ({ id, updates, breakpoint }) => {
// Only update styles if they match the selected instance
// It can potentially happen that we selected a difference instance right after we changed the style in style panel.
if (id !== selectedInstance?.id) return;
for (const update of updates) {
setCssVar(id, update.property, undefined);
}
store.createTransaction([rootInstanceContainer], (rootInstance) => {
if (rootInstance === undefined) {
return;
}
setInstanceStyleMutable(rootInstance, id, updates, breakpoint);
});
});
};
const usePreviewStyle = () => {
useSubscribe("previewStyle", ({ id, updates }) => {
for (const update of updates) {
setCssVar(id, update.property, update.value);
}
});
};
// Once we rendered the tree in editing mode, we have rendered all styles in a <style> tag.
// We keep the SSR styles just for the page on canvas to show up like it does in preview.
const useRemoveSsrStyles = () => {
useEffect(() => {
const link = document.head.querySelector('[data-webstudio="ssr"]');
link?.parentElement?.removeChild(link);
}, []);
};
const voidElements =
"area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr";
@@ -140,6 +94,24 @@ export const addMediaRules = (breakpoints: Array<Breakpoint>) => {
}
};
const wrappedRulesMap = new Map<string, StyleRule>();
const addRule = (id: string, cssRule: CssRule) => {
const key = id + cssRule.breakpoint;
const selectorText = `[data-ws-id="${id}"]`;
const rule = cssEngine.addStyleRule(selectorText, {
...cssRule,
style: toVarStyleWithFallback(id, cssRule.style),
});
wrappedRulesMap.set(key, rule);
return rule;
};
const getRule = (id: string, breakpoint: string) => {
const key = id + breakpoint;
return wrappedRulesMap.get(key);
};
export const useCssRules = ({
id,
cssRules,
@@ -147,22 +119,15 @@ export const useCssRules = ({
id: string;
cssRules: Array<CssRule>;
}) => {
const ruleMap = useRef<Map<string, StyleRule>>(new Map());
useIsomorphicLayoutEffect(() => {
for (const cssRule of cssRules) {
const key = id + cssRule.breakpoint;
let rule = ruleMap.current.get(key);
const rule = getRule(id, cssRule.breakpoint);
// It's the first time the rule is being used
if (rule === undefined) {
const selectorText = `[data-ws-id="${id}"]`;
rule = cssEngine.addStyleRule(selectorText, {
...cssRule,
style: toVarStyleWithFallback(id, cssRule.style),
});
ruleMap.current.set(key, rule);
addRule(id, cssRule);
continue;
}
// It's an update to an existing rule
const dynamicStyle = toVarStyleWithFallback(id, cssRule.style);
let property: StyleProperty;
for (property in dynamicStyle) {
@@ -172,3 +137,56 @@ export const useCssRules = ({
cssEngine.render();
}, [id, cssRules]);
};
const setCssVar = (id: string, property: string, value?: StyleValue) => {
const customProperty = `--${toVarNamespace(id, property)}`;
if (value === undefined) {
document.body.style.removeProperty(customProperty);
return;
}
document.body.style.setProperty(customProperty, toValue(value));
};
const useUpdateStyle = () => {
const [selectedInstance] = useSelectedInstance();
useSubscribe("updateStyle", ({ id, updates, breakpoint }) => {
// Only update styles if they match the selected instance
// It can potentially happen that we selected a difference instance right after we changed the style in style panel.
if (id !== selectedInstance?.id) return;
for (const update of updates) {
setCssVar(id, update.property, undefined);
}
store.createTransaction([rootInstanceContainer], (rootInstance) => {
if (rootInstance === undefined) {
return;
}
setInstanceStyleMutable(rootInstance, id, updates, breakpoint);
});
});
};
const usePreviewStyle = () => {
useSubscribe("previewStyle", ({ id, updates, breakpoint }) => {
if (getRule(id, breakpoint.id) === undefined) {
const rule = addRule(id, { breakpoint: breakpoint.id, style: {} });
for (const update of updates) {
rule.styleMap.set(update.property, update.value);
}
cssEngine.render();
}
for (const update of updates) {
setCssVar(id, update.property, update.value);
}
});
};
// Once we rendered the tree in editing mode, we have rendered all styles in a <style> tag.
// We keep the SSR styles just for the page on canvas to show up like it does in preview.
const useRemoveSsrStyles = () => {
useEffect(() => {
const link = document.head.querySelector('[data-webstudio="ssr"]');
link?.parentElement?.removeChild(link);
}, []);
};