@particle-academy/fancy-cms-ui 0.4.0 → 0.5.1

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/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var fancyDocCommons = require('@particle-academy/fancy-doc-commons');
3
4
  var jsxRuntime = require('react/jsx-runtime');
4
5
  var react = require('react');
5
6
 
@@ -19,38 +20,21 @@ function emptyDoc(id, slug = "/") {
19
20
  meta: { title: "Untitled", slug, scrollMode: "smooth" },
20
21
  theme: { name: "default" },
21
22
  breakpoints: ["base", "md", "lg"],
22
- sections: [],
23
23
  nodes: {}
24
24
  };
25
25
  }
26
-
27
- // src/document/reduce.ts
28
26
  function childrenOf(doc, parent) {
29
- if (parent === null) {
30
- return doc.sections.map((id) => doc.nodes[id]).filter((n) => Boolean(n));
31
- }
32
- const out = [];
33
- for (const id of Object.keys(doc.nodes)) {
34
- const n = doc.nodes[id];
35
- if (n.parent === parent) out.push(n);
36
- }
37
- out.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
38
- return out;
27
+ return fancyDocCommons.childrenOf(doc, parent);
28
+ }
29
+ function rootIds(doc) {
30
+ return childrenOf(doc, null).map((n) => n.id);
31
+ }
32
+ function lastRootId(doc) {
33
+ const r = rootIds(doc);
34
+ return r[r.length - 1] ?? null;
39
35
  }
40
36
  function descendantIds(doc, id) {
41
- const out = [];
42
- const stack = [id];
43
- while (stack.length) {
44
- const current = stack.pop();
45
- for (const childId of Object.keys(doc.nodes)) {
46
- const n = doc.nodes[childId];
47
- if (n.parent === current) {
48
- out.push(childId);
49
- stack.push(childId);
50
- }
51
- }
52
- }
53
- return out;
37
+ return fancyDocCommons.descendantsOf(doc, id);
54
38
  }
