Skip to content

Commit bb803af

Browse files
committed
docs: add save functionality to Template demo component
1 parent e989f91 commit bb803af

File tree

3 files changed

+142
-91
lines changed

3 files changed

+142
-91
lines changed

apps/demo/config/blocks/Template/index.tsx

+131-87
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import React from "react";
1+
import React, { useState } from "react";
22
import { ComponentConfig, Slot } from "@/core/types";
33
import styles from "./styles.module.css";
44
import { getClassNameFactory } from "@/core/lib";
55
import { Section } from "../../components/Section";
66
import { withLayout } from "../../components/Layout";
77
import { generateId } from "@/core/lib/generate-id";
8-
import { type Props } from "../../index";
8+
import { componentKey, type Props } from "../../index";
9+
import { AutoField, Button, createUsePuck, FieldLabel } from "@/core";
10+
11+
const usePuck = createUsePuck();
912

1013
async function createComponent<T extends keyof Props>(
1114
component: T,
1215
props?: Partial<Props[T]>
1316
) {
14-
// const test = dynamic(() => import("../../index"));
1517
const { conf: config } = await import("../../index");
1618

17-
// "a-arrow-down": () => import('./dist/esm/icons/a-arrow-down.js'),
18-
1919
return {
2020
type: component,
2121
props: {
@@ -33,104 +33,148 @@ export type TemplateProps = {
3333
children: Slot;
3434
};
3535

36+
type TemplateData = Record<string, { label: string; data: Slot }>;
37+
3638
export const TemplateInternal: ComponentConfig<TemplateProps> = {
3739
fields: {
3840
template: {
39-
type: "select",
40-
options: [
41-
{ label: "Template 1", value: "template_1" },
42-
{ label: "Template 2", value: "template_2" },
43-
],
41+
type: "custom",
42+
render: ({ name, value, onChange }) => {
43+
const templateKey = `puck-demo-templates:${componentKey}`;
44+
45+
const props: TemplateProps | undefined = usePuck(
46+
(s) => s.selectedItem?.props
47+
);
48+
49+
const [templates, setTemplates] = useState<TemplateData>(
50+
JSON.parse(localStorage.getItem(templateKey) ?? "{}")
51+
);
52+
53+
return (
54+
<FieldLabel label={name}>
55+
<AutoField
56+
value={value}
57+
onChange={onChange}
58+
field={{
59+
type: "select",
60+
options: [
61+
{ label: "Blank", value: "blank" },
62+
{ label: "Example 1", value: "example_1" },
63+
{ label: "Example 2", value: "example_2" },
64+
...Object.entries(templates).map(([key, template]) => ({
65+
value: key,
66+
label: template.label,
67+
})),
68+
],
69+
}}
70+
/>
71+
<div style={{ marginLeft: "auto", marginTop: 16 }}>
72+
<Button
73+
variant="secondary"
74+
onClick={() => {
75+
if (!props?.children) {
76+
return;
77+
}
78+
79+
const templateId = generateId();
80+
81+
const templateData = {
82+
...templates,
83+
[templateId]: {
84+
label: new Date().toLocaleString(),
85+
data: props.children,
86+
},
87+
};
88+
89+
localStorage.setItem(
90+
templateKey,
91+
JSON.stringify(templateData)
92+
);
93+
94+
setTemplates(templateData);
95+
96+
onChange(templateId);
97+
}}
98+
>
99+
Save new template
100+
</Button>
101+
</div>
102+
</FieldLabel>
103+
);
104+
},
44105
},
45106
children: {
46107
type: "slot",
47108
},
48109
},
49110
defaultProps: {
50-
template: "template_1",
111+
template: "example_1",
51112
children: [],
52113
},
53114
resolveData: async (data, { changed }) => {
54115
if (!changed.template) return data;
55116

56-
const templates: Record<string, Slot> = {
57-
template_1: [
58-
await createComponent("Grid", {
59-
numColumns: 2,
60-
children: [
61-
await createComponent("Card", { title: "A card", mode: "card" }),
62-
await createComponent("Flex", {
63-
direction: "column",
64-
gap: 0,
65-
children: [
66-
await createComponent("Space", {
67-
size: "32px",
68-
}),
69-
await createComponent("Heading", {
70-
text: "Template example",
71-
size: "xl",
72-
}),
73-
await createComponent("Text", {
74-
text: "Dynamically create components using the new slots API.",
75-
}),
76-
await createComponent("Space", {
77-
size: "16px",
78-
}),
79-
await createComponent("Button", {
80-
variant: "secondary",
81-
label: "Learn more",
82-
}),
83-
await createComponent("Space", {
84-
size: "32px",
85-
}),
86-
],
87-
}),
88-
],
89-
}),
90-
],
91-
template_2: [
92-
await createComponent("Grid", {
93-
numColumns: 3,
94-
children: [
95-
await createComponent("Space", {
96-
size: "32px",
97-
}),
98-
await createComponent("Flex", {
99-
direction: "column",
100-
gap: 0,
101-
justifyContent: "center",
102-
children: [
103-
await createComponent("Space", {
104-
size: "32px",
105-
}),
106-
await createComponent("Heading", {
107-
text: "Template example",
108-
size: "xl",
109-
}),
110-
await createComponent("Text", {
111-
text: "Dynamically create components using the new slots API.",
112-
}),
113-
await createComponent("Space", {
114-
size: "16px",
115-
}),
116-
await createComponent("Button", {
117-
variant: "secondary",
118-
label: "Learn more",
119-
}),
120-
await createComponent("Space", {
121-
size: "32px",
122-
}),
123-
],
124-
}),
125-
await createComponent("Space", {
126-
size: "32px",
127-
}),
128-
],
129-
}),
130-
],
117+
const templateKey = `puck-demo-templates:${componentKey}`;
118+
119+
const templates: TemplateData = {
120+
...JSON.parse(localStorage.getItem(templateKey) ?? "{}"),
121+
blank: {
122+
label: "Blank",
123+
data: [],
124+
},
125+
example_1: {
126+
label: "Example 1",
127+
data: [
128+
await createComponent("Heading", {
129+
text: "Template example.",
130+
size: "xl",
131+
}),
132+
await createComponent("Text", {
133+
text: "This component uses the slots API. Try changing template, or saving a new one via the template field.",
134+
}),
135+
],
136+
},
137+
example_2: {
138+
label: "Example 2",
139+
data: [
140+
await createComponent("Grid", {
141+
numColumns: 2,
142+
children: [
143+
await createComponent("Card", { title: "A card", mode: "card" }),
144+
await createComponent("Flex", {
145+
direction: "column",
146+
gap: 0,
147+
children: [
148+
await createComponent("Space", {
149+
size: "32px",
150+
}),
151+
await createComponent("Heading", {
152+
text: "Template example",
153+
size: "xl",
154+
}),
155+
await createComponent("Text", {
156+
text: "Dynamically create components using the new slots API.",
157+
}),
158+
await createComponent("Space", {
159+
size: "16px",
160+
}),
161+
await createComponent("Button", {
162+
variant: "secondary",
163+
label: "Learn more",
164+
}),
165+
await createComponent("Space", {
166+
size: "32px",
167+
}),
168+
],
169+
}),
170+
],
171+
}),
172+
],
173+
},
131174
};
132175

133-
const children = templates[data.props.template];
176+
const children =
177+
templates[data.props.template]?.data || templates["example_1"].data;
134178

135179
return {
136180
...data,

apps/demo/config/index.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -487,4 +487,8 @@ export const initialData: Record<string, UserData> = {
487487
},
488488
};
489489

490+
export const componentKey = Buffer.from(
491+
`${Object.keys(conf.components).join("-")}-${JSON.stringify(initialData)}`
492+
).toString("base64");
493+
490494
export default conf;

apps/demo/lib/use-demo-data.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { useEffect, useState } from "react";
2-
import config, { initialData, Props, RootProps, UserData } from "../config";
2+
import config, {
3+
componentKey,
4+
initialData,
5+
Props,
6+
RootProps,
7+
UserData,
8+
} from "../config";
39
import { Metadata, resolveAllData } from "@/core";
410

511
const isBrowser = typeof window !== "undefined";
@@ -14,9 +20,6 @@ export const useDemoData = ({
1420
metadata?: Metadata;
1521
}) => {
1622
// unique b64 key that updates each time we add / remove components
17-
const componentKey = Buffer.from(
18-
`${Object.keys(config.components).join("-")}-${JSON.stringify(initialData)}`
19-
).toString("base64");
2023

2124
const key = `puck-demo:${componentKey}:${path}`;
2225

0 commit comments

Comments
 (0)