Add props support in embed template instances (#1519)

Ref https://github.com/webstudio-is/webstudio-builder/pull/1516

This will allow further customization of react components through meta
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2023-04-23 00:47:44 +07:00
1 parent 30be97deef
commit 0f615afd01
4 files changed
+128 -12

No files matched your search

+5 -2
View File
@@ -18,6 +18,7 @@ import {
insertInstancesMutable,
reparentInstanceMutable,
getAncestorInstanceSelector,
insertPropsCopyMutable,
} from "./tree-utils";
import { removeByMutable } from "./array-utils";
@@ -25,15 +26,17 @@ export const insertNewComponentInstance = (
component: string,
dropTarget: DroppableTarget
) => {
const insertedInstances = createComponentInstance(component);
const { instances: insertedInstances, props: insertedProps } =
createComponentInstance(component);
const rootInstanceId = insertedInstances[0].id;
store.createTransaction([instancesStore], (instances) => {
store.createTransaction([instancesStore, propsStore], (instances, props) => {
insertInstancesMutable(
instances,
insertedInstances,
[rootInstanceId],
dropTarget
);
insertPropsCopyMutable(props, insertedProps, new Map());
});
selectedInstanceSelectorStore.set([
+2 -2
View File
@@ -53,7 +53,7 @@ export const areInstanceSelectorsEqual = (
export const createComponentInstance = (component: Instance["component"]) => {
const componentMeta = getComponentMeta(component);
const { children, instances } = generateDataFromEmbedTemplate(
const { children, instances, props } = generateDataFromEmbedTemplate(
componentMeta?.children ?? []
);
// put first to be interpreted as root
@@ -63,7 +63,7 @@ export const createComponentInstance = (component: Instance["component"]) => {
component,
children,
});
return instances;
return { instances, props };
};
const isInstanceDroppable = (instance: Instance) => {
+64 -1
View File
@@ -3,7 +3,7 @@ import { generateDataFromEmbedTemplate } from "./embed-template";
const expectString = expect.any(String) as unknown as string;
test("generate tree from template", () => {
test("generate data for embedding from instances and text", () => {
expect(
generateDataFromEmbedTemplate([
{ type: "text", value: "hello" },
@@ -38,5 +38,68 @@ test("generate tree from template", () => {
children: [],
},
],
props: [],
});
});
test("generate data for embedding from props", () => {
expect(
generateDataFromEmbedTemplate([
{
type: "instance",
component: "Box1",
props: [
{ type: "string", name: "data-prop1", value: "value1" },
{ type: "string", name: "data-prop2", value: "value2" },
],
children: [
{
type: "instance",
component: "Box2",
props: [{ type: "string", name: "data-prop3", value: "value3" }],
children: [],
},
],
},
])
).toEqual({
children: [{ type: "id", value: expectString }],
instances: [
{
type: "instance",
id: expectString,
component: "Box1",
children: [{ type: "id", value: expectString }],
},
{
type: "instance",
id: expectString,
component: "Box2",
children: [],
},
],
props: [
{
type: "string",
id: expectString,
instanceId: expectString,
name: "data-prop1",
value: "value1",
},
{
type: "string",
id: expectString,
instanceId: expectString,
name: "data-prop2",
value: "value2",
},
{
type: "string",
id: expectString,
instanceId: expectString,
name: "data-prop3",
value: "value3",
},
],
});
});
+57 -7
View File
@@ -1,6 +1,10 @@
import { z } from "zod";
import { nanoid } from "nanoid";
import type { Instance, InstancesList } from "@webstudio-is/project-build";
import {
type Instance,
type InstancesList,
PropsList,
} from "@webstudio-is/project-build";
const EmbedTemplateText = z.object({
type: z.literal("text"),
@@ -9,32 +13,73 @@ const EmbedTemplateText = z.object({
type EmbedTemplateText = z.infer<typeof EmbedTemplateText>;
const EmbedTemplateProp = z.union([
z.object({
type: z.literal("number"),
name: z.string(),
value: z.number(),
}),
z.object({
type: z.literal("string"),
name: z.string(),
value: z.string(),
}),
z.object({
type: z.literal("boolean"),
name: z.string(),
value: z.boolean(),
}),
z.object({
type: z.literal("string[]"),
name: z.string(),
value: z.array(z.string()),
}),
]);
type EmbedTemplateProp = z.infer<typeof EmbedTemplateProp>;
type EmbedTemplateInstance = {
type: "instance";
component: string;
props?: EmbedTemplateProp[];
children: Array<EmbedTemplateInstance | EmbedTemplateText>;
};
const EmbedTemplateInstancen: z.ZodType<EmbedTemplateInstance> = z.object({
const EmbedTemplateInstance: z.ZodType<EmbedTemplateInstance> = z.object({
type: z.literal("instance"),
component: z.string(),
props: z.optional(z.array(EmbedTemplateProp)),
children: z.lazy(() => WsEmbedTemplate),
});
export const WsEmbedTemplate = z.array(
z.union([z.lazy(() => EmbedTemplateInstancen), EmbedTemplateText])
z.union([z.lazy(() => EmbedTemplateInstance), EmbedTemplateText])
);
export type WsEmbedTemplate = z.infer<typeof WsEmbedTemplate>;
const createInstancesFromTemplate = (
treeTemplate: WsEmbedTemplate,
instances: InstancesList
instances: InstancesList,
props: PropsList
) => {
const parentChildren: Instance["children"] = [];
for (const item of treeTemplate) {
if (item.type === "instance") {
const instanceId = nanoid();
// populate props
if (item.props) {
for (const prop of item.props) {
props.push({
id: nanoid(),
instanceId,
...prop,
});
}
}
// populate instances
const instance: Instance = {
type: "instance",
id: instanceId,
@@ -43,7 +88,11 @@ const createInstancesFromTemplate = (
};
instances.push(instance);
// traverse children after to preserve top down order
instance.children = createInstancesFromTemplate(item.children, instances);
instance.children = createInstancesFromTemplate(
item.children,
instances,
props
);
parentChildren.push({
type: "id",
value: instanceId,
@@ -63,6 +112,7 @@ export const generateDataFromEmbedTemplate = (
treeTemplate: WsEmbedTemplate
) => {
const instances: InstancesList = [];
const children = createInstancesFromTemplate(treeTemplate, instances);
return { children, instances };
const props: PropsList = [];
const children = createInstancesFromTemplate(treeTemplate, instances, props);
return { children, instances, props };
};