55
39
  function patchNode(doc, id, patch) {
56
40
  const node = doc.nodes[id];
@@ -72,7 +56,6 @@ function reduce(doc, op, opts = {}) {
72
56
  return {
73
57
  ...doc,
74
58
  nodes: { ...doc.nodes, [op.node.id]: op.node },
75
- sections: op.node.parent === null ? [...doc.sections, op.node.id] : doc.sections,
76
59
  seq: doc.seq + 1
77
60
  };
78
61
  }
@@ -86,7 +69,6 @@ function reduce(doc, op, opts = {}) {
86
69
  return {
87
70
  ...doc,
88
71
  nodes,
89
- sections: doc.sections.filter((s) => !dead.has(s)),
90
72
  seq: doc.seq + 1
91
73
  };
92
74
  }
@@ -98,15 +80,9 @@ function reduce(doc, op, opts = {}) {
98
80
  if (op.parent !== null && descendantIds(doc, op.id).includes(op.parent)) {
99
81
  return invalid("move would create a cycle");
100
82
  }
101
- const wasTop = node.parent === null;
102
- const nowTop = op.parent === null;
103
- let sections = doc.sections;
104
- if (wasTop && !nowTop) sections = sections.filter((s) => s !== op.id);
105
- else if (!wasTop && nowTop) sections = [...sections, op.id];
106
83
  return {
107
84
  ...doc,
108
85
  nodes: { ...doc.nodes, [op.id]: { ...node, parent: op.parent, order: op.order } },
109
- sections,
110
86
  seq: doc.seq + 1
111
87
  };
112
88
  }
@@ -167,11 +143,18 @@ function reduce(doc, op, opts = {}) {
167
143
  return { ...doc, meta: { ...doc.meta, ...op.patch }, seq: doc.seq + 1 };
168
144
  case "set_theme":
169
145
  return { ...doc, theme: { ...doc.theme, ...op.patch }, seq: doc.seq + 1 };
170
- case "reorder_sections": {
171
- const current = new Set(doc.sections);
172
- const same = op.order.length === doc.sections.length && op.order.every((id) => current.has(id));
173
- if (!same) return invalid("reorder must be a permutation of current sections");
174
- return { ...doc, sections: [...op.order], seq: doc.seq + 1 };
146
+ case "reorder_roots": {
147
+ const currentRoots = childrenOf(doc, null);
148
+ const current = new Set(currentRoots.map((n) => n.id));
149
+ const same = op.order.length === currentRoots.length && op.order.every((id) => current.has(id));
150
+ if (!same) return invalid("reorder must be a permutation of the current roots");
151
+ const nodes = { ...doc.nodes };
152
+ let prev = null;
153
+ for (const id of op.order) {
154
+ prev = fancyDocCommons.fractionalKey(prev, null);
155
+ nodes[id] = { ...nodes[id], order: prev };
156
+ }
157
+ return { ...doc, nodes, seq: doc.seq + 1 };
175
158
  }
176
159
  default:
177
160
  return invalid(`unknown op "${op.t}"`);
@@ -189,14 +172,18 @@ function invert(doc, op) {
189
172
  if (!node) return [];
190
173
  const ids = [op.id, ...descendantIds(doc, op.id)];
191
174
  const inserts = ids.map((id) => ({ t: "insert_node", node: doc.nodes[id] }));
192
- if (node.parent === null) inserts.push({ t: "reorder_sections", order: [...doc.sections] });
175
+ if (node.parent === null) {
176
+ inserts.push({ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) });
177
+ }
193
178
  return inserts;
194
179
  }
195
180
  case "move_node": {
196
181
  const node = doc.nodes[op.id];
197
182
  if (!node) return [];
198
183
  const back = [{ t: "move_node", id: op.id, parent: node.parent, order: node.order }];
199
- if (node.parent === null) back.push({ t: "reorder_sections", order: [...doc.sections] });
184
+ if (node.parent === null) {
185
+ back.push({ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) });
186
+ }
200
187
  return back;
201
188
  }
202
189
  case "set_props": {
@@ -223,8 +210,8 @@ function invert(doc, op) {
223
210
  return [{ t: "set_meta", patch: pick(doc.meta, Object.keys(op.patch)) }];
224
211
  case "set_theme":
225
212
  return [{ t: "set_theme", patch: pick(doc.theme, Object.keys(op.patch)) }];
226
- case "reorder_sections":
227
- return [{ t: "reorder_sections", order: [...doc.sections] }];
213
+ case "reorder_roots":
214
+ return [{ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) }];
228
215
  default:
229
216
  return [];
230
217
  }
@@ -234,49 +221,46 @@ function pick(obj, keys) {
234
221
  for (const k of keys) out[k] = obj[k];
235
222
  return out;
236
223
  }
237
-
238
- // src/document/fractional.ts
239
- var BASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
240
- var RADIX = BASE.length;
241
- var MID = Math.floor(RADIX / 2);
242
- function val(c) {
243
- const i = BASE.indexOf(c);
244
- return i < 0 ? 0 : i;
245
- }
246
- function keyBetween(a, b) {
247
- if (a === null) a = "";
248
- if (b === null) {
249
- return a + BASE[MID];
224
+ function migrateDocVerbose(input2) {
225
+ const legacy = input2;
226
+ const sections = legacy.sections;
227
+ if (!Array.isArray(sections)) {
228
+ return { doc: input2, migrated: false, danglingIds: [], unlistedRootIds: [] };
250
229
  }
251
- if (a >= b) {
252
- throw new Error(`keyBetween: lower bound "${a}" is not below upper bound "${b}"`);
253
- }
254
- let result = "";
255
- let i = 0;
256
- for (; ; ) {
257
- const ca = i < a.length ? val(a[i]) : 0;
258
- const cb = i < b.length ? val(b[i]) : RADIX;
259
- if (cb - ca > 1) {
260
- result += BASE[ca + Math.floor((cb - ca) / 2)];
261
- return result;
262
- }
263
- if (ca === cb) {
264
- result += BASE[ca];
265
- i++;
230
+ const danglingIds = [];
231
+ const ordered = [];
232
+ const seen = /* @__PURE__ */ new Set();
233
+ for (const id of sections) {
234
+ if (!input2.nodes[id]) {
235
+ danglingIds.push(id);
266
236
  continue;
267
237
  }
268
- result += BASE[ca];
269
- i++;
270
- for (; ; ) {
271
- const da = i < a.length ? val(a[i]) : 0;
272
- if (RADIX - da > 1) {
273
- result += BASE[da + Math.floor((RADIX - da) / 2)];
274
- return result;
275
- }
276
- result += BASE[da];
277
- i++;
278
- }
238
+ if (seen.has(id)) continue;
239
+ seen.add(id);
240
+ ordered.push(id);
241
+ }
242
+ const unlistedRootIds = Object.values(input2.nodes).filter((n) => n.parent === null && !seen.has(n.id)).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0).map((n) => n.id);
243
+ const finalOrder = [...ordered, ...unlistedRootIds];
244
+ const nodes = { ...input2.nodes };
245
+ let prev = null;
246
+ for (const id of finalOrder) {
247
+ const key = fancyDocCommons.fractionalKey(prev, null);
248
+ nodes[id] = { ...nodes[id], order: key };
249
+ prev = key;
279
250
  }
251
+ const { sections: _dropped, ...rest } = legacy;
252
+ return {
253
+ doc: { ...rest, nodes },
254
+ migrated: true,
255
+ danglingIds,
256
+ unlistedRootIds
257
+ };
258
+ }
259
+ function migrateDoc(input2) {
260
+ return migrateDocVerbose(input2).doc;
261
+ }
262
+ function needsMigration(input2) {
263
+ return Array.isArray(input2.sections);
280
264
  }
281
265
 
282
266
  // src/render/css.ts
@@ -482,14 +466,15 @@ function RenderNode({ doc, id, registry = defaultRegistry, data }) {
482
466
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cms": node.id, "data-cms-island": node.island ? node.type : void 0, className: node.className, children: inner });
483
467
  }
484
468
  function CmsPage({
485
- doc,
469
+ doc: input2,
486
470
  registry = defaultRegistry,
487
471
  data,
488
472
  includeStyles = true
489
473
  }) {
474
+ const doc = react.useMemo(() => migrateDoc(input2), [input2]);
490
475
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
491
476
  includeStyles ? /* @__PURE__ */ jsxRuntime.jsx("style", { "data-cms-styles": "", children: emitDocCss(doc) }) : null,
492
- doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id, registry, data }, id))
477
+ rootIds(doc).map((id) => /* @__PURE__ */ jsxRuntime.jsx(RenderNode, { doc, id, registry, data }, id))
493
478
  ] });
