@particle-academy/fancy-cms-ui 0.0.1-dev.5 → 0.0.1-dev.7
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-5PWZGIYZ.js → chunk-5O6U4VIU.js} +25 -2
- package/dist/chunk-5O6U4VIU.js.map +1 -0
- package/dist/editor.cjs +384 -71
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +42 -3
- package/dist/editor.d.ts +42 -3
- package/dist/editor.js +358 -69
- package/dist/editor.js.map +1 -1
- package/dist/react.cjs +23 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-5PWZGIYZ.js.map +0 -1
package/dist/editor.cjs
CHANGED
|
@@ -233,8 +233,8 @@ var DEFAULT_BREAKPOINTS = {
|
|
|
233
233
|
|
|
234
234
|
// src/render/css.ts
|
|
235
235
|
var selectorFor = (id) => `[data-cms="${id}"]`;
|
|
236
|
-
function lengthToCss(
|
|
237
|
-
return `${
|
|
236
|
+
function lengthToCss(len2) {
|
|
237
|
+
return `${len2.value}${len2.unit}`;
|
|
238
238
|
}
|
|
239
239
|
function sizeToCss(size) {
|
|
240
240
|
if (size === "fill") return "100%";
|
|
@@ -321,9 +321,9 @@ function isParentFree(doc, node) {
|
|
|
321
321
|
function serializeRule(selector, decls, indent) {
|
|
322
322
|
const props = Object.keys(decls).sort();
|
|
323
323
|
if (props.length === 0) return "";
|
|
324
|
-
const
|
|
324
|
+
const body2 = props.map((p) => `${indent} ${p}: ${decls[p]};`).join("\n");
|
|
325
325
|
return `${indent}${selector} {
|
|
326
|
-
${
|
|
326
|
+
${body2}
|
|
327
327
|
${indent}}`;
|
|
328
328
|
}
|
|
329
329
|
function emitDocCss(doc) {
|
|
@@ -368,6 +368,12 @@ var defaultRegistry = {
|
|
|
368
368
|
frame: ({ children }) => children,
|
|
369
369
|
shape: ({ children }) => children,
|
|
370
370
|
text: ({ node }) => literal(node.props.content),
|
|
371
|
+
heading: ({ node }) => literal(node.props.content) || "Heading",
|
|
372
|
+
button: ({ node }) => {
|
|
373
|
+
const label2 = literal(node.props.label) || literal(node.props.content) || "Button";
|
|
374
|
+
const variant = literal(node.props.variant) || "primary";
|
|
375
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
|
|
376
|
+
},
|
|
371
377
|
image: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
372
378
|
"img",
|
|
373
379
|
{
|
|
@@ -377,6 +383,23 @@ var defaultRegistry = {
|
|
|
377
383
|
}
|
|
378
384
|
)
|
|
379
385
|
};
|
|
386
|
+
function buttonStyle(variant) {
|
|
387
|
+
const base = {
|
|
388
|
+
display: "inline-flex",
|
|
389
|
+
alignItems: "center",
|
|
390
|
+
gap: 6,
|
|
391
|
+
padding: "9px 16px",
|
|
392
|
+
borderRadius: 8,
|
|
393
|
+
fontWeight: 600,
|
|
394
|
+
fontSize: 14,
|
|
395
|
+
lineHeight: 1,
|
|
396
|
+
cursor: "pointer",
|
|
397
|
+
userSelect: "none"
|
|
398
|
+
};
|
|
399
|
+
if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
|
|
400
|
+
if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
|
|
401
|
+
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
402
|
+
}
|
|
380
403
|
function RenderNode({ doc, id, registry = defaultRegistry }) {
|
|
381
404
|
const node = doc.nodes[id];
|
|
382
405
|
if (!node) return null;
|
|
@@ -708,7 +731,202 @@ function keyBetween(a, b) {
|
|
|
708
731
|
return a + BASE[MID];
|
|
709
732
|
}
|
|
710
733
|
}
|
|
734
|
+
var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
|
|
735
|
+
function NodeInspector({
|
|
736
|
+
node,
|
|
737
|
+
transform,
|
|
738
|
+
measured,
|
|
739
|
+
onProps,
|
|
740
|
+
onStyle,
|
|
741
|
+
onTransform,
|
|
742
|
+
onRemove,
|
|
743
|
+
onClose
|
|
744
|
+
}) {
|
|
745
|
+
const base = node.style.base ?? {};
|
|
746
|
+
const setT = (patch) => onTransform({ ...transform, ...patch });
|
|
747
|
+
const handled = /* @__PURE__ */ new Set(["content", "label"]);
|
|
748
|
+
const extraKeys = Object.keys(node.props).filter(
|
|
749
|
+
(k) => !handled.has(k) && ["string", "number", "boolean"].includes(typeof node.props[k])
|
|
750
|
+
);
|
|
751
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panel, onPointerDown: (e) => e.stopPropagation(), children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: headRow, children: [
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2, minWidth: 0 }, children: [
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontSize: 12, textTransform: "capitalize" }, children: node.type }),
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, opacity: 0.5, fontFamily: "ui-monospace, monospace", overflow: "hidden", textOverflow: "ellipsis" }, children: node.id })
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1 } }),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...iconBtn, color: "#fca5a5" }, title: "Delete element", onClick: onRemove, children: "\u{1F5D1}" }),
|
|
759
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: iconBtn, title: "Close", onClick: onClose, children: "\u2715" })
|
|
760
|
+
] }),
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: body, children: [
|
|
762
|
+
"content" in node.props ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Content", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
763
|
+
"textarea",
|
|
764
|
+
{
|
|
765
|
+
style: { ...input2, minHeight: 64, resize: "vertical", lineHeight: 1.4 },
|
|
766
|
+
value: String(node.props.content ?? ""),
|
|
767
|
+
onChange: (e) => onProps({ content: e.target.value })
|
|
768
|
+
}
|
|
769
|
+
) }) : null,
|
|
770
|
+
"label" in node.props ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Label", children: /* @__PURE__ */ jsxRuntime.jsx("input", { style: input2, value: String(node.props.label ?? ""), onChange: (e) => onProps({ label: e.target.value }) }) }) : null,
|
|
771
|
+
extraKeys.length ? /* @__PURE__ */ jsxRuntime.jsx(Group, { label: "Properties", children: extraKeys.map((k) => /* @__PURE__ */ jsxRuntime.jsx(PropField, { name: k, value: node.props[k], onChange: (v) => onProps({ [k]: v }) }, k)) }) : null,
|
|
772
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Position & size", children: [
|
|
773
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
774
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "X", value: transform.x, placeholder: "0", onChange: (v) => setT({ x: v }) }),
|
|
775
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Y", value: transform.y, placeholder: "0", onChange: (v) => setT({ y: v }) })
|
|
776
|
+
] }),
|
|
777
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
778
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "W", value: transform.w, placeholder: `${Math.round(measured.w)}`, onChange: (v) => setT({ w: v }) }),
|
|
779
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "H", value: transform.h, placeholder: `${Math.round(measured.h)}`, onChange: (v) => setT({ h: v }) })
|
|
780
|
+
] }),
|
|
781
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Scale", value: transform.scale, placeholder: "1", step: 0.05, onChange: (v) => setT({ scale: v }) }),
|
|
783
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Rotate", value: transform.rotate, placeholder: "0", onChange: (v) => setT({ rotate: v }) })
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
786
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Opacity", value: transform.opacity, placeholder: "1", min: 0, max: 1, step: 0.05, onChange: (v) => setT({ opacity: v }) }),
|
|
787
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...miniBtn, alignSelf: "end" }, onClick: () => onTransform({}), title: "Clear transform", children: "Reset" })
|
|
788
|
+
] })
|
|
789
|
+
] }),
|
|
790
|
+
!CONTAINER_TYPES.has(node.type) ? /* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Text", children: [
|
|
791
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
792
|
+
/* @__PURE__ */ jsxRuntime.jsx(Color, { label: "Color", value: base.color ?? "#0f172a", onChange: (v) => onStyle({ color: v }) }),
|
|
793
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Size", value: base.fontSize?.value, placeholder: "16", onChange: (v) => onStyle({ fontSize: v == null ? void 0 : len(v) }) })
|
|
794
|
+
] }),
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
796
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
797
|
+
Sel,
|
|
798
|
+
{
|
|
799
|
+
label: "Weight",
|
|
800
|
+
value: String(base.fontWeight ?? ""),
|
|
801
|
+
options: [["", "\u2014"], ["400", "Regular"], ["500", "Medium"], ["600", "Semibold"], ["700", "Bold"], ["800", "Heavy"]],
|
|
802
|
+
onChange: (v) => onStyle({ fontWeight: v ? Number(v) : void 0 })
|
|
803
|
+
}
|
|
804
|
+
),
|
|
805
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
806
|
+
Sel,
|
|
807
|
+
{
|
|
808
|
+
label: "Align",
|
|
809
|
+
value: base.textAlign ?? "",
|
|
810
|
+
options: [["", "\u2014"], ["left", "Left"], ["center", "Center"], ["right", "Right"], ["justify", "Justify"]],
|
|
811
|
+
onChange: (v) => onStyle({ textAlign: v || void 0 })
|
|
812
|
+
}
|
|
813
|
+
)
|
|
814
|
+
] })
|
|
815
|
+
] }) : null,
|
|
816
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Group, { label: "Box", children: [
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
818
|
+
/* @__PURE__ */ jsxRuntime.jsx(Color, { label: "Background", value: hex(base.background), onChange: (v) => onStyle({ background: v }) }),
|
|
819
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Radius", value: base.radius?.value, placeholder: "0", onChange: (v) => onStyle({ radius: v == null ? void 0 : len(v) }) })
|
|
820
|
+
] }),
|
|
821
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Row, { children: [
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Padding", value: base.padding?.value, placeholder: "0", onChange: (v) => onStyle({ padding: v == null ? void 0 : len(v) }) }),
|
|
823
|
+
/* @__PURE__ */ jsxRuntime.jsx(Num, { label: "Gap", value: base.gap?.value, placeholder: "0", onChange: (v) => onStyle({ gap: v == null ? void 0 : len(v) }) })
|
|
824
|
+
] })
|
|
825
|
+
] })
|
|
826
|
+
] })
|
|
827
|
+
] });
|
|
828
|
+
}
|
|
829
|
+
function Group({ label: label2, children }) {
|
|
830
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, paddingBottom: 12, marginBottom: 12, borderBottom: "1px solid #1e293b" }, children: [
|
|
831
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, letterSpacing: "0.06em", textTransform: "uppercase", opacity: 0.5 }, children: label2 }),
|
|
832
|
+
children
|
|
833
|
+
] });
|
|
834
|
+
}
|
|
835
|
+
function Row({ children }) {
|
|
836
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 8 }, children });
|
|
837
|
+
}
|
|
838
|
+
function PropField({ name, value, onChange }) {
|
|
839
|
+
if (typeof value === "boolean") {
|
|
840
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12 }, children: [
|
|
841
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: value, onChange: (e) => onChange(e.target.checked) }),
|
|
842
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { textTransform: "capitalize" }, children: name })
|
|
843
|
+
] });
|
|
844
|
+
}
|
|
845
|
+
if (name === "variant") {
|
|
846
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Sel, { label: name, value: String(value), options: [["primary", "Primary"], ["ghost", "Ghost"], ["outline", "Outline"]], onChange });
|
|
847
|
+
}
|
|
848
|
+
const isNum = typeof value === "number";
|
|
849
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
850
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: name }),
|
|
851
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
852
|
+
"input",
|
|
853
|
+
{
|
|
854
|
+
style: input2,
|
|
855
|
+
type: isNum ? "number" : "text",
|
|
856
|
+
value: String(value),
|
|
857
|
+
onChange: (e) => onChange(isNum ? Number(e.target.value) : e.target.value)
|
|
858
|
+
}
|
|
859
|
+
)
|
|
860
|
+
] });
|
|
861
|
+
}
|
|
862
|
+
function Num({ label: label2, value, placeholder, onChange, step, min, max }) {
|
|
863
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
864
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
|
|
865
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
866
|
+
"input",
|
|
867
|
+
{
|
|
868
|
+
style: input2,
|
|
869
|
+
type: "number",
|
|
870
|
+
step,
|
|
871
|
+
min,
|
|
872
|
+
max,
|
|
873
|
+
value: value ?? "",
|
|
874
|
+
placeholder,
|
|
875
|
+
onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value))
|
|
876
|
+
}
|
|
877
|
+
)
|
|
878
|
+
] });
|
|
879
|
+
}
|
|
880
|
+
function Color({ label: label2, value, onChange }) {
|
|
881
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
882
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
|
|
883
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { style: { ...input2, padding: 2, height: 30 }, type: "color", value, onChange: (e) => onChange(e.target.value) })
|
|
884
|
+
] });
|
|
885
|
+
}
|
|
886
|
+
function Sel({ label: label2, value, options, onChange }) {
|
|
887
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: field, children: [
|
|
888
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: fieldLabel, children: label2 }),
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx("select", { style: input2, value, onChange: (e) => onChange(e.target.value), children: options.map(([v, l]) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: v, children: l }, v)) })
|
|
890
|
+
] });
|
|
891
|
+
}
|
|
892
|
+
var len = (value) => ({ value, unit: "px" });
|
|
893
|
+
var hex = (v) => v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
|
|
711
894
|
var Z = 2147483e3;
|
|
895
|
+
var panel = {
|
|
896
|
+
position: "fixed",
|
|
897
|
+
top: 60,
|
|
898
|
+
right: 12,
|
|
899
|
+
bottom: 96,
|
|
900
|
+
width: 264,
|
|
901
|
+
display: "flex",
|
|
902
|
+
flexDirection: "column",
|
|
903
|
+
background: "#0b1220",
|
|
904
|
+
color: "#e2e8f0",
|
|
905
|
+
border: "1px solid #1e293b",
|
|
906
|
+
borderRadius: 12,
|
|
907
|
+
boxShadow: "0 18px 48px -16px rgba(0,0,0,0.6)",
|
|
908
|
+
zIndex: Z + 2,
|
|
909
|
+
fontFamily: "system-ui, sans-serif",
|
|
910
|
+
overflow: "hidden"
|
|
911
|
+
};
|
|
912
|
+
var headRow = { display: "flex", alignItems: "center", gap: 8, padding: "10px 12px", borderBottom: "1px solid #1e293b" };
|
|
913
|
+
var body = { padding: 12, overflowY: "auto" };
|
|
914
|
+
var field = { display: "flex", flexDirection: "column", gap: 4, flex: 1, minWidth: 0 };
|
|
915
|
+
var fieldLabel = { fontSize: 10, opacity: 0.6 };
|
|
916
|
+
var input2 = {
|
|
917
|
+
width: "100%",
|
|
918
|
+
boxSizing: "border-box",
|
|
919
|
+
background: "#0f172a",
|
|
920
|
+
color: "#e2e8f0",
|
|
921
|
+
border: "1px solid #334155",
|
|
922
|
+
borderRadius: 6,
|
|
923
|
+
padding: "5px 7px",
|
|
924
|
+
font: "inherit",
|
|
925
|
+
fontSize: 12
|
|
926
|
+
};
|
|
927
|
+
var iconBtn = { background: "transparent", border: "none", color: "#e2e8f0", cursor: "pointer", fontSize: 13, padding: 4, borderRadius: 6 };
|
|
928
|
+
var miniBtn = { font: "inherit", fontSize: 11, color: "#e2e8f0", background: "#334155", border: "1px solid #475569", borderRadius: 6, padding: "5px 10px", cursor: "pointer" };
|
|
929
|
+
var Z2 = 2147483e3;
|
|
712
930
|
function EditablePage({
|
|
713
931
|
doc,
|
|
714
932
|
registry = defaultRegistry,
|
|
@@ -717,7 +935,8 @@ function EditablePage({
|
|
|
717
935
|
onNodeTransform,
|
|
718
936
|
onSelect,
|
|
719
937
|
frames = 1,
|
|
720
|
-
onProgress
|
|
938
|
+
onProgress,
|
|
939
|
+
pinned = true
|
|
721
940
|
}) {
|
|
722
941
|
const ed = useEditor(doc);
|
|
723
942
|
const [editing, setEditing] = react.useState(false);
|
|
@@ -746,35 +965,23 @@ function EditablePage({
|
|
|
746
965
|
root.querySelectorAll("[data-cms]").forEach((el) => {
|
|
747
966
|
const id = el.dataset.cms;
|
|
748
967
|
const t = id ? transforms?.[id] : void 0;
|
|
749
|
-
|
|
750
|
-
el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1})`;
|
|
751
|
-
el.style.opacity = t.opacity != null ? String(t.opacity) : "";
|
|
752
|
-
} else {
|
|
753
|
-
el.style.transform = "";
|
|
754
|
-
el.style.opacity = "";
|
|
755
|
-
}
|
|
968
|
+
applyTransform(el, t);
|
|
756
969
|
});
|
|
757
970
|
}, [transforms, ed.state.doc]);
|
|
758
971
|
react.useEffect(() => {
|
|
759
972
|
const spacer = spacerRef.current;
|
|
760
973
|
if (!spacer || !onProgress) return;
|
|
761
|
-
let raf = 0;
|
|
762
974
|
const compute = () => {
|
|
763
|
-
|
|
764
|
-
const len = spacer.offsetHeight - window.innerHeight;
|
|
975
|
+
const len2 = spacer.offsetHeight - window.innerHeight;
|
|
765
976
|
const top = spacer.getBoundingClientRect().top;
|
|
766
|
-
onProgress(
|
|
767
|
-
};
|
|
768
|
-
const onScroll = () => {
|
|
769
|
-
if (!raf) raf = requestAnimationFrame(compute);
|
|
977
|
+
onProgress(len2 > 0 ? Math.min(1, Math.max(0, -top / len2)) : 0);
|
|
770
978
|
};
|
|
771
979
|
compute();
|
|
772
|
-
window.addEventListener("scroll",
|
|
773
|
-
window.addEventListener("resize",
|
|
980
|
+
window.addEventListener("scroll", compute, { passive: true });
|
|
981
|
+
window.addEventListener("resize", compute);
|
|
774
982
|
return () => {
|
|
775
|
-
window.removeEventListener("scroll",
|
|
776
|
-
window.removeEventListener("resize",
|
|
777
|
-
if (raf) cancelAnimationFrame(raf);
|
|
983
|
+
window.removeEventListener("scroll", compute);
|
|
984
|
+
window.removeEventListener("resize", compute);
|
|
778
985
|
};
|
|
779
986
|
}, [onProgress, frames]);
|
|
780
987
|
const measureBox = react.useCallback(() => {
|
|
@@ -792,7 +999,7 @@ function EditablePage({
|
|
|
792
999
|
window.removeEventListener("scroll", measureBox);
|
|
793
1000
|
window.removeEventListener("resize", measureBox);
|
|
794
1001
|
};
|
|
795
|
-
}, [measureBox, ed.state.doc
|
|
1002
|
+
}, [measureBox, ed.state.doc]);
|
|
796
1003
|
react.useEffect(() => {
|
|
797
1004
|
if (!editing || !selection || !pageRef.current || selNode?.type !== "text") return;
|
|
798
1005
|
const el = pageRef.current.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
|
|
@@ -817,17 +1024,29 @@ function EditablePage({
|
|
|
817
1024
|
ed.select(el.getAttribute("data-cms"));
|
|
818
1025
|
}
|
|
819
1026
|
};
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1027
|
+
const addNode = react.useCallback(
|
|
1028
|
+
(kind) => {
|
|
1029
|
+
const doc2 = ed.state.doc;
|
|
1030
|
+
const parent = selNode ? CONTAINER_TYPES2.has(selNode.type) ? selNode.id : selNode.parent : doc2.sections[doc2.sections.length - 1] ?? null;
|
|
1031
|
+
const siblings = childrenOf(doc2, parent);
|
|
1032
|
+
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null);
|
|
1033
|
+
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
1034
|
+
const def = ADD_DEFAULTS[kind];
|
|
1035
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1036
|
+
ed.select(id);
|
|
1037
|
+
},
|
|
1038
|
+
[ed, selNode]
|
|
1039
|
+
);
|
|
828
1040
|
const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
|
|
1041
|
+
const setProps = (patch) => selection && ed.apply({ t: "set_props", id: selection, patch });
|
|
1042
|
+
const removeSelected = () => {
|
|
1043
|
+
if (!selection) return;
|
|
1044
|
+
ed.apply({ t: "remove_node", id: selection });
|
|
1045
|
+
ed.select(null);
|
|
1046
|
+
};
|
|
1047
|
+
const page = /* @__PURE__ */ jsxRuntime.jsx("div", { ref: pageRef, onClickCapture, children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry }) });
|
|
829
1048
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
830
|
-
/* @__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:
|
|
1049
|
+
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 }) }) : page,
|
|
831
1050
|
editing && box && selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
832
1051
|
SelectionOverlay,
|
|
833
1052
|
{
|
|
@@ -839,7 +1058,20 @@ function EditablePage({
|
|
|
839
1058
|
onLive: measureBox
|
|
840
1059
|
}
|
|
841
1060
|
) : null,
|
|
842
|
-
editing && box && selNode ? /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbar, { box, isText: selNode.type === "text", base: selNode.style.base, setStyle }) : null,
|
|
1061
|
+
editing && box && selNode ? /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbar, { box, isText: selNode.type === "text" || selNode.type === "heading", base: selNode.style.base, setStyle }) : null,
|
|
1062
|
+
editing && selNode && box ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1063
|
+
NodeInspector,
|
|
1064
|
+
{
|
|
1065
|
+
node: selNode,
|
|
1066
|
+
transform: (selection ? transforms?.[selection] : void 0) ?? {},
|
|
1067
|
+
measured: { w: box.w, h: box.h },
|
|
1068
|
+
onProps: setProps,
|
|
1069
|
+
onStyle: setStyle,
|
|
1070
|
+
onTransform: (t) => selection && onNodeTransform?.(selection, t),
|
|
1071
|
+
onRemove: removeSelected,
|
|
1072
|
+
onClose: () => ed.select(null)
|
|
1073
|
+
}
|
|
1074
|
+
) : null,
|
|
843
1075
|
revealed || editing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
844
1076
|
EditBar,
|
|
845
1077
|
{
|
|
@@ -852,12 +1084,30 @@ function EditablePage({
|
|
|
852
1084
|
canRedo: ed.canRedo,
|
|
853
1085
|
onUndo: ed.undo,
|
|
854
1086
|
onRedo: ed.redo,
|
|
855
|
-
|
|
1087
|
+
onAdd: addNode
|
|
856
1088
|
}
|
|
857
1089
|
) : null,
|
|
858
|
-
editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex:
|
|
1090
|
+
editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z2 }, children: timelineDock }) : null
|
|
859
1091
|
] });
|
|
860
1092
|
}
|
|
1093
|
+
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
|
|
1094
|
+
var ADD_DEFAULTS = {
|
|
1095
|
+
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
1096
|
+
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
1097
|
+
button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
|
|
1098
|
+
image: { type: "image", props: { src: "", alt: "" }, style: {} },
|
|
1099
|
+
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
|
|
1100
|
+
};
|
|
1101
|
+
var GRIPS = [
|
|
1102
|
+
{ key: "nw", north: true, west: true, cursor: "nwse-resize" },
|
|
1103
|
+
{ key: "n", north: true, cursor: "ns-resize" },
|
|
1104
|
+
{ key: "ne", north: true, east: true, cursor: "nesw-resize" },
|
|
1105
|
+
{ key: "e", east: true, cursor: "ew-resize" },
|
|
1106
|
+
{ key: "se", south: true, east: true, cursor: "nwse-resize" },
|
|
1107
|
+
{ key: "s", south: true, cursor: "ns-resize" },
|
|
1108
|
+
{ key: "sw", south: true, west: true, cursor: "nesw-resize" },
|
|
1109
|
+
{ key: "w", west: true, cursor: "ew-resize" }
|
|
1110
|
+
];
|
|
861
1111
|
function SelectionOverlay({
|
|
862
1112
|
box,
|
|
863
1113
|
base,
|
|
@@ -869,29 +1119,48 @@ function SelectionOverlay({
|
|
|
869
1119
|
const drag = react.useRef(null);
|
|
870
1120
|
const apply = (t) => {
|
|
871
1121
|
const el = getEl();
|
|
872
|
-
if (el)
|
|
873
|
-
el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1})`;
|
|
874
|
-
el.style.opacity = t.opacity != null ? String(t.opacity) : "";
|
|
875
|
-
}
|
|
1122
|
+
if (el) applyTransform(el, t);
|
|
876
1123
|
};
|
|
877
|
-
const
|
|
1124
|
+
const startMove = (e) => {
|
|
878
1125
|
e.stopPropagation();
|
|
879
1126
|
e.preventDefault();
|
|
880
1127
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
881
|
-
drag.current = {
|
|
1128
|
+
drag.current = { x: e.clientX, y: e.clientY, w: box.w, h: box.h, live: { ...base } };
|
|
882
1129
|
};
|
|
883
|
-
const
|
|
1130
|
+
const moveMove = (e) => {
|
|
884
1131
|
const d = drag.current;
|
|
885
1132
|
if (!d) return;
|
|
886
|
-
|
|
887
|
-
d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
|
|
888
|
-
} else {
|
|
889
|
-
const factor = Math.max(0.1, (d.w + (e.clientX - d.x)) / d.w);
|
|
890
|
-
d.live = { ...base, scale: Number(((base.scale ?? 1) * factor).toFixed(3)) };
|
|
891
|
-
}
|
|
1133
|
+
d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
|
|
892
1134
|
apply(d.live);
|
|
893
1135
|
onLive();
|
|
894
1136
|
};
|
|
1137
|
+
const startResize = (g) => (e) => {
|
|
1138
|
+
e.stopPropagation();
|
|
1139
|
+
e.preventDefault();
|
|
1140
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1141
|
+
drag.current = { x: e.clientX, y: e.clientY, w: base.w ?? box.w, h: base.h ?? box.h, live: { ...base } };
|
|
1142
|
+
e.currentTarget.dataset.grip = g.key;
|
|
1143
|
+
};
|
|
1144
|
+
const resizeMove = (g) => (e) => {
|
|
1145
|
+
const d = drag.current;
|
|
1146
|
+
if (!d) return;
|
|
1147
|
+
const dx = e.clientX - d.x;
|
|
1148
|
+
const dy = e.clientY - d.y;
|
|
1149
|
+
const live = { ...base };
|
|
1150
|
+
if (g.east) live.w = Math.max(16, Math.round(d.w + dx));
|
|
1151
|
+
if (g.west) {
|
|
1152
|
+
live.w = Math.max(16, Math.round(d.w - dx));
|
|
1153
|
+
live.x = (base.x ?? 0) + (d.w - live.w);
|
|
1154
|
+
}
|
|
1155
|
+
if (g.south) live.h = Math.max(16, Math.round(d.h + dy));
|
|
1156
|
+
if (g.north) {
|
|
1157
|
+
live.h = Math.max(16, Math.round(d.h - dy));
|
|
1158
|
+
live.y = (base.y ?? 0) + (d.h - live.h);
|
|
1159
|
+
}
|
|
1160
|
+
d.live = live;
|
|
1161
|
+
apply(live);
|
|
1162
|
+
onLive();
|
|
1163
|
+
};
|
|
895
1164
|
const end = () => {
|
|
896
1165
|
const d = drag.current;
|
|
897
1166
|
if (d) onTransform(d.live);
|
|
@@ -906,17 +1175,23 @@ function SelectionOverlay({
|
|
|
906
1175
|
outline: "2px solid #8b5cf6",
|
|
907
1176
|
borderRadius: 4,
|
|
908
1177
|
pointerEvents: "none",
|
|
909
|
-
zIndex:
|
|
1178
|
+
zIndex: Z2
|
|
910
1179
|
};
|
|
911
1180
|
const handle = {
|
|
912
1181
|
position: "fixed",
|
|
913
|
-
width:
|
|
914
|
-
height:
|
|
915
|
-
background: "#
|
|
916
|
-
border: "2px solid #
|
|
917
|
-
borderRadius:
|
|
918
|
-
zIndex:
|
|
919
|
-
touchAction: "none"
|
|
1182
|
+
width: 12,
|
|
1183
|
+
height: 12,
|
|
1184
|
+
background: "#fff",
|
|
1185
|
+
border: "2px solid #8b5cf6",
|
|
1186
|
+
borderRadius: 3,
|
|
1187
|
+
zIndex: Z2 + 1,
|
|
1188
|
+
touchAction: "none",
|
|
1189
|
+
boxSizing: "border-box"
|
|
1190
|
+
};
|
|
1191
|
+
const gripPos = (g) => {
|
|
1192
|
+
const cx = g.west ? box.x : g.east ? box.x + box.w : box.x + box.w / 2;
|
|
1193
|
+
const cy = g.north ? box.y : g.south ? box.y + box.h : box.y + box.h / 2;
|
|
1194
|
+
return { left: cx - 6, top: cy - 6, cursor: g.cursor };
|
|
920
1195
|
};
|
|
921
1196
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
922
1197
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: outline }),
|
|
@@ -925,22 +1200,23 @@ function SelectionOverlay({
|
|
|
925
1200
|
"div",
|
|
926
1201
|
{
|
|
927
1202
|
title: "Drag to move",
|
|
928
|
-
onPointerDown:
|
|
929
|
-
onPointerMove:
|
|
1203
|
+
onPointerDown: startMove,
|
|
1204
|
+
onPointerMove: moveMove,
|
|
930
1205
|
onPointerUp: end,
|
|
931
|
-
style: { ...handle, left: box.x -
|
|
1206
|
+
style: { ...handle, width: 18, height: 18, borderRadius: 9, background: "#8b5cf6", border: "2px solid #fff", left: box.x - 22, top: box.y - 22, cursor: "move" }
|
|
932
1207
|
}
|
|
933
1208
|
),
|
|
934
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1209
|
+
GRIPS.map((g) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
935
1210
|
"div",
|
|
936
1211
|
{
|
|
937
1212
|
title: "Drag to resize",
|
|
938
|
-
onPointerDown:
|
|
939
|
-
onPointerMove:
|
|
1213
|
+
onPointerDown: startResize(g),
|
|
1214
|
+
onPointerMove: resizeMove(g),
|
|
940
1215
|
onPointerUp: end,
|
|
941
|
-
style: { ...handle,
|
|
942
|
-
}
|
|
943
|
-
|
|
1216
|
+
style: { ...handle, ...gripPos(g) }
|
|
1217
|
+
},
|
|
1218
|
+
g.key
|
|
1219
|
+
))
|
|
944
1220
|
] }) : null
|
|
945
1221
|
] });
|
|
946
1222
|
}
|
|
@@ -963,7 +1239,7 @@ function FloatingToolbar({
|
|
|
963
1239
|
border: "1px solid rgba(255,255,255,0.12)",
|
|
964
1240
|
borderRadius: 999,
|
|
965
1241
|
boxShadow: "0 8px 24px -8px rgba(0,0,0,0.5)",
|
|
966
|
-
zIndex:
|
|
1242
|
+
zIndex: Z2 + 2,
|
|
967
1243
|
fontFamily: "system-ui, sans-serif",
|
|
968
1244
|
fontSize: 12
|
|
969
1245
|
};
|
|
@@ -981,6 +1257,13 @@ function FloatingToolbar({
|
|
|
981
1257
|
] })
|
|
982
1258
|
] });
|
|
983
1259
|
}
|
|
1260
|
+
var ADD_MENU = [
|
|
1261
|
+
{ kind: "text", label: "Text" },
|
|
1262
|
+
{ kind: "heading", label: "Heading" },
|
|
1263
|
+
{ kind: "button", label: "Button" },
|
|
1264
|
+
{ kind: "image", label: "Image" },
|
|
1265
|
+
{ kind: "box", label: "Box" }
|
|
1266
|
+
];
|
|
984
1267
|
function EditBar({
|
|
985
1268
|
editing,
|
|
986
1269
|
onToggle,
|
|
@@ -988,19 +1271,48 @@ function EditBar({
|
|
|
988
1271
|
canRedo,
|
|
989
1272
|
onUndo,
|
|
990
1273
|
onRedo,
|
|
991
|
-
|
|
1274
|
+
onAdd
|
|
992
1275
|
}) {
|
|
993
|
-
const
|
|
1276
|
+
const [open, setOpen] = react.useState(false);
|
|
1277
|
+
const bar = { position: "fixed", top: 12, right: 12, display: "flex", gap: 6, alignItems: "center", padding: 6, background: "#0f172a", color: "#fff", borderRadius: 999, boxShadow: "0 8px 24px -8px rgba(0,0,0,0.4)", zIndex: Z2 + 3, fontFamily: "system-ui, sans-serif", fontSize: 12 };
|
|
994
1278
|
const btn = { font: "inherit", border: "none", background: "transparent", color: "inherit", cursor: "pointer", padding: "4px 8px", borderRadius: 999 };
|
|
995
1279
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: bar, children: [
|
|
996
1280
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
|
|
997
1281
|
editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
998
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1282
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
1283
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: open ? "#1e293b" : "transparent" }, onClick: () => setOpen((v) => !v), title: "Add an element", children: "\uFF0B Add \u25BE" }),
|
|
1284
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: "calc(100% + 6px)", right: 0, background: "#0f172a", border: "1px solid #1e293b", borderRadius: 10, padding: 4, display: "flex", flexDirection: "column", minWidth: 132, boxShadow: "0 12px 32px -10px rgba(0,0,0,0.6)" }, children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1285
|
+
"button",
|
|
1286
|
+
{
|
|
1287
|
+
type: "button",
|
|
1288
|
+
style: { ...btn, textAlign: "left", borderRadius: 6, padding: "6px 10px" },
|
|
1289
|
+
onClick: () => {
|
|
1290
|
+
onAdd(m.kind);
|
|
1291
|
+
setOpen(false);
|
|
1292
|
+
},
|
|
1293
|
+
children: m.label
|
|
1294
|
+
},
|
|
1295
|
+
m.kind
|
|
1296
|
+
)) }) : null
|
|
1297
|
+
] }),
|
|
999
1298
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canUndo ? 1 : 0.4 }, disabled: !canUndo, onClick: onUndo, children: "\u21B6" }),
|
|
1000
1299
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
|
|
1001
1300
|
] }) : null
|
|
1002
1301
|
] });
|
|
1003
1302
|
}
|
|
1303
|
+
function applyTransform(el, t) {
|
|
1304
|
+
if (t && (t.x != null || t.y != null || t.scale != null || t.rotate != null || t.opacity != null || t.w != null || t.h != null)) {
|
|
1305
|
+
el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1}) rotate(${t.rotate ?? 0}deg)`;
|
|
1306
|
+
el.style.opacity = t.opacity != null ? String(t.opacity) : "";
|
|
1307
|
+
el.style.width = t.w != null ? `${t.w}px` : "";
|
|
1308
|
+
el.style.height = t.h != null ? `${t.h}px` : "";
|
|
1309
|
+
} else {
|
|
1310
|
+
el.style.transform = "";
|
|
1311
|
+
el.style.opacity = "";
|
|
1312
|
+
el.style.width = "";
|
|
1313
|
+
el.style.height = "";
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1004
1316
|
function normalizeColor(v) {
|
|
1005
1317
|
return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
|
|
1006
1318
|
}
|
|
@@ -1013,6 +1325,7 @@ exports.EditablePage = EditablePage;
|
|
|
1013
1325
|
exports.Editor = Editor;
|
|
1014
1326
|
exports.Inspector = Inspector;
|
|
1015
1327
|
exports.LayersPanel = LayersPanel;
|
|
1328
|
+
exports.NodeInspector = NodeInspector;
|
|
1016
1329
|
exports.editorReduce = editorReduce;
|
|
1017
1330
|
exports.initEditor = initEditor;
|
|
1018
1331
|
exports.useEditor = useEditor;
|