@particle-academy/fancy-cms-ui 0.0.1-dev.13 → 0.0.1-dev.15
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 +129 -43
- 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 +94 -15
- package/dist/editor.js.map +1 -1
- package/dist/react.cjs +37 -30
- 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-E5AAQX7P.js +0 -86
- package/dist/chunk-E5AAQX7P.js.map +0 -1
- package/dist/registry-Bk7GN1rA.d.cts +0 -19
- package/dist/registry-Dht9JBLU.d.ts +0 -19
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-
|
|
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-
|
|
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-
|
|
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';
|
|
@@ -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__ */
|
|
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,
|
|
@@ -851,7 +878,7 @@ function EditablePage({
|
|
|
851
878
|
},
|
|
852
879
|
[ed, selNode]
|
|
853
880
|
);
|
|
854
|
-
|
|
881
|
+
useCallback(
|
|
855
882
|
(kind, targetId) => {
|
|
856
883
|
const doc2 = ed.state.doc;
|
|
857
884
|
const target = targetId ? doc2.nodes[targetId] : null;
|
|
@@ -880,12 +907,48 @@ function EditablePage({
|
|
|
880
907
|
window.addEventListener("mousemove", onMove);
|
|
881
908
|
return () => window.removeEventListener("mousemove", onMove);
|
|
882
909
|
}, [editing, draggingEl]);
|
|
910
|
+
const [dropHint, setDropHint] = useState(null);
|
|
911
|
+
const computeDropTarget = (clientX, clientY) => {
|
|
912
|
+
const el = document.elementFromPoint(clientX, clientY)?.closest("[data-cms]");
|
|
913
|
+
if (!el) return { id: null, edge: "inside", rect: null };
|
|
914
|
+
const id = el.getAttribute("data-cms");
|
|
915
|
+
const node = ed.state.doc.nodes[id];
|
|
916
|
+
const r = el.getBoundingClientRect();
|
|
917
|
+
const rect = { x: r.left, y: r.top, w: r.width, h: r.height };
|
|
918
|
+
if (node && CONTAINER_TYPES2.has(node.type)) return { id, edge: "inside", rect };
|
|
919
|
+
return { id, edge: clientY < r.top + r.height / 2 ? "before" : "after", rect };
|
|
920
|
+
};
|
|
921
|
+
const onPageDragOver = (e) => {
|
|
922
|
+
e.preventDefault();
|
|
923
|
+
e.dataTransfer.dropEffect = "copy";
|
|
924
|
+
setDropHint(computeDropTarget(e.clientX, e.clientY));
|
|
925
|
+
};
|
|
926
|
+
const insertRelative = (kind, hint) => {
|
|
927
|
+
const doc2 = ed.state.doc;
|
|
928
|
+
let parent;
|
|
929
|
+
let order;
|
|
930
|
+
if (!hint.id || hint.edge === "inside") {
|
|
931
|
+
parent = hint.id ?? (doc2.sections[doc2.sections.length - 1] ?? null);
|
|
932
|
+
const sib = childrenOf(doc2, parent);
|
|
933
|
+
order = keyBetween(sib.length ? sib[sib.length - 1].order : null, null);
|
|
934
|
+
} else {
|
|
935
|
+
const target = doc2.nodes[hint.id];
|
|
936
|
+
parent = target.parent;
|
|
937
|
+
const sib = childrenOf(doc2, parent);
|
|
938
|
+
const idx = sib.findIndex((n) => n.id === hint.id);
|
|
939
|
+
order = hint.edge === "before" ? keyBetween(idx > 0 ? sib[idx - 1].order : null, target.order) : keyBetween(target.order, idx < sib.length - 1 ? sib[idx + 1].order : null);
|
|
940
|
+
}
|
|
941
|
+
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
942
|
+
const def = ADD_DEFAULTS[kind];
|
|
943
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
944
|
+
ed.select(id);
|
|
945
|
+
};
|
|
883
946
|
const onPageDrop = (e) => {
|
|
884
947
|
const kind = e.dataTransfer.getData("application/x-cms-element");
|
|
885
948
|
if (!kind) return;
|
|
886
949
|
e.preventDefault();
|
|
887
|
-
|
|
888
|
-
|
|
950
|
+
insertRelative(kind, computeDropTarget(e.clientX, e.clientY));
|
|
951
|
+
setDropHint(null);
|
|
889
952
|
setDraggingEl(false);
|
|
890
953
|
};
|
|
891
954
|
const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
|
|
@@ -961,9 +1024,10 @@ function EditablePage({
|
|
|
961
1024
|
ref: pageRef,
|
|
962
1025
|
onClickCapture,
|
|
963
1026
|
onContextMenu: onContextMenuSelect,
|
|
964
|
-
onDragOver: editing ?
|
|
1027
|
+
onDragOver: editing ? onPageDragOver : void 0,
|
|
1028
|
+
onDragLeave: editing ? () => setDropHint(null) : void 0,
|
|
965
1029
|
onDrop: editing ? onPageDrop : void 0,
|
|
966
|
-
children: /* @__PURE__ */ jsx(CmsPage, { doc: ed.state.doc, registry })
|
|
1030
|
+
children: /* @__PURE__ */ jsx(CmsPage, { doc: ed.state.doc, registry, data })
|
|
967
1031
|
}
|
|
968
1032
|
);
|
|
969
1033
|
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 ? (
|
|
@@ -1032,10 +1096,23 @@ function EditablePage({
|
|
|
1032
1096
|
onAdd: addNode
|
|
1033
1097
|
}
|
|
1034
1098
|
) : null,
|
|
1099
|
+
editing && dropHint?.rect ? /* @__PURE__ */ jsx(DropIndicator, { hint: dropHint }) : null,
|
|
1035
1100
|
editing ? /* @__PURE__ */ jsx(ElementPalette, { open: paletteOpen, onDragChange: setDraggingEl }) : null,
|
|
1036
1101
|
editing && timelineDock ? /* @__PURE__ */ jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z2 }, children: timelineDock }) : null
|
|
1037
1102
|
] });
|
|
1038
1103
|
}
|
|
1104
|
+
function DropIndicator({
|
|
1105
|
+
hint
|
|
1106
|
+
}) {
|
|
1107
|
+
const r = hint.rect;
|
|
1108
|
+
if (!r) return null;
|
|
1109
|
+
const color = "#ec4899";
|
|
1110
|
+
if (hint.edge === "inside") {
|
|
1111
|
+
return /* @__PURE__ */ jsx("div", { style: { position: "fixed", left: r.x, top: r.y, width: r.w, height: r.h, outline: `2px dashed ${color}`, outlineOffset: -2, borderRadius: 6, pointerEvents: "none", zIndex: Z2 + 4 } });
|
|
1112
|
+
}
|
|
1113
|
+
const y = hint.edge === "before" ? r.y : r.y + r.h;
|
|
1114
|
+
return /* @__PURE__ */ jsx("div", { style: { position: "fixed", left: r.x, top: y - 1.5, width: r.w, height: 3, background: color, borderRadius: 2, boxShadow: "0 0 0 1px #fff", pointerEvents: "none", zIndex: Z2 + 4 } });
|
|
1115
|
+
}
|
|
1039
1116
|
function ElementPalette({ open, onDragChange }) {
|
|
1040
1117
|
return /* @__PURE__ */ jsxs(
|
|
1041
1118
|
"div",
|
|
@@ -1098,7 +1175,7 @@ function ElementPalette({ open, onDragChange }) {
|
|
|
1098
1175
|
}
|
|
1099
1176
|
);
|
|
1100
1177
|
}
|
|
1101
|
-
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
|
|
1178
|
+
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device", "repeater"]);
|
|
1102
1179
|
var ADD_DEFAULTS = {
|
|
1103
1180
|
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
1104
1181
|
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
@@ -1111,7 +1188,8 @@ var ADD_DEFAULTS = {
|
|
|
1111
1188
|
card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
|
|
1112
1189
|
callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
|
|
1113
1190
|
divider: { type: "divider", props: {}, style: {} },
|
|
1114
|
-
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} }
|
|
1191
|
+
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} },
|
|
1192
|
+
repeater: { type: "repeater", props: { items: "" }, style: { gap: { value: 12, unit: "px" } }, layout: "stack" }
|
|
1115
1193
|
};
|
|
1116
1194
|
var GRIPS = [
|
|
1117
1195
|
{ key: "nw", north: true, west: true, cursor: "nwse-resize" },
|
|
@@ -1322,7 +1400,8 @@ var ADD_MENU = [
|
|
|
1322
1400
|
{ kind: "grid", label: "Grid" },
|
|
1323
1401
|
{ kind: "box", label: "Box" },
|
|
1324
1402
|
{ kind: "divider", label: "Divider" },
|
|
1325
|
-
{ kind: "code", label: "Code" }
|
|
1403
|
+
{ kind: "code", label: "Code" },
|
|
1404
|
+
{ kind: "repeater", label: "Repeater" }
|
|
1326
1405
|
];
|
|
1327
1406
|
function EditBar({
|
|
1328
1407
|
editing,
|