mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 21:15:18 +09:00
Switch to normalized props on server (#883)
Ref https://github.com/webstudio-is/webstudio-designer/issues/696 Now all props is migrated on client side, we can drop legacy store and switch to new format server side.
This commit is contained in:
1 parent
ff049a000c
commit
8635dc6755
11 files changed
+50
-146
No files matched your search
@@ -1,13 +1,9 @@
|
||||
import { useEffect } from "react";
|
||||
import type { Instance, PropsItem } from "@webstudio-is/project-build";
|
||||
import {
|
||||
getComponentMeta,
|
||||
allUserPropsContainer,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
|
||||
import { useSubscribe } from "~/shared/pubsub";
|
||||
import { utils, type InstanceInsertionSpec } from "@webstudio-is/project";
|
||||
import store from "immerhin";
|
||||
import type { Instance, PropsItem } from "@webstudio-is/project-build";
|
||||
import { getComponentMeta } from "@webstudio-is/react-sdk";
|
||||
import { utils, type InstanceInsertionSpec } from "@webstudio-is/project";
|
||||
import { useSubscribe } from "~/shared/pubsub";
|
||||
import {
|
||||
propsStore,
|
||||
rootInstanceContainer,
|
||||
@@ -64,8 +60,8 @@ export const useInsertInstance = () => {
|
||||
({ instance, dropTarget, props: insertedProps }) => {
|
||||
const selectedInstanceId = selectedInstanceIdStore.get();
|
||||
store.createTransaction(
|
||||
[rootInstanceContainer, allUserPropsContainer, propsStore],
|
||||
(rootInstance, allUserProps, props) => {
|
||||
[rootInstanceContainer, propsStore],
|
||||
(rootInstance, props) => {
|
||||
if (rootInstance === undefined) {
|
||||
return;
|
||||
}
|
||||
@@ -78,7 +74,6 @@ export const useInsertInstance = () => {
|
||||
selectedInstanceIdStore.set(instance.id);
|
||||
}
|
||||
if (insertedProps !== undefined) {
|
||||
allUserProps[instance.id] = insertedProps;
|
||||
props.push(...insertedProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { ComponentStory } from "@storybook/react";
|
||||
import type { PropsItem } from "@webstudio-is/project-build";
|
||||
import {
|
||||
allUserPropsContainer,
|
||||
getComponentMetaProps,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { getComponentMetaProps } from "@webstudio-is/react-sdk";
|
||||
import { propsStore } from "~/shared/nano-states";
|
||||
import { PropsPanel } from "./props-panel";
|
||||
|
||||
export default {
|
||||
@@ -12,17 +10,15 @@ export default {
|
||||
};
|
||||
|
||||
export const NoProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
allUserPropsContainer.set({
|
||||
"1": [
|
||||
{
|
||||
id: "disabled",
|
||||
instanceId: "instanceId",
|
||||
name: "disabled",
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
propsStore.set([
|
||||
{
|
||||
id: "disabled",
|
||||
instanceId: "1",
|
||||
name: "disabled",
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
]);
|
||||
return (
|
||||
<PropsPanel
|
||||
selectedInstance={{
|
||||
@@ -38,9 +34,7 @@ export const NoProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
};
|
||||
|
||||
export const RequiredProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
allUserPropsContainer.set({
|
||||
"1": [],
|
||||
});
|
||||
propsStore.set([]);
|
||||
return (
|
||||
<PropsPanel
|
||||
selectedInstance={{
|
||||
@@ -56,9 +50,7 @@ export const RequiredProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
};
|
||||
|
||||
export const DefaultProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
allUserPropsContainer.set({
|
||||
"1": [],
|
||||
});
|
||||
propsStore.set([]);
|
||||
return (
|
||||
<PropsPanel
|
||||
selectedInstance={{
|
||||
@@ -76,15 +68,16 @@ export const DefaultProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
const meta = getComponentMetaProps("Button") ?? {};
|
||||
|
||||
export const AllProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
allUserPropsContainer.set({
|
||||
"3": Object.entries(meta).map(([name, value]) => {
|
||||
propsStore.set(
|
||||
Object.entries(meta).map(([name, value]) => {
|
||||
return {
|
||||
id: name,
|
||||
instanceId: "3",
|
||||
name,
|
||||
value: value?.defaultValue ?? "",
|
||||
} as PropsItem;
|
||||
}),
|
||||
});
|
||||
})
|
||||
);
|
||||
return (
|
||||
<PropsPanel
|
||||
selectedInstance={{
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import store from "immerhin";
|
||||
import type { Instance, PropsItem } from "@webstudio-is/project-build";
|
||||
import {
|
||||
allUserPropsContainer,
|
||||
getComponentMetaProps,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { type Publish } from "~/shared/pubsub";
|
||||
import { propsStore, useInstanceProps } from "~/shared/nano-states";
|
||||
import { Control } from "./control";
|
||||
import { CollapsibleSection, ComponentInfo } from "~/designer/shared/inspector";
|
||||
import { getComponentMetaProps } from "@webstudio-is/react-sdk";
|
||||
import {
|
||||
theme,
|
||||
Box,
|
||||
@@ -30,10 +23,14 @@ import {
|
||||
ExclamationTriangleIcon,
|
||||
ChevronDownIcon,
|
||||
} from "@webstudio-is/icons";
|
||||
import { type Publish } from "~/shared/pubsub";
|
||||
import { propsStore, useInstanceProps } from "~/shared/nano-states";
|
||||
import { CollapsibleSection, ComponentInfo } from "~/designer/shared/inspector";
|
||||
import {
|
||||
removeByMutable,
|
||||
replaceByOrAppendMutable,
|
||||
} from "~/shared/array-utils";
|
||||
import { Control } from "./control";
|
||||
import { usePropsLogic, type UserPropValue } from "./use-props-logic";
|
||||
import {
|
||||
useStyleData,
|
||||
@@ -236,39 +233,19 @@ export const PropsPanel = ({ selectedInstance, publish }: PropsPanelProps) => {
|
||||
selectedInstance,
|
||||
|
||||
updateProps: (update) => {
|
||||
store.createTransaction(
|
||||
[allUserPropsContainer, propsStore],
|
||||
(allUserProps, props) => {
|
||||
let instanceProps = allUserProps[instanceId];
|
||||
if (instanceProps === undefined) {
|
||||
instanceProps = [];
|
||||
allUserProps[instanceId] = instanceProps;
|
||||
}
|
||||
replaceByOrAppendMutable(
|
||||
instanceProps,
|
||||
update,
|
||||
(item) => item.id === update.id
|
||||
);
|
||||
replaceByOrAppendMutable(
|
||||
props,
|
||||
update,
|
||||
(item) => item.id === update.id
|
||||
);
|
||||
}
|
||||
);
|
||||
store.createTransaction([propsStore], (props) => {
|
||||
replaceByOrAppendMutable(
|
||||
props,
|
||||
update,
|
||||
(item) => item.id === update.id
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
deleteProp: (id) => {
|
||||
store.createTransaction(
|
||||
[allUserPropsContainer, propsStore],
|
||||
(allUserProps, props) => {
|
||||
const instanceProps = allUserProps[instanceId];
|
||||
if (instanceProps) {
|
||||
removeByMutable(instanceProps, (prop) => prop.id === id);
|
||||
}
|
||||
removeByMutable(props, (prop) => prop.id === id);
|
||||
}
|
||||
);
|
||||
store.createTransaction([propsStore], (props) => {
|
||||
removeByMutable(props, (prop) => prop.id === id);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -24,9 +24,8 @@ export const loadCanvasData = async (
|
||||
throw new Error(`Page ${pageIdOrPath} not found`);
|
||||
}
|
||||
|
||||
const [tree, props, breakpoints, designTokens, assets] = await Promise.all([
|
||||
const [tree, breakpoints, designTokens, assets] = await Promise.all([
|
||||
projectDb.tree.loadById(page.treeId),
|
||||
projectDb.props.loadByTreeId(page.treeId),
|
||||
projectDb.breakpoints.load(build.id),
|
||||
designTokensDb.load(build.id),
|
||||
loadByProject(project.id),
|
||||
@@ -42,7 +41,6 @@ export const loadCanvasData = async (
|
||||
|
||||
return {
|
||||
tree,
|
||||
props,
|
||||
breakpoints: breakpoints.values,
|
||||
designTokens,
|
||||
buildId: build.id,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import store, { type Change } from "immerhin";
|
||||
import type { WritableAtom } from "nanostores";
|
||||
import { useEffect } from "react";
|
||||
import { allUserPropsContainer } from "@webstudio-is/react-sdk";
|
||||
import { type Publish, subscribe } from "~/shared/pubsub";
|
||||
import {
|
||||
rootInstanceContainer,
|
||||
@@ -44,8 +43,7 @@ export const registerContainers = () => {
|
||||
store.register("breakpoints", breakpointsContainer);
|
||||
store.register("root", rootInstanceContainer);
|
||||
store.register("styles", stylesContainer);
|
||||
store.register("props", allUserPropsContainer);
|
||||
store.register("props-modern", propsStore);
|
||||
store.register("props", propsStore);
|
||||
store.register("designTokens", designTokensContainer);
|
||||
// synchronize whole states
|
||||
clientStores.set("selectedInstanceId", selectedInstanceIdStore);
|
||||
@@ -143,7 +141,6 @@ const syncStoresState = (name: SyncEventSource, publish: Publish) => {
|
||||
],
|
||||
},
|
||||
});
|
||||
//
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import warnOnce from "warn-once";
|
||||
import { type Tree, StoredProps, Props } from "@webstudio-is/project-build";
|
||||
import { type AllUserProps, type InstanceProps } from "@webstudio-is/react-sdk";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import { prisma } from "@webstudio-is/prisma-client";
|
||||
import { formatAsset } from "@webstudio-is/asset-uploader/server";
|
||||
@@ -59,35 +58,6 @@ export const parseProps = async (propsString: string) => {
|
||||
return props;
|
||||
};
|
||||
|
||||
export const loadByTreeId = async (treeId: Tree["id"]) => {
|
||||
const tree = await prisma.tree.findUnique({
|
||||
where: { id: treeId },
|
||||
});
|
||||
|
||||
if (tree === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const props = await parseProps(tree.props);
|
||||
|
||||
const instancePropsMap = new Map<string, InstanceProps>();
|
||||
for (const propsItem of props) {
|
||||
let instanceProps = instancePropsMap.get(propsItem.instanceId);
|
||||
if (instanceProps === undefined) {
|
||||
instanceProps = {
|
||||
id: "",
|
||||
instanceId: propsItem.instanceId,
|
||||
treeId,
|
||||
props: [],
|
||||
};
|
||||
instancePropsMap.set(propsItem.instanceId, instanceProps);
|
||||
}
|
||||
instanceProps.props.push(propsItem);
|
||||
}
|
||||
|
||||
return Array.from(instancePropsMap.values());
|
||||
};
|
||||
|
||||
export const serializeProps = (props: Props) => {
|
||||
const storedProps: StoredProps = [];
|
||||
for (const prop of props) {
|
||||
@@ -111,25 +81,18 @@ export const patch = async (
|
||||
{ treeId }: { treeId: Tree["id"] },
|
||||
patches: Array<Patch>
|
||||
) => {
|
||||
const allProps = await loadByTreeId(treeId);
|
||||
|
||||
// We should get rid of this by applying patches on the client to the original
|
||||
// model instead of the instanceId map.
|
||||
// The map is handy for accessing props, but probably should be just cached interface, not the main data structure.
|
||||
const allPropsMapByInstanceId = allProps.reduce((acc, prop) => {
|
||||
acc[prop.instanceId] = prop.props;
|
||||
return acc;
|
||||
}, {} as AllUserProps);
|
||||
const nextProps = applyPatches<AllUserProps>(
|
||||
allPropsMapByInstanceId,
|
||||
patches
|
||||
);
|
||||
|
||||
const props: Props = Object.values(nextProps).flat();
|
||||
const tree = await prisma.tree.findUnique({
|
||||
where: { id: treeId },
|
||||
});
|
||||
if (tree === null) {
|
||||
return;
|
||||
}
|
||||
const props = await parseProps(tree.props);
|
||||
const patchedProps = applyPatches(props, patches);
|
||||
|
||||
await prisma.tree.update({
|
||||
data: {
|
||||
props: serializeProps(props),
|
||||
props: serializeProps(patchedProps),
|
||||
},
|
||||
where: { id: treeId },
|
||||
});
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./types";
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { InstanceProps as DbInstanceProps } from "@webstudio-is/prisma-client";
|
||||
import type { PropsItem } from "@webstudio-is/project-build";
|
||||
|
||||
export type InstanceProps = Omit<DbInstanceProps, "props"> & {
|
||||
props: Array<PropsItem>;
|
||||
};
|
||||
@@ -1,9 +1,7 @@
|
||||
export * from "./css";
|
||||
export * from "./tree";
|
||||
export * from "./components";
|
||||
export * from "./user-props/all-user-props";
|
||||
export * from "./pubsub";
|
||||
export * from "./db";
|
||||
export * from "./app";
|
||||
export {
|
||||
customComponents,
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Breakpoint } from "@webstudio-is/css-data";
|
||||
import type { DesignToken } from "@webstudio-is/design-tokens";
|
||||
import type { Tree } from "@webstudio-is/project-build";
|
||||
import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
import type { InstanceProps } from "../db";
|
||||
import { createElementsTree } from "./create-elements-tree";
|
||||
import { WrapperComponent } from "./wrapper-component";
|
||||
import { registerComponents } from "../components";
|
||||
@@ -16,7 +15,6 @@ export type Data = {
|
||||
tree: Tree | null;
|
||||
breakpoints: Array<Breakpoint>;
|
||||
designTokens: Array<DesignToken>;
|
||||
props: Array<InstanceProps>;
|
||||
assets: Array<Asset>;
|
||||
params?: Params;
|
||||
};
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { atom } from "nanostores";
|
||||
import type { Instance, PropsItem } from "@webstudio-is/project-build";
|
||||
|
||||
export type AllUserProps = {
|
||||
[id: Instance["id"]]: PropsItem[];
|
||||
};
|
||||
|
||||
export const allUserPropsContainer = atom<AllUserProps>({});
|
||||
Reference in new issue
Block a user