@particle-academy/fancy-cms-ui 0.7.1 → 0.9.0
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/CHANGELOG.md +448 -0
- package/LICENSE +21 -0
- package/agent-guide/professional-pages.md +107 -0
- package/dist/{chunk-SYRU2X45.js → chunk-BVMSHUQF.js} +6 -4
- package/dist/{chunk-SYRU2X45.js.map → chunk-BVMSHUQF.js.map} +1 -1
- package/dist/chunk-IRG5IKKO.js +716 -0
- package/dist/chunk-IRG5IKKO.js.map +1 -0
- package/dist/{chunk-R7HDS4VG.js → chunk-TJL26EAX.js} +3 -3
- package/dist/{chunk-R7HDS4VG.js.map → chunk-TJL26EAX.js.map} +1 -1
- package/dist/editor.cjs +325 -103
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +2 -2
- package/dist/editor.d.ts +2 -2
- package/dist/editor.js +3 -3
- package/dist/index.cjs +335 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -4
- package/dist/react.cjs +304 -85
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/{registry-CUNaBqTs.d.cts → registry-F2TAMjhx.d.cts} +33 -1
- package/dist/{registry-CUNaBqTs.d.ts → registry-F2TAMjhx.d.ts} +33 -1
- package/dist/{useEditor-wdlYQMHg.d.cts → useEditor-B2kVZJA0.d.cts} +1 -1
- package/dist/{useEditor-aPH5falI.d.ts → useEditor-BQSictH3.d.ts} +1 -1
- package/package.json +107 -105
- package/dist/chunk-LY6ID2J2.js +0 -494
- package/dist/chunk-LY6ID2J2.js.map +0 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import { childrenOf as childrenOf$1, descendantsOf, fractionalKey } from '@particle-academy/fancy-doc-commons';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import { createElement, useMemo, Fragment } from 'react';
|
|
4
|
+
|
|
5
|
+
// src/document/types.ts
|
|
6
|
+
function isBinding(v) {
|
|
7
|
+
return typeof v === "object" && v !== null && typeof v.$bind === "string";
|
|
8
|
+
}
|
|
9
|
+
function hasNodeMap(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
11
|
+
const nodes = value.nodes;
|
|
12
|
+
return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
|
|
13
|
+
}
|
|
14
|
+
function withNodeDefaults(node) {
|
|
15
|
+
const props = typeof node.props === "object" && node.props !== null ? node.props : {};
|
|
16
|
+
const style = typeof node.style === "object" && node.style !== null ? node.style : { base: {} };
|
|
17
|
+
const base = typeof style.base === "object" && style.base !== null ? style.base : {};
|
|
18
|
+
return props === node.props && style === node.style && base === style.base ? node : { ...node, props, style: { ...style, base } };
|
|
19
|
+
}
|
|
20
|
+
var DEFAULT_BREAKPOINTS = {
|
|
21
|
+
base: 0,
|
|
22
|
+
md: 768,
|
|
23
|
+
lg: 1024
|
|
24
|
+
};
|
|
25
|
+
function emptyDoc(id, slug = "/") {
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
seq: 0,
|
|
29
|
+
meta: { title: "Untitled", slug, scrollMode: "smooth" },
|
|
30
|
+
theme: { name: "default" },
|
|
31
|
+
breakpoints: ["base", "md", "lg"],
|
|
32
|
+
nodes: {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/document/own.ts
|
|
37
|
+
var has = Object.prototype.hasOwnProperty;
|
|
38
|
+
function own(map, key) {
|
|
39
|
+
return map != null && has.call(map, key) ? map[key] : void 0;
|
|
40
|
+
}
|
|
41
|
+
function setOwn(map, key, value) {
|
|
42
|
+
Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/document/reduce.ts
|
|
46
|
+
function childrenOf(doc, parent) {
|
|
47
|
+
return childrenOf$1(doc, parent);
|
|
48
|
+
}
|
|
49
|
+
function rootIds(doc) {
|
|
50
|
+
if (!hasNodeMap(doc)) return [];
|
|
51
|
+
return childrenOf(doc, null).map((n) => n.id);
|
|
52
|
+
}
|
|
53
|
+
function lastRootId(doc) {
|
|
54
|
+
const r = rootIds(doc);
|
|
55
|
+
return r[r.length - 1] ?? null;
|
|
56
|
+
}
|
|
57
|
+
function descendantIds(doc, id) {
|
|
58
|
+
return descendantsOf(doc, id);
|
|
59
|
+
}
|
|
60
|
+
function patchNode(doc, id, patch) {
|
|
61
|
+
const node = own(doc.nodes, id);
|
|
62
|
+
return {
|
|
63
|
+
...doc,
|
|
64
|
+
nodes: { ...doc.nodes, [id]: { ...node, ...patch } },
|
|
65
|
+
seq: doc.seq + 1
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function reduce(doc, op, opts = {}) {
|
|
69
|
+
const invalid = (reason) => {
|
|
70
|
+
opts.onInvalid?.(op, reason);
|
|
71
|
+
return doc;
|
|
72
|
+
};
|
|
73
|
+
switch (op.t) {
|
|
74
|
+
case "insert_node": {
|
|
75
|
+
if (own(doc.nodes, op.node.id)) return invalid("node id already exists");
|
|
76
|
+
if (op.node.parent !== null && !own(doc.nodes, op.node.parent)) return invalid("parent not found");
|
|
77
|
+
return {
|
|
78
|
+
...doc,
|
|
79
|
+
nodes: { ...doc.nodes, [op.node.id]: op.node },
|
|
80
|
+
seq: doc.seq + 1
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case "remove_node": {
|
|
84
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
85
|
+
const dead = /* @__PURE__ */ new Set([op.id, ...descendantIds(doc, op.id)]);
|
|
86
|
+
const nodes = {};
|
|
87
|
+
for (const id of Object.keys(doc.nodes)) {
|
|
88
|
+
if (!dead.has(id)) setOwn(nodes, id, own(doc.nodes, id));
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
...doc,
|
|
92
|
+
nodes,
|
|
93
|
+
seq: doc.seq + 1
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
case "move_node": {
|
|
97
|
+
const node = own(doc.nodes, op.id);
|
|
98
|
+
if (!node) return invalid("node not found");
|
|
99
|
+
if (op.parent === op.id) return invalid("cannot parent a node to itself");
|
|
100
|
+
if (op.parent !== null && !own(doc.nodes, op.parent)) return invalid("new parent not found");
|
|
101
|
+
if (op.parent !== null && descendantIds(doc, op.id).includes(op.parent)) {
|
|
102
|
+
return invalid("move would create a cycle");
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
...doc,
|
|
106
|
+
nodes: { ...doc.nodes, [op.id]: { ...node, parent: op.parent, order: op.order } },
|
|
107
|
+
seq: doc.seq + 1
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
case "set_node_props": {
|
|
111
|
+
const node = own(doc.nodes, op.id);
|
|
112
|
+
if (!node) return invalid("node not found");
|
|
113
|
+
return patchNode(doc, op.id, {
|
|
114
|
+
props: { ...node.props, ...op.patch }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
case "set_layout": {
|
|
118
|
+
if (!own(doc.nodes, op.id)) return invalid("node not found");
|
|
119
|
+
return patchNode(doc, op.id, { layout: op.layout });
|
|
120
|
+
}
|
|
121
|
+
case "set_style": {
|
|
122
|
+
const node = own(doc.nodes, op.id);
|
|
123
|
+
if (!node) return invalid("node not found");
|
|
124
|
+
const prev = own(node.style, op.breakpoint) ?? {};
|
|
125
|
+
return patchNode(doc, op.id, {
|
|
126
|
+
style: { ...node.style, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
case "set_constraints": {
|
|
130
|
+
const node = own(doc.nodes, op.id);
|
|
131
|
+
if (!node) return invalid("node not found");
|
|
132
|
+
const base = node.constraints ?? { base: { width: "hug", height: "hug" } };
|
|
133
|
+
const prev = own(base, op.breakpoint) ?? {};
|
|
134
|
+
return patchNode(doc, op.id, {
|
|
135
|
+
constraints: { ...base, [op.breakpoint]: { ...prev, ...op.patch } }
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
case "set_animation": {
|
|
139
|
+
const node = own(doc.nodes, op.id);
|
|
140
|
+
if (!node) return invalid("node not found");
|
|
141
|
+
const list = node.animations ?? [];
|
|
142
|
+
const idx = list.findIndex((a) => a.id === op.anim.id);
|
|
143
|
+
return patchNode(doc, op.id, {
|
|
144
|
+
animations: idx >= 0 ? list.map((a, i) => i === idx ? op.anim : a) : [...list, op.anim]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
case "remove_animation": {
|
|
148
|
+
const node = own(doc.nodes, op.id);
|
|
149
|
+
if (!node) return invalid("node not found");
|
|
150
|
+
return patchNode(doc, op.id, {
|
|
151
|
+
animations: (node.animations ?? []).filter((a) => a.id !== op.animId)
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
case "set_action": {
|
|
155
|
+
const node = own(doc.nodes, op.id);
|
|
156
|
+
if (!node) return invalid("node not found");
|
|
157
|
+
const list = node.actions ?? [];
|
|
158
|
+
let actions;
|
|
159
|
+
if (op.action === null) actions = list.filter((_, i) => i !== op.index);
|
|
160
|
+
else if (op.index >= list.length) actions = [...list, op.action];
|
|
161
|
+
else actions = list.map((a, i) => i === op.index ? op.action : a);
|
|
162
|
+
return patchNode(doc, op.id, { actions });
|
|
163
|
+
}
|
|
164
|
+
case "set_meta":
|
|
165
|
+
return { ...doc, meta: { ...doc.meta, ...op.patch }, seq: doc.seq + 1 };
|
|
166
|
+
case "set_theme":
|
|
167
|
+
return { ...doc, theme: { ...doc.theme, ...op.patch }, seq: doc.seq + 1 };
|
|
168
|
+
case "reorder_roots": {
|
|
169
|
+
const currentRoots = childrenOf(doc, null);
|
|
170
|
+
const current = new Set(currentRoots.map((n) => n.id));
|
|
171
|
+
const same = op.order.length === currentRoots.length && new Set(op.order).size === op.order.length && op.order.every((id) => current.has(id));
|
|
172
|
+
if (!same) return invalid("reorder must be a permutation of the current roots");
|
|
173
|
+
const nodes = { ...doc.nodes };
|
|
174
|
+
let prev = null;
|
|
175
|
+
for (const id of op.order) {
|
|
176
|
+
prev = fractionalKey(prev, null);
|
|
177
|
+
setOwn(nodes, id, { ...own(nodes, id), order: prev });
|
|
178
|
+
}
|
|
179
|
+
return { ...doc, nodes, seq: doc.seq + 1 };
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
return invalid(`unknown op "${op.t}"`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function reduceAll(doc, ops, opts) {
|
|
186
|
+
return ops.reduce((d, op) => reduce(d, op, opts), doc);
|
|
187
|
+
}
|
|
188
|
+
function invert(doc, op) {
|
|
189
|
+
switch (op.t) {
|
|
190
|
+
case "insert_node":
|
|
191
|
+
return [{ t: "remove_node", id: op.node.id }];
|
|
192
|
+
case "remove_node": {
|
|
193
|
+
const node = own(doc.nodes, op.id);
|
|
194
|
+
if (!node) return [];
|
|
195
|
+
const ids = [op.id, ...descendantIds(doc, op.id)];
|
|
196
|
+
const inserts = ids.map((id) => ({ t: "insert_node", node: own(doc.nodes, id) }));
|
|
197
|
+
if (node.parent === null) {
|
|
198
|
+
inserts.push({ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) });
|
|
199
|
+
}
|
|
200
|
+
return inserts;
|
|
201
|
+
}
|
|
202
|
+
case "move_node": {
|
|
203
|
+
const node = own(doc.nodes, op.id);
|
|
204
|
+
if (!node) return [];
|
|
205
|
+
const back = [{ t: "move_node", id: op.id, parent: node.parent, order: node.order }];
|
|
206
|
+
if (node.parent === null) {
|
|
207
|
+
back.push({ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) });
|
|
208
|
+
}
|
|
209
|
+
return back;
|
|
210
|
+
}
|
|
211
|
+
case "set_node_props": {
|
|
212
|
+
const node = own(doc.nodes, op.id);
|
|
213
|
+
if (!node) return [];
|
|
214
|
+
const prev = {};
|
|
215
|
+
for (const k of Object.keys(op.patch)) prev[k] = node.props[k];
|
|
216
|
+
return [{ t: "set_node_props", id: op.id, patch: prev }];
|
|
217
|
+
}
|
|
218
|
+
case "set_layout": {
|
|
219
|
+
const node = own(doc.nodes, op.id);
|
|
220
|
+
if (!node) return [];
|
|
221
|
+
return [{ t: "set_layout", id: op.id, layout: node.layout }];
|
|
222
|
+
}
|
|
223
|
+
case "set_style": {
|
|
224
|
+
const node = own(doc.nodes, op.id);
|
|
225
|
+
if (!node) return [];
|
|
226
|
+
const prevBp = node.style[op.breakpoint] ?? {};
|
|
227
|
+
const prev = {};
|
|
228
|
+
for (const k of Object.keys(op.patch)) prev[k] = prevBp[k];
|
|
229
|
+
return [{ t: "set_style", id: op.id, breakpoint: op.breakpoint, patch: prev }];
|
|
230
|
+
}
|
|
231
|
+
case "set_meta":
|
|
232
|
+
return [{ t: "set_meta", patch: pick(doc.meta, Object.keys(op.patch)) }];
|
|
233
|
+
case "set_theme":
|
|
234
|
+
return [{ t: "set_theme", patch: pick(doc.theme, Object.keys(op.patch)) }];
|
|
235
|
+
case "reorder_roots":
|
|
236
|
+
return [{ t: "reorder_roots", order: childrenOf(doc, null).map((n) => n.id) }];
|
|
237
|
+
default:
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function pick(obj, keys) {
|
|
242
|
+
const out = {};
|
|
243
|
+
for (const k of keys) out[k] = obj[k];
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
function normalizeDoc(input) {
|
|
247
|
+
if (hasNodeMap(input)) return input;
|
|
248
|
+
const id = typeof input === "object" && input !== null && !Array.isArray(input) && typeof input.id === "string" ? input.id : "malformed";
|
|
249
|
+
return emptyDoc(id);
|
|
250
|
+
}
|
|
251
|
+
function migrateDocVerbose(input) {
|
|
252
|
+
const normalized = normalizeDoc(input);
|
|
253
|
+
const legacy = normalized;
|
|
254
|
+
const sections = legacy.sections;
|
|
255
|
+
if (!Array.isArray(sections)) {
|
|
256
|
+
return { doc: normalized, migrated: false, danglingIds: [], unlistedRootIds: [] };
|
|
257
|
+
}
|
|
258
|
+
const danglingIds = [];
|
|
259
|
+
const ordered = [];
|
|
260
|
+
const seen = /* @__PURE__ */ new Set();
|
|
261
|
+
for (const id of sections) {
|
|
262
|
+
if (!own(normalized.nodes, id)) {
|
|
263
|
+
danglingIds.push(id);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (seen.has(id)) continue;
|
|
267
|
+
seen.add(id);
|
|
268
|
+
ordered.push(id);
|
|
269
|
+
}
|
|
270
|
+
const unlistedRootIds = Object.values(normalized.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);
|
|
271
|
+
const finalOrder = [...ordered, ...unlistedRootIds];
|
|
272
|
+
const nodes = { ...normalized.nodes };
|
|
273
|
+
let prev = null;
|
|
274
|
+
for (const id of finalOrder) {
|
|
275
|
+
const key = fractionalKey(prev, null);
|
|
276
|
+
setOwn(nodes, id, { ...own(nodes, id), order: key });
|
|
277
|
+
prev = key;
|
|
278
|
+
}
|
|
279
|
+
const { sections: _dropped, ...rest } = legacy;
|
|
280
|
+
return {
|
|
281
|
+
doc: { ...rest, nodes },
|
|
282
|
+
migrated: true,
|
|
283
|
+
danglingIds,
|
|
284
|
+
unlistedRootIds
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function migrateDoc(input) {
|
|
288
|
+
return migrateDocVerbose(input).doc;
|
|
289
|
+
}
|
|
290
|
+
function needsMigration(input) {
|
|
291
|
+
return Array.isArray(input.sections);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/render/css.ts
|
|
295
|
+
function cssString(value) {
|
|
296
|
+
let out = "";
|
|
297
|
+
for (const ch of value) {
|
|
298
|
+
const code = ch.codePointAt(0);
|
|
299
|
+
if (ch === "\\" || ch === '"') out += `\\${ch}`;
|
|
300
|
+
else if (code < 32 || code === 127 || ch === "<") out += `\\${code.toString(16)} `;
|
|
301
|
+
else out += ch;
|
|
302
|
+
}
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
|
|
306
|
+
var BASE_CSS = [
|
|
307
|
+
"/* fancy-cms base */",
|
|
308
|
+
":where([data-cms]) {\n box-sizing: border-box;\n}",
|
|
309
|
+
":where(h1, h2, h3, h4, h5, h6, p, ul, ol)[data-cms] {\n margin: 0;\n}",
|
|
310
|
+
":where(ul, ol)[data-cms] {\n padding-left: 1.25em;\n}",
|
|
311
|
+
":where(a[data-cms]) {\n color: inherit;\n}",
|
|
312
|
+
":where([data-cms] > img) {\n display: block;\n height: 100%;\n object-fit: cover;\n width: 100%;\n}",
|
|
313
|
+
':where([data-cms] > img[data-cms-fit="contain"]) {\n object-fit: contain;\n}',
|
|
314
|
+
':where([data-cms] > img[data-cms-fit="fill"]) {\n object-fit: fill;\n}',
|
|
315
|
+
':where([data-cms] > img[data-cms-fit="none"]) {\n object-fit: none;\n}',
|
|
316
|
+
':where([data-cms] > img[data-cms-fit="scale-down"]) {\n object-fit: scale-down;\n}',
|
|
317
|
+
":where([data-cms-button]) {\n align-items: center;\n border: 1px solid var(--cms-color-primary, #7c3aed);\n border-radius: 8px;\n cursor: pointer;\n display: inline-flex;\n font-weight: 600;\n gap: 6px;\n justify-content: center;\n line-height: 1.2;\n padding: 10px 18px;\n text-decoration: none;\n}",
|
|
318
|
+
':where([data-cms-button="primary"]) {\n background: var(--cms-color-primary, #7c3aed);\n color: #ffffff;\n}',
|
|
319
|
+
':where([data-cms-button="outline"]) {\n background: transparent;\n color: var(--cms-color-primary, #7c3aed);\n}',
|
|
320
|
+
':where([data-cms-button="ghost"]) {\n background: transparent;\n border-color: currentColor;\n color: inherit;\n}',
|
|
321
|
+
":where([data-cms-callout]) {\n background: #3b82f614;\n border-left: 3px solid #3b82f6;\n border-radius: 10px;\n padding: 12px 14px;\n}",
|
|
322
|
+
':where([data-cms-callout="success"]) {\n background: #10b98114;\n border-left-color: #10b981;\n}',
|
|
323
|
+
':where([data-cms-callout="warning"]) {\n background: #f59e0b14;\n border-left-color: #f59e0b;\n}',
|
|
324
|
+
':where([data-cms-callout="danger"]) {\n background: #ef444414;\n border-left-color: #ef4444;\n}',
|
|
325
|
+
":where([data-cms] > hr) {\n border: none;\n border-top: 1px solid #e2e8f0;\n margin: 0;\n}",
|
|
326
|
+
":where([data-cms] > pre) {\n background: #0b1220;\n border-radius: 10px;\n color: #e2e8f0;\n font-family: ui-monospace, monospace;\n font-size: 13px;\n margin: 0;\n overflow-x: auto;\n padding: 12px 14px;\n}"
|
|
327
|
+
].join("\n\n");
|
|
328
|
+
var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
|
|
329
|
+
function lengthToCss(len) {
|
|
330
|
+
if (typeof len !== "object" || len === null) return null;
|
|
331
|
+
const { value, unit } = len;
|
|
332
|
+
if (typeof value !== "number" || !Number.isFinite(value) || typeof unit !== "string" || !UNITS.has(unit)) return null;
|
|
333
|
+
return `${value}${unit}`;
|
|
334
|
+
}
|
|
335
|
+
function sizeToCss(size) {
|
|
336
|
+
if (size === "fill") return "100%";
|
|
337
|
+
if (size === "hug") return "auto";
|
|
338
|
+
return lengthToCss(size);
|
|
339
|
+
}
|
|
340
|
+
function numberToCss(value) {
|
|
341
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
342
|
+
}
|
|
343
|
+
function putLen(d, props, len) {
|
|
344
|
+
const css = lengthToCss(len);
|
|
345
|
+
if (css !== null) for (const p of props) d[p] = css;
|
|
346
|
+
}
|
|
347
|
+
var ALIGN = {
|
|
348
|
+
start: "flex-start",
|
|
349
|
+
center: "center",
|
|
350
|
+
end: "flex-end",
|
|
351
|
+
stretch: "stretch"
|
|
352
|
+
};
|
|
353
|
+
var JUSTIFY = {
|
|
354
|
+
start: "flex-start",
|
|
355
|
+
center: "center",
|
|
356
|
+
end: "flex-end",
|
|
357
|
+
between: "space-between",
|
|
358
|
+
around: "space-around"
|
|
359
|
+
};
|
|
360
|
+
function safeCssValue(value) {
|
|
361
|
+
if (/[{}<]/.test(value)) return null;
|
|
362
|
+
let depth = 0;
|
|
363
|
+
let quote = null;
|
|
364
|
+
for (const ch of value) {
|
|
365
|
+
if (quote) {
|
|
366
|
+
if (ch === quote) quote = null;
|
|
367
|
+
} else if (ch === '"' || ch === "'") {
|
|
368
|
+
quote = ch;
|
|
369
|
+
} else if (ch === "(") {
|
|
370
|
+
depth++;
|
|
371
|
+
} else if (ch === ")") {
|
|
372
|
+
depth = Math.max(0, depth - 1);
|
|
373
|
+
} else if (ch === ";" && depth === 0) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
function put(d, prop, value) {
|
|
380
|
+
if (value === void 0) return;
|
|
381
|
+
const safe = safeCssValue(value);
|
|
382
|
+
if (safe !== null) d[prop] = safe;
|
|
383
|
+
}
|
|
384
|
+
var OVERFLOW = /* @__PURE__ */ new Set(["visible", "hidden", "auto"]);
|
|
385
|
+
var TEXT_TRANSFORM = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
386
|
+
var BACKGROUND_SIZE = /* @__PURE__ */ new Set(["cover", "contain", "auto"]);
|
|
387
|
+
var TEXT_ALIGN = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
388
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["row", "column"]);
|
|
389
|
+
function keyword(map, value) {
|
|
390
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(map, value) ? map[value] : null;
|
|
391
|
+
}
|
|
392
|
+
function inSet(set, value) {
|
|
393
|
+
return typeof value === "string" && set.has(value) ? value : null;
|
|
394
|
+
}
|
|
395
|
+
function styleDecls(style) {
|
|
396
|
+
const d = {};
|
|
397
|
+
putLen(d, ["padding"], style.padding);
|
|
398
|
+
putLen(d, ["padding-left", "padding-right"], style.paddingX);
|
|
399
|
+
putLen(d, ["padding-top", "padding-bottom"], style.paddingY);
|
|
400
|
+
putLen(d, ["margin"], style.margin);
|
|
401
|
+
if (style.marginX === "auto") d["margin-left"] = d["margin-right"] = "auto";
|
|
402
|
+
else putLen(d, ["margin-left", "margin-right"], style.marginX);
|
|
403
|
+
putLen(d, ["margin-top", "margin-bottom"], style.marginY);
|
|
404
|
+
putLen(d, ["border-radius"], style.radius);
|
|
405
|
+
putLen(d, ["max-width"], style.maxWidth);
|
|
406
|
+
putLen(d, ["min-height"], style.minHeight);
|
|
407
|
+
put(d, "aspect-ratio", typeof style.aspectRatio === "string" ? style.aspectRatio : void 0);
|
|
408
|
+
if (style.sticky === true) {
|
|
409
|
+
d["position"] = "sticky";
|
|
410
|
+
d["top"] = "0";
|
|
411
|
+
}
|
|
412
|
+
if (typeof style.zIndex === "number" && Number.isFinite(style.zIndex)) d["z-index"] = String(Math.trunc(style.zIndex));
|
|
413
|
+
const overflow = inSet(OVERFLOW, style.overflow);
|
|
414
|
+
if (overflow) d["overflow"] = overflow;
|
|
415
|
+
put(d, "background", typeof style.background === "string" ? style.background : void 0);
|
|
416
|
+
const backgroundSize = inSet(BACKGROUND_SIZE, style.backgroundSize);
|
|
417
|
+
if (backgroundSize) d["background-size"] = backgroundSize;
|
|
418
|
+
put(d, "background-position", typeof style.backgroundPosition === "string" ? style.backgroundPosition : void 0);
|
|
419
|
+
put(d, "color", typeof style.color === "string" ? style.color : void 0);
|
|
420
|
+
const opacity = numberToCss(style.opacity);
|
|
421
|
+
if (opacity !== null) d["opacity"] = opacity;
|
|
422
|
+
put(d, "font-family", typeof style.fontFamily === "string" ? style.fontFamily : void 0);
|
|
423
|
+
putLen(d, ["font-size"], style.fontSize);
|
|
424
|
+
const fontWeight = numberToCss(style.fontWeight);
|
|
425
|
+
if (fontWeight !== null) d["font-weight"] = fontWeight;
|
|
426
|
+
const lineHeight = numberToCss(style.lineHeight);
|
|
427
|
+
if (lineHeight !== null) d["line-height"] = lineHeight;
|
|
428
|
+
const textAlign = inSet(TEXT_ALIGN, style.textAlign);
|
|
429
|
+
if (textAlign) d["text-align"] = textAlign;
|
|
430
|
+
putLen(d, ["letter-spacing"], style.letterSpacing);
|
|
431
|
+
const textTransform = inSet(TEXT_TRANSFORM, style.textTransform);
|
|
432
|
+
if (textTransform) d["text-transform"] = textTransform;
|
|
433
|
+
put(d, "border", typeof style.border === "string" ? style.border : void 0);
|
|
434
|
+
put(d, "box-shadow", typeof style.boxShadow === "string" ? style.boxShadow : void 0);
|
|
435
|
+
put(d, "transform", typeof style.transform === "string" ? style.transform : void 0);
|
|
436
|
+
put(d, "filter", typeof style.filter === "string" ? style.filter : void 0);
|
|
437
|
+
putLen(d, ["gap"], style.gap);
|
|
438
|
+
const align = keyword(ALIGN, style.align);
|
|
439
|
+
if (align) d["align-items"] = align;
|
|
440
|
+
const justify = keyword(JUSTIFY, style.justify);
|
|
441
|
+
if (justify) d["justify-content"] = justify;
|
|
442
|
+
return d;
|
|
443
|
+
}
|
|
444
|
+
function shownDisplay(node) {
|
|
445
|
+
if (node.layout === "stack") return "flex";
|
|
446
|
+
if (node.layout === "grid") return "grid";
|
|
447
|
+
return "block";
|
|
448
|
+
}
|
|
449
|
+
function gridColumns(style) {
|
|
450
|
+
const min = lengthToCss(style.columnMin);
|
|
451
|
+
if (min !== null) return `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`;
|
|
452
|
+
if (typeof style.columns === "number" && Number.isFinite(style.columns)) return `repeat(${Math.max(1, Math.trunc(style.columns))}, 1fr)`;
|
|
453
|
+
return void 0;
|
|
454
|
+
}
|
|
455
|
+
function containerDecls(node, style, isBase) {
|
|
456
|
+
const d = {};
|
|
457
|
+
if (node.layout === "stack") {
|
|
458
|
+
if (isBase) d["display"] = "flex";
|
|
459
|
+
const direction = inSet(DIRECTIONS, style.direction);
|
|
460
|
+
if (isBase || direction) d["flex-direction"] = direction ?? "column";
|
|
461
|
+
if (style.wrap !== void 0) d["flex-wrap"] = style.wrap ? "wrap" : "nowrap";
|
|
462
|
+
} else if (node.layout === "grid") {
|
|
463
|
+
if (isBase) d["display"] = "grid";
|
|
464
|
+
const cols = gridColumns(style);
|
|
465
|
+
if (isBase || cols !== void 0) d["grid-template-columns"] = cols ?? "repeat(1, 1fr)";
|
|
466
|
+
} else if (node.layout === "free" && isBase) {
|
|
467
|
+
d["position"] = "relative";
|
|
468
|
+
}
|
|
469
|
+
return d;
|
|
470
|
+
}
|
|
471
|
+
function visibilityDecls(node, style, isBase) {
|
|
472
|
+
if (style.hidden === true) return { display: "none" };
|
|
473
|
+
if (style.hidden === false && !isBase) return { display: shownDisplay(node) };
|
|
474
|
+
return {};
|
|
475
|
+
}
|
|
476
|
+
function themeDecls(theme) {
|
|
477
|
+
const d = {};
|
|
478
|
+
const walk = (value, path) => {
|
|
479
|
+
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
|
|
480
|
+
const key = path.map((p) => p.toLowerCase().replace(/[^a-z0-9-]/g, "")).filter(Boolean).join("-");
|
|
481
|
+
if (key) put(d, `--cms-${key}`, String(value));
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
485
|
+
for (const [k, v] of Object.entries(value)) walk(v, [...path, k]);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
if (typeof theme === "object" && theme !== null && !Array.isArray(theme)) {
|
|
489
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
490
|
+
if (k !== "name") walk(v, [k]);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return d;
|
|
494
|
+
}
|
|
495
|
+
function constraintDecls(c, parentFree) {
|
|
496
|
+
const d = {};
|
|
497
|
+
const transforms = [];
|
|
498
|
+
if (parentFree) {
|
|
499
|
+
d["position"] = "absolute";
|
|
500
|
+
putLen(d, ["left"], c.left);
|
|
501
|
+
putLen(d, ["right"], c.right);
|
|
502
|
+
putLen(d, ["top"], c.top);
|
|
503
|
+
putLen(d, ["bottom"], c.bottom);
|
|
504
|
+
if (c.centerX) {
|
|
505
|
+
d["left"] = "50%";
|
|
506
|
+
transforms.push("translateX(-50%)");
|
|
507
|
+
}
|
|
508
|
+
if (c.centerY) {
|
|
509
|
+
d["top"] = "50%";
|
|
510
|
+
transforms.push("translateY(-50%)");
|
|
511
|
+
}
|
|
512
|
+
if (transforms.length) d["transform"] = transforms.join(" ");
|
|
513
|
+
}
|
|
514
|
+
const width = sizeToCss(c.width);
|
|
515
|
+
if (width !== null) d["width"] = width;
|
|
516
|
+
const height = sizeToCss(c.height);
|
|
517
|
+
if (height !== null) d["height"] = height;
|
|
518
|
+
return d;
|
|
519
|
+
}
|
|
520
|
+
function isParentFree(doc, node) {
|
|
521
|
+
if (node.parent === null) return false;
|
|
522
|
+
const parent = own(doc.nodes, node.parent);
|
|
523
|
+
return !parent || parent.layout === void 0 || parent.layout === "free";
|
|
524
|
+
}
|
|
525
|
+
function serializeRule(selector, decls, indent) {
|
|
526
|
+
const props = Object.keys(decls).sort();
|
|
527
|
+
if (props.length === 0) return "";
|
|
528
|
+
const body = props.map((p) => `${indent} ${p}: ${decls[p]};`).join("\n");
|
|
529
|
+
return `${indent}${selector} {
|
|
530
|
+
${body}
|
|
531
|
+
${indent}}`;
|
|
532
|
+
}
|
|
533
|
+
function emitDocCss(doc) {
|
|
534
|
+
if (!hasNodeMap(doc)) return "";
|
|
535
|
+
const breakpointPx = DEFAULT_BREAKPOINTS;
|
|
536
|
+
const blocks = [];
|
|
537
|
+
const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
|
|
538
|
+
if (themeRule) blocks.push(themeRule);
|
|
539
|
+
for (const id of Object.keys(doc.nodes).sort()) {
|
|
540
|
+
const node = withNodeDefaults(doc.nodes[id]);
|
|
541
|
+
const selector = selectorFor(id);
|
|
542
|
+
const parentFree = isParentFree(doc, node);
|
|
543
|
+
const base = {
|
|
544
|
+
...containerDecls(node, node.style.base, true),
|
|
545
|
+
...node.constraints ? constraintDecls(node.constraints.base, parentFree) : {},
|
|
546
|
+
...styleDecls(node.style.base),
|
|
547
|
+
...visibilityDecls(node, node.style.base, true)
|
|
548
|
+
};
|
|
549
|
+
const baseRule = serializeRule(selector, base, "");
|
|
550
|
+
if (baseRule) blocks.push(baseRule);
|
|
551
|
+
for (const bp of doc.breakpoints) {
|
|
552
|
+
if (bp === "base") continue;
|
|
553
|
+
const px = own(breakpointPx, bp);
|
|
554
|
+
if (typeof px !== "number") continue;
|
|
555
|
+
const bpStyle = own(node.style, bp);
|
|
556
|
+
const bpConstraints = node.constraints ? own(node.constraints, bp) : void 0;
|
|
557
|
+
const decls = {
|
|
558
|
+
...bpStyle ? containerDecls(node, bpStyle, false) : {},
|
|
559
|
+
...bpConstraints ? constraintDecls(bpConstraints, parentFree) : {},
|
|
560
|
+
...bpStyle ? styleDecls(bpStyle) : {},
|
|
561
|
+
...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
|
|
562
|
+
};
|
|
563
|
+
const inner = serializeRule(selector, decls, " ");
|
|
564
|
+
if (inner) blocks.push(`@media (min-width: ${px}px) {
|
|
565
|
+
${inner}
|
|
566
|
+
}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (blocks.length === 0 && Object.keys(doc.nodes).length === 0) return "";
|
|
570
|
+
return [BASE_CSS, ...blocks].join("\n\n") + "\n";
|
|
571
|
+
}
|
|
572
|
+
function getPath(data, path) {
|
|
573
|
+
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
|
|
574
|
+
}
|
|
575
|
+
function resolveValue(v, data) {
|
|
576
|
+
if (v === void 0 || v === null) return v;
|
|
577
|
+
if (isBinding(v)) return getPath(data, v.$bind);
|
|
578
|
+
return v;
|
|
579
|
+
}
|
|
580
|
+
var LANDMARK_TAGS = ["section", "header", "nav", "main", "footer", "article", "aside", "div"];
|
|
581
|
+
var LANDMARKS = new Set(LANDMARK_TAGS);
|
|
582
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["stack", "grid", "frame", "shape", "card"]);
|
|
583
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
584
|
+
function safeHref(raw) {
|
|
585
|
+
const href = raw.trim();
|
|
586
|
+
if (!href) return null;
|
|
587
|
+
const probe = href.replace(/[\u0000- \u007f]+/g, "");
|
|
588
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
589
|
+
if (scheme && !SAFE_SCHEMES.has(scheme[1].toLowerCase())) return null;
|
|
590
|
+
return href;
|
|
591
|
+
}
|
|
592
|
+
var ANCHOR_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
593
|
+
function anchorFor(node, text) {
|
|
594
|
+
const anchor = text(node.props.anchor);
|
|
595
|
+
return ANCHOR_PATTERN.test(anchor) ? anchor : null;
|
|
596
|
+
}
|
|
597
|
+
function elementFor(node, text) {
|
|
598
|
+
const as = text(node.props.as);
|
|
599
|
+
switch (node.type) {
|
|
600
|
+
case "heading": {
|
|
601
|
+
const level = Number(text(node.props.level) || 2);
|
|
602
|
+
return { tag: Number.isInteger(level) && level >= 1 && level <= 6 ? `h${level}` : "h2" };
|
|
603
|
+
}
|
|
604
|
+
case "text":
|
|
605
|
+
return { tag: "p" };
|
|
606
|
+
case "section":
|
|
607
|
+
return { tag: LANDMARKS.has(as) ? as : "section" };
|
|
608
|
+
case "list":
|
|
609
|
+
return { tag: text(node.props.ordered) === "true" ? "ol" : "ul" };
|
|
610
|
+
case "button":
|
|
611
|
+
case "link": {
|
|
612
|
+
const href = safeHref(text(node.props.href));
|
|
613
|
+
const attrs = {};
|
|
614
|
+
if (href !== null) {
|
|
615
|
+
attrs.href = href;
|
|
616
|
+
if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
|
|
617
|
+
}
|
|
618
|
+
if (node.type === "button") attrs["data-cms-button"] = BUTTON_VARIANTS.has(text(node.props.variant)) ? text(node.props.variant) : "primary";
|
|
619
|
+
return { tag: href !== null ? "a" : node.type === "link" ? "span" : "div", attrs };
|
|
620
|
+
}
|
|
621
|
+
case "callout": {
|
|
622
|
+
const variant = text(node.props.variant);
|
|
623
|
+
return { tag: "div", attrs: { "data-cms-callout": CALLOUT_VARIANTS.has(variant) ? variant : "info" } };
|
|
624
|
+
}
|
|
625
|
+
default:
|
|
626
|
+
return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
var defaultRegistry = {
|
|
630
|
+
section: ({ children }) => children,
|
|
631
|
+
stack: ({ children }) => children,
|
|
632
|
+
grid: ({ children }) => children,
|
|
633
|
+
frame: ({ children }) => children,
|
|
634
|
+
shape: ({ children }) => children,
|
|
635
|
+
card: ({ children }) => children,
|
|
636
|
+
text: ({ node, text }) => text(node.props.content),
|
|
637
|
+
heading: ({ node, text }) => text(node.props.content) || "Heading",
|
|
638
|
+
// Rich text — inline-formatted HTML (gradient spans, links, <code>, …) that
|
|
639
|
+
// plain `text` can't express. The author owns the markup (it's their content).
|
|
640
|
+
richtext: ({ node, text }) => /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: text(node.props.html) } }),
|
|
641
|
+
// The [data-cms] element is already the <a> (see elementFor), so a link renders its label only.
|
|
642
|
+
link: ({ node, text }) => text(node.props.content) || "link",
|
|
643
|
+
list: ({ node, resolve }) => {
|
|
644
|
+
const items = resolve(node.props.items);
|
|
645
|
+
return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
|
|
646
|
+
},
|
|
647
|
+
// Looks come from the base stylesheet (render/css.ts BASE_CSS), never from
|
|
648
|
+
// inline style objects, so the PHP renderer emits the same markup.
|
|
649
|
+
divider: () => /* @__PURE__ */ jsx("hr", {}),
|
|
650
|
+
callout: ({ node, text }) => text(node.props.content) || "Callout",
|
|
651
|
+
code: ({ node, text }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: text(node.props.content) }) }),
|
|
652
|
+
button: ({ node, text }) => text(node.props.label) || text(node.props.content) || "Button",
|
|
653
|
+
image: ({ node, text }) => {
|
|
654
|
+
const fit = text(node.props.fit);
|
|
655
|
+
return /* @__PURE__ */ jsx(
|
|
656
|
+
"img",
|
|
657
|
+
{
|
|
658
|
+
src: text(node.props.src) || void 0,
|
|
659
|
+
alt: text(node.props.alt),
|
|
660
|
+
loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
|
|
661
|
+
"data-cms-fit": IMAGE_FITS.has(fit) && fit !== "cover" ? fit : void 0
|
|
662
|
+
}
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
|
|
667
|
+
var BUTTON_VARIANTS = /* @__PURE__ */ new Set(["primary", "outline", "ghost"]);
|
|
668
|
+
var CALLOUT_VARIANTS = /* @__PURE__ */ new Set(["info", "success", "warning", "danger"]);
|
|
669
|
+
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
670
|
+
const stored = own(doc.nodes, id);
|
|
671
|
+
if (!stored) return null;
|
|
672
|
+
const node = withNodeDefaults(stored);
|
|
673
|
+
if (node.type === "repeater") {
|
|
674
|
+
const arr = resolveValue(node.props.items, data);
|
|
675
|
+
const items = Array.isArray(arr) ? arr : [];
|
|
676
|
+
const template = childrenOf(doc, id)[0];
|
|
677
|
+
return /* @__PURE__ */ jsx("div", { "data-cms": node.id, className: node.className, children: template ? items.map((item, index) => /* @__PURE__ */ jsx(RenderNode, { doc, id: template.id, registry, data: { ...data, item, index } }, index)) : null });
|
|
678
|
+
}
|
|
679
|
+
const kids = childrenOf(doc, id).map((child) => /* @__PURE__ */ jsx(RenderNode, { doc, id: child.id, registry, data }, child.id));
|
|
680
|
+
const resolve = (v) => resolveValue(v, data);
|
|
681
|
+
const text = (v) => {
|
|
682
|
+
const r = resolve(v);
|
|
683
|
+
return r === void 0 || r === null ? "" : String(r);
|
|
684
|
+
};
|
|
685
|
+
const renderer = registry[node.type];
|
|
686
|
+
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
687
|
+
const { tag, attrs } = elementFor(node, text);
|
|
688
|
+
const anchor = anchorFor(node, text);
|
|
689
|
+
return createElement(
|
|
690
|
+
tag,
|
|
691
|
+
{
|
|
692
|
+
"data-cms": node.id,
|
|
693
|
+
"data-cms-island": node.island ? node.type : void 0,
|
|
694
|
+
className: node.className,
|
|
695
|
+
...anchor ? { id: anchor } : {},
|
|
696
|
+
...attrs
|
|
697
|
+
},
|
|
698
|
+
inner
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
function CmsPage({
|
|
702
|
+
doc: input,
|
|
703
|
+
registry = defaultRegistry,
|
|
704
|
+
data,
|
|
705
|
+
includeStyles = true
|
|
706
|
+
}) {
|
|
707
|
+
const doc = useMemo(() => migrateDoc(input), [input]);
|
|
708
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
709
|
+
includeStyles ? /* @__PURE__ */ jsx("style", { "data-cms-styles": "", children: emitDocCss(doc) }) : null,
|
|
710
|
+
rootIds(doc).map((id) => /* @__PURE__ */ jsx(RenderNode, { doc, id, registry, data }, id))
|
|
711
|
+
] });
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export { CmsPage, DEFAULT_BREAKPOINTS, RenderNode, childrenOf, defaultRegistry, descendantIds, emitDocCss, emptyDoc, getPath, hasNodeMap, invert, isBinding, lastRootId, migrateDoc, migrateDocVerbose, needsMigration, reduce, reduceAll, resolveValue, rootIds, withNodeDefaults };
|
|
715
|
+
//# sourceMappingURL=chunk-IRG5IKKO.js.map
|
|
716
|
+
//# sourceMappingURL=chunk-IRG5IKKO.js.map
|