494
479
  }
495
480
  function CmsRegion({
@@ -504,7 +489,7 @@ function CmsRegion({
504
489
  ] });
505
490
  }
506
491
  function initEditor(doc) {
507
- return { doc, past: [], future: [], selection: null };
492
+ return { doc: migrateDoc(doc), past: [], future: [], selection: null };
508
493
  }
509
494
  function editorReduce(state, action) {
510
495
  switch (action.type) {
@@ -671,7 +656,7 @@ function isAncestorOrSelf(doc, id, maybeAncestor) {
671
656
  }
672
657
  function LayersPanel({ doc, selection, onSelect, onMove }) {
673
658
  const [overId, setOverId] = react.useState(null);
674
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", padding: 8, fontSize: 13, fontFamily: "system-ui, sans-serif" }, children: doc.sections.map((id) => /* @__PURE__ */ jsxRuntime.jsx(LayerRow, { doc, id, depth: 0, selection, onSelect, onMove, overId, setOverId }, id)) });
659
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", padding: 8, fontSize: 13, fontFamily: "system-ui, sans-serif" }, children: rootIds(doc).map((id) => /* @__PURE__ */ jsxRuntime.jsx(LayerRow, { doc, id, depth: 0, selection, onSelect, onMove, overId, setOverId }, id)) });
675
660
  }
676
661
  function LayerRow({ doc, id, depth, selection, onSelect, onMove, overId, setOverId }) {
677
662
  const node = doc.nodes[id];
@@ -688,7 +673,7 @@ function LayerRow({ doc, id, depth, selection, onSelect, onMove, overId, setOver
688
673
  const siblings = childrenOf(doc, node.parent);
689
674
  const idx = siblings.findIndex((n) => n.id === id);
690
675
  const next = siblings[idx + 1];
691
- onMove(dragId, node.parent, keyBetween(node.order, next ? next.order : null));
676
+ onMove(dragId, node.parent, fancyDocCommons.fractionalKey(node.order, next ? next.order : null));
692
677
  };
693
678
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
694
679
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -856,8 +841,6 @@ function Inspector({ doc, selection, apply }) {
856
841
  )
857
842
  ] });
858
843
  }
