mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-07 20:44:05 +09:00
Replace UserProp with PropsItem (#833)
Here is migration to new props type with more safe conversion to db format. Added two versions StoredPropsItem for and PropsItem for client and got rid of "as" casting.
This commit is contained in:
1 parent
ca08925962
commit
bb288f309b
20 files changed
+147
-158
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import type { Instance, Styles, UserProp } from "@webstudio-is/react-sdk";
|
||||
import type { Instance, PropsItem, Styles } from "@webstudio-is/react-sdk";
|
||||
import { getBrowserStyle } from "@webstudio-is/react-sdk";
|
||||
import { publish, subscribe, subscribeAll } from "~/shared/pubsub";
|
||||
import {
|
||||
@@ -53,7 +53,7 @@ export const SelectedInstanceConnector = ({
|
||||
instanceElementRef: { current: undefined | HTMLElement };
|
||||
instance: Instance;
|
||||
instanceStyles: Styles;
|
||||
instanceProps: undefined | UserProp[];
|
||||
instanceProps: undefined | PropsItem[];
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
const element = instanceElementRef.current;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext
|
||||
import {
|
||||
type Instance,
|
||||
type OnChangeChildren,
|
||||
type UserProp,
|
||||
type PropsItem,
|
||||
renderWrapperComponentChildren,
|
||||
getComponent,
|
||||
idAttribute,
|
||||
@@ -44,7 +44,7 @@ const ContentEditable = ({
|
||||
return <Component ref={ref} {...props} contentEditable={true} />;
|
||||
};
|
||||
|
||||
type UserProps = Record<UserProp["prop"], string | number | boolean>;
|
||||
type UserProps = Record<PropsItem["name"], string | number | boolean>;
|
||||
|
||||
type WrapperComponentDevProps = {
|
||||
instance: Instance;
|
||||
@@ -81,7 +81,7 @@ export const WrapperComponentDev = ({
|
||||
}
|
||||
for (const item of instanceProps) {
|
||||
if (item.type !== "asset") {
|
||||
result[item.prop] = item.value;
|
||||
result[item.name] = item.value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
type Instance,
|
||||
type UserProp,
|
||||
type PropsItem,
|
||||
getComponentMeta,
|
||||
allUserPropsContainer,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
@@ -31,7 +31,7 @@ declare module "~/shared/pubsub" {
|
||||
insertInstance: {
|
||||
instance: Instance;
|
||||
dropTarget?: { parentId: Instance["id"]; position: number };
|
||||
props?: Array<UserProp>;
|
||||
props?: Array<PropsItem>;
|
||||
};
|
||||
unselectInstance: undefined;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ export const Primitive: ComponentStoryObj<typeof Control> = {
|
||||
args: {
|
||||
component: "Image",
|
||||
userProp: {
|
||||
prop: "src",
|
||||
name: "src",
|
||||
id: "ddf20afe-65a8-478e-a52d-1be2d2f70830",
|
||||
instanceId: "instanceId",
|
||||
type: "string",
|
||||
value: "",
|
||||
},
|
||||
@@ -21,8 +22,9 @@ export const Options: ComponentStoryObj<typeof Control> = {
|
||||
args: {
|
||||
component: "Image",
|
||||
userProp: {
|
||||
prop: "loading",
|
||||
name: "loading",
|
||||
id: "ddf20afe-65a8-478e-a52d-1be2d2f70830",
|
||||
instanceId: "instanceId",
|
||||
type: "string",
|
||||
value: "",
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getComponentMetaProps,
|
||||
type Instance,
|
||||
type UserProp,
|
||||
type PropsItem,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import warnOnce from "warn-once";
|
||||
import {
|
||||
@@ -179,7 +179,7 @@ const assertUnreachable = (_arg: never, errorMessage: string) => {
|
||||
|
||||
type ControlProps = {
|
||||
component: Instance["component"];
|
||||
userProp: UserProp;
|
||||
userProp: PropsItem;
|
||||
onChangePropValue: (value: UserPropValue) => void;
|
||||
setCssProperty: SetProperty;
|
||||
};
|
||||
@@ -193,7 +193,7 @@ export function Control({
|
||||
}: ControlProps) {
|
||||
const meta = getComponentMetaProps(component);
|
||||
|
||||
const argType = meta[userProp.prop];
|
||||
const argType = meta[userProp.name];
|
||||
|
||||
// argType can be undefined in case of new property created
|
||||
const defaultValue = argType?.defaultValue ?? "";
|
||||
@@ -202,7 +202,7 @@ export function Control({
|
||||
if (type == null) {
|
||||
warnOnce(
|
||||
true,
|
||||
`No control type for prop "${userProp.prop}" component "${component}" found`
|
||||
`No control type for prop "${userProp.name}" component "${component}" found`
|
||||
);
|
||||
return <NotImplemented />;
|
||||
}
|
||||
@@ -211,14 +211,14 @@ export function Control({
|
||||
warnOnce(
|
||||
true,
|
||||
`Control type "${typeof type}" for prop "${
|
||||
userProp.prop
|
||||
userProp.name
|
||||
}" component "${component}" is not a string`
|
||||
);
|
||||
|
||||
return <NotImplemented />;
|
||||
}
|
||||
|
||||
if (component === "Image" && userProp.prop === "src") {
|
||||
if (component === "Image" && userProp.name === "src") {
|
||||
const asset = userProp.type === "asset" ? userProp.value : null;
|
||||
|
||||
return (
|
||||
@@ -298,7 +298,7 @@ export function Control({
|
||||
|
||||
warnOnce(
|
||||
options == null,
|
||||
`options is not an array of strings for prop: ${userProp.prop} component: ${component}`
|
||||
`options is not an array of strings for prop: ${userProp.name} component: ${component}`
|
||||
);
|
||||
|
||||
const DEFAULT_OPTIONS: string[] = [];
|
||||
@@ -356,7 +356,7 @@ export function Control({
|
||||
|
||||
warnOnce(
|
||||
true,
|
||||
`Control type "${type}" is not implemented for prop: "${userProp.prop}" in component "${component}"`
|
||||
`Control type "${type}" is not implemented for prop: "${userProp.name}" in component "${component}"`
|
||||
);
|
||||
|
||||
return <NotImplemented />;
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ComponentStory } from "@storybook/react";
|
||||
import {
|
||||
allUserPropsContainer,
|
||||
getComponentMetaProps,
|
||||
type UserProp,
|
||||
type PropsItem,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { PropsPanel } from "./props-panel";
|
||||
|
||||
@@ -16,7 +16,8 @@ export const NoProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
"1": [
|
||||
{
|
||||
id: "disabled",
|
||||
prop: "disabled",
|
||||
instanceId: "instanceId",
|
||||
name: "disabled",
|
||||
type: "boolean",
|
||||
value: true,
|
||||
},
|
||||
@@ -73,12 +74,12 @@ const meta = getComponentMetaProps("Button");
|
||||
|
||||
export const AllProps: ComponentStory<typeof PropsPanel> = () => {
|
||||
allUserPropsContainer.set({
|
||||
"3": Object.entries(meta).map(([prop, value]) => {
|
||||
"3": Object.entries(meta).map(([name, value]) => {
|
||||
return {
|
||||
id: prop,
|
||||
prop,
|
||||
id: name,
|
||||
name,
|
||||
value: value?.defaultValue ?? "",
|
||||
} as UserProp;
|
||||
} as PropsItem;
|
||||
}),
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
getComponentMetaProps,
|
||||
useAllUserProps,
|
||||
type Instance,
|
||||
type UserProp,
|
||||
type PropsItem,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { type Publish } from "~/shared/pubsub";
|
||||
import { Control } from "./control";
|
||||
@@ -124,7 +124,7 @@ const Combobox = ({
|
||||
};
|
||||
|
||||
type PropertyProps = {
|
||||
userProp: UserProp;
|
||||
userProp: PropsItem;
|
||||
component: Instance["component"];
|
||||
onChangePropName: (name: string) => void;
|
||||
onChangePropValue: (value: UserPropValue) => void;
|
||||
@@ -146,12 +146,12 @@ const Property = ({
|
||||
}: PropertyProps) => {
|
||||
const metaProps = getComponentMetaProps(component);
|
||||
|
||||
const argType = metaProps[userProp.prop as keyof typeof metaProps];
|
||||
const argType = metaProps[userProp.name as keyof typeof metaProps];
|
||||
const isInvalid =
|
||||
userProp.prop != null &&
|
||||
userProp.prop.length > 0 &&
|
||||
userProp.name != null &&
|
||||
userProp.name.length > 0 &&
|
||||
typeof argType === "undefined" &&
|
||||
!userProp.prop.match(/^data-(.)+/);
|
||||
!userProp.name.match(/^data-(.)+/);
|
||||
|
||||
const allProps = Object.keys(metaProps).filter(
|
||||
(propName) => existingProps.includes(propName) === false
|
||||
@@ -167,12 +167,12 @@ const Property = ({
|
||||
placeholder="Property"
|
||||
readOnly={true}
|
||||
state={isInvalid ? "invalid" : undefined}
|
||||
value={userProp.prop}
|
||||
value={userProp.name}
|
||||
/>
|
||||
) : (
|
||||
<Combobox
|
||||
items={allProps}
|
||||
value={userProp.prop}
|
||||
value={userProp.name}
|
||||
onItemSelect={(name) => {
|
||||
if (name != null) {
|
||||
setError(undefined);
|
||||
@@ -195,7 +195,7 @@ const Property = ({
|
||||
/>
|
||||
)}
|
||||
{isInvalid || error !== undefined ? (
|
||||
<Tooltip content={error ?? `Invalid property name: ${userProp.prop}`}>
|
||||
<Tooltip content={error ?? `Invalid property name: ${userProp.name}`}>
|
||||
<ExclamationTriangleIcon width={12} height={12} />
|
||||
</Tooltip>
|
||||
) : (
|
||||
@@ -282,7 +282,7 @@ export const PropsPanel = ({
|
||||
/>
|
||||
);
|
||||
|
||||
const existingProps = userProps.map((userProp) => userProp.prop);
|
||||
const existingProps = userProps.map((userProp) => userProp.name);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
|
||||
@@ -27,7 +27,7 @@ describe("usePropsLogic", () => {
|
||||
);
|
||||
expect(result.current.userProps.length).toEqual(1);
|
||||
expect(result.current.userProps[0]).toMatchObject({
|
||||
prop: "href",
|
||||
name: "href",
|
||||
value: "",
|
||||
});
|
||||
});
|
||||
@@ -50,11 +50,11 @@ describe("usePropsLogic", () => {
|
||||
})
|
||||
);
|
||||
expect(res1.current.userProps[0]).toMatchObject({
|
||||
prop: "tag",
|
||||
name: "tag",
|
||||
value: "h1",
|
||||
});
|
||||
expect(res2.current.userProps[0]).toMatchObject({
|
||||
prop: "type",
|
||||
name: "type",
|
||||
value: "submit",
|
||||
});
|
||||
});
|
||||
@@ -70,7 +70,7 @@ describe("usePropsLogic", () => {
|
||||
);
|
||||
expect(result.current.userProps.length).toEqual(1);
|
||||
expect(result.current.userProps[0]).toMatchObject({
|
||||
prop: "type",
|
||||
name: "type",
|
||||
value: "submit",
|
||||
});
|
||||
});
|
||||
@@ -81,7 +81,8 @@ describe("usePropsLogic", () => {
|
||||
props: [
|
||||
{
|
||||
id: "default",
|
||||
prop: "type",
|
||||
instanceId: "instanceId",
|
||||
name: "type",
|
||||
type: "string",
|
||||
value: "submit",
|
||||
},
|
||||
@@ -93,7 +94,7 @@ describe("usePropsLogic", () => {
|
||||
);
|
||||
expect(result.current.userProps.length).toEqual(1);
|
||||
expect(result.current.userProps[0]).toMatchObject({
|
||||
prop: "type",
|
||||
name: "type",
|
||||
value: "submit",
|
||||
});
|
||||
});
|
||||
@@ -115,7 +116,7 @@ describe("usePropsLogic", () => {
|
||||
});
|
||||
|
||||
expect(result.current.userProps.length).toEqual(2);
|
||||
expect(result.current.userProps[1]).toMatchObject({ prop: "", value: "" });
|
||||
expect(result.current.userProps[1]).toMatchObject({ name: "", value: "" });
|
||||
});
|
||||
|
||||
test("should respect initialProps ordering", () => {
|
||||
@@ -124,35 +125,40 @@ describe("usePropsLogic", () => {
|
||||
props: [
|
||||
{
|
||||
id: "22",
|
||||
prop: "aria-label",
|
||||
instanceId: "instanceId",
|
||||
name: "aria-label",
|
||||
type: "string",
|
||||
value: "https://example.com",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: "0",
|
||||
prop: "alt",
|
||||
instanceId: "instanceId",
|
||||
name: "alt",
|
||||
type: "string",
|
||||
value: "alt text",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
prop: "width",
|
||||
instanceId: "instanceId",
|
||||
name: "width",
|
||||
type: "number",
|
||||
value: 101,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: "33",
|
||||
prop: "title",
|
||||
instanceId: "instanceId",
|
||||
name: "title",
|
||||
type: "string",
|
||||
value: "title text",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
prop: "src",
|
||||
instanceId: "instanceId",
|
||||
name: "src",
|
||||
type: "string",
|
||||
value: "https://example.com",
|
||||
required: true,
|
||||
@@ -166,7 +172,7 @@ describe("usePropsLogic", () => {
|
||||
|
||||
const imgMeta = getComponentMeta("Image");
|
||||
|
||||
const propNames = result.current.userProps.map((userProp) => userProp.prop);
|
||||
const propNames = result.current.userProps.map((userProp) => userProp.name);
|
||||
|
||||
expect(imgMeta.initialProps).toEqual(
|
||||
propNames.slice(0, imgMeta.initialProps?.length ?? 0)
|
||||
@@ -175,9 +181,9 @@ describe("usePropsLogic", () => {
|
||||
expect(propNames.slice(imgMeta.initialProps?.length ?? 0)).toEqual(
|
||||
result.current.userProps
|
||||
.filter(
|
||||
(userProp) => imgMeta.initialProps?.includes(userProp.prop) === false
|
||||
(userProp) => imgMeta.initialProps?.includes(userProp.name) === false
|
||||
)
|
||||
.map((userProp) => userProp.prop)
|
||||
.map((userProp) => userProp.name)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -194,7 +200,8 @@ describe("usePropsLogic", () => {
|
||||
expect(
|
||||
result.current.isRequired({
|
||||
id: "2",
|
||||
prop: "src",
|
||||
instanceId: "instanceId",
|
||||
name: "src",
|
||||
type: "string",
|
||||
value: "https://example.com",
|
||||
required: false,
|
||||
@@ -215,7 +222,8 @@ describe("usePropsLogic", () => {
|
||||
expect(
|
||||
result.current.isRequired({
|
||||
id: "2",
|
||||
prop: "arial-label",
|
||||
instanceId: "instanceId",
|
||||
name: "arial-label",
|
||||
type: "string",
|
||||
value: "https://example.com",
|
||||
required: true,
|
||||
@@ -225,7 +233,8 @@ describe("usePropsLogic", () => {
|
||||
expect(
|
||||
result.current.isRequired({
|
||||
id: "2",
|
||||
prop: "arial-label",
|
||||
instanceId: "instanceId",
|
||||
name: "arial-label",
|
||||
type: "string",
|
||||
value: "https://example.com",
|
||||
required: false,
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useMemo, useState } from "react";
|
||||
import ObjectId from "bson-objectid";
|
||||
import warnOnce from "warn-once";
|
||||
import type {
|
||||
UserProp,
|
||||
Instance,
|
||||
PropsItem,
|
||||
MetaProps,
|
||||
ComponentName,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import type { SelectedInstanceData } from "@webstudio-is/project";
|
||||
|
||||
export type UserPropValue = UserProp extends infer T
|
||||
export type UserPropValue = PropsItem extends infer T
|
||||
? T extends { value: unknown; type: unknown }
|
||||
? { value: T["value"]; type: T["type"] }
|
||||
: never
|
||||
@@ -73,20 +74,25 @@ const getRequiredPropsList = (component: ComponentName) => {
|
||||
};
|
||||
|
||||
type UsePropsLogic = {
|
||||
props: UserProp[];
|
||||
props: PropsItem[];
|
||||
selectedInstanceData: SelectedInstanceData;
|
||||
updateProps: (update: UserProp) => void;
|
||||
deleteProp: (id: UserProp["id"]) => void;
|
||||
updateProps: (update: PropsItem) => void;
|
||||
deleteProp: (id: PropsItem["id"]) => void;
|
||||
};
|
||||
|
||||
const getPropsItemFromMetaProps = (metaProps: MetaProps, name: string) => {
|
||||
const getPropsItemFromMetaProps = (
|
||||
instanceId: Instance["id"],
|
||||
metaProps: MetaProps,
|
||||
name: string
|
||||
) => {
|
||||
const metaPropValue = metaProps[name];
|
||||
if (metaPropValue === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
id: ObjectId().toString(),
|
||||
prop: name,
|
||||
instanceId,
|
||||
name,
|
||||
...getValueFromPropMeta(metaPropValue),
|
||||
};
|
||||
};
|
||||
@@ -100,7 +106,7 @@ export const usePropsLogic = ({
|
||||
updateProps,
|
||||
deleteProp,
|
||||
}: UsePropsLogic) => {
|
||||
const { component } = selectedInstanceData;
|
||||
const { id: instanceId, component } = selectedInstanceData;
|
||||
|
||||
const metaProps = useMemo(
|
||||
() => getComponentMetaProps(component),
|
||||
@@ -110,24 +116,24 @@ export const usePropsLogic = ({
|
||||
() => getRequiredPropsList(component),
|
||||
[component]
|
||||
);
|
||||
const requiredPropsByName = new Map<string, UserProp>();
|
||||
const requiredPropsByName = new Map<string, PropsItem>();
|
||||
for (const name of requiredPropsList) {
|
||||
const propsItem = getPropsItemFromMetaProps(metaProps, name);
|
||||
const propsItem = getPropsItemFromMetaProps(instanceId, metaProps, name);
|
||||
if (propsItem) {
|
||||
requiredPropsByName.set(name, propsItem);
|
||||
}
|
||||
}
|
||||
|
||||
const [addedProps, setAddedProps] = useState<UserProp[]>([]);
|
||||
const addedPropsById = new Map<string, UserProp>();
|
||||
const [addedProps, setAddedProps] = useState<PropsItem[]>([]);
|
||||
const addedPropsById = new Map<string, PropsItem>();
|
||||
for (const propsItem of addedProps) {
|
||||
addedPropsById.set(propsItem.id, propsItem);
|
||||
}
|
||||
|
||||
const propsById = new Map<string, UserProp>();
|
||||
const propsById = new Map<string, PropsItem>();
|
||||
for (const propsItem of props) {
|
||||
if (requiredPropsByName.has(propsItem.prop)) {
|
||||
requiredPropsByName.set(propsItem.prop, propsItem);
|
||||
if (requiredPropsByName.has(propsItem.name)) {
|
||||
requiredPropsByName.set(propsItem.name, propsItem);
|
||||
continue;
|
||||
}
|
||||
if (addedPropsById.has(propsItem.id)) {
|
||||
@@ -145,20 +151,23 @@ export const usePropsLogic = ({
|
||||
...addedPropsById.values(),
|
||||
];
|
||||
|
||||
const handleChangePropName = (propsItem: UserProp, name: string) => {
|
||||
const handleChangePropName = (propsItem: PropsItem, name: string) => {
|
||||
// prevent changing name of required props
|
||||
if (isRequired(propsItem)) {
|
||||
return;
|
||||
}
|
||||
const typedValue = getValueFromPropMeta(metaProps[name]);
|
||||
updateProps({ id: propsItem.id, prop: name, ...typedValue });
|
||||
updateProps({ id: propsItem.id, instanceId, name, ...typedValue });
|
||||
};
|
||||
|
||||
const handleChangePropValue = (propsItem: UserProp, value: UserPropValue) => {
|
||||
const handleChangePropValue = (
|
||||
propsItem: PropsItem,
|
||||
value: UserPropValue
|
||||
) => {
|
||||
updateProps({ ...propsItem, ...value });
|
||||
};
|
||||
|
||||
const handleDeleteProp = (propsItem: UserProp) => {
|
||||
const handleDeleteProp = (propsItem: PropsItem) => {
|
||||
// required prop should never be deleted
|
||||
if (isRequired(propsItem)) {
|
||||
return;
|
||||
@@ -173,15 +182,16 @@ export const usePropsLogic = ({
|
||||
...prev,
|
||||
{
|
||||
id: ObjectId().toString(),
|
||||
prop: "",
|
||||
instanceId,
|
||||
name: "",
|
||||
type: "string",
|
||||
value: "",
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const isRequired = (propsItem: UserProp) => {
|
||||
return propsItem.required || requiredPropsList.includes(propsItem.prop);
|
||||
const isRequired = (propsItem: PropsItem) => {
|
||||
return propsItem.required || requiredPropsList.includes(propsItem.name);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Instance, UserProps } from "@webstudio-is/react-sdk";
|
||||
import { Instance, Props } from "@webstudio-is/react-sdk";
|
||||
import { z } from "zod";
|
||||
|
||||
const TYPE = "@webstudio/instance/v0.1" as const;
|
||||
|
||||
const InstanceCopyData = z.object({
|
||||
instance: Instance,
|
||||
props: UserProps.optional(),
|
||||
props: Props.optional(),
|
||||
});
|
||||
|
||||
export type InstanceCopyData = z.infer<typeof InstanceCopyData>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { utils } from "@webstudio-is/project";
|
||||
import type { Instance, UserProp } from "@webstudio-is/react-sdk";
|
||||
import type { Instance, PropsItem } from "@webstudio-is/react-sdk";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { type InstanceCopyData, serialize, deserialize } from "./serialize";
|
||||
|
||||
@@ -38,7 +38,7 @@ const isInstanceClipboardEvent = (
|
||||
type Props = {
|
||||
selectedInstanceData?: InstanceCopyData | undefined;
|
||||
allowAnyTarget?: boolean;
|
||||
onPaste: (instance: Instance, props?: UserProp[]) => void;
|
||||
onPaste: (instance: Instance, props?: PropsItem[]) => void;
|
||||
onCut: (instance: Instance) => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import {
|
||||
type Tree,
|
||||
type AllUserProps,
|
||||
type InstanceProps,
|
||||
type PropsItem,
|
||||
Props,
|
||||
StoredProps,
|
||||
} from "@webstudio-is/react-sdk";
|
||||
import { applyPatches, type Patch } from "immer";
|
||||
import { prisma } from "@webstudio-is/prisma-client";
|
||||
@@ -19,7 +18,7 @@ export const loadByTreeId = async (treeId: Tree["id"]) => {
|
||||
return [];
|
||||
}
|
||||
|
||||
const props = Props.parse(JSON.parse(tree.props));
|
||||
const props = StoredProps.parse(JSON.parse(tree.props));
|
||||
|
||||
// Fin all assetId in all props
|
||||
const assetIds: string[] = [];
|
||||
@@ -64,7 +63,8 @@ export const loadByTreeId = async (treeId: Tree["id"]) => {
|
||||
if (asset) {
|
||||
instanceProps.props.push({
|
||||
id: propsItem.id,
|
||||
prop: propsItem.name,
|
||||
instanceId: propsItem.instanceId,
|
||||
name: propsItem.name,
|
||||
required: propsItem.required,
|
||||
type: propsItem.type,
|
||||
value: asset,
|
||||
@@ -77,13 +77,7 @@ export const loadByTreeId = async (treeId: Tree["id"]) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
instanceProps.props.push({
|
||||
id: propsItem.id,
|
||||
prop: propsItem.name,
|
||||
required: propsItem.required,
|
||||
type: propsItem.type,
|
||||
value: propsItem.value,
|
||||
} as InstanceProps["props"][number]);
|
||||
instanceProps.props.push(propsItem);
|
||||
}
|
||||
|
||||
return Array.from(instancePropsMap.values());
|
||||
@@ -107,28 +101,21 @@ export const patch = async (
|
||||
patches
|
||||
);
|
||||
|
||||
const props: Props = [];
|
||||
const props: StoredProps = [];
|
||||
for (const [instanceId, instanceProps] of Object.entries(nextProps)) {
|
||||
for (const prop of instanceProps) {
|
||||
if (prop.type === "asset") {
|
||||
for (const propsItem of instanceProps) {
|
||||
if (propsItem.type === "asset") {
|
||||
props.push({
|
||||
id: prop.id,
|
||||
id: propsItem.id,
|
||||
instanceId,
|
||||
name: prop.prop,
|
||||
required: prop.required,
|
||||
type: prop.type,
|
||||
value: prop.value.id,
|
||||
name: propsItem.name,
|
||||
required: propsItem.required,
|
||||
type: propsItem.type,
|
||||
value: propsItem.value.id,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
props.push({
|
||||
id: prop.id,
|
||||
instanceId,
|
||||
name: prop.prop,
|
||||
required: prop.required,
|
||||
type: prop.type,
|
||||
value: prop.value,
|
||||
} as PropsItem);
|
||||
props.push(propsItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UserProp } from "@webstudio-is/react-sdk";
|
||||
import type { PropsItem } from "@webstudio-is/react-sdk";
|
||||
import ObjectId from "bson-objectid";
|
||||
|
||||
export const cloneUserProps = (props: Array<UserProp>): Array<UserProp> =>
|
||||
export const cloneUserProps = (props: Array<PropsItem>): Array<PropsItem> =>
|
||||
props.map((prop) => ({ ...prop, id: ObjectId().toString() }));
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { Asset } from "@webstudio-is/asset-uploader";
|
||||
|
||||
const basePropsItem = {
|
||||
id: z.string(),
|
||||
@@ -7,7 +8,7 @@ const basePropsItem = {
|
||||
required: z.optional(z.boolean()),
|
||||
};
|
||||
|
||||
export const PropsItem = z.discriminatedUnion("type", [
|
||||
const SharedPropsItem = z.union([
|
||||
z.object({
|
||||
...basePropsItem,
|
||||
type: z.literal("number"),
|
||||
@@ -23,14 +24,33 @@ export const PropsItem = z.discriminatedUnion("type", [
|
||||
type: z.literal("boolean"),
|
||||
value: z.boolean(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const StoredPropsItem = z.union([
|
||||
SharedPropsItem,
|
||||
z.object({
|
||||
...basePropsItem,
|
||||
type: z.literal("asset"),
|
||||
// In database we hold asset.id
|
||||
// asset.id is stored in database
|
||||
value: z.string(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type StoredPropsItem = z.infer<typeof StoredPropsItem>;
|
||||
|
||||
export const StoredProps = z.array(StoredPropsItem);
|
||||
|
||||
export type StoredProps = z.infer<typeof StoredProps>;
|
||||
|
||||
export const PropsItem = z.union([
|
||||
SharedPropsItem,
|
||||
z.object({
|
||||
...basePropsItem,
|
||||
type: z.literal("asset"),
|
||||
value: Asset,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type PropsItem = z.infer<typeof PropsItem>;
|
||||
|
||||
export const Props = z.array(PropsItem);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { InstanceProps as DbInstanceProps } from "@webstudio-is/prisma-client";
|
||||
import type { UserProp } from "../user-props";
|
||||
import type { Instance } from "./instance";
|
||||
import type { Props } from "./props";
|
||||
import type { Props, PropsItem } from "./props";
|
||||
import type { PresetStyles, Styles } from "./style";
|
||||
|
||||
export type Tree = {
|
||||
@@ -13,5 +12,5 @@ export type Tree = {
|
||||
};
|
||||
|
||||
export type InstanceProps = Omit<DbInstanceProps, "props"> & {
|
||||
props: Array<UserProp>;
|
||||
props: Array<PropsItem>;
|
||||
};
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useRef } from "react";
|
||||
import { atom } from "nanostores";
|
||||
import { useStore } from "@nanostores/react";
|
||||
import type { InstanceProps, Instance } from "../db";
|
||||
import { UserProp } from "./schema";
|
||||
import type { InstanceProps, Instance, PropsItem } from "../db";
|
||||
|
||||
export type AllUserProps = {
|
||||
[id: Instance["id"]]: UserProp[];
|
||||
[id: Instance["id"]]: PropsItem[];
|
||||
};
|
||||
|
||||
export const allUserPropsContainer = atom<AllUserProps>({});
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from "./all-user-props";
|
||||
export * from "./use-user-props";
|
||||
export * from "./schema";
|
||||
@@ -1,35 +0,0 @@
|
||||
import { z } from "zod";
|
||||
import { Asset } from "@webstudio-is/asset-uploader";
|
||||
|
||||
const baseUserProps = {
|
||||
id: z.string(),
|
||||
prop: z.string(),
|
||||
required: z.optional(z.boolean()),
|
||||
};
|
||||
|
||||
export const UserProp = z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
...baseUserProps,
|
||||
type: z.literal("number"),
|
||||
value: z.number(),
|
||||
}),
|
||||
z.object({
|
||||
...baseUserProps,
|
||||
type: z.literal("string"),
|
||||
value: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
...baseUserProps,
|
||||
type: z.literal("boolean"),
|
||||
value: z.boolean(),
|
||||
}),
|
||||
z.object({
|
||||
...baseUserProps,
|
||||
type: z.literal("asset"),
|
||||
value: Asset,
|
||||
}),
|
||||
]);
|
||||
|
||||
export const UserProps = z.array(UserProp);
|
||||
|
||||
export type UserProp = z.infer<typeof UserProp>;
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import { type Instance } from "../db";
|
||||
import { type UserProp } from "./schema";
|
||||
import { PropsItem, type Instance } from "../db";
|
||||
import { useAllUserProps } from "./all-user-props";
|
||||
import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
|
||||
@@ -9,7 +8,7 @@ import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
*/
|
||||
export const useUserPropsAsset = (
|
||||
instanceId: Instance["id"],
|
||||
propName: UserProp["prop"]
|
||||
propName: PropsItem["name"]
|
||||
): Asset | undefined => {
|
||||
const allUserProps = useAllUserProps();
|
||||
|
||||
@@ -18,7 +17,7 @@ export const useUserPropsAsset = (
|
||||
if (propsData == null) {
|
||||
return undefined;
|
||||
}
|
||||
const prop = propsData.find((prop) => prop.prop === propName);
|
||||
const prop = propsData.find((prop) => prop.name === propName);
|
||||
|
||||
if (prop == null) {
|
||||
return undefined;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useMemo } from "react";
|
||||
import { type Instance } from "../db";
|
||||
import { type UserProp } from "./schema";
|
||||
import type { Instance, PropsItem } from "../db";
|
||||
import { useAllUserProps } from "./all-user-props";
|
||||
|
||||
type UserProps = { [prop: UserProp["prop"]]: string | number | boolean };
|
||||
type UserProps = { [name: PropsItem["name"]]: string | number | boolean };
|
||||
|
||||
/**
|
||||
* User props mapped in prop:value format,
|
||||
@@ -21,7 +20,7 @@ export const useUserProps = (instanceId: Instance["id"]) => {
|
||||
|
||||
for (const userProp of propsData) {
|
||||
if (userProp.type !== "asset") {
|
||||
result[userProp.prop] = userProp.value;
|
||||
result[userProp.name] = userProp.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user