@particle-academy/fancy-cms-ui 0.0.1-dev.6 → 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 +380 -61
- 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 +354 -59
- 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,22 +965,16 @@ 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
974
|
const compute = () => {
|
|
762
|
-
const
|
|
975
|
+
const len2 = spacer.offsetHeight - window.innerHeight;
|
|
763
976
|
const top = spacer.getBoundingClientRect().top;
|
|
764
|
-
onProgress(
|
|
977
|
+
onProgress(len2 > 0 ? Math.min(1, Math.max(0, -top / len2)) : 0);
|
|
765
978
|
};
|
|
766
979
|
compute();
|
|
767
980
|
window.addEventListener("scroll", compute, { passive: true });
|
|
@@ -786,7 +999,7 @@ function EditablePage({
|
|
|
786
999
|
window.removeEventListener("scroll", measureBox);
|
|
787
1000
|
window.removeEventListener("resize", measureBox);
|
|
788
1001
|
};
|
|
789
|
-
}, [measureBox, ed.state.doc
|
|
1002
|
+
}, [measureBox, ed.state.doc]);
|
|
790
1003
|
react.useEffect(() => {
|
|
791
1004
|
if (!editing || !selection || !pageRef.current || selNode?.type !== "text") return;
|
|
792
1005
|
const el = pageRef.current.querySelector(`[data-cms="${cssEscape2(selection)}"]`);
|
|
@@ -811,17 +1024,29 @@ function EditablePage({
|
|
|
811
1024
|
ed.select(el.getAttribute("data-cms"));
|
|
812
1025
|
}
|
|
813
1026
|
};
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
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
|
+
);
|
|
822
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 }) });
|
|
823
1048
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
824
|
-
/* @__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,
|
|
825
1050
|
editing && box && selection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
826
1051
|
SelectionOverlay,
|
|
827
1052
|
{
|
|
@@ -833,7 +1058,20 @@ function EditablePage({
|
|
|
833
1058
|
onLive: measureBox
|
|
834
1059
|
}
|
|
835
1060
|
) : null,
|
|
836
|
-
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,
|
|
837
1075
|
revealed || editing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
838
1076
|
EditBar,
|
|
839
1077
|
{
|
|
@@ -846,12 +1084,30 @@ function EditablePage({
|
|
|
846
1084
|
canRedo: ed.canRedo,
|
|
847
1085
|
onUndo: ed.undo,
|
|
848
1086
|
onRedo: ed.redo,
|
|
849
|
-
|
|
1087
|
+
onAdd: addNode
|
|
850
1088
|
}
|
|
851
1089
|
) : null,
|
|
852
|
-
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
|
|
853
1091
|
] });
|
|
854
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
|
+
];
|
|
855
1111
|
function SelectionOverlay({
|
|
856
1112
|
box,
|
|
857
1113
|
base,
|
|
@@ -863,29 +1119,48 @@ function SelectionOverlay({
|
|
|
863
1119
|
const drag = react.useRef(null);
|
|
864
1120
|
const apply = (t) => {
|
|
865
1121
|
const el = getEl();
|
|
866
|
-
if (el)
|
|
867
|
-
el.style.transform = `translate3d(${t.x ?? 0}px, ${t.y ?? 0}px, 0) scale(${t.scale ?? 1})`;
|
|
868
|
-
el.style.opacity = t.opacity != null ? String(t.opacity) : "";
|
|
869
|
-
}
|
|
1122
|
+
if (el) applyTransform(el, t);
|
|
870
1123
|
};
|
|
871
|
-
const
|
|
1124
|
+
const startMove = (e) => {
|
|
872
1125
|
e.stopPropagation();
|
|
873
1126
|
e.preventDefault();
|
|
874
1127
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
875
|
-
drag.current = {
|
|
1128
|
+
drag.current = { x: e.clientX, y: e.clientY, w: box.w, h: box.h, live: { ...base } };
|
|
876
1129
|
};
|
|
877
|
-
const
|
|
1130
|
+
const moveMove = (e) => {
|
|
878
1131
|
const d = drag.current;
|
|
879
1132
|
if (!d) return;
|
|
880
|
-
|
|
881
|
-
d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
|
|
882
|
-
} else {
|
|
883
|
-
const factor = Math.max(0.1, (d.w + (e.clientX - d.x)) / d.w);
|
|
884
|
-
d.live = { ...base, scale: Number(((base.scale ?? 1) * factor).toFixed(3)) };
|
|
885
|
-
}
|
|
1133
|
+
d.live = { ...base, x: (base.x ?? 0) + (e.clientX - d.x), y: (base.y ?? 0) + (e.clientY - d.y) };
|
|
886
1134
|
apply(d.live);
|
|
887
1135
|
onLive();
|
|
888
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
|
+
};
|
|
889
1164
|
const end = () => {
|
|
890
1165
|
const d = drag.current;
|
|
891
1166
|
if (d) onTransform(d.live);
|
|
@@ -900,17 +1175,23 @@ function SelectionOverlay({
|
|
|
900
1175
|
outline: "2px solid #8b5cf6",
|
|
901
1176
|
borderRadius: 4,
|
|
902
1177
|
pointerEvents: "none",
|
|
903
|
-
zIndex:
|
|
1178
|
+
zIndex: Z2
|
|
904
1179
|
};
|
|
905
1180
|
const handle = {
|
|
906
1181
|
position: "fixed",
|
|
907
|
-
width:
|
|
908
|
-
height:
|
|
909
|
-
background: "#
|
|
910
|
-
border: "2px solid #
|
|
911
|
-
borderRadius:
|
|
912
|
-
zIndex:
|
|
913
|
-
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 };
|
|
914
1195
|
};
|
|
915
1196
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
916
1197
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: outline }),
|
|
@@ -919,22 +1200,23 @@ function SelectionOverlay({
|
|
|
919
1200
|
"div",
|
|
920
1201
|
{
|
|
921
1202
|
title: "Drag to move",
|
|
922
|
-
onPointerDown:
|
|
923
|
-
onPointerMove:
|
|
1203
|
+
onPointerDown: startMove,
|
|
1204
|
+
onPointerMove: moveMove,
|
|
924
1205
|
onPointerUp: end,
|
|
925
|
-
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" }
|
|
926
1207
|
}
|
|
927
1208
|
),
|
|
928
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1209
|
+
GRIPS.map((g) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
929
1210
|
"div",
|
|
930
1211
|
{
|
|
931
1212
|
title: "Drag to resize",
|
|
932
|
-
onPointerDown:
|
|
933
|
-
onPointerMove:
|
|
1213
|
+
onPointerDown: startResize(g),
|
|
1214
|
+
onPointerMove: resizeMove(g),
|
|
934
1215
|
onPointerUp: end,
|
|
935
|
-
style: { ...handle,
|
|
936
|
-
}
|
|
937
|
-
|
|
1216
|
+
style: { ...handle, ...gripPos(g) }
|
|
1217
|
+
},
|
|
1218
|
+
g.key
|
|
1219
|
+
))
|
|
938
1220
|
] }) : null
|
|
939
1221
|
] });
|
|
940
1222
|
}
|
|
@@ -957,7 +1239,7 @@ function FloatingToolbar({
|
|
|
957
1239
|
border: "1px solid rgba(255,255,255,0.12)",
|
|
958
1240
|
borderRadius: 999,
|
|
959
1241
|
boxShadow: "0 8px 24px -8px rgba(0,0,0,0.5)",
|
|
960
|
-
zIndex:
|
|
1242
|
+
zIndex: Z2 + 2,
|
|
961
1243
|
fontFamily: "system-ui, sans-serif",
|
|
962
1244
|
fontSize: 12
|
|
963
1245
|
};
|
|
@@ -975,6 +1257,13 @@ function FloatingToolbar({
|
|
|
975
1257
|
] })
|
|
976
1258
|
] });
|
|
977
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
|
+
];
|
|
978
1267
|
function EditBar({
|
|
979
1268
|
editing,
|
|
980
1269
|
onToggle,
|
|
@@ -982,19 +1271,48 @@ function EditBar({
|
|
|
982
1271
|
canRedo,
|
|
983
1272
|
onUndo,
|
|
984
1273
|
onRedo,
|
|
985
|
-
|
|
1274
|
+
onAdd
|
|
986
1275
|
}) {
|
|
987
|
-
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 };
|
|
988
1278
|
const btn = { font: "inherit", border: "none", background: "transparent", color: "inherit", cursor: "pointer", padding: "4px 8px", borderRadius: 999 };
|
|
989
1279
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: bar, children: [
|
|
990
1280
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
|
|
991
1281
|
editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
992
|
-
/* @__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
|
+
] }),
|
|
993
1298
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canUndo ? 1 : 0.4 }, disabled: !canUndo, onClick: onUndo, children: "\u21B6" }),
|
|
994
1299
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, opacity: canRedo ? 1 : 0.4 }, disabled: !canRedo, onClick: onRedo, children: "\u21B7" })
|
|
995
1300
|
] }) : null
|
|
996
1301
|
] });
|
|
997
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
|
+
}
|
|
998
1316
|
function normalizeColor(v) {
|
|
999
1317
|
return v && /^#[0-9a-fA-F]{6}$/.test(v) ? v : "#ffffff";
|
|
1000
1318
|
}
|
|
@@ -1007,6 +1325,7 @@ exports.EditablePage = EditablePage;
|
|
|
1007
1325
|
exports.Editor = Editor;
|
|
1008
1326
|
exports.Inspector = Inspector;
|
|
1009
1327
|
exports.LayersPanel = LayersPanel;
|
|
1328
|
+
exports.NodeInspector = NodeInspector;
|
|
1010
1329
|
exports.editorReduce = editorReduce;
|
|
1011
1330
|
exports.initEditor = initEditor;
|
|
1012
1331
|
exports.useEditor = useEditor;
|