859
-
860
- // src/editor/insert.ts
861
844
  var CONTAINER_TYPES = /* @__PURE__ */ new Set([
862
845
  "section",
863
846
  "frame",
@@ -905,12 +888,12 @@ function resolveInsertParent(doc, selectedId) {
905
888
  if (selected) {
906
889
  return CONTAINER_TYPES.has(selected.type) ? selected.id : selected.parent;
907
890
  }
908
- return doc.sections[doc.sections.length - 1] ?? null;
891
+ return lastRootId(doc);
909
892
  }
910
893
  function buildInsertOp(doc, kind, selectedId, targetId) {
911
894
  const parent = targetId !== void 0 ? targetId && doc.nodes[targetId] ? CONTAINER_TYPES.has(doc.nodes[targetId].type) ? targetId : doc.nodes[targetId].parent : null : resolveInsertParent(doc, selectedId);
912
895
  const siblings = childrenOf(doc, parent);
913
- const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
896
+ const order = fancyDocCommons.fractionalKey(siblings.length ? siblings[siblings.length - 1].order : null, null);
914
897
  const id = `n${doc.seq + 1}-${Math.floor(performance.now())}`;
915
898
  const def = ADD_DEFAULTS[kind];
916
899
  return {
@@ -1077,6 +1060,14 @@ var DARK_VARS = "color-scheme:dark;--fcms-bg:#0b0f19;--fcms-fg:#e5e7eb;--fcms-mu
1077
1060
  var LIGHT_VARS = "color-scheme:light;--fcms-bg:#ffffff;--fcms-fg:#0f172a;--fcms-muted:#64748b;--fcms-border:#e2e8f0;--fcms-canvas:#f8fafc;--fcms-input-bg:#ffffff;--fcms-row-fg:#334155;--fcms-sel-bg:#ede9fe;--fcms-sel-fg:#5b21b6;--fcms-drop:#db2777;--fcms-danger:#dc2626;";
1078
1061
  var CHROME_CSS = `.fancy-cms-editor{--fcms-accent:#8b5cf6;${LIGHT_VARS}}@media (prefers-color-scheme:dark){.fancy-cms-editor{${DARK_VARS}}}:where(.light,[data-theme="light"]) .fancy-cms-editor{${LIGHT_VARS}}:where(.dark,[data-theme="dark"]) .fancy-cms-editor{${DARK_VARS}}`;
1079
1062
 
1063
+ Object.defineProperty(exports, "fractionalKey", {
1064
+ enumerable: true,
1065
+ get: function () { return fancyDocCommons.fractionalKey; }
1066
+ });
1067
+ Object.defineProperty(exports, "keyBetween", {
1068
+ enumerable: true,
1069
+ get: function () { return fancyDocCommons.fractionalKey; }
1070
+ });
1080
1071
  exports.ADD_DEFAULTS = ADD_DEFAULTS;
1081
1072
  exports.ADD_MENU = ADD_MENU;
1082
1073
  exports.CONTAINER_TYPES = CONTAINER_TYPES;
@@ -1099,11 +1090,15 @@ exports.getPath = getPath;
1099
1090
  exports.initEditor = initEditor;
1100
1091
  exports.invert = invert;
1101
1092
  exports.isBinding = isBinding;
1102
- exports.keyBetween = keyBetween;
1093
+ exports.lastRootId = lastRootId;
1094
+ exports.migrateDoc = migrateDoc;
1095
+ exports.migrateDocVerbose = migrateDocVerbose;
1096
+ exports.needsMigration = needsMigration;
1103
1097
  exports.reduce = reduce;
1104
1098
  exports.reduceAll = reduceAll;
1105
1099
  exports.resolveInsertParent = resolveInsertParent;
1106
1100
  exports.resolveValue = resolveValue;
1101
+ exports.rootIds = rootIds;
1107
1102
  exports.useEditor = useEditor;
1108
1103
  //# sourceMappingURL=index.cjs.map
1109
1104
  //# sourceMappingURL=index.cjs.map