@particle-academy/fancy-cms-ui 0.7.0 → 0.8.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 +395 -0
- package/LICENSE +21 -0
- package/agent-guide/professional-pages.md +107 -0
- package/dist/{chunk-YOAZRQKN.js → chunk-IOMIYI45.js} +6 -4
- package/dist/{chunk-YOAZRQKN.js.map → chunk-IOMIYI45.js.map} +1 -1
- package/dist/{chunk-IWQQINCF.js → chunk-SJYKJ6SG.js} +300 -83
- package/dist/chunk-SJYKJ6SG.js.map +1 -0
- package/dist/{chunk-BWGAUTKG.js → chunk-V6QIOJI7.js} +3 -3
- package/dist/{chunk-BWGAUTKG.js.map → chunk-V6QIOJI7.js.map} +1 -1
- package/dist/editor.cjs +313 -94
- 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 +301 -81
- 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 +288 -62
- 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-CiPir27w.d.cts → registry-B4FaQDip.d.cts} +31 -1
- package/dist/{registry-CiPir27w.d.ts → registry-B4FaQDip.d.ts} +31 -1
- package/dist/{useEditor-CoxNeC53.d.cts → useEditor-CIRm2oi-.d.cts} +1 -1
- package/dist/{useEditor-DAE36OYU.d.ts → useEditor-DZDD9qpS.d.ts} +1 -1
- package/package.json +107 -105
- package/dist/chunk-IWQQINCF.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-
|
|
2
|
-
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as
|
|
3
|
-
import { P as PageOp } from './useEditor-
|
|
4
|
-
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-
|
|
1
|
+
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-B4FaQDip.cjs';
|
|
2
|
+
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue } from './registry-B4FaQDip.cjs';
|
|
3
|
+
import { P as PageOp } from './useEditor-CIRm2oi-.cjs';
|
|
4
|
+
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-CIRm2oi-.cjs';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
export { CmsPage, CmsPageProps, CmsRegion, CmsRegionProps, RenderNode, RenderNodeProps } from './react.cjs';
|
|
7
7
|
import 'react';
|
|
@@ -120,7 +120,7 @@ declare function emitDocCss(doc: PageDoc): string;
|
|
|
120
120
|
*/
|
|
121
121
|
/** Node types that accept children, so an insert lands *inside* them. */
|
|
122
122
|
declare const CONTAINER_TYPES: Set<string>;
|
|
123
|
-
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater";
|
|
123
|
+
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater" | "list";
|
|
124
124
|
declare const ADD_DEFAULTS: Record<AddKind, {
|
|
125
125
|
type: string;
|
|
126
126
|
props: Record<string, Json>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-
|
|
2
|
-
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as
|
|
3
|
-
import { P as PageOp } from './useEditor-
|
|
4
|
-
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-
|
|
1
|
+
import { B as Binding, P as PageDoc, m as NodeId, N as Node, J as Json, q as StyleProps, L as LayoutMode } from './registry-B4FaQDip.js';
|
|
2
|
+
export { A as Action, a as ActionEvent, b as ActionHandler, c as Anim, d as AnimDriver, e as Bound, f as Breakpoint, C as Constraints, D as DEFAULT_BREAKPOINTS, g as DataContext, E as ElementContext, h as ElementRegistry, i as ElementRenderer, K as Keyframe, j as LegacyPageDoc, k as Length, l as LengthUnit, M as MigrateResult, n as PageMeta, o as PerBreakpoint, S as ScrollMode, p as SizeMode, T as ThemeTokens, r as defaultRegistry, s as emptyDoc, t as getPath, u as hasNodeMap, v as isBinding, w as migrateDoc, x as migrateDocVerbose, y as needsMigration, z as resolveValue } from './registry-B4FaQDip.js';
|
|
3
|
+
import { P as PageOp } from './useEditor-DZDD9qpS.js';
|
|
4
|
+
export { A as Actor, C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, O as OpEnvelope, h as PageOpType, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-DZDD9qpS.js';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
export { CmsPage, CmsPageProps, CmsRegion, CmsRegionProps, RenderNode, RenderNodeProps } from './react.js';
|
|
7
7
|
import 'react';
|
|
@@ -120,7 +120,7 @@ declare function emitDocCss(doc: PageDoc): string;
|
|
|
120
120
|
*/
|
|
121
121
|
/** Node types that accept children, so an insert lands *inside* them. */
|
|
122
122
|
declare const CONTAINER_TYPES: Set<string>;
|
|
123
|
-
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater";
|
|
123
|
+
type AddKind = "text" | "heading" | "button" | "link" | "image" | "box" | "stack" | "grid" | "card" | "callout" | "divider" | "code" | "richtext" | "repeater" | "list";
|
|
124
124
|
declare const ADD_DEFAULTS: Record<AddKind, {
|
|
125
125
|
type: string;
|
|
126
126
|
props: Record<string, Json>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { CmsRegion } from './chunk-
|
|
2
|
-
export { ADD_DEFAULTS, ADD_MENU, CONTAINER_TYPES, Canvas, Editor, Inspector, LayersPanel, buildInsertOp, editorReduce, initEditor, resolveInsertParent, useEditor } from './chunk-
|
|
3
|
-
import { isBinding } from './chunk-
|
|
4
|
-
export { CmsPage, DEFAULT_BREAKPOINTS, RenderNode, childrenOf, defaultRegistry, descendantIds, emitDocCss, emptyDoc, getPath, invert, isBinding, lastRootId, migrateDoc, migrateDocVerbose, needsMigration, reduce, reduceAll, resolveValue, rootIds } from './chunk-
|
|
1
|
+
export { CmsRegion } from './chunk-V6QIOJI7.js';
|
|
2
|
+
export { ADD_DEFAULTS, ADD_MENU, CONTAINER_TYPES, Canvas, Editor, Inspector, LayersPanel, buildInsertOp, editorReduce, initEditor, resolveInsertParent, useEditor } from './chunk-IOMIYI45.js';
|
|
3
|
+
import { isBinding } from './chunk-SJYKJ6SG.js';
|
|
4
|
+
export { CmsPage, DEFAULT_BREAKPOINTS, RenderNode, childrenOf, defaultRegistry, descendantIds, emitDocCss, emptyDoc, getPath, hasNodeMap, invert, isBinding, lastRootId, migrateDoc, migrateDocVerbose, needsMigration, reduce, reduceAll, resolveValue, rootIds } from './chunk-SJYKJ6SG.js';
|
|
5
5
|
export { fractionalKey, fractionalKey as keyBetween } from '@particle-academy/fancy-doc-commons';
|
|
6
6
|
|
|
7
7
|
// src/document/bindings.ts
|
package/dist/react.cjs
CHANGED
|
@@ -1,26 +1,53 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var react = require('react');
|
|
3
4
|
var fancyDocCommons = require('@particle-academy/fancy-doc-commons');
|
|
4
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
-
var react = require('react');
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
function childrenOf(doc, parent) {
|
|
9
|
-
return fancyDocCommons.childrenOf(doc, parent);
|
|
10
|
-
}
|
|
11
|
-
function rootIds(doc) {
|
|
12
|
-
return childrenOf(doc, null).map((n) => n.id);
|
|
13
|
-
}
|
|
7
|
+
// src/react/RenderNode.tsx
|
|
14
8
|
|
|
15
9
|
// src/document/types.ts
|
|
16
10
|
function isBinding(v) {
|
|
17
11
|
return typeof v === "object" && v !== null && typeof v.$bind === "string";
|
|
18
12
|
}
|
|
13
|
+
function hasNodeMap(value) {
|
|
14
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
15
|
+
const nodes = value.nodes;
|
|
16
|
+
return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
|
|
17
|
+
}
|
|
19
18
|
var DEFAULT_BREAKPOINTS = {
|
|
20
19
|
base: 0,
|
|
21
20
|
md: 768,
|
|
22
21
|
lg: 1024
|
|
23
22
|
};
|
|
23
|
+
function emptyDoc(id, slug = "/") {
|
|
24
|
+
return {
|
|
25
|
+
id,
|
|
26
|
+
seq: 0,
|
|
27
|
+
meta: { title: "Untitled", slug, scrollMode: "smooth" },
|
|
28
|
+
theme: { name: "default" },
|
|
29
|
+
breakpoints: ["base", "md", "lg"],
|
|
30
|
+
nodes: {}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/document/own.ts
|
|
35
|
+
var has = Object.prototype.hasOwnProperty;
|
|
36
|
+
function own(map, key) {
|
|
37
|
+
return map != null && has.call(map, key) ? map[key] : void 0;
|
|
38
|
+
}
|
|
39
|
+
function setOwn(map, key, value) {
|
|
40
|
+
Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/document/reduce.ts
|
|
44
|
+
function childrenOf(doc, parent) {
|
|
45
|
+
return fancyDocCommons.childrenOf(doc, parent);
|
|
46
|
+
}
|
|
47
|
+
function rootIds(doc) {
|
|
48
|
+
if (!hasNodeMap(doc)) return [];
|
|
49
|
+
return childrenOf(doc, null).map((n) => n.id);
|
|
50
|
+
}
|
|
24
51
|
function getPath(data, path) {
|
|
25
52
|
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
|
|
26
53
|
}
|
|
@@ -29,6 +56,48 @@ function resolveValue(v, data) {
|
|
|
29
56
|
if (isBinding(v)) return getPath(data, v.$bind);
|
|
30
57
|
return v;
|
|
31
58
|
}
|
|
59
|
+
var LANDMARK_TAGS = ["section", "header", "nav", "main", "footer", "article", "aside", "div"];
|
|
60
|
+
var LANDMARKS = new Set(LANDMARK_TAGS);
|
|
61
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["stack", "grid", "frame", "shape", "card"]);
|
|
62
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
63
|
+
function safeHref(raw) {
|
|
64
|
+
const href = raw.trim();
|
|
65
|
+
if (!href) return null;
|
|
66
|
+
const probe = href.replace(/[\u0000- \u007f]+/g, "");
|
|
67
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(probe);
|
|
68
|
+
if (scheme && !SAFE_SCHEMES.has(scheme[1].toLowerCase())) return null;
|
|
69
|
+
return href;
|
|
70
|
+
}
|
|
71
|
+
var ANCHOR_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
72
|
+
function anchorFor(node, text) {
|
|
73
|
+
const anchor = text(node.props.anchor);
|
|
74
|
+
return ANCHOR_PATTERN.test(anchor) ? anchor : null;
|
|
75
|
+
}
|
|
76
|
+
function elementFor(node, text) {
|
|
77
|
+
const as = text(node.props.as);
|
|
78
|
+
switch (node.type) {
|
|
79
|
+
case "heading": {
|
|
80
|
+
const level = Number(text(node.props.level) || 2);
|
|
81
|
+
return { tag: Number.isInteger(level) && level >= 1 && level <= 6 ? `h${level}` : "h2" };
|
|
82
|
+
}
|
|
83
|
+
case "text":
|
|
84
|
+
return { tag: "p" };
|
|
85
|
+
case "section":
|
|
86
|
+
return { tag: LANDMARKS.has(as) ? as : "section" };
|
|
87
|
+
case "list":
|
|
88
|
+
return { tag: text(node.props.ordered) === "true" ? "ol" : "ul" };
|
|
89
|
+
case "button":
|
|
90
|
+
case "link": {
|
|
91
|
+
const href = safeHref(text(node.props.href));
|
|
92
|
+
if (href === null) return { tag: node.type === "link" ? "span" : "div" };
|
|
93
|
+
const attrs = { href };
|
|
94
|
+
if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
|
|
95
|
+
return { tag: "a", attrs };
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
32
101
|
var defaultRegistry = {
|
|
33
102
|
section: ({ children }) => children,
|
|
34
103
|
stack: ({ children }) => children,
|
|
@@ -41,7 +110,12 @@ var defaultRegistry = {
|
|
|
41
110
|
// Rich text — inline-formatted HTML (gradient spans, links, <code>, …) that
|
|
42
111
|
// plain `text` can't express. The author owns the markup (it's their content).
|
|
43
112
|
richtext: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: text(node.props.html) } }),
|
|
44
|
-
|
|
113
|
+
// The [data-cms] element is already the <a> (see elementFor), so a link renders its label only.
|
|
114
|
+
link: ({ node, text }) => text(node.props.content) || "link",
|
|
115
|
+
list: ({ node, resolve }) => {
|
|
116
|
+
const items = resolve(node.props.items);
|
|
117
|
+
return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
|
|
118
|
+
},
|
|
45
119
|
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
46
120
|
callout: ({ node, text }) => {
|
|
47
121
|
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[text(node.props.variant)] ?? "#3b82f6";
|
|
@@ -53,15 +127,20 @@ var defaultRegistry = {
|
|
|
53
127
|
const variant = text(node.props.variant) || "primary";
|
|
54
128
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label });
|
|
55
129
|
},
|
|
56
|
-
image: ({ node, text }) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
130
|
+
image: ({ node, text }) => {
|
|
131
|
+
const fit = text(node.props.fit);
|
|
132
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
133
|
+
"img",
|
|
134
|
+
{
|
|
135
|
+
src: text(node.props.src),
|
|
136
|
+
alt: text(node.props.alt),
|
|
137
|
+
loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
|
|
138
|
+
style: { width: "100%", height: "100%", objectFit: IMAGE_FITS.has(fit) ? fit : "cover", display: "block" }
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
}
|
|
64
142
|
};
|
|
143
|
+
var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
|
|
65
144
|
function buttonStyle(variant) {
|
|
66
145
|
const base = {
|
|
67
146
|
display: "inline-flex",
|
|
@@ -80,7 +159,7 @@ function buttonStyle(variant) {
|
|
|
80
159
|
return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
|
|
81
160
|
}
|
|
82
161
|
function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
83
|
-
const node = doc.nodes
|
|
162
|
+
const node = own(doc.nodes, id);
|
|
84
163
|
if (!node) return null;
|
|
85
164
|
if (node.type === "repeater") {
|
|
86
165
|
const arr = resolveValue(node.props.items, data);
|
|
@@ -96,19 +175,37 @@ function RenderNode({ doc, id, registry = defaultRegistry, data }) {
|
|
|
96
175
|
};
|
|
97
176
|
const renderer = registry[node.type];
|
|
98
177
|
const inner = renderer ? renderer({ node, doc, children: kids, data, resolve, text }) : node.island ? null : kids;
|
|
99
|
-
|
|
178
|
+
const { tag, attrs } = elementFor(node, text);
|
|
179
|
+
const anchor = anchorFor(node, text);
|
|
180
|
+
return react.createElement(
|
|
181
|
+
tag,
|
|
182
|
+
{
|
|
183
|
+
"data-cms": node.id,
|
|
184
|
+
"data-cms-island": node.island ? node.type : void 0,
|
|
185
|
+
className: node.className,
|
|
186
|
+
...anchor ? { id: anchor } : {},
|
|
187
|
+
...attrs
|
|
188
|
+
},
|
|
189
|
+
inner
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
function normalizeDoc(input) {
|
|
193
|
+
if (hasNodeMap(input)) return input;
|
|
194
|
+
const id = typeof input === "object" && input !== null && !Array.isArray(input) && typeof input.id === "string" ? input.id : "malformed";
|
|
195
|
+
return emptyDoc(id);
|
|
100
196
|
}
|
|
101
197
|
function migrateDocVerbose(input) {
|
|
102
|
-
const
|
|
198
|
+
const normalized = normalizeDoc(input);
|
|
199
|
+
const legacy = normalized;
|
|
103
200
|
const sections = legacy.sections;
|
|
104
201
|
if (!Array.isArray(sections)) {
|
|
105
|
-
return { doc:
|
|
202
|
+
return { doc: normalized, migrated: false, danglingIds: [], unlistedRootIds: [] };
|
|
106
203
|
}
|
|
107
204
|
const danglingIds = [];
|
|
108
205
|
const ordered = [];
|
|
109
206
|
const seen = /* @__PURE__ */ new Set();
|
|
110
207
|
for (const id of sections) {
|
|
111
|
-
if (!
|
|
208
|
+
if (!own(normalized.nodes, id)) {
|
|
112
209
|
danglingIds.push(id);
|
|
113
210
|
continue;
|
|
114
211
|
}
|
|
@@ -116,13 +213,13 @@ function migrateDocVerbose(input) {
|
|
|
116
213
|
seen.add(id);
|
|
117
214
|
ordered.push(id);
|
|
118
215
|
}
|
|
119
|
-
const unlistedRootIds = Object.values(
|
|
216
|
+
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);
|
|
120
217
|
const finalOrder = [...ordered, ...unlistedRootIds];
|
|
121
|
-
const nodes = { ...
|
|
218
|
+
const nodes = { ...normalized.nodes };
|
|
122
219
|
let prev = null;
|
|
123
220
|
for (const id of finalOrder) {
|
|
124
221
|
const key = fancyDocCommons.fractionalKey(prev, null);
|
|
125
|
-
nodes
|
|
222
|
+
setOwn(nodes, id, { ...own(nodes, id), order: key });
|
|
126
223
|
prev = key;
|
|
127
224
|
}
|
|
128
225
|
const { sections: _dropped, ...rest } = legacy;
|
|
@@ -138,15 +235,36 @@ function migrateDoc(input) {
|
|
|
138
235
|
}
|
|
139
236
|
|
|
140
237
|
// src/render/css.ts
|
|
141
|
-
|
|
238
|
+
function cssString(value) {
|
|
239
|
+
let out = "";
|
|
240
|
+
for (const ch of value) {
|
|
241
|
+
const code = ch.codePointAt(0);
|
|
242
|
+
if (ch === "\\" || ch === '"') out += `\\${ch}`;
|
|
243
|
+
else if (code < 32 || code === 127 || ch === "<") out += `\\${code.toString(16)} `;
|
|
244
|
+
else out += ch;
|
|
245
|
+
}
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
|
|
249
|
+
var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
|
|
142
250
|
function lengthToCss(len) {
|
|
143
|
-
|
|
251
|
+
if (typeof len !== "object" || len === null) return null;
|
|
252
|
+
const { value, unit } = len;
|
|
253
|
+
if (typeof value !== "number" || !Number.isFinite(value) || typeof unit !== "string" || !UNITS.has(unit)) return null;
|
|
254
|
+
return `${value}${unit}`;
|
|
144
255
|
}
|
|
145
256
|
function sizeToCss(size) {
|
|
146
257
|
if (size === "fill") return "100%";
|
|
147
258
|
if (size === "hug") return "auto";
|
|
148
259
|
return lengthToCss(size);
|
|
149
260
|
}
|
|
261
|
+
function numberToCss(value) {
|
|
262
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
263
|
+
}
|
|
264
|
+
function putLen(d, props, len) {
|
|
265
|
+
const css = lengthToCss(len);
|
|
266
|
+
if (css !== null) for (const p of props) d[p] = css;
|
|
267
|
+
}
|
|
150
268
|
var ALIGN = {
|
|
151
269
|
start: "flex-start",
|
|
152
270
|
center: "center",
|
|
@@ -160,51 +278,150 @@ var JUSTIFY = {
|
|
|
160
278
|
between: "space-between",
|
|
161
279
|
around: "space-around"
|
|
162
280
|
};
|
|
281
|
+
function safeCssValue(value) {
|
|
282
|
+
if (/[{}<]/.test(value)) return null;
|
|
283
|
+
let depth = 0;
|
|
284
|
+
let quote = null;
|
|
285
|
+
for (const ch of value) {
|
|
286
|
+
if (quote) {
|
|
287
|
+
if (ch === quote) quote = null;
|
|
288
|
+
} else if (ch === '"' || ch === "'") {
|
|
289
|
+
quote = ch;
|
|
290
|
+
} else if (ch === "(") {
|
|
291
|
+
depth++;
|
|
292
|
+
} else if (ch === ")") {
|
|
293
|
+
depth = Math.max(0, depth - 1);
|
|
294
|
+
} else if (ch === ";" && depth === 0) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
function put(d, prop, value) {
|
|
301
|
+
if (value === void 0) return;
|
|
302
|
+
const safe = safeCssValue(value);
|
|
303
|
+
if (safe !== null) d[prop] = safe;
|
|
304
|
+
}
|
|
305
|
+
var OVERFLOW = /* @__PURE__ */ new Set(["visible", "hidden", "auto"]);
|
|
306
|
+
var TEXT_TRANSFORM = /* @__PURE__ */ new Set(["none", "uppercase", "lowercase", "capitalize"]);
|
|
307
|
+
var BACKGROUND_SIZE = /* @__PURE__ */ new Set(["cover", "contain", "auto"]);
|
|
308
|
+
var TEXT_ALIGN = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
309
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["row", "column"]);
|
|
310
|
+
function keyword(map, value) {
|
|
311
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(map, value) ? map[value] : null;
|
|
312
|
+
}
|
|
313
|
+
function inSet(set, value) {
|
|
314
|
+
return typeof value === "string" && set.has(value) ? value : null;
|
|
315
|
+
}
|
|
163
316
|
function styleDecls(style) {
|
|
164
317
|
const d = {};
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (style.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (style.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (style.
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
318
|
+
putLen(d, ["padding"], style.padding);
|
|
319
|
+
putLen(d, ["padding-left", "padding-right"], style.paddingX);
|
|
320
|
+
putLen(d, ["padding-top", "padding-bottom"], style.paddingY);
|
|
321
|
+
putLen(d, ["margin"], style.margin);
|
|
322
|
+
if (style.marginX === "auto") d["margin-left"] = d["margin-right"] = "auto";
|
|
323
|
+
else putLen(d, ["margin-left", "margin-right"], style.marginX);
|
|
324
|
+
putLen(d, ["margin-top", "margin-bottom"], style.marginY);
|
|
325
|
+
putLen(d, ["border-radius"], style.radius);
|
|
326
|
+
putLen(d, ["max-width"], style.maxWidth);
|
|
327
|
+
putLen(d, ["min-height"], style.minHeight);
|
|
328
|
+
put(d, "aspect-ratio", typeof style.aspectRatio === "string" ? style.aspectRatio : void 0);
|
|
329
|
+
if (style.sticky === true) {
|
|
330
|
+
d["position"] = "sticky";
|
|
331
|
+
d["top"] = "0";
|
|
332
|
+
}
|
|
333
|
+
if (typeof style.zIndex === "number" && Number.isFinite(style.zIndex)) d["z-index"] = String(Math.trunc(style.zIndex));
|
|
334
|
+
const overflow = inSet(OVERFLOW, style.overflow);
|
|
335
|
+
if (overflow) d["overflow"] = overflow;
|
|
336
|
+
put(d, "background", typeof style.background === "string" ? style.background : void 0);
|
|
337
|
+
const backgroundSize = inSet(BACKGROUND_SIZE, style.backgroundSize);
|
|
338
|
+
if (backgroundSize) d["background-size"] = backgroundSize;
|
|
339
|
+
put(d, "background-position", typeof style.backgroundPosition === "string" ? style.backgroundPosition : void 0);
|
|
340
|
+
put(d, "color", typeof style.color === "string" ? style.color : void 0);
|
|
341
|
+
const opacity = numberToCss(style.opacity);
|
|
342
|
+
if (opacity !== null) d["opacity"] = opacity;
|
|
343
|
+
put(d, "font-family", typeof style.fontFamily === "string" ? style.fontFamily : void 0);
|
|
344
|
+
putLen(d, ["font-size"], style.fontSize);
|
|
345
|
+
const fontWeight = numberToCss(style.fontWeight);
|
|
346
|
+
if (fontWeight !== null) d["font-weight"] = fontWeight;
|
|
347
|
+
const lineHeight = numberToCss(style.lineHeight);
|
|
348
|
+
if (lineHeight !== null) d["line-height"] = lineHeight;
|
|
349
|
+
const textAlign = inSet(TEXT_ALIGN, style.textAlign);
|
|
350
|
+
if (textAlign) d["text-align"] = textAlign;
|
|
351
|
+
putLen(d, ["letter-spacing"], style.letterSpacing);
|
|
352
|
+
const textTransform = inSet(TEXT_TRANSFORM, style.textTransform);
|
|
353
|
+
if (textTransform) d["text-transform"] = textTransform;
|
|
354
|
+
put(d, "border", typeof style.border === "string" ? style.border : void 0);
|
|
355
|
+
put(d, "box-shadow", typeof style.boxShadow === "string" ? style.boxShadow : void 0);
|
|
356
|
+
put(d, "transform", typeof style.transform === "string" ? style.transform : void 0);
|
|
357
|
+
put(d, "filter", typeof style.filter === "string" ? style.filter : void 0);
|
|
358
|
+
putLen(d, ["gap"], style.gap);
|
|
359
|
+
const align = keyword(ALIGN, style.align);
|
|
360
|
+
if (align) d["align-items"] = align;
|
|
361
|
+
const justify = keyword(JUSTIFY, style.justify);
|
|
362
|
+
if (justify) d["justify-content"] = justify;
|
|
184
363
|
return d;
|
|
185
364
|
}
|
|
186
|
-
function
|
|
365
|
+
function shownDisplay(node) {
|
|
366
|
+
if (node.layout === "stack") return "flex";
|
|
367
|
+
if (node.layout === "grid") return "grid";
|
|
368
|
+
return "block";
|
|
369
|
+
}
|
|
370
|
+
function gridColumns(style) {
|
|
371
|
+
const min = lengthToCss(style.columnMin);
|
|
372
|
+
if (min !== null) return `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`;
|
|
373
|
+
if (typeof style.columns === "number" && Number.isFinite(style.columns)) return `repeat(${Math.max(1, Math.trunc(style.columns))}, 1fr)`;
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
function containerDecls(node, style, isBase) {
|
|
187
377
|
const d = {};
|
|
188
378
|
if (node.layout === "stack") {
|
|
189
|
-
d["display"] = "flex";
|
|
190
|
-
|
|
379
|
+
if (isBase) d["display"] = "flex";
|
|
380
|
+
const direction = inSet(DIRECTIONS, style.direction);
|
|
381
|
+
if (isBase || direction) d["flex-direction"] = direction ?? "column";
|
|
382
|
+
if (style.wrap !== void 0) d["flex-wrap"] = style.wrap ? "wrap" : "nowrap";
|
|
191
383
|
} else if (node.layout === "grid") {
|
|
192
|
-
d["display"] = "grid";
|
|
193
|
-
|
|
194
|
-
|
|
384
|
+
if (isBase) d["display"] = "grid";
|
|
385
|
+
const cols = gridColumns(style);
|
|
386
|
+
if (isBase || cols !== void 0) d["grid-template-columns"] = cols ?? "repeat(1, 1fr)";
|
|
387
|
+
} else if (node.layout === "free" && isBase) {
|
|
195
388
|
d["position"] = "relative";
|
|
196
389
|
}
|
|
197
390
|
return d;
|
|
198
391
|
}
|
|
392
|
+
function visibilityDecls(node, style, isBase) {
|
|
393
|
+
if (style.hidden === true) return { display: "none" };
|
|
394
|
+
if (style.hidden === false && !isBase) return { display: shownDisplay(node) };
|
|
395
|
+
return {};
|
|
396
|
+
}
|
|
397
|
+
function themeDecls(theme) {
|
|
398
|
+
const d = {};
|
|
399
|
+
const walk = (value, path) => {
|
|
400
|
+
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
|
|
401
|
+
const key = path.map((p) => p.toLowerCase().replace(/[^a-z0-9-]/g, "")).filter(Boolean).join("-");
|
|
402
|
+
if (key) put(d, `--cms-${key}`, String(value));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
406
|
+
for (const [k, v] of Object.entries(value)) walk(v, [...path, k]);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
if (typeof theme === "object" && theme !== null && !Array.isArray(theme)) {
|
|
410
|
+
for (const [k, v] of Object.entries(theme)) {
|
|
411
|
+
if (k !== "name") walk(v, [k]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return d;
|
|
415
|
+
}
|
|
199
416
|
function constraintDecls(c, parentFree) {
|
|
200
417
|
const d = {};
|
|
201
418
|
const transforms = [];
|
|
202
419
|
if (parentFree) {
|
|
203
420
|
d["position"] = "absolute";
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
421
|
+
putLen(d, ["left"], c.left);
|
|
422
|
+
putLen(d, ["right"], c.right);
|
|
423
|
+
putLen(d, ["top"], c.top);
|
|
424
|
+
putLen(d, ["bottom"], c.bottom);
|
|
208
425
|
if (c.centerX) {
|
|
209
426
|
d["left"] = "50%";
|
|
210
427
|
transforms.push("translateX(-50%)");
|
|
@@ -215,13 +432,15 @@ function constraintDecls(c, parentFree) {
|
|
|
215
432
|
}
|
|
216
433
|
if (transforms.length) d["transform"] = transforms.join(" ");
|
|
217
434
|
}
|
|
218
|
-
|
|
219
|
-
if (
|
|
435
|
+
const width = sizeToCss(c.width);
|
|
436
|
+
if (width !== null) d["width"] = width;
|
|
437
|
+
const height = sizeToCss(c.height);
|
|
438
|
+
if (height !== null) d["height"] = height;
|
|
220
439
|
return d;
|
|
221
440
|
}
|
|
222
441
|
function isParentFree(doc, node) {
|
|
223
442
|
if (node.parent === null) return false;
|
|
224
|
-
const parent = doc.nodes
|
|
443
|
+
const parent = own(doc.nodes, node.parent);
|
|
225
444
|
return !parent || parent.layout === void 0 || parent.layout === "free";
|
|
226
445
|
}
|
|
227
446
|
function serializeRule(selector, decls, indent) {
|
|
@@ -233,16 +452,20 @@ ${body}
|
|
|
233
452
|
${indent}}`;
|
|
234
453
|
}
|
|
235
454
|
function emitDocCss(doc) {
|
|
455
|
+
if (!hasNodeMap(doc)) return "";
|
|
236
456
|
const breakpointPx = DEFAULT_BREAKPOINTS;
|
|
237
457
|
const blocks = [];
|
|
458
|
+
const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
|
|
459
|
+
if (themeRule) blocks.push(themeRule);
|
|
238
460
|
for (const id of Object.keys(doc.nodes).sort()) {
|
|
239
461
|
const node = doc.nodes[id];
|
|
240
462
|
const selector = selectorFor(id);
|
|
241
463
|
const parentFree = isParentFree(doc, node);
|
|
242
464
|
const base = {
|
|
243
|
-
...
|
|
465
|
+
...containerDecls(node, node.style.base, true),
|
|
244
466
|
...node.constraints ? constraintDecls(node.constraints.base, parentFree) : {},
|
|
245
|
-
...styleDecls(node.style.base)
|
|
467
|
+
...styleDecls(node.style.base),
|
|
468
|
+
...visibilityDecls(node, node.style.base, true)
|
|
246
469
|
};
|
|
247
470
|
const baseRule = serializeRule(selector, base, "");
|
|
248
471
|
if (baseRule) blocks.push(baseRule);
|
|
@@ -250,9 +473,12 @@ function emitDocCss(doc) {
|
|
|
250
473
|
if (bp === "base") continue;
|
|
251
474
|
const px = breakpointPx[bp];
|
|
252
475
|
if (px === void 0) continue;
|
|
476
|
+
const bpStyle = node.style[bp];
|
|
253
477
|
const decls = {
|
|
478
|
+
...bpStyle ? containerDecls(node, bpStyle, false) : {},
|
|
254
479
|
...node.constraints?.[bp] ? constraintDecls(node.constraints[bp], parentFree) : {},
|
|
255
|
-
...
|
|
480
|
+
...bpStyle ? styleDecls(bpStyle) : {},
|
|
481
|
+
...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
|
|
256
482
|
};
|
|
257
483
|
const inner = serializeRule(selector, decls, " ");
|
|
258
484
|
if (inner) blocks.push(`@media (min-width: ${px}px) {
|