@particle-academy/fancy-cms-ui 0.0.1-dev.12 → 0.0.1-dev.13

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.
@@ -13,8 +13,16 @@ var defaultRegistry = {
13
13
  grid: ({ children }) => children,
14
14
  frame: ({ children }) => children,
15
15
  shape: ({ children }) => children,
16
+ card: ({ children }) => children,
16
17
  text: ({ node }) => literal(node.props.content),
17
18
  heading: ({ node }) => literal(node.props.content) || "Heading",
19
+ link: ({ node }) => /* @__PURE__ */ jsx("a", { href: literal(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: literal(node.props.content) || "link" }),
20
+ divider: () => /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
21
+ callout: ({ node }) => {
22
+ const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[literal(node.props.variant)] ?? "#3b82f6";
23
+ 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: literal(node.props.content) || "Callout" }) });
24
+ },
25
+ code: ({ node }) => /* @__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: literal(node.props.content) }) }),
18
26
  button: ({ node }) => {
19
27
  const label = literal(node.props.label) || literal(node.props.content) || "Button";
20
28
  const variant = literal(node.props.variant) || "primary";
@@ -74,5 +82,5 @@ function CmsPage({
74
82
  }
75
83
 
76
84
  export { CmsPage, RenderNode, defaultRegistry };
77
- //# sourceMappingURL=chunk-ZV7CV54P.js.map
78
- //# sourceMappingURL=chunk-ZV7CV54P.js.map
85
+ //# sourceMappingURL=chunk-E5AAQX7P.js.map
86
+ //# sourceMappingURL=chunk-E5AAQX7P.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-E5AAQX7P.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport { isBinding, type Bound, type Json, type Node, type PageDoc } from \"../document/types\";\n\nexport interface ElementContext {\n node: Node;\n doc: PageDoc;\n /** Pre-rendered child nodes, in order. */\n children: ReactNode;\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/** Resolve a literal prop to a string (bindings resolve in Phase 3). */\nfunction literal(v: Bound<Json> | undefined): string {\n if (v === undefined || v === null) return \"\";\n if (isBinding(v)) return \"\";\n return String(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 }) => literal(node.props.content),\n heading: ({ node }) => literal(node.props.content) || \"Heading\",\n link: ({ node }) => (\n <a href={literal(node.props.href) || \"#\"} style={{ color: \"inherit\", textDecoration: \"underline\" }}>\n {literal(node.props.content) || \"link\"}\n </a>\n ),\n divider: () => <hr style={{ border: \"none\", borderTop: \"1px solid #e2e8f0\", margin: 0 }} />,\n callout: ({ node }) => {\n const tone = ({ info: \"#3b82f6\", success: \"#10b981\", warning: \"#f59e0b\", danger: \"#ef4444\" } as Record<string, string>)[literal(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 }}>{literal(node.props.content) || \"Callout\"}</span>\n </div>\n );\n },\n code: ({ node }) => (\n <pre style={{ margin: 0, padding: \"12px 14px\", borderRadius: 10, background: \"#0b1220\", color: \"#e2e8f0\", fontFamily: \"ui-monospace, monospace\", fontSize: 13, overflowX: \"auto\" }}>\n <code>{literal(node.props.content)}</code>\n </pre>\n ),\n button: ({ node }) => {\n const label = literal(node.props.label) || literal(node.props.content) || \"Button\";\n const variant = literal(node.props.variant) || \"primary\";\n return <span data-cms-button={variant} style={buttonStyle(variant)}>{label}</span>;\n },\n image: ({ node }) => (\n <img\n src={literal(node.props.src)}\n alt={literal(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 { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface RenderNodeProps {\n doc: PageDoc;\n id: string;\n registry?: ElementRegistry;\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 */\nexport function RenderNode({ doc, id, registry = defaultRegistry }: RenderNodeProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n\n const kids = childrenOf(doc, id).map((child) => (\n <RenderNode key={child.id} doc={doc} id={child.id} registry={registry} />\n ));\n\n const renderer = registry[node.type];\n const inner = renderer ? renderer({ node, doc, children: kids }) : node.island ? null : kids;\n\n return (\n <div\n data-cms={node.id}\n data-cms-island={node.island ? node.type : undefined}\n className={node.className}\n >\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 ElementRegistry } from \"./registry\";\n\nexport interface CmsPageProps {\n doc: PageDoc;\n registry?: ElementRegistry;\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 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} />\n ))}\n </Fragment>\n );\n}\n"]}
package/dist/editor.cjs CHANGED
@@ -372,8 +372,16 @@ var defaultRegistry = {
372
372
  grid: ({ children }) => children,
373
373
  frame: ({ children }) => children,
374
374
  shape: ({ children }) => children,
375
+ card: ({ children }) => children,
375
376
  text: ({ node }) => literal(node.props.content),
376
377
  heading: ({ node }) => literal(node.props.content) || "Heading",
378
+ link: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: literal(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: literal(node.props.content) || "link" }),
379
+ divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
380
+ callout: ({ node }) => {
381
+ const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[literal(node.props.variant)] ?? "#3b82f6";
382
+ 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: literal(node.props.content) || "Callout" }) });
383
+ },
384
+ code: ({ node }) => /* @__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: literal(node.props.content) }) }),
377
385
  button: ({ node }) => {
378
386
  const label2 = literal(node.props.label) || literal(node.props.content) || "Button";
379
387
  const variant = literal(node.props.variant) || "primary";
@@ -802,7 +810,7 @@ function Toolbar({ ed }) {
802
810
  }
803
811
  );
804
812
  }
805
- var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
813
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
806
814
  function NodeInspector({
807
815
  node,
808
816
  transform,
@@ -1250,7 +1258,7 @@ function EditablePage({
1250
1258
  const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
1251
1259
  const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
1252
1260
  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 } } } });
1261
+ ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
1254
1262
  ed.select(id);
1255
1263
  },
1256
1264
  [ed, selNode]
@@ -1264,7 +1272,7 @@ function EditablePage({
1264
1272
  const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
1265
1273
  const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
1266
1274
  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 } } } });
1275
+ ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
1268
1276
  ed.select(id);
1269
1277
  },
1270
1278
  [ed]
@@ -1502,13 +1510,20 @@ function ElementPalette({ open, onDragChange }) {
1502
1510
  }
1503
1511
  );
1504
1512
  }
1505
- var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
1513
+ var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
1506
1514
  var ADD_DEFAULTS = {
1507
1515
  text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
1508
1516
  heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
1509
1517
  button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
1518
+ link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
1510
1519
  image: { type: "image", props: { src: "", alt: "" }, style: {} },
1511
- box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
1520
+ box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
1521
+ stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
1522
+ grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
1523
+ card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
1524
+ callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
1525
+ divider: { type: "divider", props: {}, style: {} },
1526
+ code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} }
1512
1527
  };
1513
1528
  var GRIPS = [
1514
1529
  { key: "nw", north: true, west: true, cursor: "nwse-resize" },
@@ -1708,11 +1723,18 @@ function FloatingToolbar({
1708
1723
  ] });
1709
1724
  }
1710
1725
  var ADD_MENU = [
1711
- { kind: "text", label: "Text" },
1712
1726
  { kind: "heading", label: "Heading" },
1727
+ { kind: "text", label: "Text" },
1713
1728
  { kind: "button", label: "Button" },
1729
+ { kind: "link", label: "Link" },
1714
1730
  { kind: "image", label: "Image" },
1715
- { kind: "box", label: "Box" }
1731
+ { kind: "card", label: "Card" },
1732
+ { kind: "callout", label: "Callout" },
1733
+ { kind: "stack", label: "Stack" },
1734
+ { kind: "grid", label: "Grid" },
1735
+ { kind: "box", label: "Box" },
1736
+ { kind: "divider", label: "Divider" },
1737
+ { kind: "code", label: "Code" }
1716
1738
  ];
1717
1739
  function EditBar({
1718
1740
  editing,