@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/editor.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
2
  import { P as PageDoc, i as NodeId, N as Node, m as StyleProps, L as LayoutMode } from './types-CpfPEq3V.cjs';
3
- import { a as ElementRegistry } from './registry-Bk7GN1rA.cjs';
3
+ import { a as ElementRegistry, D as DataContext } from './registry-ni9y0dSw.cjs';
4
4
  import { P as PageOp } from './ops-CdBZarAT.cjs';
5
5
 
6
6
  interface EditorProps {
@@ -28,6 +28,8 @@ interface NodeTransform {
28
28
  interface EditablePageProps {
29
29
  doc: PageDoc;
30
30
  registry?: ElementRegistry;
31
+ /** Data context for `{ $bind }` props + repeaters (server props, etc.). */
32
+ data?: DataContext;
31
33
  /** Slot for the fancy-motion timeline dock (rendered at the bottom in EditMode). */
32
34
  timelineDock?: ReactNode;
33
35
  /** Per-node transforms applied to the page (e.g. the sampled keyframe snapshot). */
@@ -54,7 +56,7 @@ interface EditablePageProps {
54
56
  * toolbar styles it, and the selection can be **dragged to move** / **resized**
55
57
  * — committed as transforms the host captures into a timeline keyframe.
56
58
  */
57
- declare function EditablePage({ doc, registry, timelineDock, transforms, onNodeTransform, onSelect, frames, onProgress, pinned, }: EditablePageProps): ReactElement;
59
+ declare function EditablePage({ doc, registry, data, timelineDock, transforms, onNodeTransform, onSelect, frames, onProgress, pinned, }: EditablePageProps): ReactElement;
58
60
 
59
61
  interface CanvasProps {
60
62
  doc: PageDoc;
package/dist/editor.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
2
  import { P as PageDoc, i as NodeId, N as Node, m as StyleProps, L as LayoutMode } from './types-CpfPEq3V.js';
3
- import { a as ElementRegistry } from './registry-Dht9JBLU.js';
3
+ import { a as ElementRegistry, D as DataContext } from './registry-C3PioMnL.js';
4
4
  import { P as PageOp } from './ops-BsM6kZxY.js';
5
5
 
6
6
  interface EditorProps {
@@ -28,6 +28,8 @@ interface NodeTransform {
28
28
  interface EditablePageProps {
29
29
  doc: PageDoc;
30
30
  registry?: ElementRegistry;
31
+ /** Data context for `{ $bind }` props + repeaters (server props, etc.). */
32
+ data?: DataContext;
31
33
  /** Slot for the fancy-motion timeline dock (rendered at the bottom in EditMode). */
32
34
  timelineDock?: ReactNode;
33
35
  /** Per-node transforms applied to the page (e.g. the sampled keyframe snapshot). */
@@ -54,7 +56,7 @@ interface EditablePageProps {
54
56
  * toolbar styles it, and the selection can be **dragged to move** / **resized**
55
57
  * — committed as transforms the host captures into a timeline keyframe.
56
58
  */
57
- declare function EditablePage({ doc, registry, timelineDock, transforms, onNodeTransform, onSelect, frames, onProgress, pinned, }: EditablePageProps): ReactElement;
59
+ declare function EditablePage({ doc, registry, data, timelineDock, transforms, onNodeTransform, onSelect, frames, onProgress, pinned, }: EditablePageProps): ReactElement;
58
60
 
59
61
  interface CanvasProps {
60
62
  doc: PageDoc;
package/dist/editor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { keyBetween } from './chunk-7YYLYBLI.js';
2
- import { CmsPage, defaultRegistry } from './chunk-ZV7CV54P.js';
3
- import { reduce, childrenOf } from './chunk-3YZMELJJ.js';
2
+ import { CmsPage, defaultRegistry } from './chunk-IXPYY4S2.js';
3
+ import { reduce, childrenOf, isBinding } from './chunk-3YZMELJJ.js';
4
4
  import { useReducer, useCallback, useMemo, useRef, useState, useLayoutEffect, Fragment, useEffect } from 'react';
5
5
  import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
6
6
  import { ContextMenu } from '@particle-academy/react-fancy';
@@ -398,7 +398,7 @@ function Toolbar({ ed }) {
398
398
  }
399
399
  );
400
400
  }
401
- var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
401
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
402
402
  function NodeInspector({
403
403
  node,
404
404
  transform,
@@ -414,7 +414,7 @@ function NodeInspector({
414
414
  const setT = (patch) => onTransform({ ...transform, ...patch });
415
415
  const handled = /* @__PURE__ */ new Set(["content", "label"]);
416
416
  const extraKeys = Object.keys(node.props).filter(
417
- (k) => !handled.has(k) && ["string", "number", "boolean"].includes(typeof node.props[k])
417
+ (k) => !handled.has(k) && (["string", "number", "boolean"].includes(typeof node.props[k]) || isBinding(node.props[k]))
418
418
  );
419
419
  return /* @__PURE__ */ jsxs("div", { style: panel, onPointerDown: (e) => e.stopPropagation(), children: [
420
420
  /* @__PURE__ */ jsxs("div", { style: headRow, children: [
@@ -539,24 +539,50 @@ function Row({ children }) {
539
539
  return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8 }, children });
540
540
  }
541
541
  function PropField({ name, value, onChange }) {
542
+ const bound = isBinding(value);
543
+ const link = /* @__PURE__ */ jsx(
544
+ "button",
545
+ {
546
+ type: "button",
547
+ title: bound ? "Unbind (use a literal value)" : "Bind to data (e.g. item.name)",
548
+ onClick: () => onChange(bound ? "" : { $bind: "" }),
549
+ style: { background: "none", border: "none", cursor: "pointer", padding: 0, fontSize: 11, color: bound ? "#a78bfa" : "#64748b" },
550
+ children: "\u{1F517}"
551
+ }
552
+ );
553
+ if (bound) {
554
+ return /* @__PURE__ */ jsxs("label", { style: field, children: [
555
+ /* @__PURE__ */ jsxs("span", { style: { ...fieldLabel, display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
556
+ name,
557
+ " ",
558
+ link
559
+ ] }),
560
+ /* @__PURE__ */ jsx("input", { style: { ...input2, borderColor: "#7c3aed" }, placeholder: "data path \xB7 e.g. item.name", value: value.$bind, onChange: (e) => onChange({ $bind: e.target.value }) })
561
+ ] });
562
+ }
542
563
  if (typeof value === "boolean") {
543
564
  return /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12 }, children: [
544
565
  /* @__PURE__ */ jsx("input", { type: "checkbox", checked: value, onChange: (e) => onChange(e.target.checked) }),
545
- /* @__PURE__ */ jsx("span", { style: { textTransform: "capitalize" }, children: name })
566
+ /* @__PURE__ */ jsx("span", { style: { textTransform: "capitalize", flex: 1 }, children: name }),
567
+ link
546
568
  ] });
547
569
  }
548
570
  if (name === "variant") {
549
- return /* @__PURE__ */ jsx(Sel, { label: name, value: String(value), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
571
+ return /* @__PURE__ */ jsx(Sel, { label: name, value: String(value ?? ""), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
550
572
  }
551
573
  const isNum = typeof value === "number";
552
574
  return /* @__PURE__ */ jsxs("label", { style: field, children: [
553
- /* @__PURE__ */ jsx("span", { style: fieldLabel, children: name }),
575
+ /* @__PURE__ */ jsxs("span", { style: { ...fieldLabel, display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
576
+ name,
577
+ " ",
578
+ link
579
+ ] }),
554
580
  /* @__PURE__ */ jsx(
555
581
  "input",
556
582
  {
557
583
  style: input2,
558
584
  type: isNum ? "number" : "text",
559
- value: String(value),
585
+ value: String(value ?? ""),
560
586
  onChange: (e) => onChange(isNum ? Number(e.target.value) : e.target.value)
561
587
  }
562
588
  )
@@ -744,6 +770,7 @@ var SNAP = 6;
744
770
  function EditablePage({
745
771
  doc,
746
772
  registry = defaultRegistry,
773
+ data,
747
774
  timelineDock,
748
775
  transforms,
749
776
  onNodeTransform,
@@ -846,7 +873,7 @@ function EditablePage({
846
873
  const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
847
874
  const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
848
875
  const def = ADD_DEFAULTS[kind];
849
- ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
876
+ ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
850
877
  ed.select(id);
851
878
  },
852
879
  [ed, selNode]
@@ -860,7 +887,7 @@ function EditablePage({
860
887
  const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
861
888
  const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
862
889
  const def = ADD_DEFAULTS[kind];
863
- ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
890
+ ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
864
891
  ed.select(id);
865
892
  },
866
893
  [ed]
@@ -963,7 +990,7 @@ function EditablePage({
963
990
  onContextMenu: onContextMenuSelect,
964
991
  onDragOver: editing ? (e) => e.preventDefault() : void 0,
965
992
  onDrop: editing ? onPageDrop : void 0,
966
- children: /* @__PURE__ */ jsx(CmsPage, { doc: ed.state.doc, registry })
993
+ children: /* @__PURE__ */ jsx(CmsPage, { doc: ed.state.doc, registry, data })
967
994
  }
968
995
  );
969
996
  const pageShell = pinned ? /* @__PURE__ */ jsx("div", { ref: spacerRef, style: { height: `${Math.max(1, frames) * 100}vh` }, children: /* @__PURE__ */ jsx("div", { style: { position: "sticky", top: 0, height: "100vh", overflow: "hidden" }, children: page }) }) : frames > 1 ? (
@@ -1098,13 +1125,21 @@ function ElementPalette({ open, onDragChange }) {
1098
1125
  }
1099
1126
  );
1100
1127
  }
1101
- var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
1128
+ var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device", "repeater"]);
1102
1129
  var ADD_DEFAULTS = {
1103
1130
  text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
1104
1131
  heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
1105
1132
  button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
1133
+ link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
1106
1134
  image: { type: "image", props: { src: "", alt: "" }, style: {} },
1107
- box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
1135
+ box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
1136
+ stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
1137
+ grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
1138
+ card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
1139
+ callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
1140
+ divider: { type: "divider", props: {}, style: {} },
1141
+ code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
1142
+ repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
1108
1143
  };
1109
1144
  var GRIPS = [
1110
1145
  { key: "nw", north: true, west: true, cursor: "nwse-resize" },
@@ -1304,11 +1339,19 @@ function FloatingToolbar({
1304
1339
  ] });
1305
1340
  }
1306
1341
  var ADD_MENU = [
1307
- { kind: "text", label: "Text" },
1308
1342
  { kind: "heading", label: "Heading" },
1343
+ { kind: "text", label: "Text" },
1309
1344
  { kind: "button", label: "Button" },
1345
+ { kind: "link", label: "Link" },
1310
1346
  { kind: "image", label: "Image" },
1311
- { kind: "box", label: "Box" }
1347
+ { kind: "card", label: "Card" },
1348
+ { kind: "callout", label: "Callout" },
1349
+ { kind: "stack", label: "Stack" },
1350
+ { kind: "grid", label: "Grid" },
1351
+ { kind: "box", label: "Box" },
1352
+ { kind: "divider", label: "Divider" },
1353
+ { kind: "code", label: "Code" },
1354
+ { kind: "repeater", label: "Repeater" }
1312
1355
  ];
1313
1356
  function EditBar({
1314
1357
  editing,