mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 04:55:06 +09:00
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:
1 parent
d00c646b10
commit
eb66eccca1
8 files changed
+105
-99
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user