mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-05 11:38:36 +09:00
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:
1 parent
30be97deef
commit
0f615afd01
4 files changed
+128
-12
No files matched your search
@@ -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([
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -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 };
|
||||
};
|
||||
Reference in new issue
Block a user