@particle-academy/fancy-cms-ui 0.0.1-dev.12 → 0.0.1-dev.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-IXPYY4S2.js +93 -0
- package/dist/chunk-IXPYY4S2.js.map +1 -0
- package/dist/editor.cjs +96 -38
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +4 -2
- package/dist/editor.d.ts +4 -2
- package/dist/editor.js +58 -15
- package/dist/editor.js.map +1 -1
- package/dist/react.cjs +40 -25
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +12 -4
- package/dist/react.d.ts +12 -4
- package/dist/react.js +2 -2
- package/dist/registry-C3PioMnL.d.ts +27 -0
- package/dist/registry-ni9y0dSw.d.cts +27 -0
- package/package.json +1 -1
- package/dist/chunk-ZV7CV54P.js +0 -78
- package/dist/chunk-ZV7CV54P.js.map +0 -1
- package/dist/registry-Bk7GN1rA.d.cts +0 -19
- package/dist/registry-Dht9JBLU.d.ts +0 -19
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { childrenOf, emitDocCss, isBinding } from './chunk-3YZMELJJ.js';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
|
+
|
|
5
|
+
function getPath(data, path) {
|
|
6
|
+
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
|
|
7
|
+
}
|
|
8
|
+
function resolveValue(v, data) {
|
|
9
|
+
if (v === void 0 || v === null) return v;
|
|
10
|
+
if (isBinding(v)) return getPath(data, v.$bind);
|
|
11
|
+
return v;
|
|
12
|
+
}
|
|
13
|
+
var defaultRegistry = {
|
|
14
|
+
section: ({ children }) => children,
|
|
15
|
+
stack: ({ children }) => children,
|
|
16
|
+
grid: ({ children }) => children,
|
|
17
|
+
frame: ({ children }) => children,
|
|
18
|
+
shape: ({ children }) => children,
|
|
19
|
+
card: ({ children }) => children,
|
|
20
|
+
text: ({ node, text }) => text(node.props.content),
|
|
21
|
+
heading: ({ node, text }) => text(node.props.content) || "Heading",
|
|
22
|
+
link: ({ node, text }) => /* @__PURE__ */ jsx("a", { href: text(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: text(node.props.content) || "link" }),
|
|
23
|
+
divider: () => /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
24
|
+
callout: ({ node, text }) => {
|
|
25
|
+
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[text(node.props.variant)] ?? "#3b82f6";
|
|
26
|
+
return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 10, padding: "12px 14px", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }, children: /* @__PURE__ */ jsx("span", { style: { fontSize: 13 }, children: text(node.props.content) || "Callout" }) });
|
|
27
|
+
},
|
|
28
|
+
code: ({ node, text }) => /* @__PURE__ */ jsx("pre", { style: { margin: 0, padding: "12px 14px", borderRadius: 10, background: "#0b1220", color: "#e2e8f0", fontFamily: "ui-monospace, monospace", fontSize: 13, overflowX: "auto" }, children: /* @__PURE__ */ jsx("code", { children: text(node.props.content) }) }),
|
|
29
|
+
button: ({ node, text }) => {
|
|
30
|
+
const label = text(node.props.label) || text(node.props.content) || "Button";
|
|
31
|
+
const variant = text(node.props.variant) || "primary";
|
|
32
|
+
return /* @__PURE__ */ jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label });
|
|
33
|
+
},
|
|
34
|
+
image: ({ node, text }) => /* @__PURE__ */ jsx(
|
|
35
|
+
"img",
|
|
36
|
+
{
|
|
37
|
+
src: text(node.props.src),
|
|
38
|
+
alt: text(node.props.alt),
|
|
39
|
+
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
};
|
|
43
|
+
function buttonStyle(variant) {
|
|
44
|
+
const base = {
|
|
45
|
+
display: "inline-flex",
|
|
46
|
+
alignItems: "center",
|
|
47
|
+
gap: 6,
|
|
48
|
+
padding: "9px 16px",
|
|
49
|
+
borderRadius: 8,
|
|
50
|
+
fontWeight: 600,
|
|
51
|
+
fontSize: 14,
|
|
52
|
+
lineHeight: 1,
|
|
53
|
+
cursor: "pointer",
|
|
54
|
+
userSelect: "none"
|
|
55
|
+
};
|
|
56
|
+
if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
|
|
57
|
+
if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
|
|
58
|
+
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
59
|
+
}
|
|
60
|
+
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
61
|
+
const node = doc.nodes[id];
|
|
62
|
+
if (!node) return null;
|
|
63
|
+
if (node.type === "repeater") {
|
|
64
|
+
const arr = resolveValue(node.props.items, data);
|
|
65
|
+
const items = Array.isArray(arr) ? arr : [];
|
|
66
|
+
const template = childrenOf(doc, id)[0];
|
|
67
|
+
return /* @__PURE__ */ jsx("div", { "data-cms": node.id, className: node.className, children: template ? items.map((item, index) => /* @__PURE__ */ jsx(RenderNode, { doc, id: template.id, registry, data: { ...data, item, index } }, index)) : null });
|
|
68
|
+
}
|
|
69
|
+
const kids = childrenOf(doc, id).map((child) => /* @__PURE__ */ jsx(RenderNode, { doc, id: child.id, registry, data }, child.id));
|
|
70
|
+
const resolve = (v) => resolveValue(v, data);
|
|
71
|
+
const text = (v) => {
|
|
72
|
+
const r = resolve(v);
|
|
73
|
+
return r === void 0 || r === null ? "" : String(r);
|
|
74
|
+
};
|
|
75
|
+
const renderer = registry[node.type];
|
|
76
|
+
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
77
|
+
return /* @__PURE__ */ jsx("div", { "data-cms": node.id, "data-cms-island": node.island ? node.type : void 0, className: node.className, children: inner });
|
|
78
|
+
}
|
|
79
|
+
function CmsPage({
|
|
80
|
+
doc,
|
|
81
|
+
registry = defaultRegistry,
|
|
82
|
+
data,
|
|
83
|
+
includeStyles = true
|
|
84
|
+
}) {
|
|
85
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
86
|
+
includeStyles ? /* @__PURE__ */ jsx("style", { "data-cms-styles": "", children: emitDocCss(doc) }) : null,
|
|
87
|
+
doc.sections.map((id) => /* @__PURE__ */ jsx(RenderNode, { doc, id, registry, data }, id))
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { CmsPage, RenderNode, defaultRegistry };
|
|
92
|
+
//# sourceMappingURL=chunk-IXPYY4S2.js.map
|
|
93
|
+
//# sourceMappingURL=chunk-IXPYY4S2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/registry.tsx","../src/react/RenderNode.tsx","../src/react/CmsPage.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-IXPYY4S2.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport { isBinding, type Bound, type Json, type Node, type PageDoc } from \"../document/types\";\n\n/** The page data context bindings resolve against (e.g. server props, repeater `item`). */\nexport type DataContext = Record<string, unknown>;\n\nexport interface ElementContext {\n node: Node;\n doc: PageDoc;\n /** Pre-rendered child nodes, in order. */\n children: ReactNode;\n /** The data context this node renders in (page data + any repeater scope). */\n data?: DataContext;\n /** Resolve a (possibly bound) value to its raw resolved value. */\n resolve: (v: Bound<Json> | undefined) => unknown;\n /** Resolve a (possibly bound) value to a display string. */\n text: (v: Bound<Json> | undefined) => string;\n}\n\n/**\n * Renders the INNER content of a node. The outer `[data-cms]` wrapper is added\n * by {@link RenderNode}, so a renderer only returns what goes inside.\n */\nexport type ElementRenderer = (ctx: ElementContext) => ReactNode;\n\nexport type ElementRegistry = Record<string, ElementRenderer>;\n\n/** Read a dotted path (`a.b.c`) out of the data context. */\nexport function getPath(data: DataContext | undefined, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]), data);\n}\n\n/** Resolve a value: a `{ $bind }` reads from the data context; anything else is literal. */\nexport function resolveValue(v: Bound<Json> | undefined, data?: DataContext): unknown {\n if (v === undefined || v === null) return v;\n if (isBinding(v)) return getPath(data, v.$bind);\n return v;\n}\n\n/** Built-in native elements. Containers render their children; leaves render content. */\nexport const defaultRegistry: ElementRegistry = {\n section: ({ children }) => children,\n stack: ({ children }) => children,\n grid: ({ children }) => children,\n frame: ({ children }) => children,\n shape: ({ children }) => children,\n card: ({ children }) => children,\n text: ({ node, text }) => text(node.props.content),\n heading: ({ node, text }) => text(node.props.content) || \"Heading\",\n link: ({ node, text }) => (\n <a href={text(node.props.href) || \"#\"} style={{ color: \"inherit\", textDecoration: \"underline\" }}>\n {text(node.props.content) || \"link\"}\n </a>\n ),\n divider: () => <hr style={{ border: \"none\", borderTop: \"1px solid #e2e8f0\", margin: 0 }} />,\n callout: ({ node, text }) => {\n const tone = ({ info: \"#3b82f6\", success: \"#10b981\", warning: \"#f59e0b\", danger: \"#ef4444\" } as Record<string, string>)[text(node.props.variant)] ?? \"#3b82f6\";\n return (\n <div style={{ display: \"flex\", gap: 10, padding: \"12px 14px\", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }}>\n <span style={{ fontSize: 13 }}>{text(node.props.content) || \"Callout\"}</span>\n </div>\n );\n },\n code: ({ node, text }) => (\n <pre style={{ margin: 0, padding: \"12px 14px\", borderRadius: 10, background: \"#0b1220\", color: \"#e2e8f0\", fontFamily: \"ui-monospace, monospace\", fontSize: 13, overflowX: \"auto\" }}>\n <code>{text(node.props.content)}</code>\n </pre>\n ),\n button: ({ node, text }) => {\n const label = text(node.props.label) || text(node.props.content) || \"Button\";\n const variant = text(node.props.variant) || \"primary\";\n return <span data-cms-button={variant} style={buttonStyle(variant)}>{label}</span>;\n },\n image: ({ node, text }) => (\n <img\n src={text(node.props.src)}\n alt={text(node.props.alt)}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n ),\n};\n\n/**\n * A self-contained button look so the `button` primitive renders sensibly with\n * zero host CSS. Hosts that want their own button classes override the `button`\n * renderer in their registry (e.g. the sandbox maps it to `.btn .btn-primary`).\n */\nfunction buttonStyle(variant: string): import(\"react\").CSSProperties {\n const base: import(\"react\").CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"9px 16px\",\n borderRadius: 8,\n fontWeight: 600,\n fontSize: 14,\n lineHeight: 1,\n cursor: \"pointer\",\n userSelect: \"none\",\n };\n if (variant === \"ghost\") return { ...base, background: \"transparent\", color: \"inherit\", border: \"1px solid currentColor\" };\n if (variant === \"outline\") return { ...base, background: \"transparent\", color: \"#7c3aed\", border: \"1px solid #7c3aed\" };\n return { ...base, background: \"#7c3aed\", color: \"#fff\", border: \"1px solid #7c3aed\" };\n}\n","import type { ReactElement } from \"react\";\nimport type { Bound, Json, PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { defaultRegistry, resolveValue, type DataContext, type ElementRegistry } from \"./registry\";\n\nexport interface RenderNodeProps {\n doc: PageDoc;\n id: string;\n registry?: ElementRegistry;\n /** Data context bindings resolve against (page data + any repeater scope). */\n data?: DataContext;\n}\n\n/**\n * Render one node (and its subtree) to a `[data-cms]`-tagged element. The CSS\n * emitted by `emitDocCss` targets these `data-cms` handles. Interactive /\n * 3rd-party **islands** with no registered renderer become an empty, sized\n * placeholder the host hydrates client-side — mirroring the PHP renderer.\n *\n * `repeater` nodes are special-cased: their first child is a template rendered\n * once per item of the bound `items` array, each in a scope that exposes `item`\n * and `index` to nested `{ $bind: \"item.…\" }` bindings.\n */\nexport function RenderNode({ doc, id, registry = defaultRegistry, data }: RenderNodeProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n\n if (node.type === \"repeater\") {\n const arr = resolveValue(node.props.items as Bound<Json> | undefined, data);\n const items = Array.isArray(arr) ? arr : [];\n const template = childrenOf(doc, id)[0];\n return (\n <div data-cms={node.id} className={node.className}>\n {template\n ? items.map((item, index) => (\n <RenderNode key={index} doc={doc} id={template.id} registry={registry} data={{ ...data, item, index }} />\n ))\n : null}\n </div>\n );\n }\n\n const kids = childrenOf(doc, id).map((child) => (\n <RenderNode key={child.id} doc={doc} id={child.id} registry={registry} data={data} />\n ));\n\n const resolve = (v: Bound<Json> | undefined) => resolveValue(v, data);\n const text = (v: Bound<Json> | undefined): string => {\n const r = resolve(v);\n return r === undefined || r === null ? \"\" : String(r);\n };\n\n const renderer = registry[node.type];\n const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;\n\n return (\n <div data-cms={node.id} data-cms-island={node.island ? node.type : undefined} className={node.className}>\n {inner}\n </div>\n );\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { emitDocCss } from \"../render/css\";\nimport { RenderNode } from \"./RenderNode\";\nimport { defaultRegistry, type DataContext, type ElementRegistry } from \"./registry\";\n\nexport interface CmsPageProps {\n doc: PageDoc;\n registry?: ElementRegistry;\n /** Data context that `{ $bind: \"…\" }` props + repeaters resolve against. */\n data?: DataContext;\n /** Inject the compiled stylesheet. Set false when the host emits CSS itself. */\n includeStyles?: boolean;\n}\n\n/** Render a full page document: the compiled stylesheet + every section. */\nexport function CmsPage({\n doc,\n registry = defaultRegistry,\n data,\n includeStyles = true,\n}: CmsPageProps): ReactElement {\n return (\n <Fragment>\n {includeStyles ? <style data-cms-styles=\"\">{emitDocCss(doc)}</style> : null}\n {doc.sections.map((id) => (\n <RenderNode key={id} doc={doc} id={id} registry={registry} data={data} />\n ))}\n </Fragment>\n );\n}\n"]}
|
package/dist/editor.cjs
CHANGED
|
@@ -361,10 +361,13 @@ ${inner}
|
|
|
361
361
|
}
|
|
362
362
|
return blocks.join("\n\n") + (blocks.length ? "\n" : "");
|
|
363
363
|
}
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
364
|
+
function getPath(data, path) {
|
|
365
|
+
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
|
|
366
|
+
}
|
|
367
|
+
function resolveValue(v, data) {
|
|
368
|
+
if (v === void 0 || v === null) return v;
|
|
369
|
+
if (isBinding(v)) return getPath(data, v.$bind);
|
|
370
|
+
return v;
|
|
368
371
|
}
|
|
369
372
|
var defaultRegistry = {
|
|
370
373
|
section: ({ children }) => children,
|
|
@@ -372,18 +375,26 @@ var defaultRegistry = {
|
|
|
372
375
|
grid: ({ children }) => children,
|
|
373
376
|
frame: ({ children }) => children,
|
|
374
377
|
shape: ({ children }) => children,
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
card: ({ children }) => children,
|
|
379
|
+
text: ({ node, text }) => text(node.props.content),
|
|
380
|
+
heading: ({ node, text }) => text(node.props.content) || "Heading",
|
|
381
|
+
link: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: text(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: text(node.props.content) || "link" }),
|
|
382
|
+
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
383
|
+
callout: ({ node, text }) => {
|
|
384
|
+
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[text(node.props.variant)] ?? "#3b82f6";
|
|
385
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 10, padding: "12px 14px", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 13 }, children: text(node.props.content) || "Callout" }) });
|
|
386
|
+
},
|
|
387
|
+
code: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { margin: 0, padding: "12px 14px", borderRadius: 10, background: "#0b1220", color: "#e2e8f0", fontFamily: "ui-monospace, monospace", fontSize: 13, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: text(node.props.content) }) }),
|
|
388
|
+
button: ({ node, text }) => {
|
|
389
|
+
const label2 = text(node.props.label) || text(node.props.content) || "Button";
|
|
390
|
+
const variant = text(node.props.variant) || "primary";
|
|
380
391
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
|
|
381
392
|
},
|
|
382
|
-
image: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
393
|
+
image: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
383
394
|
"img",
|
|
384
395
|
{
|
|
385
|
-
src:
|
|
386
|
-
alt:
|
|
396
|
+
src: text(node.props.src),
|
|
397
|
+
alt: text(node.props.alt),
|
|
387
398
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
388
399
|
}
|
|
389
400
|
)
|
|
@@ -405,30 +416,34 @@ function buttonStyle(variant) {
|
|
|
405
416
|
if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
|
|
406
417
|
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
407
418
|
}
|
|
408
|
-
function RenderNode({ doc, id, registry = defaultRegistry }) {
|
|
419
|
+
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
409
420
|
const node = doc.nodes[id];
|
|
410
421
|
if (!node) return null;
|
|
411
|
-
|
|
422
|
+
if (node.type === "repeater") {
|
|
423
|
+
const arr = resolveValue(node.props.items, data);
|
|
424
|
+
const items = Array.isArray(arr) ? arr : [];
|
|
425
|
+
const template = childrenOf(doc, id)[0];
|
|
426
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cms": node.id, className: node.className, children: template ? items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id: template.id, registry, data: { ...data, item, index } }, index)) : null });
|
|
427
|
+
}
|
|
428
|
+
const kids = childrenOf(doc, id).map((child) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id: child.id, registry, data }, child.id));
|
|
429
|
+
const resolve = (v) => resolveValue(v, data);
|
|
430
|
+
const text = (v) => {
|
|
431
|
+
const r = resolve(v);
|
|
432
|
+
return r === void 0 || r === null ? "" : String(r);
|
|
433
|
+
};
|
|
412
434
|
const renderer = registry[node.type];
|
|
413
|
-
const inner = renderer ? renderer({ node, doc, children: kids }) : node.island ? null : kids;
|
|
414
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
415
|
-
"div",
|
|
416
|
-
{
|
|
417
|
-
"data-cms": node.id,
|
|
418
|
-
"data-cms-island": node.island ? node.type : void 0,
|
|
419
|
-
className: node.className,
|
|
420
|
-
children: inner
|
|
421
|
-
}
|
|
422
|
-
);
|
|
435
|
+
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
436
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cms": node.id, "data-cms-island": node.island ? node.type : void 0, className: node.className, children: inner });
|
|
423
437
|
}
|
|
424
438
|
function CmsPage({
|
|
425
439
|
doc,
|
|
426
440
|
registry = defaultRegistry,
|
|
441
|
+
data,
|
|
427
442
|
includeStyles = true
|
|
428
443
|
}) {
|
|
429
444
|
return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
|
|
430
445
|
includeStyles ? /* @__PURE__ */ jsxRuntime.jsx("style", { "data-cms-styles": "", children: emitDocCss(doc) }) : null,
|
|
431
|
-
doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id, registry }, id))
|
|
446
|
+
doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id, registry, data }, id))
|
|
432
447
|
] });
|
|
433
448
|
}
|
|
434
449
|
function Canvas({ doc, selection, onSelect, apply }) {
|
|
@@ -802,7 +817,7 @@ function Toolbar({ ed }) {
|
|
|
802
817
|
}
|
|
803
818
|
);
|
|
804
819
|
}
|
|
805
|
-
var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
|
|
820
|
+
var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
|
|
806
821
|
function NodeInspector({
|
|
807
822
|
node,
|
|
808
823
|
transform,
|
|
@@ -818,7 +833,7 @@ function NodeInspector({
|
|
|
818
833
|
const setT = (patch) => onTransform({ ...transform, ...patch });
|
|
819
834
|
const handled = /* @__PURE__ */ new Set(["content", "label"]);
|
|
820
835
|
const extraKeys = Object.keys(node.props).filter(
|
|
821
|
-
(k) => !handled.has(k) && ["string", "number", "boolean"].includes(typeof node.props[k])
|
|
836
|
+
(k) => !handled.has(k) && (["string", "number", "boolean"].includes(typeof node.props[k]) || isBinding(node.props[k]))
|
|
822
837
|
);
|
|
823
838
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panel, onPointerDown: (e) => e.stopPropagation(), children: [
|
|
824
839
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: headRow, children: [
|
|
@@ -943,24 +958,50 @@ function Row({ children }) {
|
|
|
943
958
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 8 }, children });
|
|
944
959
|
}
|
|
945
960
|
function PropField({ name, value, onChange }) {
|
|
961
|
+
const bound = isBinding(value);
|
|
962
|
+
const link = /* @__PURE__ */ jsxRuntime.jsx(
|
|
963
|
+
"button",
|
|
964
|
+
{
|
|
965
|
+
type: "button",
|
|
966
|
+
title: bound ? "Unbind (use a literal value)" : "Bind to data (e.g. item.name)",
|
|
967
|
+
onClick: () => onChange(bound ? "" : { $bind: "" }),
|
|
968
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontSize: 11, color: bound ? "#a78bfa" : "#64748b" },
|
|
969
|
+
children: "\u{1F517}"
|
|
970
|
+
}
|
|
971
|
+
);
|
|
972
|
+
if (bound) {
|
|
973
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
974
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...fieldLabel, display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
975
|
+
name,
|
|
976
|
+
" ",
|
|
977
|
+
link
|
|
978
|
+
] }),
|
|
979
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { style: { ...input2, borderColor: "#7c3aed" }, placeholder: "data path \xB7 e.g. item.name", value: value.$bind, onChange: (e) => onChange({ $bind: e.target.value }) })
|
|
980
|
+
] });
|
|
981
|
+
}
|
|
946
982
|
if (typeof value === "boolean") {
|
|
947
983
|
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12 }, children: [
|
|
948
984
|
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: value, onChange: (e) => onChange(e.target.checked) }),
|
|
949
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { textTransform: "capitalize" }, children: name })
|
|
985
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { textTransform: "capitalize", flex: 1 }, children: name }),
|
|
986
|
+
link
|
|
950
987
|
] });
|
|
951
988
|
}
|
|
952
989
|
if (name === "variant") {
|
|
953
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Sel, { label: name, value: String(value), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
|
|
990
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Sel, { label: name, value: String(value ?? ""), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
|
|
954
991
|
}
|
|
955
992
|
const isNum = typeof value === "number";
|
|
956
993
|
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
957
|
-
/* @__PURE__ */ jsxRuntime.
|
|
994
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...fieldLabel, display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
995
|
+
name,
|
|
996
|
+
" ",
|
|
997
|
+
link
|
|
998
|
+
] }),
|
|
958
999
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
959
1000
|
"input",
|
|
960
1001
|
{
|
|
961
1002
|
style: input2,
|
|
962
1003
|
type: isNum ? "number" : "text",
|
|
963
|
-
value: String(value),
|
|
1004
|
+
value: String(value ?? ""),
|
|
964
1005
|
onChange: (e) => onChange(isNum ? Number(e.target.value) : e.target.value)
|
|
965
1006
|
}
|
|
966
1007
|
)
|
|
@@ -1148,6 +1189,7 @@ var SNAP = 6;
|
|
|
1148
1189
|
function EditablePage({
|
|
1149
1190
|
doc,
|
|
1150
1191
|
registry = defaultRegistry,
|
|
1192
|
+
data,
|
|
1151
1193
|
timelineDock,
|
|
1152
1194
|
transforms,
|
|
1153
1195
|
onNodeTransform,
|
|
@@ -1250,7 +1292,7 @@ function EditablePage({
|
|
|
1250
1292
|
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1251
1293
|
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
1252
1294
|
const def = ADD_DEFAULTS[kind];
|
|
1253
|
-
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1295
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1254
1296
|
ed.select(id);
|
|
1255
1297
|
},
|
|
1256
1298
|
[ed, selNode]
|
|
@@ -1264,7 +1306,7 @@ function EditablePage({
|
|
|
1264
1306
|
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1265
1307
|
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
1266
1308
|
const def = ADD_DEFAULTS[kind];
|
|
1267
|
-
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1309
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1268
1310
|
ed.select(id);
|
|
1269
1311
|
},
|
|
1270
1312
|
[ed]
|
|
@@ -1367,7 +1409,7 @@ function EditablePage({
|
|
|
1367
1409
|
onContextMenu: onContextMenuSelect,
|
|
1368
1410
|
onDragOver: editing ? (e) => e.preventDefault() : void 0,
|
|
1369
1411
|
onDrop: editing ? onPageDrop : void 0,
|
|
1370
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry })
|
|
1412
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry, data })
|
|
1371
1413
|
}
|
|
1372
1414
|
);
|
|
1373
1415
|
const pageShell = pinned ? /* @__PURE__ */ jsxRuntime.jsx("div", { ref: spacerRef, style: { height: `${Math.max(1, frames) * 100}vh` }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "sticky", top: 0, height: "100vh", overflow: "hidden" }, children: page }) }) : frames > 1 ? (
|
|
@@ -1502,13 +1544,21 @@ function ElementPalette({ open, onDragChange }) {
|
|
|
1502
1544
|
}
|
|
1503
1545
|
);
|
|
1504
1546
|
}
|
|
1505
|
-
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
|
|
1547
|
+
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device", "repeater"]);
|
|
1506
1548
|
var ADD_DEFAULTS = {
|
|
1507
1549
|
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
1508
1550
|
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
1509
1551
|
button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
|
|
1552
|
+
link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
|
|
1510
1553
|
image: { type: "image", props: { src: "", alt: "" }, style: {} },
|
|
1511
|
-
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
|
|
1554
|
+
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
|
|
1555
|
+
stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
1556
|
+
grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
|
|
1557
|
+
card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
|
|
1558
|
+
callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
|
|
1559
|
+
divider: { type: "divider", props: {}, style: {} },
|
|
1560
|
+
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
1561
|
+
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
1512
1562
|
};
|
|
1513
1563
|
var GRIPS = [
|
|
1514
1564
|
{ key: "nw", north: true, west: true, cursor: "nwse-resize" },
|
|
@@ -1708,11 +1758,19 @@ function FloatingToolbar({
|
|
|
1708
1758
|
] });
|
|
1709
1759
|
}
|
|
1710
1760
|
var ADD_MENU = [
|
|
1711
|
-
{ kind: "text", label: "Text" },
|
|
1712
1761
|
{ kind: "heading", label: "Heading" },
|
|
1762
|
+
{ kind: "text", label: "Text" },
|
|
1713
1763
|
{ kind: "button", label: "Button" },
|
|
1764
|
+
{ kind: "link", label: "Link" },
|
|
1714
1765
|
{ kind: "image", label: "Image" },
|
|
1715
|
-
{ kind: "
|
|
1766
|
+
{ kind: "card", label: "Card" },
|
|
1767
|
+
{ kind: "callout", label: "Callout" },
|
|
1768
|
+
{ kind: "stack", label: "Stack" },
|
|
1769
|
+
{ kind: "grid", label: "Grid" },
|
|
1770
|
+
{ kind: "box", label: "Box" },
|
|
1771
|
+
{ kind: "divider", label: "Divider" },
|
|
1772
|
+
{ kind: "code", label: "Code" },
|
|
1773
|
+
{ kind: "repeater", label: "Repeater" }
|
|
1716
1774
|
];
|
|
1717
1775
|
function EditBar({
|
|
1718
1776
|
editing,
|