@particle-academy/fancy-cms-ui 0.0.1-dev.8 → 0.0.1-dev.9
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-HYHNMW47.js → chunk-3YZMELJJ.js} +11 -2
- package/dist/chunk-3YZMELJJ.js.map +1 -0
- package/dist/{chunk-5O6U4VIU.js → chunk-ZV7CV54P.js} +3 -3
- package/dist/{chunk-5O6U4VIU.js.map → chunk-ZV7CV54P.js.map} +1 -1
- package/dist/editor.cjs +154 -62
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +9 -5
- package/dist/editor.d.ts +9 -5
- package/dist/editor.js +108 -20
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{ops-C_s0FGeV.d.cts → ops-BsM6kZxY.d.ts} +5 -1
- package/dist/{ops-nevYkDzK.d.ts → ops-CdBZarAT.d.cts} +5 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-HYHNMW47.js.map +0 -1
package/dist/editor.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { P as PageDoc, N as Node, m as StyleProps } from './types-CpfPEq3V.cjs';
|
|
2
|
+
import { P as PageDoc, i as NodeId, N as Node, m as StyleProps, L as LayoutMode } from './types-CpfPEq3V.cjs';
|
|
3
3
|
import { a as ElementRegistry } from './registry-Bk7GN1rA.cjs';
|
|
4
|
-
import { P as PageOp } from './ops-
|
|
4
|
+
import { P as PageOp } from './ops-CdBZarAT.cjs';
|
|
5
5
|
|
|
6
6
|
interface EditorProps {
|
|
7
7
|
/** Initial document; edits are surfaced via {@link EditorProps.onChange}. */
|
|
@@ -73,9 +73,11 @@ interface LayersPanelProps {
|
|
|
73
73
|
doc: PageDoc;
|
|
74
74
|
selection: string | null;
|
|
75
75
|
onSelect: (id: string) => void;
|
|
76
|
+
/** Reparent/reorder a node (drag-to-reorder). Omit to disable dragging. */
|
|
77
|
+
onMove?: (id: NodeId, parent: NodeId | null, order: string) => void;
|
|
76
78
|
}
|
|
77
|
-
/** The section/layer tree. Click a row to select. */
|
|
78
|
-
declare function LayersPanel({ doc, selection, onSelect }: LayersPanelProps): ReactElement;
|
|
79
|
+
/** The section/layer tree. Click a row to select; drag a row onto another to reorder. */
|
|
80
|
+
declare function LayersPanel({ doc, selection, onSelect, onMove }: LayersPanelProps): ReactElement;
|
|
79
81
|
|
|
80
82
|
interface InspectorProps {
|
|
81
83
|
doc: PageDoc;
|
|
@@ -98,6 +100,8 @@ interface NodeInspectorProps {
|
|
|
98
100
|
onProps: (patch: Record<string, unknown>) => void;
|
|
99
101
|
/** Patch the node's base style. */
|
|
100
102
|
onStyle: (patch: Partial<StyleProps>) => void;
|
|
103
|
+
/** Set how a container arranges its children (free / stack / grid). */
|
|
104
|
+
onLayout?: (layout: LayoutMode | undefined) => void;
|
|
101
105
|
/** Commit a transform (position / size / opacity / scale / rotate) into the active keyframe. */
|
|
102
106
|
onTransform: (t: NodeTransform) => void;
|
|
103
107
|
onRemove: () => void;
|
|
@@ -111,7 +115,7 @@ interface NodeInspectorProps {
|
|
|
111
115
|
* box. Every control writes through the op-spine or the active keyframe, so edits
|
|
112
116
|
* are undoable and animatable.
|
|
113
117
|
*/
|
|
114
|
-
declare function NodeInspector({ node, transform, measured, onProps, onStyle, onTransform, onRemove, onClose, }: NodeInspectorProps): ReactElement;
|
|
118
|
+
declare function NodeInspector({ node, transform, measured, onProps, onStyle, onLayout, onTransform, onRemove, onClose, }: NodeInspectorProps): ReactElement;
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Editor state engine — pure and framework-agnostic so it's unit-testable.
|
package/dist/editor.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { P as PageDoc, N as Node, m as StyleProps } from './types-CpfPEq3V.js';
|
|
2
|
+
import { P as PageDoc, i as NodeId, N as Node, m as StyleProps, L as LayoutMode } from './types-CpfPEq3V.js';
|
|
3
3
|
import { a as ElementRegistry } from './registry-Dht9JBLU.js';
|
|
4
|
-
import { P as PageOp } from './ops-
|
|
4
|
+
import { P as PageOp } from './ops-BsM6kZxY.js';
|
|
5
5
|
|
|
6
6
|
interface EditorProps {
|
|
7
7
|
/** Initial document; edits are surfaced via {@link EditorProps.onChange}. */
|
|
@@ -73,9 +73,11 @@ interface LayersPanelProps {
|
|
|
73
73
|
doc: PageDoc;
|
|
74
74
|
selection: string | null;
|
|
75
75
|
onSelect: (id: string) => void;
|
|
76
|
+
/** Reparent/reorder a node (drag-to-reorder). Omit to disable dragging. */
|
|
77
|
+
onMove?: (id: NodeId, parent: NodeId | null, order: string) => void;
|
|
76
78
|
}
|
|
77
|
-
/** The section/layer tree. Click a row to select. */
|
|
78
|
-
declare function LayersPanel({ doc, selection, onSelect }: LayersPanelProps): ReactElement;
|
|
79
|
+
/** The section/layer tree. Click a row to select; drag a row onto another to reorder. */
|
|
80
|
+
declare function LayersPanel({ doc, selection, onSelect, onMove }: LayersPanelProps): ReactElement;
|
|
79
81
|
|
|
80
82
|
interface InspectorProps {
|
|
81
83
|
doc: PageDoc;
|
|
@@ -98,6 +100,8 @@ interface NodeInspectorProps {
|
|
|
98
100
|
onProps: (patch: Record<string, unknown>) => void;
|
|
99
101
|
/** Patch the node's base style. */
|
|
100
102
|
onStyle: (patch: Partial<StyleProps>) => void;
|
|
103
|
+
/** Set how a container arranges its children (free / stack / grid). */
|
|
104
|
+
onLayout?: (layout: LayoutMode | undefined) => void;
|
|
101
105
|
/** Commit a transform (position / size / opacity / scale / rotate) into the active keyframe. */
|
|
102
106
|
onTransform: (t: NodeTransform) => void;
|
|
103
107
|
onRemove: () => void;
|
|
@@ -111,7 +115,7 @@ interface NodeInspectorProps {
|
|
|
111
115
|
* box. Every control writes through the op-spine or the active keyframe, so edits
|
|
112
116
|
* are undoable and animatable.
|
|
113
117
|
*/
|
|
114
|
-
declare function NodeInspector({ node, transform, measured, onProps, onStyle, onTransform, onRemove, onClose, }: NodeInspectorProps): ReactElement;
|
|
118
|
+
declare function NodeInspector({ node, transform, measured, onProps, onStyle, onLayout, onTransform, onRemove, onClose, }: NodeInspectorProps): ReactElement;
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Editor state engine — pure and framework-agnostic so it's unit-testable.
|
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-
|
|
2
|
+
import { CmsPage, defaultRegistry } from './chunk-ZV7CV54P.js';
|
|
3
|
+
import { reduce, childrenOf } 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';
|
|
@@ -157,31 +157,66 @@ function Canvas({ doc, selection, onSelect, apply }) {
|
|
|
157
157
|
function cssEscape(value) {
|
|
158
158
|
return value.replace(/["\\]/g, "\\$&");
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
var MIME = "application/x-cms-node";
|
|
161
|
+
function isAncestorOrSelf(doc, id, maybeAncestor) {
|
|
162
|
+
for (let cur = id; cur; cur = doc.nodes[cur]?.parent ?? null) {
|
|
163
|
+
if (cur === maybeAncestor) return true;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
function LayersPanel({ doc, selection, onSelect, onMove }) {
|
|
168
|
+
const [overId, setOverId] = useState(null);
|
|
169
|
+
return /* @__PURE__ */ jsx("div", { style: { overflow: "auto", padding: 8, fontSize: 13, fontFamily: "system-ui, sans-serif" }, children: doc.sections.map((id) => /* @__PURE__ */ jsx(LayerRow, { doc, id, depth: 0, selection, onSelect, onMove, overId, setOverId }, id)) });
|
|
162
170
|
}
|
|
163
|
-
function LayerRow({ doc, id, depth, selection, onSelect }) {
|
|
171
|
+
function LayerRow({ doc, id, depth, selection, onSelect, onMove, overId, setOverId }) {
|
|
164
172
|
const node = doc.nodes[id];
|
|
165
173
|
if (!node) return null;
|
|
166
174
|
const selected = selection === id;
|
|
175
|
+
const draggable = Boolean(onMove);
|
|
176
|
+
const drop = (e) => {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
e.stopPropagation();
|
|
179
|
+
setOverId(null);
|
|
180
|
+
if (!onMove) return;
|
|
181
|
+
const dragId = e.dataTransfer.getData(MIME);
|
|
182
|
+
if (!dragId || dragId === id || isAncestorOrSelf(doc, id, dragId)) return;
|
|
183
|
+
const siblings = childrenOf(doc, node.parent);
|
|
184
|
+
const idx = siblings.findIndex((n) => n.id === id);
|
|
185
|
+
const next = siblings[idx + 1];
|
|
186
|
+
onMove(dragId, node.parent, keyBetween(node.order, next ? next.order : null));
|
|
187
|
+
};
|
|
167
188
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
168
189
|
/* @__PURE__ */ jsxs(
|
|
169
190
|
"button",
|
|
170
191
|
{
|
|
171
192
|
type: "button",
|
|
193
|
+
draggable,
|
|
172
194
|
onClick: () => onSelect(id),
|
|
195
|
+
onDragStart: (e) => {
|
|
196
|
+
e.dataTransfer.setData(MIME, id);
|
|
197
|
+
e.dataTransfer.effectAllowed = "move";
|
|
198
|
+
},
|
|
199
|
+
onDragOver: (e) => {
|
|
200
|
+
if (!onMove) return;
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
e.dataTransfer.dropEffect = "move";
|
|
203
|
+
if (overId !== id) setOverId(id);
|
|
204
|
+
},
|
|
205
|
+
onDragLeave: () => overId === id && setOverId(null),
|
|
206
|
+
onDrop: drop,
|
|
173
207
|
style: {
|
|
174
208
|
display: "block",
|
|
175
209
|
width: "100%",
|
|
176
210
|
textAlign: "left",
|
|
177
211
|
border: "none",
|
|
178
212
|
borderRadius: 6,
|
|
179
|
-
cursor: "pointer",
|
|
213
|
+
cursor: draggable ? "grab" : "pointer",
|
|
180
214
|
padding: "4px 8px",
|
|
181
215
|
paddingLeft: 8 + depth * 14,
|
|
182
216
|
background: selected ? "#ede9fe" : "transparent",
|
|
183
217
|
color: selected ? "#5b21b6" : "#334155",
|
|
184
|
-
font: "inherit"
|
|
218
|
+
font: "inherit",
|
|
219
|
+
boxShadow: overId === id ? "inset 0 -2px 0 0 #8b5cf6" : "none"
|
|
185
220
|
},
|
|
186
221
|
children: [
|
|
187
222
|
/* @__PURE__ */ jsx("span", { style: { opacity: 0.6 }, children: node.type }),
|
|
@@ -190,17 +225,7 @@ function LayerRow({ doc, id, depth, selection, onSelect }) {
|
|
|
190
225
|
]
|
|
191
226
|
}
|
|
192
227
|
),
|
|
193
|
-
childrenOf(doc, id).map((child) => /* @__PURE__ */ jsx(
|
|
194
|
-
LayerRow,
|
|
195
|
-
{
|
|
196
|
-
doc,
|
|
197
|
-
id: child.id,
|
|
198
|
-
depth: depth + 1,
|
|
199
|
-
selection,
|
|
200
|
-
onSelect
|
|
201
|
-
},
|
|
202
|
-
child.id
|
|
203
|
-
))
|
|
228
|
+
childrenOf(doc, id).map((child) => /* @__PURE__ */ jsx(LayerRow, { doc, id: child.id, depth: depth + 1, selection, onSelect, onMove, overId, setOverId }, child.id))
|
|
204
229
|
] });
|
|
205
230
|
}
|
|
206
231
|
var label = {
|
|
@@ -332,7 +357,15 @@ function Editor({ defaultValue, onChange }) {
|
|
|
332
357
|
return /* @__PURE__ */ jsxs("div", { style: shell, children: [
|
|
333
358
|
/* @__PURE__ */ jsxs("div", { style: { borderRight: "1px solid #e2e8f0", display: "flex", flexDirection: "column", minHeight: 0 }, children: [
|
|
334
359
|
/* @__PURE__ */ jsx(Toolbar, { ed }),
|
|
335
|
-
/* @__PURE__ */ jsx(
|
|
360
|
+
/* @__PURE__ */ jsx(
|
|
361
|
+
LayersPanel,
|
|
362
|
+
{
|
|
363
|
+
doc: ed.state.doc,
|
|
364
|
+
selection: ed.state.selection,
|
|
365
|
+
onSelect: ed.select,
|
|
366
|
+
onMove: (id, parent, order) => ed.apply({ t: "move_node", id, parent, order })
|
|
367
|
+
}
|
|
368
|
+
)
|
|
336
369
|
] }),
|
|
337
370
|
/* @__PURE__ */ jsx(Canvas, { doc: ed.state.doc, selection: ed.state.selection, onSelect: ed.select, apply: ed.apply }),
|
|
338
371
|
/* @__PURE__ */ jsx("div", { style: { borderLeft: "1px solid #e2e8f0", minHeight: 0 }, children: /* @__PURE__ */ jsx(Inspector, { doc: ed.state.doc, selection: ed.state.selection, apply: ed.apply }) })
|
|
@@ -372,6 +405,7 @@ function NodeInspector({
|
|
|
372
405
|
measured,
|
|
373
406
|
onProps,
|
|
374
407
|
onStyle,
|
|
408
|
+
onLayout,
|
|
375
409
|
onTransform,
|
|
376
410
|
onRemove,
|
|
377
411
|
onClose
|
|
@@ -454,9 +488,44 @@ function NodeInspector({
|
|
|
454
488
|
] }),
|
|
455
489
|
/* @__PURE__ */ jsxs(Row, { children: [
|
|
456
490
|
/* @__PURE__ */ jsx(Num, { label: "Padding", value: base.padding?.value, placeholder: "0", onChange: (v) => onStyle({ padding: v == null ? void 0 : len(v) }) }),
|
|
491
|
+
/* @__PURE__ */ jsx(Num, { label: "Margin", value: base.margin?.value, placeholder: "0", onChange: (v) => onStyle({ margin: v == null ? void 0 : len(v) }) })
|
|
492
|
+
] }),
|
|
493
|
+
/* @__PURE__ */ jsx(Row, { children: /* @__PURE__ */ jsx(Txt, { label: "Border", value: base.border ?? "", placeholder: "1px solid #e2e8f0", onChange: (v) => onStyle({ border: v || void 0 }) }) })
|
|
494
|
+
] }),
|
|
495
|
+
CONTAINER_TYPES.has(node.type) ? /* @__PURE__ */ jsxs(Group, { label: "Layout", children: [
|
|
496
|
+
/* @__PURE__ */ jsxs(Row, { children: [
|
|
497
|
+
/* @__PURE__ */ jsx(
|
|
498
|
+
Sel,
|
|
499
|
+
{
|
|
500
|
+
label: "Mode",
|
|
501
|
+
value: node.layout ?? "free",
|
|
502
|
+
options: [["free", "Free"], ["stack", "Stack"], ["grid", "Grid"]],
|
|
503
|
+
onChange: (v) => onLayout?.(v)
|
|
504
|
+
}
|
|
505
|
+
),
|
|
457
506
|
/* @__PURE__ */ jsx(Num, { label: "Gap", value: base.gap?.value, placeholder: "0", onChange: (v) => onStyle({ gap: v == null ? void 0 : len(v) }) })
|
|
507
|
+
] }),
|
|
508
|
+
/* @__PURE__ */ jsxs(Row, { children: [
|
|
509
|
+
/* @__PURE__ */ jsx(
|
|
510
|
+
Sel,
|
|
511
|
+
{
|
|
512
|
+
label: "Align",
|
|
513
|
+
value: base.align ?? "",
|
|
514
|
+
options: [["", "\u2014"], ["start", "Start"], ["center", "Center"], ["end", "End"], ["stretch", "Stretch"]],
|
|
515
|
+
onChange: (v) => onStyle({ align: v || void 0 })
|
|
516
|
+
}
|
|
517
|
+
),
|
|
518
|
+
/* @__PURE__ */ jsx(
|
|
519
|
+
Sel,
|
|
520
|
+
{
|
|
521
|
+
label: "Justify",
|
|
522
|
+
value: base.justify ?? "",
|
|
523
|
+
options: [["", "\u2014"], ["start", "Start"], ["center", "Center"], ["end", "End"], ["between", "Between"], ["around", "Around"]],
|
|
524
|
+
onChange: (v) => onStyle({ justify: v || void 0 })
|
|
525
|
+
}
|
|
526
|
+
)
|
|
458
527
|
] })
|
|
459
|
-
] })
|
|
528
|
+
] }) : null
|
|
460
529
|
] })
|
|
461
530
|
] });
|
|
462
531
|
}
|
|
@@ -511,6 +580,12 @@ function Num({ label: label2, value, placeholder, onChange, step, min, max }) {
|
|
|
511
580
|
)
|
|
512
581
|
] });
|
|
513
582
|
}
|
|
583
|
+
function Txt({ label: label2, value, placeholder, onChange }) {
|
|
584
|
+
return /* @__PURE__ */ jsxs("label", { style: field, children: [
|
|
585
|
+
/* @__PURE__ */ jsx("span", { style: fieldLabel, children: label2 }),
|
|
586
|
+
/* @__PURE__ */ jsx("input", { style: input2, type: "text", value, placeholder, onChange: (e) => onChange(e.target.value) })
|
|
587
|
+
] });
|
|
588
|
+
}
|
|
514
589
|
function Color({ label: label2, value, onChange }) {
|
|
515
590
|
return /* @__PURE__ */ jsxs("label", { style: field, children: [
|
|
516
591
|
/* @__PURE__ */ jsx("span", { style: fieldLabel, children: label2 }),
|
|
@@ -777,6 +852,7 @@ function EditablePage({
|
|
|
777
852
|
);
|
|
778
853
|
const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
|
|
779
854
|
const setProps = (patch) => selection && ed.apply({ t: "set_props", id: selection, patch });
|
|
855
|
+
const setLayout = (layout) => selection && ed.apply({ t: "set_layout", id: selection, layout });
|
|
780
856
|
const removeSelected = () => {
|
|
781
857
|
if (!selection) return;
|
|
782
858
|
ed.apply({ t: "remove_node", id: selection });
|
|
@@ -801,6 +877,8 @@ function EditablePage({
|
|
|
801
877
|
ed.select(id);
|
|
802
878
|
requestAnimationFrame(() => pageRef.current?.querySelector(`[data-cms="${cssEscape2(id)}"]`)?.focus());
|
|
803
879
|
};
|
|
880
|
+
const nudgeRef = useRef({ transforms, onNodeTransform });
|
|
881
|
+
nudgeRef.current = { transforms, onNodeTransform };
|
|
804
882
|
useEffect(() => {
|
|
805
883
|
if (!editing) return;
|
|
806
884
|
const onKey = (e) => {
|
|
@@ -819,6 +897,15 @@ function EditablePage({
|
|
|
819
897
|
} else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "v") {
|
|
820
898
|
e.preventDefault();
|
|
821
899
|
paste(selection);
|
|
900
|
+
} else if (selection && /^Arrow(Left|Right|Up|Down)$/.test(e.key)) {
|
|
901
|
+
const { transforms: tf, onNodeTransform: commit } = nudgeRef.current;
|
|
902
|
+
if (!commit) return;
|
|
903
|
+
e.preventDefault();
|
|
904
|
+
const step = e.shiftKey ? 10 : 1;
|
|
905
|
+
const dx = e.key === "ArrowLeft" ? -step : e.key === "ArrowRight" ? step : 0;
|
|
906
|
+
const dy = e.key === "ArrowUp" ? -step : e.key === "ArrowDown" ? step : 0;
|
|
907
|
+
const cur = tf?.[selection] ?? {};
|
|
908
|
+
commit(selection, { ...cur, x: (cur.x ?? 0) + dx, y: (cur.y ?? 0) + dy });
|
|
822
909
|
}
|
|
823
910
|
};
|
|
824
911
|
window.addEventListener("keydown", onKey);
|
|
@@ -872,6 +959,7 @@ function EditablePage({
|
|
|
872
959
|
measured: { w: box.w, h: box.h },
|
|
873
960
|
onProps: setProps,
|
|
874
961
|
onStyle: setStyle,
|
|
962
|
+
onLayout: setLayout,
|
|
875
963
|
onTransform: (t) => selection && onNodeTransform?.(selection, t),
|
|
876
964
|
onRemove: removeSelected,
|
|
877
965
|
onClose: () => ed.select(null)
|