mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-09 22:14:29 +09:00
refactor: decouple style updates from react (#4021)
Removed setProperty, deleteProperty and createBatchUpdates from react hook. All parameters can be accessed from global stores. Note: review with hidden spaces.
This commit is contained in:
1 parent
1643fcde54
commit
f67e2e3778
5 files changed
+129
-164
No files matched your search
@@ -163,7 +163,7 @@ export const Inspector = ({ navigatorLayout }: InspectorProps) => {
|
||||
<Separator />
|
||||
<PanelTabsContent value="style" css={contentStyle} tabIndex={-1}>
|
||||
<InstanceInfo instance={selectedInstance} />
|
||||
<StylePanel selectedInstance={selectedInstance} />
|
||||
<StylePanel />
|
||||
</PanelTabsContent>
|
||||
<PanelTabsContent
|
||||
value="settings"
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
$breakpoints,
|
||||
$instances,
|
||||
$registeredComponentMetas,
|
||||
$selectedInstanceSelector,
|
||||
$styleSources,
|
||||
} from "~/shared/nano-states";
|
||||
import { getInstanceLabel } from "~/shared/instance-utils";
|
||||
@@ -31,7 +30,7 @@ import type {
|
||||
} from "~/shared/style-object-model";
|
||||
import { createComputedStyleDeclStore } from "./shared/model";
|
||||
import { StyleSourceBadge } from "./style-source";
|
||||
import { useStyleData } from "./shared/use-style-data";
|
||||
import { createBatchUpdate } from "./shared/use-style-data";
|
||||
|
||||
export const PropertyInfo = ({
|
||||
title,
|
||||
@@ -185,8 +184,6 @@ export const PropertyLabel = ({
|
||||
description: string;
|
||||
properties: [StyleProperty, ...StyleProperty[]];
|
||||
}) => {
|
||||
const instanceSelector = useStore($selectedInstanceSelector);
|
||||
const { createBatchUpdate } = useStyleData(instanceSelector?.[0] ?? "");
|
||||
const $styles = useMemo(() => {
|
||||
return computed(
|
||||
properties.map(createComputedStyleDeclStore),
|
||||
@@ -253,8 +250,6 @@ export const PropertySectionLabel = ({
|
||||
description: string;
|
||||
properties: [StyleProperty, ...StyleProperty[]];
|
||||
}) => {
|
||||
const instanceSelector = useStore($selectedInstanceSelector);
|
||||
const { createBatchUpdate } = useStyleData(instanceSelector?.[0] ?? "");
|
||||
const $styles = useMemo(() => {
|
||||
return computed(
|
||||
properties.map(createComputedStyleDeclStore),
|
||||
|
||||
@@ -374,16 +374,8 @@ const LayoutSectionFlex = ({
|
||||
<ToggleControl
|
||||
property="flexWrap"
|
||||
items={[
|
||||
{
|
||||
name: "nowrap",
|
||||
label: "No Wrap",
|
||||
icon: NoWrapIcon,
|
||||
},
|
||||
{
|
||||
name: "wrap",
|
||||
label: "Wrap",
|
||||
icon: WrapIcon,
|
||||
},
|
||||
{ name: "nowrap", label: "No Wrap", icon: NoWrapIcon },
|
||||
{ name: "wrap", label: "Wrap", icon: WrapIcon },
|
||||
]}
|
||||
currentStyle={currentStyle}
|
||||
setProperty={setProperty}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useCallback } from "react";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import {
|
||||
type Breakpoint,
|
||||
type Instance,
|
||||
@@ -9,13 +7,13 @@ import {
|
||||
import type { StyleProperty, StyleValue } from "@webstudio-is/css-engine";
|
||||
import {
|
||||
$selectedBreakpoint,
|
||||
$selectedInstanceSelector,
|
||||
$selectedOrLastStyleSourceSelector,
|
||||
$selectedStyleSource,
|
||||
$styleSourceSelections,
|
||||
$styleSources,
|
||||
$styles,
|
||||
} from "~/shared/nano-states";
|
||||
import { useStyleInfo } from "./style-info";
|
||||
import { serverSyncStore } from "~/shared/sync";
|
||||
import { $ephemeralStyles } from "~/canvas/stores";
|
||||
|
||||
@@ -57,161 +55,145 @@ export type CreateBatchUpdate = () => {
|
||||
publish: (options?: StyleUpdateOptions) => void;
|
||||
};
|
||||
|
||||
export const useStyleData = (selectedInstanceId: Instance["id"]) => {
|
||||
const selectedBreakpoint = useStore($selectedBreakpoint);
|
||||
const publishUpdates = (
|
||||
type: "update" | "preview",
|
||||
updates: StyleUpdates["updates"],
|
||||
options: StyleUpdateOptions
|
||||
) => {
|
||||
if (updates.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentStyle = useStyleInfo();
|
||||
const selectedInstanceSelector = $selectedInstanceSelector.get();
|
||||
const selectedInstanceId = selectedInstanceSelector?.[0];
|
||||
const selectedBreakpoint = $selectedBreakpoint.get();
|
||||
const selectedStyleSource = $selectedStyleSource.get();
|
||||
const styleSourceSelector = $selectedOrLastStyleSourceSelector.get();
|
||||
|
||||
const publishUpdates = useCallback(
|
||||
(
|
||||
type: "update" | "preview",
|
||||
updates: StyleUpdates["updates"],
|
||||
options: StyleUpdateOptions
|
||||
) => {
|
||||
const selectedStyleSource = $selectedStyleSource.get();
|
||||
const styleSourceSelector = $selectedOrLastStyleSourceSelector.get();
|
||||
if (
|
||||
selectedInstanceId === undefined ||
|
||||
selectedBreakpoint === undefined ||
|
||||
selectedStyleSource === undefined ||
|
||||
styleSourceSelector === undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === "preview") {
|
||||
const ephemeralStyles: ReturnType<typeof $ephemeralStyles.get> = [];
|
||||
for (const update of updates) {
|
||||
if (update.operation === "set") {
|
||||
ephemeralStyles.push({
|
||||
breakpointId: selectedBreakpoint.id,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
value: update.value,
|
||||
listed: options.listed,
|
||||
});
|
||||
}
|
||||
}
|
||||
$ephemeralStyles.set(ephemeralStyles);
|
||||
return;
|
||||
}
|
||||
|
||||
$ephemeralStyles.set([]);
|
||||
serverSyncStore.createTransaction(
|
||||
[$styleSourceSelections, $styleSources, $styles],
|
||||
(styleSourceSelections, styleSources, styles) => {
|
||||
const instanceId = selectedInstanceId;
|
||||
const breakpointId = selectedBreakpoint.id;
|
||||
// set only selected style source and update selection with it
|
||||
// generated local style source will not be written if not selected
|
||||
styleSources.set(selectedStyleSource.id, selectedStyleSource);
|
||||
const selectionValues =
|
||||
styleSourceSelections.get(instanceId)?.values ?? [];
|
||||
if (
|
||||
updates.length === 0 ||
|
||||
selectedBreakpoint === undefined ||
|
||||
selectedStyleSource === undefined ||
|
||||
styleSourceSelector === undefined
|
||||
selectionValues.includes(styleSourceSelector.styleSourceId) === false
|
||||
) {
|
||||
return;
|
||||
styleSourceSelections.set(instanceId, {
|
||||
instanceId,
|
||||
values: [...selectionValues, styleSourceSelector.styleSourceId],
|
||||
});
|
||||
}
|
||||
|
||||
if (type === "preview") {
|
||||
const ephemeralStyles: ReturnType<typeof $ephemeralStyles.get> = [];
|
||||
for (const update of updates) {
|
||||
if (update.operation === "set") {
|
||||
ephemeralStyles.push({
|
||||
breakpointId: selectedBreakpoint.id,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
value: update.value,
|
||||
listed: options.listed,
|
||||
});
|
||||
}
|
||||
for (const update of updates) {
|
||||
if (update.operation === "set") {
|
||||
const styleDecl: StyleDecl = {
|
||||
breakpointId,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
value: update.value,
|
||||
listed: options.listed,
|
||||
};
|
||||
styles.set(getStyleDeclKey(styleDecl), styleDecl);
|
||||
}
|
||||
|
||||
if (update.operation === "delete") {
|
||||
const styleDeclKey = getStyleDeclKey({
|
||||
breakpointId,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
});
|
||||
styles.delete(styleDeclKey);
|
||||
}
|
||||
$ephemeralStyles.set(ephemeralStyles);
|
||||
return;
|
||||
}
|
||||
|
||||
$ephemeralStyles.set([]);
|
||||
serverSyncStore.createTransaction(
|
||||
[$styleSourceSelections, $styleSources, $styles],
|
||||
(styleSourceSelections, styleSources, styles) => {
|
||||
const instanceId = selectedInstanceId;
|
||||
const breakpointId = selectedBreakpoint.id;
|
||||
// set only selected style source and update selection with it
|
||||
// generated local style source will not be written if not selected
|
||||
styleSources.set(selectedStyleSource.id, selectedStyleSource);
|
||||
const selectionValues =
|
||||
styleSourceSelections.get(instanceId)?.values ?? [];
|
||||
if (
|
||||
selectionValues.includes(styleSourceSelector.styleSourceId) ===
|
||||
false
|
||||
) {
|
||||
styleSourceSelections.set(instanceId, {
|
||||
instanceId,
|
||||
values: [...selectionValues, styleSourceSelector.styleSourceId],
|
||||
});
|
||||
}
|
||||
|
||||
for (const update of updates) {
|
||||
if (update.operation === "set") {
|
||||
const styleDecl: StyleDecl = {
|
||||
breakpointId,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
value: update.value,
|
||||
listed: options.listed,
|
||||
};
|
||||
styles.set(getStyleDeclKey(styleDecl), styleDecl);
|
||||
}
|
||||
|
||||
if (update.operation === "delete") {
|
||||
const styleDeclKey = getStyleDeclKey({
|
||||
breakpointId,
|
||||
styleSourceId: styleSourceSelector.styleSourceId,
|
||||
state: styleSourceSelector.state,
|
||||
property: update.property,
|
||||
});
|
||||
styles.delete(styleDeclKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
[selectedBreakpoint, selectedInstanceId]
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const setProperty = useCallback<SetProperty>(
|
||||
(property) => {
|
||||
return (value, options: StyleUpdateOptions = { isEphemeral: false }) => {
|
||||
if (value.type !== "invalid") {
|
||||
const updates = [{ operation: "set" as const, property, value }];
|
||||
const type = options.isEphemeral ? "preview" : "update";
|
||||
|
||||
publishUpdates(type, updates, options);
|
||||
}
|
||||
};
|
||||
},
|
||||
[publishUpdates]
|
||||
);
|
||||
|
||||
const deleteProperty = useCallback(
|
||||
(
|
||||
property: StyleProperty,
|
||||
options: StyleUpdateOptions = { isEphemeral: false }
|
||||
) => {
|
||||
const updates = [{ operation: "delete" as const, property }];
|
||||
export const setProperty: SetProperty = (property) => {
|
||||
return (value, options: StyleUpdateOptions = { isEphemeral: false }) => {
|
||||
if (value.type !== "invalid") {
|
||||
const updates = [{ operation: "set" as const, property, value }];
|
||||
const type = options.isEphemeral ? "preview" : "update";
|
||||
|
||||
publishUpdates(type, updates, options);
|
||||
},
|
||||
[publishUpdates]
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const createBatchUpdate = useCallback(() => {
|
||||
let updates: StyleUpdates["updates"] = [];
|
||||
export const deleteProperty = (
|
||||
property: StyleProperty,
|
||||
options: StyleUpdateOptions = { isEphemeral: false }
|
||||
) => {
|
||||
const updates = [{ operation: "delete" as const, property }];
|
||||
const type = options.isEphemeral ? "preview" : "update";
|
||||
publishUpdates(type, updates, options);
|
||||
};
|
||||
|
||||
const setProperty = (property: StyleProperty) => {
|
||||
const setValue = (value: StyleValue) => {
|
||||
if (value.type === "invalid") {
|
||||
return;
|
||||
}
|
||||
export const createBatchUpdate = () => {
|
||||
let updates: StyleUpdates["updates"] = [];
|
||||
|
||||
updates.push({ operation: "set", property, value });
|
||||
};
|
||||
return setValue;
|
||||
};
|
||||
const setProperty = (property: StyleProperty) => {
|
||||
const setValue = (value: StyleValue) => {
|
||||
if (value.type === "invalid") {
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteProperty = (property: StyleProperty) => {
|
||||
updates.push({ operation: "delete", property });
|
||||
updates.push({ operation: "set", property, value });
|
||||
};
|
||||
return setValue;
|
||||
};
|
||||
|
||||
const publish = (options: StyleUpdateOptions = { isEphemeral: false }) => {
|
||||
if (!updates.length) {
|
||||
return;
|
||||
}
|
||||
const type = options.isEphemeral ? "preview" : "update";
|
||||
publishUpdates(type, updates, options);
|
||||
updates = [];
|
||||
};
|
||||
const deleteProperty = (property: StyleProperty) => {
|
||||
updates.push({ operation: "delete", property });
|
||||
};
|
||||
|
||||
return {
|
||||
setProperty,
|
||||
deleteProperty,
|
||||
publish,
|
||||
};
|
||||
}, [publishUpdates]);
|
||||
const publish = (options: StyleUpdateOptions = { isEphemeral: false }) => {
|
||||
if (!updates.length) {
|
||||
return;
|
||||
}
|
||||
const type = options.isEphemeral ? "preview" : "update";
|
||||
publishUpdates(type, updates, options);
|
||||
updates = [];
|
||||
};
|
||||
|
||||
return {
|
||||
currentStyle,
|
||||
setProperty,
|
||||
deleteProperty,
|
||||
createBatchUpdate,
|
||||
publish,
|
||||
};
|
||||
};
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
Separator,
|
||||
ScrollArea,
|
||||
} from "@webstudio-is/design-system";
|
||||
import type { Instance } from "@webstudio-is/sdk";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import { computed } from "nanostores";
|
||||
import type { HtmlTags } from "html-tags";
|
||||
|
||||
import { useStyleData } from "./shared/use-style-data";
|
||||
|
||||
import {
|
||||
setProperty,
|
||||
deleteProperty,
|
||||
createBatchUpdate,
|
||||
} from "./shared/use-style-data";
|
||||
import { StyleSourcesSection } from "./style-source-section";
|
||||
import { $selectedInstanceRenderState } from "~/shared/nano-states";
|
||||
import {
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
} from "~/shared/nano-states";
|
||||
import { sections } from "./sections";
|
||||
import { useParentStyle } from "./parent-style";
|
||||
import type { StyleInfo } from "./shared/style-info";
|
||||
import { useStyleInfo, type StyleInfo } from "./shared/style-info";
|
||||
import { toValue } from "@webstudio-is/css-engine";
|
||||
|
||||
const $selectedInstanceTag = computed(
|
||||
@@ -48,13 +49,8 @@ const shouldRenderCategory = (
|
||||
return true;
|
||||
};
|
||||
|
||||
type StylePanelProps = {
|
||||
selectedInstance: Instance;
|
||||
};
|
||||
|
||||
export const StylePanel = ({ selectedInstance }: StylePanelProps) => {
|
||||
const { currentStyle, setProperty, deleteProperty, createBatchUpdate } =
|
||||
useStyleData(selectedInstance.id);
|
||||
export const StylePanel = () => {
|
||||
const currentStyle = useStyleInfo();
|
||||
|
||||
const selectedInstanceRenderState = useStore($selectedInstanceRenderState);
|
||||
const selectedInstanceTag = useStore($selectedInstanceTag);
|
||||
|
||||
Reference in new issue
Block a user