Move selected style source into separate store (#955)

Ref https://github.com/webstudio-is/webstudio-designer/issues/807

this will let to reuse the logic and newly generated local style source
with style source input.
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2023-02-07 00:17:06 +03:00
1 parent d00c646b10
commit eb66eccca1
8 files changed
+105 -99

No files matched your search

+2
View File
@@ -37,6 +37,7 @@ import {
useSubscribeScrollState,
useIsPreviewMode,
useSetAssets,
useSetTreeId,
} from "~/shared/nano-states";
import { usePublishScrollState } from "./shared/use-publish-scroll-state";
import { useDragAndDrop } from "./shared/use-drag-drop";
@@ -119,6 +120,7 @@ export const Canvas = ({ data }: CanvasProps): JSX.Element | null => {
throw new Error("Tree is null");
}
const isDesignerReady = useSubscribeDesignerReady();
useSetTreeId(data.tree.id);
useSetAssets(data.assets);
useSetBreakpoints(data.build.breakpoints);
useSetProps(data.tree.props);
+3 -1
View File
@@ -35,6 +35,7 @@ import {
useIsPreviewMode,
useSetAuthPermit,
useSetIsPreviewMode,
useSetTreeId,
} from "~/shared/nano-states";
import { useClientSettings } from "./shared/client-settings";
import { Navigator } from "./features/sidebar-left";
@@ -266,6 +267,7 @@ export const Designer = ({
useSetAuthPermit(authPermit);
useSubscribeBreakpoints();
useSetProject(project);
useSetTreeId(treeId);
useSetPages(pages);
useSetCurrentPageId(pageId);
const [publish, publishRef] = usePublish();
@@ -354,7 +356,7 @@ export const Designer = ({
{dragAndDropState.isDragging ? (
<TreePrevew />
) : (
<Inspector treeId={treeId} publish={publish} />
<Inspector publish={publish} />
)}
</SidePanel>
<Footer />
@@ -1,6 +1,5 @@
import { useRef } from "react";
import { useStore } from "@nanostores/react";
import { Tree } from "@webstudio-is/project-build";
import type { Publish } from "~/shared/pubsub";
import {
DeprecatedText2,
@@ -21,7 +20,6 @@ import { theme } from "@webstudio-is/design-system";
import { selectedInstanceStore } from "~/shared/nano-states";
type InspectorProps = {
treeId: Tree["id"];
publish: Publish;
};
@@ -31,7 +29,7 @@ const contentStyle = {
overflow: "auto",
};
export const Inspector = ({ treeId, publish }: InspectorProps) => {
export const Inspector = ({ publish }: InspectorProps) => {
const selectedInstance = useStore(selectedInstanceStore);
const tabsRef = useRef<HTMLDivElement>(null);
@@ -71,15 +69,10 @@ export const Inspector = ({ treeId, publish }: InspectorProps) => {
</TabsTrigger>
</TabsList>
<TabsContent value="style" css={contentStyle}>
<StylePanel
treeId={treeId}
publish={publish}
selectedInstance={selectedInstance}
/>
<StylePanel publish={publish} selectedInstance={selectedInstance} />
</TabsContent>
<TabsContent value="props" css={contentStyle}>
<PropsPanel
treeId={treeId}
publish={publish}
key={selectedInstance.id /* Re-render when instance changes */}
selectedInstance={selectedInstance}
@@ -21,7 +21,6 @@ export const NoProps: ComponentStory<typeof PropsPanel> = () => {
]);
return (
<PropsPanel
treeId="treeId"
selectedInstance={{
type: "instance",
id: "1",
@@ -38,7 +37,6 @@ export const RequiredProps: ComponentStory<typeof PropsPanel> = () => {
propsStore.set([]);
return (
<PropsPanel
treeId="treeId"
selectedInstance={{
type: "instance",
id: "1",
@@ -55,7 +53,6 @@ export const DefaultProps: ComponentStory<typeof PropsPanel> = () => {
propsStore.set([]);
return (
<PropsPanel
treeId="treeId"
selectedInstance={{
type: "instance",
id: "1",
@@ -83,7 +80,6 @@ export const AllProps: ComponentStory<typeof PropsPanel> = () => {
);
return (
<PropsPanel
treeId="treeId"
selectedInstance={{
type: "instance",
id: "3",
@@ -1,6 +1,6 @@
import { useState } from "react";
import store from "immerhin";
import type { Instance, Prop, Tree } from "@webstudio-is/project-build";
import type { Instance, Prop } from "@webstudio-is/project-build";
import { getComponentMetaProps } from "@webstudio-is/react-sdk";
import {
theme,
@@ -213,16 +213,11 @@ const Property = ({
};
type PropsPanelProps = {
treeId: Tree["id"];
publish: Publish;
selectedInstance: Instance;
};
export const PropsPanel = ({
treeId,
selectedInstance,
publish,
}: PropsPanelProps) => {
export const PropsPanel = ({ selectedInstance, publish }: PropsPanelProps) => {
const instanceId = selectedInstance.id;
const instanceProps = useInstanceProps(instanceId);
@@ -255,7 +250,6 @@ export const PropsPanel = ({
});
const { setProperty: setCssProperty } = useStyleData({
treeId,
selectedInstance,
publish,
});
@@ -1,14 +1,13 @@
import { useCallback, useMemo } from "react";
import { useStore } from "@nanostores/react";
import { nanoid } from "nanoid";
import { useCallback } from "react";
import store from "immerhin";
import warnOnce from "warn-once";
import type { Instance, Tree } from "@webstudio-is/project-build";
import type { Instance } from "@webstudio-is/project-build";
import type { StyleUpdates } from "@webstudio-is/project";
import type { StyleProperty, StyleValue } from "@webstudio-is/css-data";
import { type Publish } from "~/shared/pubsub";
import {
styleSourceSelectionsIndexStore,
selectedInstanceStyleSourcesStore,
selectedStyleSourceStore,
styleSourceSelectionsStore,
styleSourcesStore,
stylesStore,
@@ -21,7 +20,6 @@ import {
// @todo: must be removed, now it's only for compatibility with existing code
import { parseCssValue } from "./parse-css-value";
import { useStyleInfo } from "./style-info";
import { shallowComputed } from "~/shared/store-utils";
declare module "~/shared/pubsub" {
export interface PubsubMap {
@@ -31,7 +29,6 @@ declare module "~/shared/pubsub" {
}
type UseStyleData = {
treeId: Tree["id"];
publish: Publish;
selectedInstance: Instance;
};
@@ -56,34 +53,22 @@ export type CreateBatchUpdate = () => {
publish: (options?: StyleUpdateOptions) => void;
};
export const useStyleData = ({
treeId,
selectedInstance,
publish,
}: UseStyleData) => {
export const useStyleData = ({ selectedInstance, publish }: UseStyleData) => {
const [selectedBreakpoint] = useSelectedBreakpoint();
const instanceStyleSourcesStore = useMemo(() => {
return shallowComputed(
[styleSourceSelectionsIndexStore],
(styleSourceSelectionsIndex) => {
return (
styleSourceSelectionsIndex.styleSourcesByInstanceId.get(
selectedInstance.id
) ?? []
);
}
);
}, [selectedInstance.id]);
const instanceStyleSources = useStore(instanceStyleSourcesStore);
const localStyleSource = instanceStyleSources.find(
(styleSource) => styleSource.type === "local"
);
const currentStyle = useStyleInfo();
const publishUpdates = useCallback(
(type: "update" | "preview", updates: StyleUpdates["updates"]) => {
if (updates.length === 0 || selectedBreakpoint === undefined) {
const selectedStyleSource = selectedStyleSourceStore.get();
const selectedInstanceStyleSources =
selectedInstanceStyleSourcesStore.get();
if (
updates.length === 0 ||
selectedBreakpoint === undefined ||
selectedStyleSource === undefined
) {
return;
}
publish({
@@ -104,24 +89,24 @@ export const useStyleData = ({
(styleSourceSelections, styleSources, styles) => {
const instanceId = selectedInstance.id;
const breakpointId = selectedBreakpoint.id;
const styleSourceId = localStyleSource?.id ?? nanoid();
// crate style source and its selection on currently selected instance
if (localStyleSource === undefined) {
styleSources.push({
type: "local",
id: styleSourceId,
treeId,
});
replaceByOrAppendMutable(
styleSourceSelections,
{
instanceId,
values: [styleSourceId],
},
(styleSourceSelection) =>
styleSourceSelection.instanceId === instanceId
);
}
const styleSourceId = selectedStyleSource.id;
replaceByOrAppendMutable(
styleSources,
selectedStyleSource,
(item) => item.id === styleSourceId
);
const selections = selectedInstanceStyleSources.map(
(styleSource) => styleSource.id
);
replaceByOrAppendMutable(
styleSourceSelections,
{
instanceId,
values: [...selections, styleSourceId],
},
(styleSourceSelection) =>
styleSourceSelection.instanceId === instanceId
);
for (const update of updates) {
if (update.operation === "set") {
@@ -153,7 +138,7 @@ export const useStyleData = ({
}
);
},
[publish, selectedBreakpoint, selectedInstance, localStyleSource, treeId]
[publish, selectedBreakpoint, selectedInstance]
);
// @deprecated should not be called
@@ -6,7 +6,7 @@ import {
DeprecatedParagraph,
SearchField,
} from "@webstudio-is/design-system";
import type { Instance, Tree } from "@webstudio-is/project-build";
import type { Instance } from "@webstudio-is/project-build";
import { useStyleData } from "./shared/use-style-data";
import { StyleSettings } from "./style-settings";
import { useState } from "react";
@@ -17,19 +17,13 @@ import {
import { theme } from "@webstudio-is/design-system";
type StylePanelProps = {
treeId: Tree["id"];
publish: Publish;
selectedInstance: Instance;
};
export const StylePanel = ({
treeId,
selectedInstance,
publish,
}: StylePanelProps) => {
export const StylePanel = ({ selectedInstance, publish }: StylePanelProps) => {
const { currentStyle, setProperty, deleteProperty, createBatchUpdate } =
useStyleData({
treeId,
selectedInstance,
publish,
});
@@ -1,15 +1,17 @@
import { useMemo } from "react";
import { atom, computed, type WritableAtom } from "nanostores";
import { useStore } from "@nanostores/react";
import { nanoid } from "nanoid";
import type { AuthPermit } from "@webstudio-is/trpc-interface";
import type { Asset } from "@webstudio-is/asset-uploader";
import type {
import {
Instance,
Props,
Styles,
StyleSource,
StyleSources,
StyleSourceSelections,
Tree,
} from "@webstudio-is/project-build";
import type { Breakpoint, Style } from "@webstudio-is/css-data";
import type {
@@ -29,6 +31,13 @@ const useValue = <T>(atom: WritableAtom<T>) => {
return [value, atom.set] as const;
};
export const treeIdStore = atom<undefined | Tree["id"]>(undefined);
export const useSetTreeId = (treeId: Tree["id"]) => {
useSyncInitializeOnce(() => {
treeIdStore.set(treeId);
});
};
export const rootInstanceContainer = atom<Instance | undefined>();
export const useRootInstance = () => useValue(rootInstanceContainer);
export const useSetRootInstance = (root: Instance) => {
@@ -124,26 +133,8 @@ export const useSetStyleSourceSelections = (
});
};
export const styleSourceSelectionsIndexStore = computed(
[styleSourceSelectionsStore, styleSourcesIndexStore],
(styleSourceSelections, styleSourcesIndex) => {
const { styleSourcesById } = styleSourcesIndex;
const styleSourcesByInstanceId = new Map<Instance["id"], StyleSource[]>();
for (const { instanceId, values } of styleSourceSelections) {
const styleSources: StyleSources = [];
for (const styleSourceId of values) {
const styleSource = styleSourcesById.get(styleSourceId);
if (styleSource === undefined) {
continue;
}
styleSources.push(styleSource);
}
styleSourcesByInstanceId.set(instanceId, styleSources);
}
return {
styleSourcesByInstanceId,
};
}
const selectedStyleSourceIdStore = atom<undefined | StyleSource["id"]>(
undefined
);
/**
@@ -236,6 +227,55 @@ export const selectedInstanceStore = computed(
);
export const selectedInstanceBrowserStyleStore = atom<undefined | Style>();
export const selectedInstanceStyleSourcesStore = computed(
[
styleSourceSelectionsStore,
styleSourcesIndexStore,
selectedInstanceIdStore,
treeIdStore,
],
(styleSourceSelections, styleSourcesIndex, selectedInstanceId, treeId) => {
const styleSourceIds =
styleSourceSelections.find(
(styleSourceSelection) =>
styleSourceSelection.instanceId === selectedInstanceId
)?.values ?? [];
const { styleSourcesById } = styleSourcesIndex;
const selectedInstanceStyleSources: StyleSources = [];
// generate style source when selection is empty or not exist
// it is synchronized whenever instance style sources
// or styles are updated
if (styleSourceIds.length === 0 && treeId !== undefined) {
selectedInstanceStyleSources.push({
type: "local",
treeId,
id: nanoid(),
});
}
for (const styleSourceId of styleSourceIds) {
const styleSource = styleSourcesById.get(styleSourceId);
if (styleSource !== undefined) {
selectedInstanceStyleSources.push(styleSource);
}
}
return selectedInstanceStyleSources;
}
);
/**
* Provide selected style source with fallback
* to local style source of selected instance
*/
export const selectedStyleSourceStore = computed(
[selectedInstanceStyleSourcesStore, selectedStyleSourceIdStore],
(styleSources, selectedStyleSourceId) => {
return (
styleSources.find((item) => item.id === selectedStyleSourceId) ??
styleSources.find((item) => item.type === "local")
);
}
);
export const hoveredInstanceIdStore = atom<undefined | Instance["id"]>(
undefined
);