@unlayer/react-elements 0.1.22 → 0.2.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/README.md +43 -2
- package/dist/index.cjs +1367 -478
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -9
- package/dist/index.d.ts +48 -9
- package/dist/index.js +1341 -471
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -1,9 +1,349 @@
|
|
|
1
|
-
import { ButtonDefaults, DividerDefaults, HeadingDefaults, HtmlDefaults, ImageDefaults, MenuDefaults, ParagraphDefaults, SocialDefaults, TableDefaults, VideoDefaults, BodyDefaults, RowDefaults, ColumnDefaults, ButtonExporters, DividerExporters, HeadingExporters, HtmlExporters, ImageExporters, MenuExporters, ParagraphExporters, SocialExporters, TableExporters, VideoExporters, schemaVersion as schemaVersion$1,
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { ButtonDefaults, DividerDefaults, HeadingDefaults, HtmlDefaults, ImageDefaults, MenuDefaults, ParagraphDefaults, SocialDefaults, TableDefaults, VideoDefaults, BodyDefaults, RowDefaults, ColumnDefaults, RowExporters, ContentExporters, ColumnExporters, ButtonExporters, DividerExporters, HeadingExporters, HtmlExporters, ImageExporters, MenuExporters, ParagraphExporters, SocialExporters, TableExporters, VideoExporters, BodyExporters, schemaVersion as schemaVersion$1, heads } from '@unlayer/exporters';
|
|
2
|
+
import React4, { Suspense, useMemo, useContext, createContext } from 'react';
|
|
3
|
+
import * as defaultBuild from 'react-dom/server';
|
|
4
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/components/Button.tsx
|
|
7
|
+
var SHARED = Symbol.for("@unlayer/react-elements/collecting");
|
|
8
|
+
var frames = globalThis[SHARED] ?? (globalThis[SHARED] = []);
|
|
9
|
+
function collecting() {
|
|
10
|
+
return frames[frames.length - 1] ?? void 0;
|
|
11
|
+
}
|
|
12
|
+
function within(frame, run) {
|
|
13
|
+
frames.push(frame);
|
|
14
|
+
try {
|
|
15
|
+
return run();
|
|
16
|
+
} finally {
|
|
17
|
+
frames.pop();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var MARK = "unlayer-mark";
|
|
21
|
+
var RECORDS = Symbol.for("@unlayer/react-elements/records");
|
|
22
|
+
var COLLECTING_ID = "unlayer-collect-";
|
|
23
|
+
function marker(collection, at, edge) {
|
|
24
|
+
return React4.createElement(MARK, { key: `${at}${edge ?? ""}`, "data-n": collection.nonce, "data-i": at, ...edge ? { "data-e": edge } : {} });
|
|
25
|
+
}
|
|
26
|
+
var SHARED2 = Symbol.for("@unlayer/react-elements/render-builds");
|
|
27
|
+
var using = globalThis[SHARED2] ?? (globalThis[SHARED2] = []);
|
|
28
|
+
var same = (a, b) => a.renderToString === b.renderToString;
|
|
29
|
+
function dispatcher() {
|
|
30
|
+
const internals = React4;
|
|
31
|
+
return internals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE?.H ?? internals.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentDispatcher?.current ?? null;
|
|
32
|
+
}
|
|
33
|
+
var dispatchers = /* @__PURE__ */ new Map();
|
|
34
|
+
function dispatcherOf(build) {
|
|
35
|
+
if (!dispatchers.has(build)) {
|
|
36
|
+
let seen = null;
|
|
37
|
+
try {
|
|
38
|
+
class Probe extends React4.Component {
|
|
39
|
+
render() {
|
|
40
|
+
seen = dispatcher();
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
build.renderToStaticMarkup(React4.createElement(Probe));
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
dispatchers.set(build, seen);
|
|
48
|
+
}
|
|
49
|
+
return dispatchers.get(build);
|
|
50
|
+
}
|
|
51
|
+
var second;
|
|
52
|
+
function secondBuild(outer) {
|
|
53
|
+
if (second === void 0) {
|
|
54
|
+
second = null;
|
|
55
|
+
const node = globalThis.process;
|
|
56
|
+
const requirers = [
|
|
57
|
+
// (`module`'s, read so that a bundler doesn't take it for a require of a path worked out when it runs.)
|
|
58
|
+
() => {
|
|
59
|
+
return void 0;
|
|
60
|
+
},
|
|
61
|
+
// From this module's URL (as a stack names it: webpack 4 can't parse the syntax for it), the app's script, then
|
|
62
|
+
// the folder Node started in; only where `react` is this React. Another React's isn't loaded (React 19's browser
|
|
63
|
+
// build keeps Node running).
|
|
64
|
+
...[/file:\/\/\S+?(?=:\d+:\d+)/.exec(String(new Error().stack))?.[0], node?.argv?.[1], `${node?.cwd?.()}/`].map((from) => () => {
|
|
65
|
+
const required = node?.getBuiltinModule?.("module")?.createRequire(from);
|
|
66
|
+
return required?.("react")?.createElement === React4.createElement ? required : void 0;
|
|
67
|
+
})
|
|
68
|
+
];
|
|
69
|
+
const ids = ["react-dom/server.bun", ...Number.parseInt(React4.version) >= 19 ? [] : ["react-dom/server.browser"], "react-dom/server.node"];
|
|
70
|
+
search: for (const id of ids) {
|
|
71
|
+
for (const requirer of requirers) {
|
|
72
|
+
try {
|
|
73
|
+
const build = requirer()?.(id);
|
|
74
|
+
if (build?.renderToStaticMarkup && !same(build, defaultBuild) && ![null, outer].includes(dispatcherOf(build))) {
|
|
75
|
+
second = build;
|
|
76
|
+
break search;
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return second ?? void 0;
|
|
84
|
+
}
|
|
85
|
+
var buildOf = (outer) => outer === null ? void 0 : [defaultBuild, second].find((b) => !!b && dispatcherOf(b) === outer);
|
|
86
|
+
function withBuild(render, own = false) {
|
|
87
|
+
const outer = own ? null : dispatcher();
|
|
88
|
+
let build = current();
|
|
89
|
+
const theirs = !own && (using.length === 0 || collecting() !== void 0) ? buildOf(outer) : void 0;
|
|
90
|
+
const busy = [];
|
|
91
|
+
if (theirs) {
|
|
92
|
+
const free = (b) => !same(b, theirs) && !using.some((u) => same(u, b));
|
|
93
|
+
const other = free(defaultBuild) ? defaultBuild : secondBuild(outer);
|
|
94
|
+
if (other && free(other)) {
|
|
95
|
+
busy.push(theirs);
|
|
96
|
+
build = other;
|
|
97
|
+
} else build = theirs;
|
|
98
|
+
}
|
|
99
|
+
using.push(...busy, build);
|
|
100
|
+
try {
|
|
101
|
+
return render();
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (build !== theirs && !rendersHere(build)) throw Object.assign(new Error("[Unlayer] react-dom can't render with this React (React Server Components', as in Next.js's edge runtime): render emails in the Node.js runtime."), { cause: error });
|
|
104
|
+
throw error;
|
|
105
|
+
} finally {
|
|
106
|
+
using.length -= busy.length + 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function rendersHere(build) {
|
|
110
|
+
try {
|
|
111
|
+
build.renderToStaticMarkup(React4.createElement(() => null));
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function current() {
|
|
118
|
+
return using[using.length - 1] ?? defaultBuild;
|
|
119
|
+
}
|
|
120
|
+
var renderToStaticMarkup = (element2, options) => current().renderToStaticMarkup(element2, options);
|
|
121
|
+
var renderToString = (element2, options) => current().renderToString(element2, options);
|
|
122
|
+
|
|
123
|
+
// src/utils/unwrap-root.ts
|
|
124
|
+
var ROOT_NAMES = /* @__PURE__ */ new Set(["Body", "Email", "Page", "Document"]);
|
|
125
|
+
var CONTAINER_NAMES = /* @__PURE__ */ new Set([...ROOT_NAMES, "Row", "Column"]);
|
|
126
|
+
var MEMO = Symbol.for("react.memo");
|
|
127
|
+
var FORWARD_REF = Symbol.for("react.forward_ref");
|
|
128
|
+
var PROVIDER = Symbol.for("react.provider");
|
|
129
|
+
var CONTEXT = Symbol.for("react.context");
|
|
130
|
+
function getDisplayName(element2) {
|
|
131
|
+
return element2.type?.displayName;
|
|
132
|
+
}
|
|
133
|
+
function componentName(element2) {
|
|
134
|
+
const type = element2.type;
|
|
135
|
+
return type?.displayName || type?.name;
|
|
136
|
+
}
|
|
137
|
+
function isElementsType(type) {
|
|
138
|
+
return !!type?.[UNLAYER_RENDER_KEY] || CONTAINER_NAMES.has(type?.displayName);
|
|
139
|
+
}
|
|
140
|
+
function isElementsComponent(element2) {
|
|
141
|
+
return isElementsType(element2.type);
|
|
142
|
+
}
|
|
143
|
+
var ASYNC_TEMPLATE = "async and suspending templates aren't supported: load the data first and pass it as props";
|
|
144
|
+
var isThenable = (value) => typeof value?.then === "function";
|
|
145
|
+
var settle = (value) => {
|
|
146
|
+
if (isThenable(value)) value.then(void 0, () => {
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
function isAsyncComponent(type) {
|
|
150
|
+
while (type?.$$typeof === MEMO) type = type.type;
|
|
151
|
+
return (type?.$$typeof === FORWARD_REF ? type.render : type)?.[Symbol.toStringTag] === "AsyncFunction";
|
|
152
|
+
}
|
|
153
|
+
function callIsolated(call, prefix, providers = []) {
|
|
154
|
+
let result;
|
|
155
|
+
let error;
|
|
156
|
+
let ran = false;
|
|
157
|
+
let threw = false;
|
|
158
|
+
let calling = false;
|
|
159
|
+
const Probe = () => {
|
|
160
|
+
if (calling) return null;
|
|
161
|
+
ran = calling = true;
|
|
162
|
+
threw = false;
|
|
163
|
+
try {
|
|
164
|
+
result = call();
|
|
165
|
+
} catch (cause) {
|
|
166
|
+
threw = true;
|
|
167
|
+
error = cause;
|
|
168
|
+
} finally {
|
|
169
|
+
calling = false;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
};
|
|
173
|
+
const probe = providers.reduceRight((inside, provider) => React4.cloneElement(provider, void 0, inside), React4.createElement(Probe));
|
|
174
|
+
renderToStaticMarkup(probe, prefix ? { identifierPrefix: prefix } : void 0);
|
|
175
|
+
settle(error);
|
|
176
|
+
settle(result);
|
|
177
|
+
if (threw) throw isThenable(error) ? new Error(ASYNC_TEMPLATE) : error;
|
|
178
|
+
if (!ran) throw new Error("the template wasn't rendered");
|
|
179
|
+
if (isThenable(result)) throw new Error(ASYNC_TEMPLATE);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
function unwrapRoot(element2, why = {}) {
|
|
183
|
+
let current2 = element2;
|
|
184
|
+
const providers = [];
|
|
185
|
+
for (let depth = 0; ; depth++) {
|
|
186
|
+
if (ROOT_NAMES.has(getDisplayName(current2) ?? "")) break;
|
|
187
|
+
if (elementsOwn(current2)) break;
|
|
188
|
+
if (depth === 20) {
|
|
189
|
+
why.unread = "it wraps more than 20 components";
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
const type = current2.type;
|
|
193
|
+
const props = current2.props;
|
|
194
|
+
if (type?.$$typeof === MEMO) {
|
|
195
|
+
current2 = React4.createElement(type.type, props);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if ((type?.$$typeof === PROVIDER || type?.$$typeof === CONTEXT) && "value" in props) {
|
|
199
|
+
const inside = React4.Children.toArray(props.children);
|
|
200
|
+
if (inside.length !== 1 || !React4.isValidElement(inside[0])) break;
|
|
201
|
+
providers.push(current2);
|
|
202
|
+
current2 = inside[0];
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (type === React4.Fragment || type === React4.StrictMode || type === React4.Profiler) {
|
|
206
|
+
const inside = React4.Children.toArray(props.children);
|
|
207
|
+
if (inside.length !== 1 || !React4.isValidElement(inside[0])) break;
|
|
208
|
+
current2 = inside[0];
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
let produced;
|
|
212
|
+
const prefix = depth ? `w${depth}-` : void 0;
|
|
213
|
+
if (type?.$$typeof === FORWARD_REF) produced = callIsolated(() => type.render({ ...props }, null), prefix, providers);
|
|
214
|
+
else if (typeof type === "function" && !type.prototype?.isReactComponent) produced = callIsolated(() => type({ ...props }), prefix, providers);
|
|
215
|
+
else {
|
|
216
|
+
if (type?.prototype?.isReactComponent) why.unread = `<${componentName(current2)}> is a class component, which isn't called to find it`;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
if (!React4.isValidElement(produced)) {
|
|
220
|
+
if (holdsRoot(produced)) why.unread = "it doesn't return a root element";
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
current2 = produced;
|
|
224
|
+
}
|
|
225
|
+
if (!providers.length || !ROOT_NAMES.has(getDisplayName(current2) ?? "")) return current2;
|
|
226
|
+
const children = providers.reduceRight((inside, provider) => React4.cloneElement(provider, void 0, inside), current2.props.children);
|
|
227
|
+
return React4.cloneElement(current2, void 0, children);
|
|
228
|
+
}
|
|
229
|
+
var elementsOwn = (element2) => {
|
|
230
|
+
const type = element2.type;
|
|
231
|
+
return !!(type?.[UNLAYER_RENDER_KEY] || type?.[RECORDS]);
|
|
232
|
+
};
|
|
233
|
+
function holdsRoot(node) {
|
|
234
|
+
if (Array.isArray(node)) return node.some(holdsRoot);
|
|
235
|
+
if (!React4.isValidElement(node)) return false;
|
|
236
|
+
return ROOT_NAMES.has(getDisplayName(node) ?? "") || !elementsOwn(node) && holdsRoot(node.props.children);
|
|
237
|
+
}
|
|
238
|
+
var UNWRAP_ADVICE = "Pass the root element (<Email>\u2026</Email>), or a function component that returns one.";
|
|
239
|
+
function htmlRoot(element2, api) {
|
|
240
|
+
if (typeof element2.type === "string" || isElementsComponent(element2)) return element2;
|
|
241
|
+
let reason;
|
|
242
|
+
try {
|
|
243
|
+
const why = {};
|
|
244
|
+
const root = unwrapRoot(element2, why);
|
|
245
|
+
if (ROOT_NAMES.has(getDisplayName(root) ?? "")) return root;
|
|
246
|
+
if (!why.unread && !holdsRoot(root)) return element2;
|
|
247
|
+
reason = why.unread ?? "its root is inside other markup";
|
|
248
|
+
} catch (cause) {
|
|
249
|
+
if (cause instanceof Error && cause.message === ASYNC_TEMPLATE) throw new Error(`[Unlayer] ${api}: ${ASYNC_TEMPLATE}.`);
|
|
250
|
+
reason = cause instanceof Error ? cause.message : String(cause);
|
|
251
|
+
}
|
|
252
|
+
console.warn(`[Unlayer] ${api}: couldn't unwrap <${componentName(element2) || "wrapper"}> (${reason}): its root's fonts, lang, dir and phone styles are ignored. ${UNWRAP_ADVICE}`);
|
|
253
|
+
return element2;
|
|
254
|
+
}
|
|
255
|
+
function guard(children, collection, inContainer = false) {
|
|
256
|
+
return React4.Children.toArray(children).map((child) => {
|
|
257
|
+
const placed = guarded(child, collection, inContainer);
|
|
258
|
+
return placed === child && React4.isValidElement(child) ? React4.cloneElement(child) : placed;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function guarded(child, collection, inContainer) {
|
|
262
|
+
if (!React4.isValidElement(child)) return child;
|
|
263
|
+
if (isElementsType(child.type)) {
|
|
264
|
+
if (child.type[RECORDS]) return child;
|
|
265
|
+
if (!olderContainer(child.type)) return bounded(child, collection);
|
|
266
|
+
return recordedForOlder(child, collection, inContainer);
|
|
267
|
+
}
|
|
268
|
+
if (!child.type || typeof child.type === "object" && !("$$typeof" in child.type)) return child;
|
|
269
|
+
if (typeof child.type === "string") {
|
|
270
|
+
const inside = child.props.children;
|
|
271
|
+
const element2 = inside !== void 0 && hasComponent(inside) ? React4.cloneElement(child, void 0, guard(inside, collection, inContainer)) : child;
|
|
272
|
+
return collection.markup ? bounded(element2, collection) : element2;
|
|
273
|
+
}
|
|
274
|
+
if (child.type === React4.Fragment) {
|
|
275
|
+
return React4.createElement(React4.Fragment, { key: child.key }, guard(child.props.children, collection, inContainer));
|
|
276
|
+
}
|
|
277
|
+
return bounded(child, collection);
|
|
278
|
+
}
|
|
279
|
+
function bounded(child, collection) {
|
|
280
|
+
const at = collection.recorded.push({ kind: "boundary", element: child }) - 1;
|
|
281
|
+
if (isAsyncComponent(child.type)) return marker(collection, at, "f");
|
|
282
|
+
return React4.createElement(
|
|
283
|
+
Suspense,
|
|
284
|
+
{ key: child.key ?? `b${at}`, fallback: marker(collection, at, "f") },
|
|
285
|
+
marker(collection, at, "o"),
|
|
286
|
+
child,
|
|
287
|
+
marker(collection, at, "c")
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
function olderContainer(type) {
|
|
291
|
+
return !/^(Row|Column)$/.test(type?.displayName ?? "") || /\b_config\b/.test(String(type));
|
|
292
|
+
}
|
|
293
|
+
function recordedForOlder(child, collection, inContainer) {
|
|
294
|
+
const type = child.type;
|
|
295
|
+
const props = child.props;
|
|
296
|
+
if (type.displayName === "Row" || type.displayName === "Column") {
|
|
297
|
+
const at2 = collection.recorded.push({ kind: type.displayName === "Row" ? "row" : "column", props }) - 1;
|
|
298
|
+
return /* @__PURE__ */ jsxs(React4.Fragment, { children: [
|
|
299
|
+
marker(collection, at2, "o"),
|
|
300
|
+
guard(props.children, collection, true),
|
|
301
|
+
marker(collection, at2, "c")
|
|
302
|
+
] }, child.key ?? `r${at2}`);
|
|
303
|
+
}
|
|
304
|
+
if (!type.__unlayerRender && !inContainer) return child;
|
|
305
|
+
if (!type.__unlayerRender) {
|
|
306
|
+
console.warn(`[Unlayer] <${type.displayName}> is a root: it can't be placed in a Row or Column, so it's left out.`);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
const at = collection.recorded.push({ kind: "block", type: child.type, props }) - 1;
|
|
310
|
+
return React4.cloneElement(marker(collection, at), { key: child.key ?? `b${at}` });
|
|
311
|
+
}
|
|
312
|
+
function hasComponent(children) {
|
|
313
|
+
return React4.Children.toArray(children).some(
|
|
314
|
+
(child) => React4.isValidElement(child) && (typeof child.type === "string" || child.type === React4.Fragment ? hasComponent(child.props.children) : !isElementsType(child.type))
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
function Sentinel({ record, real }) {
|
|
318
|
+
let collected2 = !!collecting()?.blind;
|
|
319
|
+
if (!collected2) {
|
|
320
|
+
try {
|
|
321
|
+
collected2 = React4.useId().slice(1).startsWith(COLLECTING_ID);
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return collected2 ? record() : within(null, real);
|
|
326
|
+
}
|
|
327
|
+
var named = /* @__PURE__ */ new Map();
|
|
328
|
+
function whileCollecting(record, real, name) {
|
|
329
|
+
let type = named.get(name);
|
|
330
|
+
if (!type) named.set(name, type = Object.assign((props) => Sentinel(props), { displayName: name }));
|
|
331
|
+
return React4.createElement(type, { record, real });
|
|
332
|
+
}
|
|
333
|
+
function recordContainer(kind, props) {
|
|
334
|
+
const collection = collecting();
|
|
335
|
+
const at = collection.recorded.push({ kind, props }) - 1;
|
|
336
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
337
|
+
marker(collection, at, "o"),
|
|
338
|
+
guard(props.children, collection, true),
|
|
339
|
+
marker(collection, at, "c")
|
|
340
|
+
] });
|
|
341
|
+
}
|
|
342
|
+
function recordBlock(type, props) {
|
|
343
|
+
const collection = collecting();
|
|
344
|
+
const at = collection.recorded.push({ kind: "block", type, props }) - 1;
|
|
345
|
+
return marker(collection, at);
|
|
346
|
+
}
|
|
7
347
|
|
|
8
348
|
// src/utils/image-sizing.ts
|
|
9
349
|
function toPx(value) {
|
|
@@ -15,11 +355,11 @@ function toPx(value) {
|
|
|
15
355
|
function edges(value) {
|
|
16
356
|
if (value == null) return { left: 0, right: 0 };
|
|
17
357
|
if (typeof value === "number") return { left: value, right: value };
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
return { left:
|
|
22
|
-
return { left:
|
|
358
|
+
const parts2 = String(value).trim().split(/\s+/).map((p) => parseFloat(p) || 0);
|
|
359
|
+
if (parts2.length === 1) return { left: parts2[0], right: parts2[0] };
|
|
360
|
+
if (parts2.length === 2 || parts2.length === 3)
|
|
361
|
+
return { left: parts2[1], right: parts2[1] };
|
|
362
|
+
return { left: parts2[3] || 0, right: parts2[1] || 0 };
|
|
23
363
|
}
|
|
24
364
|
function borderEdges(border) {
|
|
25
365
|
if (!border || typeof border !== "object") return { left: 0, right: 0 };
|
|
@@ -209,7 +549,7 @@ function paragraphAlign(format) {
|
|
|
209
549
|
}
|
|
210
550
|
return void 0;
|
|
211
551
|
}
|
|
212
|
-
function paragraphNodeToHtml(node,
|
|
552
|
+
function paragraphNodeToHtml(node, childrenHtml22) {
|
|
213
553
|
const isInlineTool = !!node.isInlineTool;
|
|
214
554
|
const tag = isInlineTool ? "span" : "p";
|
|
215
555
|
const dir = dirAttribute(node.direction);
|
|
@@ -233,7 +573,7 @@ function paragraphNodeToHtml(node, childrenHtml) {
|
|
|
233
573
|
styleParts.push(`text-align: ${align}`);
|
|
234
574
|
}
|
|
235
575
|
const style = styleParts.length > 0 ? ` style="${escapeHtml(styleParts.join("; ") + ";")}"` : "";
|
|
236
|
-
return `<${tag}${dir}${style}>${
|
|
576
|
+
return `<${tag}${dir}${style}>${childrenHtml22 || "<br>"}</${tag}>`;
|
|
237
577
|
}
|
|
238
578
|
function nodeToHtml(node) {
|
|
239
579
|
if (!node || typeof node !== "object") return "";
|
|
@@ -244,32 +584,32 @@ function nodeToHtml(node) {
|
|
|
244
584
|
if (type === "linebreak") {
|
|
245
585
|
return "<br>";
|
|
246
586
|
}
|
|
247
|
-
const
|
|
587
|
+
const childrenHtml22 = (node.children || []).map(nodeToHtml).join("");
|
|
248
588
|
switch (type) {
|
|
249
589
|
case "root":
|
|
250
|
-
return
|
|
590
|
+
return childrenHtml22;
|
|
251
591
|
case "paragraph":
|
|
252
592
|
case "extended-paragraph":
|
|
253
|
-
return paragraphNodeToHtml(node,
|
|
593
|
+
return paragraphNodeToHtml(node, childrenHtml22);
|
|
254
594
|
case "heading": {
|
|
255
595
|
const tag = /^h[1-6]$/.test(node.tag) ? node.tag : "h1";
|
|
256
596
|
const dir = dirAttribute(node.direction);
|
|
257
|
-
return `<${tag}${dir}>${
|
|
597
|
+
return `<${tag}${dir}>${childrenHtml22}</${tag}>`;
|
|
258
598
|
}
|
|
259
599
|
case "link": {
|
|
260
600
|
const href = node.url ? ` href="${escapeHtml(node.url)}"` : "";
|
|
261
601
|
const target = node.target ? ` target="${escapeHtml(node.target)}"` : "";
|
|
262
602
|
const rel = node.rel ? ` rel="${escapeHtml(node.rel)}"` : "";
|
|
263
|
-
return `<a${href}${target}${rel}>${
|
|
603
|
+
return `<a${href}${target}${rel}>${childrenHtml22}</a>`;
|
|
264
604
|
}
|
|
265
605
|
case "list": {
|
|
266
606
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
267
|
-
return `<${tag}>${
|
|
607
|
+
return `<${tag}>${childrenHtml22}</${tag}>`;
|
|
268
608
|
}
|
|
269
609
|
case "listitem":
|
|
270
|
-
return `<li>${
|
|
610
|
+
return `<li>${childrenHtml22}</li>`;
|
|
271
611
|
default:
|
|
272
|
-
return
|
|
612
|
+
return childrenHtml22;
|
|
273
613
|
}
|
|
274
614
|
}
|
|
275
615
|
function generateHtmlFromTextJson(textJson) {
|
|
@@ -300,6 +640,57 @@ function mergeValues(defaults, userValues) {
|
|
|
300
640
|
}
|
|
301
641
|
return result;
|
|
302
642
|
}
|
|
643
|
+
var CHANNEL = /^-?(?:\d+(?:\.\d*)?|\.\d+)%?$/;
|
|
644
|
+
function normalizeColor(value) {
|
|
645
|
+
const text = value.trim();
|
|
646
|
+
const open = text.indexOf("(");
|
|
647
|
+
if (!/^rgba?$/i.test(text.slice(0, open)) || !text.endsWith(")")) return value;
|
|
648
|
+
const [rgb, slashed, extra] = text.slice(open + 1, -1).split("/");
|
|
649
|
+
const channels = rgb.trim().split(/\s*,\s*|\s+/);
|
|
650
|
+
if (slashed !== void 0) channels.push(slashed.trim());
|
|
651
|
+
if (extra !== void 0 || channels.length > 4 || channels.length < (slashed === void 0 ? 3 : 4) || !channels.every((c) => CHANNEL.test(c))) return value;
|
|
652
|
+
const num = (text2, full) => text2.endsWith("%") ? parseFloat(text2) / 100 * full : parseFloat(text2);
|
|
653
|
+
const [r, g, b] = channels.slice(0, 3).map((text2) => Math.max(0, Math.min(255, Math.round(num(text2, 255)))));
|
|
654
|
+
const alpha = channels[3] === void 0 ? 1 : num(channels[3], 1);
|
|
655
|
+
if (alpha < 1) return `rgba(${r}, ${g}, ${b}, ${Math.max(0, alpha)})`;
|
|
656
|
+
return `#${[r, g, b].map((n) => n.toString(16).padStart(2, "0")).join("")}`;
|
|
657
|
+
}
|
|
658
|
+
function cssValueSafe(text) {
|
|
659
|
+
if (/[<>{};]|\/\*|\*\//.test(text)) return false;
|
|
660
|
+
const open = [];
|
|
661
|
+
for (const c of text) {
|
|
662
|
+
if (c === "(" || c === "[") open.push(c === "(" ? ")" : "]");
|
|
663
|
+
else if ((c === ")" || c === "]") && open.pop() !== c) return false;
|
|
664
|
+
}
|
|
665
|
+
return !open.length;
|
|
666
|
+
}
|
|
667
|
+
function cssSafeValues(value) {
|
|
668
|
+
if (typeof value === "string") return cssValueSafe(value) ? value : void 0;
|
|
669
|
+
if (Array.isArray(value)) return value.map(cssSafeValues);
|
|
670
|
+
if (!isPlainObject(value)) return value;
|
|
671
|
+
const out = {};
|
|
672
|
+
for (const [k, v] of Object.entries(value)) {
|
|
673
|
+
const safe = cssSafeValues(v);
|
|
674
|
+
if (safe !== void 0 || v === void 0) out[k] = safe;
|
|
675
|
+
}
|
|
676
|
+
return out;
|
|
677
|
+
}
|
|
678
|
+
function normalizeFontStack(value) {
|
|
679
|
+
return value.replace(/"([^"]*)"?/g, (_, name) => `'${name.replace(/'/g, "\\'")}'`);
|
|
680
|
+
}
|
|
681
|
+
function normalizeCssValues(value, key) {
|
|
682
|
+
if (typeof value === "string") return key === "fontFamily" ? normalizeFontStack(value) : key === "color" || key?.endsWith("Color") ? normalizeColor(value) : value;
|
|
683
|
+
if (Array.isArray(value)) return value.map((item) => normalizeCssValues(item));
|
|
684
|
+
if (!isPlainObject(value)) return value;
|
|
685
|
+
const out = {};
|
|
686
|
+
for (const [k, v] of Object.entries(value)) out[k] = normalizeCssValues(v, key === "fontFamily" ? key : k);
|
|
687
|
+
return out;
|
|
688
|
+
}
|
|
689
|
+
function isPlainObject(value) {
|
|
690
|
+
if (value === null || typeof value !== "object") return false;
|
|
691
|
+
const proto = Object.getPrototypeOf(value);
|
|
692
|
+
return proto === Object.prototype || proto === null;
|
|
693
|
+
}
|
|
303
694
|
var nestedStructureCache = /* @__PURE__ */ new WeakMap();
|
|
304
695
|
function analyzeNestedStructure(defaultValues) {
|
|
305
696
|
const cached = nestedStructureCache.get(defaultValues);
|
|
@@ -344,24 +735,39 @@ function normalizeCssProps(props) {
|
|
|
344
735
|
}
|
|
345
736
|
}
|
|
346
737
|
}
|
|
347
|
-
|
|
738
|
+
var ENTITIES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
739
|
+
function escapeText(text, quotes) {
|
|
740
|
+
return text.replace(quotes ? /[&<>"']/g : /[&<>]/g, (c) => ENTITIES[c]);
|
|
741
|
+
}
|
|
742
|
+
function escapeKeepingTags(text, quotes, escape = (part) => escapeText(part, quotes)) {
|
|
743
|
+
return text.split(/(<%[\w.]+%>)/).map((part, i) => i % 2 ? part : escape(part)).join("");
|
|
744
|
+
}
|
|
745
|
+
var menuText = (text) => escapeKeepingTags(text, false, (part) => part.replace(/&(?!(?:#\d+|#x[\da-f]+|[a-z][\da-z]*);)|[<>"]/gi, (c) => ENTITIES[c]));
|
|
746
|
+
function flattenChildrenText(node, breaks) {
|
|
348
747
|
if (node == null || typeof node === "boolean") return "";
|
|
349
748
|
if (typeof node === "string") return node;
|
|
350
749
|
if (typeof node === "number") return String(node);
|
|
351
|
-
if (Array.isArray(node)) return node.map(flattenChildrenText).join("");
|
|
750
|
+
if (Array.isArray(node)) return node.map((child) => flattenChildrenText(child, breaks)).join("");
|
|
352
751
|
if (typeof node === "object" && "props" in node) {
|
|
353
|
-
return flattenChildrenText(node.props?.children);
|
|
752
|
+
return breaks && node.type === "br" ? "<br/>" : flattenChildrenText(node.props?.children, breaks);
|
|
354
753
|
}
|
|
355
754
|
return "";
|
|
356
755
|
}
|
|
756
|
+
function childrenHtml(children) {
|
|
757
|
+
return flattenChildrenText(children, true).split(/(<br\s*\/?>)/i).map((part, i) => i % 2 ? "<br/>" : escapeKeepingTags(part)).join("");
|
|
758
|
+
}
|
|
357
759
|
function mapSemanticProps(props, defaultValues, componentType) {
|
|
358
|
-
const { children, values, ...restProps } = props;
|
|
760
|
+
const { children, values, mobile, hideOnMobile: hideMobileProp, hideOnDesktop: hideDesktopProp, ...restProps } = props;
|
|
761
|
+
const hideOnMobile = componentType === "Column" ? void 0 : hideMobileProp;
|
|
762
|
+
const hideOnDesktop = componentType === "Column" ? void 0 : hideDesktopProp;
|
|
359
763
|
const userProps = { ...restProps };
|
|
360
764
|
const result = values ? { ...values } : {};
|
|
361
765
|
if (children !== void 0 && !result.text && !result.textJson) {
|
|
362
766
|
const textContent = typeof children === "string" ? children : flattenChildrenText(children);
|
|
363
767
|
if (componentType === "Paragraph") {
|
|
364
768
|
result.textJson = textToTextJson(textContent);
|
|
769
|
+
} else if (componentType === "Heading" || componentType === "Button") {
|
|
770
|
+
result.text = childrenHtml(children);
|
|
365
771
|
} else {
|
|
366
772
|
result.text = textContent;
|
|
367
773
|
}
|
|
@@ -439,15 +845,15 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
439
845
|
}
|
|
440
846
|
if (defaultValues && typeof defaultValues === "object" && "border" in defaultValues) {
|
|
441
847
|
const borderSideRe = /^border(Top|Right|Bottom|Left)(Width|Style|Color)$/;
|
|
442
|
-
const
|
|
848
|
+
const collected2 = {};
|
|
443
849
|
for (const key of Object.keys(final)) {
|
|
444
850
|
if (borderSideRe.test(key)) {
|
|
445
|
-
|
|
851
|
+
collected2[key] = final[key];
|
|
446
852
|
delete final[key];
|
|
447
853
|
}
|
|
448
854
|
}
|
|
449
|
-
if (Object.keys(
|
|
450
|
-
final.border = { ...final.border || {}, ...
|
|
855
|
+
if (Object.keys(collected2).length > 0) {
|
|
856
|
+
final.border = { ...final.border || {}, ...collected2 };
|
|
451
857
|
}
|
|
452
858
|
if (final.border && typeof final.border === "object") {
|
|
453
859
|
const b = { ...final.border };
|
|
@@ -460,7 +866,31 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
460
866
|
final.border = b;
|
|
461
867
|
}
|
|
462
868
|
}
|
|
463
|
-
|
|
869
|
+
if (mobile || hideOnMobile !== void 0 || hideOnDesktop !== void 0) {
|
|
870
|
+
const overrides = { ...final._override };
|
|
871
|
+
if (mobile || hideOnMobile !== void 0) {
|
|
872
|
+
const { width, maxWidth, autoWidth, ...phone } = mobile || {};
|
|
873
|
+
const mapped = mapSemanticProps(phone, defaultValues, componentType);
|
|
874
|
+
if (componentType === "Image" && (width !== void 0 || maxWidth !== void 0 || autoWidth !== void 0)) {
|
|
875
|
+
mapped.src = {
|
|
876
|
+
...overrides.mobile?.src || {},
|
|
877
|
+
...autoWidth !== void 0 ? { autoWidth } : { autoWidth: false },
|
|
878
|
+
...width !== void 0 || maxWidth !== void 0 ? { maxWidth: width ?? maxWidth } : {}
|
|
879
|
+
};
|
|
880
|
+
} else if (width !== void 0 || autoWidth !== void 0) {
|
|
881
|
+
const key = componentType === "Button" ? "size" : "width";
|
|
882
|
+
mapped[key] = { autoWidth: autoWidth ?? false, ...width !== void 0 ? { width: typeof width === "number" ? `${width}px` : width } : {} };
|
|
883
|
+
}
|
|
884
|
+
overrides.mobile = { ...overrides.mobile, ...cssSafeValues(mapped), ...hideOnMobile !== void 0 ? { hideMobile: hideOnMobile } : {} };
|
|
885
|
+
}
|
|
886
|
+
if (hideOnDesktop !== void 0) overrides.desktop = { ...overrides.desktop, hideDesktop: hideOnDesktop };
|
|
887
|
+
final._override = overrides;
|
|
888
|
+
}
|
|
889
|
+
if (componentType === "Row" && final.noStackMobile === true) {
|
|
890
|
+
final._override = { ...final._override, mobile: { ...final._override?.mobile, noStackMobile: true } };
|
|
891
|
+
final.noStackMobile = false;
|
|
892
|
+
}
|
|
893
|
+
return normalizeCssValues(final);
|
|
464
894
|
}
|
|
465
895
|
function normalizeLinkValue(value) {
|
|
466
896
|
if (value == null) return void 0;
|
|
@@ -482,26 +912,39 @@ function normalizeLinkValue(value) {
|
|
|
482
912
|
}
|
|
483
913
|
return void 0;
|
|
484
914
|
}
|
|
485
|
-
function
|
|
915
|
+
function safeLink(link) {
|
|
916
|
+
const url = link.url === void 0 || link.url === null ? link.url : String(link.url);
|
|
917
|
+
return typeof url === "string" ? { ...link, url: url.replace(/<%[\w.]+%>|["<>]/g, (c) => c.length > 1 ? c : encodeURIComponent(c)) } : link;
|
|
918
|
+
}
|
|
919
|
+
function normalizeValuesForExporter(values, componentName2, mode) {
|
|
486
920
|
if (values == null || typeof values !== "object") return values;
|
|
487
921
|
const out = { ...values };
|
|
488
922
|
for (const key of ["href", "action"]) {
|
|
489
923
|
if (out[key] !== void 0) {
|
|
490
924
|
const normalized = normalizeLinkValue(out[key]);
|
|
491
|
-
if (normalized !== void 0) out[key] = normalized;
|
|
925
|
+
if (normalized !== void 0) out[key] = safeLink(normalized);
|
|
492
926
|
}
|
|
493
927
|
}
|
|
494
|
-
if (
|
|
928
|
+
if (componentName2 === "Button" && typeof out.text === "string") {
|
|
929
|
+
out.text = escapeKeepingTags(out.text, true);
|
|
930
|
+
}
|
|
931
|
+
if (componentName2 === "Menu" && out.menu && Array.isArray(out.menu.items)) {
|
|
495
932
|
out.menu = {
|
|
496
933
|
...out.menu,
|
|
497
934
|
items: out.menu.items.map((item) => {
|
|
498
|
-
if (!item
|
|
499
|
-
const
|
|
500
|
-
if (
|
|
501
|
-
|
|
935
|
+
if (!item) return item;
|
|
936
|
+
const next = mode === "email" && typeof item.text === "string" ? { ...item, text: menuText(item.text) } : item;
|
|
937
|
+
if (next.link === void 0) return next;
|
|
938
|
+
const normalized = normalizeLinkValue(next.link);
|
|
939
|
+
if (normalized === void 0) return next;
|
|
940
|
+
return { ...next, link: safeLink(normalized) };
|
|
502
941
|
})
|
|
503
942
|
};
|
|
504
943
|
}
|
|
944
|
+
if (componentName2 === "Social" && out.icons && Array.isArray(out.icons.icons)) {
|
|
945
|
+
const named2 = (icon) => mode === "email" && typeof icon.name === "string" ? { ...icon, name: escapeText(icon.name, true) } : icon;
|
|
946
|
+
out.icons = { ...out.icons, icons: out.icons.icons.map((icon) => icon && typeof icon === "object" ? named2(safeLink(icon)) : icon) };
|
|
947
|
+
}
|
|
505
948
|
return out;
|
|
506
949
|
}
|
|
507
950
|
var HTML_ENTITIES = {
|
|
@@ -594,9 +1037,11 @@ function htmlToPlainText(html) {
|
|
|
594
1037
|
text = text.replace(/<head[\s\S]*?<\/head>/gi, "");
|
|
595
1038
|
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
596
1039
|
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
1040
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
597
1041
|
text = text.replace(/<h[12][^>]*>([\s\S]*?)<\/h[12]>/gi, (_, content) => {
|
|
598
1042
|
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
599
|
-
|
|
1043
|
+
const upper = clean.replace(/&#?\w+;|[^&]+|&/g, (part) => /^&#?\w+;$/.test(part) ? part : part.toUpperCase());
|
|
1044
|
+
return "\n" + upper + "\n";
|
|
600
1045
|
});
|
|
601
1046
|
text = text.replace(/<h[3-6][^>]*>([\s\S]*?)<\/h[3-6]>/gi, (_, content) => {
|
|
602
1047
|
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
@@ -618,7 +1063,6 @@ function htmlToPlainText(html) {
|
|
|
618
1063
|
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
619
1064
|
return "- " + clean + "\n";
|
|
620
1065
|
});
|
|
621
|
-
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
622
1066
|
text = text.replace(/<\/(?:p|div|tr|blockquote|section|article|header|footer|main|nav|aside)>/gi, "\n");
|
|
623
1067
|
text = text.replace(/<\/(?:ul|ol|table|thead|tbody|tfoot)>/gi, "\n");
|
|
624
1068
|
text = text.replace(/<\/(?:td|th)>/gi, " ");
|
|
@@ -665,6 +1109,15 @@ function nextHtmlId(config, prefix) {
|
|
|
665
1109
|
ids[prefix] = (ids[prefix] || 0) + 1;
|
|
666
1110
|
return `${prefix}_${ids[prefix]}`;
|
|
667
1111
|
}
|
|
1112
|
+
function safeName(value, spaces = false) {
|
|
1113
|
+
const text = String(value ?? "");
|
|
1114
|
+
return spaces ? text.replace(/[\t\n\f\r]/g, " ").replace(/["'<>&`\u0000-\u001f\u007f]/g, "") : text.replace(/["'<>&`\s\u0000-\u001f\u007f]/g, "");
|
|
1115
|
+
}
|
|
1116
|
+
var safeMeta = (meta) => ({ ...meta, htmlID: safeName(meta.htmlID), htmlClassNames: safeName(meta.htmlClassNames, true) });
|
|
1117
|
+
function textDirectionOf(value) {
|
|
1118
|
+
const dir = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
1119
|
+
return dir === "ltr" || dir === "rtl" || dir === "auto" ? dir : void 0;
|
|
1120
|
+
}
|
|
668
1121
|
function renderComponent(config) {
|
|
669
1122
|
const { type, values, className, style, args = [], innerHTML, _config, exporter, metaContext } = config;
|
|
670
1123
|
try {
|
|
@@ -672,7 +1125,7 @@ function renderComponent(config) {
|
|
|
672
1125
|
const exporterConfig = {
|
|
673
1126
|
generateHtmlFromTextJson,
|
|
674
1127
|
toSafeHtml: cfg.toSafeHtml,
|
|
675
|
-
textDirection: cfg.textDirection,
|
|
1128
|
+
textDirection: textDirectionOf(cfg.textDirection),
|
|
676
1129
|
cdnBaseUrl: cfg.cdnBaseUrl
|
|
677
1130
|
};
|
|
678
1131
|
let html;
|
|
@@ -736,21 +1189,20 @@ function createItemComponent(config) {
|
|
|
736
1189
|
});
|
|
737
1190
|
const finalValues = mergeValues(config.defaultValues, mappedValues);
|
|
738
1191
|
const metaName = config.metaName ?? config.name.toLowerCase();
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
htmlClassNames: `u_content_${metaName}`,
|
|
744
|
-
...finalValues._meta || {}
|
|
745
|
-
}
|
|
1192
|
+
const meta = {
|
|
1193
|
+
htmlID: _metaHtmlId ?? `u_content_${metaName}_${index + 1}`,
|
|
1194
|
+
htmlClassNames: `u_content_${metaName}`,
|
|
1195
|
+
...finalValues._meta || {}
|
|
746
1196
|
};
|
|
1197
|
+
const valuesWithMeta = { ...finalValues, _meta: safeMeta(meta) };
|
|
747
1198
|
const safeBodyValues = {
|
|
748
1199
|
contentWidth: "500px",
|
|
749
1200
|
...bodyValues
|
|
750
1201
|
};
|
|
751
1202
|
const valuesForExporter = normalizeValuesForExporter(
|
|
752
1203
|
valuesWithMeta,
|
|
753
|
-
config.name
|
|
1204
|
+
config.name,
|
|
1205
|
+
mode
|
|
754
1206
|
);
|
|
755
1207
|
const exportSrc = valuesForExporter.src;
|
|
756
1208
|
if (exportSrc && typeof exportSrc === "object" && exportSrc.autoWidth === false) {
|
|
@@ -785,10 +1237,12 @@ function createItemComponent(config) {
|
|
|
785
1237
|
});
|
|
786
1238
|
}
|
|
787
1239
|
const ItemComponent = (props) => {
|
|
1240
|
+
if (collecting()) return whileCollecting(() => recordBlock(ItemComponent, props), () => renderFn(props), ItemComponent.displayName ?? "Block");
|
|
788
1241
|
return renderFn(props);
|
|
789
1242
|
};
|
|
790
1243
|
ItemComponent.displayName = config.displayName || config.name;
|
|
791
1244
|
ItemComponent[UNLAYER_RENDER_KEY] = renderFn;
|
|
1245
|
+
ItemComponent[RECORDS] = true;
|
|
792
1246
|
ItemComponent[UNLAYER_CONFIG_KEY] = {
|
|
793
1247
|
name: config.name,
|
|
794
1248
|
defaultValues: config.defaultValues,
|
|
@@ -817,9 +1271,9 @@ var Button = createItemComponent({
|
|
|
817
1271
|
DEFAULT_VALUES,
|
|
818
1272
|
"Button"
|
|
819
1273
|
);
|
|
820
|
-
const
|
|
821
|
-
if (
|
|
822
|
-
const s =
|
|
1274
|
+
const size2 = mapped.size;
|
|
1275
|
+
if (size2 && typeof size2 === "object" && !Array.isArray(size2)) {
|
|
1276
|
+
const s = size2;
|
|
823
1277
|
if (typeof s.width === "number") {
|
|
824
1278
|
s.width = `${s.width}px`;
|
|
825
1279
|
} else if (typeof s.width === "string" && /^\d+(?:\.\d+)?$/.test(s.width.trim())) {
|
|
@@ -1228,6 +1682,35 @@ var {
|
|
|
1228
1682
|
var BODY_DEFAULTS = bodyRest;
|
|
1229
1683
|
var ROW_DEFAULTS = { ...RowDefaults };
|
|
1230
1684
|
var COLUMN_DEFAULTS = { ...ColumnDefaults };
|
|
1685
|
+
|
|
1686
|
+
// src/utils/render-scope.ts
|
|
1687
|
+
var scoped;
|
|
1688
|
+
function scopeCache(key, make) {
|
|
1689
|
+
if (!scoped) return make();
|
|
1690
|
+
if (!scoped.has(key)) scoped.set(key, make());
|
|
1691
|
+
return scoped.get(key);
|
|
1692
|
+
}
|
|
1693
|
+
function withRenderScope(render, own = false) {
|
|
1694
|
+
const scope = () => {
|
|
1695
|
+
if (scoped) return render();
|
|
1696
|
+
scoped = /* @__PURE__ */ new WeakMap();
|
|
1697
|
+
try {
|
|
1698
|
+
return render();
|
|
1699
|
+
} finally {
|
|
1700
|
+
scoped = void 0;
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
return withBuild(() => within(null, scope), own);
|
|
1704
|
+
}
|
|
1705
|
+
var warnedLoose = /* @__PURE__ */ new Set();
|
|
1706
|
+
function looseText(child, container) {
|
|
1707
|
+
const text = String(child);
|
|
1708
|
+
if (text.trim() && !warnedLoose.has(container)) {
|
|
1709
|
+
warnedLoose.add(container);
|
|
1710
|
+
console.warn(`${container}: text placed straight in a ${container} shows as text and isn't kept in the editor's design. Put it in a <Paragraph>, or HTML in an <Html> block.`);
|
|
1711
|
+
}
|
|
1712
|
+
return escapeKeepingTags(text, true);
|
|
1713
|
+
}
|
|
1231
1714
|
var DEFAULT_VALUES12 = ROW_DEFAULTS;
|
|
1232
1715
|
var DEFAULT_BODY_VALUES = BODY_DEFAULTS;
|
|
1233
1716
|
function getWidthPercentages(cells) {
|
|
@@ -1257,8 +1740,8 @@ ${maxQuery} {
|
|
|
1257
1740
|
.u-row { width: 100% !important; }
|
|
1258
1741
|
.u-row .u-col { display: block !important; width: 100% !important; min-width: 320px !important; max-width: 100% !important; }
|
|
1259
1742
|
.u-row .u-col > div { margin: 0 auto; }
|
|
1260
|
-
.no-stack .u-col { min-width: 0 !important; display: table-cell !important; }
|
|
1261
|
-
${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width: ${value}% !important; }`).join("\n")}
|
|
1743
|
+
.u-row.no-stack .u-col { min-width: 0 !important; display: table-cell !important; }
|
|
1744
|
+
${widths.map(({ value, className }) => ` .u-row.no-stack .u-col-${className} { width: ${value}% !important; }`).join("\n")}
|
|
1262
1745
|
}`;
|
|
1263
1746
|
}
|
|
1264
1747
|
const baseCSS = `
|
|
@@ -1298,17 +1781,18 @@ function renderRowToHtml(innerHTML, values, bodyValues, mode, cells, collection
|
|
|
1298
1781
|
variant: mode
|
|
1299
1782
|
});
|
|
1300
1783
|
const contentWidth = toContentWidthPx(bodyValues);
|
|
1301
|
-
const
|
|
1302
|
-
return
|
|
1784
|
+
const css2 = generateGridCSS(cells, mode, contentWidth);
|
|
1785
|
+
return css2 ? `<style>${css2}</style>${html}` : html;
|
|
1303
1786
|
}
|
|
1304
1787
|
function processChildren(children, cells, bodyValues, rowValues, mode, _config) {
|
|
1305
1788
|
if (!children) return "";
|
|
1306
1789
|
let innerHTML = "";
|
|
1307
|
-
const childrenArray =
|
|
1308
|
-
|
|
1309
|
-
|
|
1790
|
+
const childrenArray = React4.Children.toArray(children);
|
|
1791
|
+
let index = 0;
|
|
1792
|
+
childrenArray.forEach((child) => {
|
|
1793
|
+
if (!React4.isValidElement(child)) {
|
|
1310
1794
|
if (typeof child === "string" || typeof child === "number") {
|
|
1311
|
-
innerHTML +=
|
|
1795
|
+
innerHTML += looseText(child, "Row");
|
|
1312
1796
|
}
|
|
1313
1797
|
return;
|
|
1314
1798
|
}
|
|
@@ -1317,7 +1801,7 @@ function processChildren(children, cells, bodyValues, rowValues, mode, _config)
|
|
|
1317
1801
|
if (isColumn && typeof child.type === "function") {
|
|
1318
1802
|
const rendered = child.type({
|
|
1319
1803
|
...child.props,
|
|
1320
|
-
index
|
|
1804
|
+
index: index++,
|
|
1321
1805
|
cells,
|
|
1322
1806
|
bodyValues,
|
|
1323
1807
|
rowValues,
|
|
@@ -1327,7 +1811,7 @@ function processChildren(children, cells, bodyValues, rowValues, mode, _config)
|
|
|
1327
1811
|
if (rendered?.props?.dangerouslySetInnerHTML?.__html) {
|
|
1328
1812
|
innerHTML += rendered.props.dangerouslySetInnerHTML.__html;
|
|
1329
1813
|
}
|
|
1330
|
-
} else if (
|
|
1814
|
+
} else if (React4.isValidElement(child)) {
|
|
1331
1815
|
const name = child.type?.displayName || child.type?.name || "Unknown";
|
|
1332
1816
|
console.warn(
|
|
1333
1817
|
`Row: <${name}> is not a valid Row child. Only <Column> components can be direct children of <Row>. Wrap it in a <Column>: <Row><Column><${name} /></Column></Row>`
|
|
@@ -1336,7 +1820,7 @@ function processChildren(children, cells, bodyValues, rowValues, mode, _config)
|
|
|
1336
1820
|
});
|
|
1337
1821
|
return innerHTML;
|
|
1338
1822
|
}
|
|
1339
|
-
|
|
1823
|
+
function renderRow(props) {
|
|
1340
1824
|
const {
|
|
1341
1825
|
layout,
|
|
1342
1826
|
cells: propsCells,
|
|
@@ -1351,16 +1835,16 @@ var Row = (props) => {
|
|
|
1351
1835
|
...semanticProps
|
|
1352
1836
|
} = props;
|
|
1353
1837
|
const mode = modeProp ?? _config?.mode ?? "web";
|
|
1838
|
+
const columnCount = React4.Children.toArray(children).filter(
|
|
1839
|
+
(c) => React4.isValidElement(c) && /^Column$/.test(c.type?.displayName || c.type?.name || "")
|
|
1840
|
+
).length;
|
|
1354
1841
|
let cells;
|
|
1355
1842
|
if (layout) {
|
|
1356
|
-
validateColumnLayout(layout,
|
|
1843
|
+
if (columnCount !== layout.expectedColumns) validateColumnLayout(layout, React4.Children.count(children));
|
|
1357
1844
|
cells = layout.cells;
|
|
1358
1845
|
} else if (propsCells) {
|
|
1359
1846
|
cells = propsCells;
|
|
1360
1847
|
} else {
|
|
1361
|
-
const columnCount = React.Children.toArray(children).filter(
|
|
1362
|
-
(c) => React.isValidElement(c) && /^Column$/.test(c.type?.displayName || c.type?.name || "")
|
|
1363
|
-
).length;
|
|
1364
1848
|
cells = Array(Math.max(1, columnCount)).fill(1);
|
|
1365
1849
|
}
|
|
1366
1850
|
const safeBodyValues = { ...DEFAULT_BODY_VALUES, ...bodyValues };
|
|
@@ -1369,15 +1853,7 @@ var Row = (props) => {
|
|
|
1369
1853
|
DEFAULT_VALUES12,
|
|
1370
1854
|
"Row"
|
|
1371
1855
|
);
|
|
1372
|
-
const valuesWithMeta = {
|
|
1373
|
-
...values,
|
|
1374
|
-
cells,
|
|
1375
|
-
_meta: {
|
|
1376
|
-
htmlID: nextHtmlId(_config, "u_row"),
|
|
1377
|
-
htmlClassNames: "u_row",
|
|
1378
|
-
...values._meta || {}
|
|
1379
|
-
}
|
|
1380
|
-
};
|
|
1856
|
+
const valuesWithMeta = { ...values, cells, _meta: safeMeta({ htmlID: nextHtmlId(_config, "u_row"), htmlClassNames: "u_row", ...values._meta || {} }) };
|
|
1381
1857
|
const innerHTML = processChildren(
|
|
1382
1858
|
children,
|
|
1383
1859
|
cells,
|
|
@@ -1400,10 +1876,11 @@ var Row = (props) => {
|
|
|
1400
1876
|
console.error("Row rendering failed:", error);
|
|
1401
1877
|
return /* @__PURE__ */ jsx("div", { className, style, children });
|
|
1402
1878
|
}
|
|
1403
|
-
}
|
|
1879
|
+
}
|
|
1880
|
+
var Row = (props) => collecting() ? whileCollecting(() => recordContainer("row", props), () => renderRow(props), "Row") : renderRow(props);
|
|
1404
1881
|
Row.displayName = "Row";
|
|
1882
|
+
Row[RECORDS] = true;
|
|
1405
1883
|
var Row_default = Row;
|
|
1406
|
-
var DEFAULT_CONTAINER_PADDING2 = "10px";
|
|
1407
1884
|
var DEFAULT_VALUES13 = COLUMN_DEFAULTS;
|
|
1408
1885
|
function renderColumnToHtml(innerHTML, values, index, cells, bodyValues, rowValues, mode) {
|
|
1409
1886
|
const columnExporter = ColumnExporters[mode] || ColumnExporters.web;
|
|
@@ -1414,6 +1891,7 @@ function renderContentToHtml(innerHTML, values, bodyValues, mode) {
|
|
|
1414
1891
|
return contentExporter(innerHTML, values, bodyValues, {});
|
|
1415
1892
|
}
|
|
1416
1893
|
var Column = (props) => {
|
|
1894
|
+
if (collecting()) return whileCollecting(() => recordContainer("column", props), () => Column(props), "Column");
|
|
1417
1895
|
const {
|
|
1418
1896
|
children,
|
|
1419
1897
|
index = 0,
|
|
@@ -1432,27 +1910,20 @@ var Column = (props) => {
|
|
|
1432
1910
|
DEFAULT_VALUES13,
|
|
1433
1911
|
"Column"
|
|
1434
1912
|
);
|
|
1435
|
-
const valuesWithMeta = {
|
|
1436
|
-
...values,
|
|
1437
|
-
_meta: {
|
|
1438
|
-
htmlID: nextHtmlId(_config, "u_column"),
|
|
1439
|
-
htmlClassNames: "u_column",
|
|
1440
|
-
...values._meta || {}
|
|
1441
|
-
}
|
|
1442
|
-
};
|
|
1913
|
+
const valuesWithMeta = { ...values, _meta: safeMeta({ htmlID: nextHtmlId(_config, "u_column"), htmlClassNames: "u_column", ...values._meta || {} }) };
|
|
1443
1914
|
let innerHTML = "";
|
|
1444
1915
|
if (children) {
|
|
1445
1916
|
try {
|
|
1446
|
-
const childrenArray =
|
|
1917
|
+
const childrenArray = React4.Children.toArray(children);
|
|
1447
1918
|
childrenArray.forEach((child, childIndex) => {
|
|
1448
1919
|
if (typeof child === "string" || typeof child === "number") {
|
|
1449
|
-
innerHTML +=
|
|
1450
|
-
} else if (
|
|
1920
|
+
innerHTML += looseText(child, "Column");
|
|
1921
|
+
} else if (React4.isValidElement(child)) {
|
|
1451
1922
|
if (typeof child.type === "function") {
|
|
1452
1923
|
const ComponentType = child.type;
|
|
1453
1924
|
const renderFn = ComponentType[UNLAYER_RENDER_KEY] || ComponentType;
|
|
1454
|
-
const
|
|
1455
|
-
const contentHtmlId = nextHtmlId(_config, `u_content_${
|
|
1925
|
+
const componentName2 = ComponentType?.[UNLAYER_CONFIG_KEY]?.metaName ?? (ComponentType?.displayName || ComponentType?.name || "component").toLowerCase();
|
|
1926
|
+
const contentHtmlId = nextHtmlId(_config, `u_content_${componentName2}`);
|
|
1456
1927
|
const rendered = renderFn({
|
|
1457
1928
|
...child.props,
|
|
1458
1929
|
_config,
|
|
@@ -1467,13 +1938,26 @@ var Column = (props) => {
|
|
|
1467
1938
|
const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
|
|
1468
1939
|
if (!componentHTML && ComponentType?.[UNLAYER_CONFIG_KEY]?.omitEmptyOutput) return;
|
|
1469
1940
|
const childProps = child.props;
|
|
1470
|
-
const rawContainerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ??
|
|
1941
|
+
const rawContainerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING;
|
|
1471
1942
|
const containerPadding = typeof rawContainerPadding === "number" ? `${rawContainerPadding}px` : rawContainerPadding;
|
|
1943
|
+
const device = ComponentType?.[UNLAYER_CONFIG_KEY]?.contentType === "custom" ? {} : childProps;
|
|
1472
1944
|
const contentValues = {
|
|
1473
1945
|
containerPadding,
|
|
1946
|
+
_override: {
|
|
1947
|
+
...childProps.values?._override,
|
|
1948
|
+
mobile: {
|
|
1949
|
+
...childProps.values?._override?.mobile,
|
|
1950
|
+
...device.hideOnMobile !== void 0 ? { hideMobile: device.hideOnMobile } : {}
|
|
1951
|
+
},
|
|
1952
|
+
desktop: {
|
|
1953
|
+
...childProps.values?._override?.desktop,
|
|
1954
|
+
...device.hideOnDesktop !== void 0 ? { hideDesktop: device.hideOnDesktop } : {}
|
|
1955
|
+
}
|
|
1956
|
+
},
|
|
1474
1957
|
_meta: {
|
|
1475
1958
|
htmlID: contentHtmlId,
|
|
1476
|
-
htmlClassNames: `u_content_${
|
|
1959
|
+
htmlClassNames: `u_content_${componentName2}`,
|
|
1960
|
+
...childProps.values?._meta?.htmlID ? { htmlID: safeName(childProps.values._meta.htmlID) } : {}
|
|
1477
1961
|
}
|
|
1478
1962
|
};
|
|
1479
1963
|
innerHTML += renderContentToHtml(
|
|
@@ -1512,7 +1996,330 @@ var Column = (props) => {
|
|
|
1512
1996
|
}
|
|
1513
1997
|
};
|
|
1514
1998
|
Column.displayName = "Column";
|
|
1999
|
+
Column[RECORDS] = true;
|
|
1515
2000
|
var Column_default = Column;
|
|
2001
|
+
var KEPT = Symbol.for("@unlayer/react-elements/kept");
|
|
2002
|
+
function parse(html, collection) {
|
|
2003
|
+
const root = [];
|
|
2004
|
+
const open = [root];
|
|
2005
|
+
const pattern = new RegExp(`<${MARK} data-n="${collection.nonce}" data-i="(\\d+)"(?: data-e="([ocf])")?></${MARK}>`, "g");
|
|
2006
|
+
let last = 0;
|
|
2007
|
+
for (const match of html.matchAll(pattern)) {
|
|
2008
|
+
const into = open[open.length - 1];
|
|
2009
|
+
if (match.index > last) into.push({ kind: "raw", html: html.slice(last, match.index) });
|
|
2010
|
+
last = match.index + match[0].length;
|
|
2011
|
+
const at = Number(match[1]);
|
|
2012
|
+
const edge = match[2];
|
|
2013
|
+
if (edge === "o") {
|
|
2014
|
+
const children = [];
|
|
2015
|
+
into.push({ kind: collection.recorded[at].kind, at, children });
|
|
2016
|
+
open.push(children);
|
|
2017
|
+
} else if (edge === "c") open.pop();
|
|
2018
|
+
else into.push({ kind: edge === "f" ? "failed" : "block", at });
|
|
2019
|
+
}
|
|
2020
|
+
if (last < html.length) open[open.length - 1].push({ kind: "raw", html: html.slice(last) });
|
|
2021
|
+
if (open.length !== 1) throw new Error("[Unlayer] a collected render's markers don't pair up");
|
|
2022
|
+
return root;
|
|
2023
|
+
}
|
|
2024
|
+
var TEXT_ENTITIES = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" };
|
|
2025
|
+
var VOID = /^(area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)$/i;
|
|
2026
|
+
function parts(html) {
|
|
2027
|
+
const out = [];
|
|
2028
|
+
let at = 0;
|
|
2029
|
+
const text = (s) => s && out.push({ text: s.replace(/&|<|>|"|'/g, (e) => TEXT_ENTITIES[e]) });
|
|
2030
|
+
while (at < html.length) {
|
|
2031
|
+
const start = html.indexOf("<", at);
|
|
2032
|
+
if (start < 0) {
|
|
2033
|
+
text(html.slice(at));
|
|
2034
|
+
break;
|
|
2035
|
+
}
|
|
2036
|
+
text(html.slice(at, start));
|
|
2037
|
+
const name = /^<([a-zA-Z][\w:-]*)/.exec(html.slice(start))?.[1];
|
|
2038
|
+
if (!name) {
|
|
2039
|
+
out.push({ tag: "#", html: html.slice(start) });
|
|
2040
|
+
break;
|
|
2041
|
+
}
|
|
2042
|
+
const openEnd = html.indexOf(">", start);
|
|
2043
|
+
if (VOID.test(name) || html[openEnd - 1] === "/") {
|
|
2044
|
+
out.push({ tag: name, html: html.slice(start, openEnd + 1) });
|
|
2045
|
+
at = openEnd + 1;
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
const tags = new RegExp(`<(/?)${name}(?=[\\s>/])`, "gi");
|
|
2049
|
+
tags.lastIndex = openEnd + 1;
|
|
2050
|
+
let depth = 1;
|
|
2051
|
+
let end = -1;
|
|
2052
|
+
for (let m = tags.exec(html); m; m = tags.exec(html)) {
|
|
2053
|
+
depth += m[1] ? -1 : 1;
|
|
2054
|
+
if (depth === 0) {
|
|
2055
|
+
end = m.index;
|
|
2056
|
+
break;
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
if (end < 0) {
|
|
2060
|
+
out.push({ tag: "#", html: html.slice(start) });
|
|
2061
|
+
break;
|
|
2062
|
+
}
|
|
2063
|
+
const close = html.indexOf(">", end) + 1;
|
|
2064
|
+
out.push({ tag: name, html: html.slice(start, close), inner: html.slice(openEnd + 1, end) });
|
|
2065
|
+
at = close;
|
|
2066
|
+
}
|
|
2067
|
+
return out;
|
|
2068
|
+
}
|
|
2069
|
+
function innerType(type) {
|
|
2070
|
+
while (type?.$$typeof === MEMO) type = type.type;
|
|
2071
|
+
return type;
|
|
2072
|
+
}
|
|
2073
|
+
var recorded = (c, node) => c.collection.recorded[node.at];
|
|
2074
|
+
var nameOf = (element2) => {
|
|
2075
|
+
if (typeof element2.type === "string") return element2.type;
|
|
2076
|
+
const type = innerType(element2.type);
|
|
2077
|
+
return type?.displayName || type?.name || "component";
|
|
2078
|
+
};
|
|
2079
|
+
function leftOut(c, node) {
|
|
2080
|
+
if (c.fatal) throw c.fatal;
|
|
2081
|
+
if (!c.told.has(node.at)) {
|
|
2082
|
+
c.told.add(node.at);
|
|
2083
|
+
try {
|
|
2084
|
+
tell(recorded(c, node).element);
|
|
2085
|
+
} catch (error) {
|
|
2086
|
+
c.fatal = error;
|
|
2087
|
+
throw error;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
return [];
|
|
2091
|
+
}
|
|
2092
|
+
function calledAgain(element2) {
|
|
2093
|
+
const type = innerType(element2.type);
|
|
2094
|
+
if (type?.$$typeof !== FORWARD_REF && (typeof type !== "function" || type.prototype?.isReactComponent)) return void 0;
|
|
2095
|
+
const props = { ...element2.props };
|
|
2096
|
+
for (const key in type.defaultProps) if (props[key] === void 0) props[key] = type.defaultProps[key];
|
|
2097
|
+
return { out: callIsolated(() => type.$$typeof === FORWARD_REF ? type.render(props, null) : type(props)) };
|
|
2098
|
+
}
|
|
2099
|
+
function tell(element2) {
|
|
2100
|
+
const type = innerType(element2.type);
|
|
2101
|
+
const name = nameOf(element2);
|
|
2102
|
+
const async = () => console.warn(`[Unlayer] <${name}> is async or suspends: Elements renders synchronously, so it's left out. Load its data first and pass it as props.`);
|
|
2103
|
+
if (isAsyncComponent(type)) return async();
|
|
2104
|
+
try {
|
|
2105
|
+
const again = calledAgain(element2);
|
|
2106
|
+
const inner = again ? collected(again.out) : collected(element2, false);
|
|
2107
|
+
const failed = (nodes) => nodes.flatMap((n) => n.kind === "failed" ? [n] : "children" in n ? failed(n.children) : []);
|
|
2108
|
+
const found = failed(inner.nodes);
|
|
2109
|
+
for (const node of found) tell(recorded(inner, node).element);
|
|
2110
|
+
if (!found.length) console.error(`[Unlayer] <${name}> failed while rendering, so it's left out.`);
|
|
2111
|
+
} catch (error) {
|
|
2112
|
+
const message = String(error?.message);
|
|
2113
|
+
if (/Element type is invalid|Objects are not valid as a React child|Minified React error #(130|31)\b/.test(message)) throw error;
|
|
2114
|
+
if (isThenable(error) || /async and suspending|suspended|Minified React error #426\b/.test(message)) async();
|
|
2115
|
+
else console.error(`[Unlayer] <${name}> threw while rendering, so it's left out:`, error);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function markupOnly(c, nodes) {
|
|
2119
|
+
let out = "";
|
|
2120
|
+
for (const node of nodes) {
|
|
2121
|
+
if (node.kind === "raw") out += node.html;
|
|
2122
|
+
else if (node.kind === "failed") leftOut(c, node);
|
|
2123
|
+
else if (node.kind !== "boundary") return void 0;
|
|
2124
|
+
else {
|
|
2125
|
+
const inner = markupOnly(c, node.children);
|
|
2126
|
+
if (inner === void 0) return void 0;
|
|
2127
|
+
out += inner;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return out;
|
|
2131
|
+
}
|
|
2132
|
+
function keptMarkup(c, node) {
|
|
2133
|
+
c.kept ?? (c.kept = /* @__PURE__ */ new Map());
|
|
2134
|
+
if (!c.kept.has(node.at)) c.kept.set(node.at, keptMarkupOnce(c, node));
|
|
2135
|
+
return c.kept.get(node.at);
|
|
2136
|
+
}
|
|
2137
|
+
function keptMarkupOnce(c, node) {
|
|
2138
|
+
const raw = markupOnly(c, node.children);
|
|
2139
|
+
if (raw === void 0) return void 0;
|
|
2140
|
+
const list = parts(raw);
|
|
2141
|
+
if (!list.some((p) => "tag" in p)) return void 0;
|
|
2142
|
+
const element2 = recorded(c, node).element;
|
|
2143
|
+
const one = list.length === 1 || list.some((p) => "tag" in p && p.tag === "#") ? /^<([a-zA-Z][\w:-]*)\b[^>]*>([\s\S]*)<\/\1>$/.exec(raw) : null;
|
|
2144
|
+
let given = false;
|
|
2145
|
+
if (one) {
|
|
2146
|
+
try {
|
|
2147
|
+
const out = calledAgain(element2)?.out;
|
|
2148
|
+
given = React4.isValidElement(out) && typeof out.type === "string" && out.props.dangerouslySetInnerHTML != null;
|
|
2149
|
+
} catch {
|
|
2150
|
+
given = one[1].toLowerCase() === "div";
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
const html = one && given ? one[2] : raw;
|
|
2154
|
+
const Rendered = () => React4.createElement("div", { dangerouslySetInnerHTML: { __html: html } });
|
|
2155
|
+
Rendered.displayName = nameOf(element2);
|
|
2156
|
+
const { containerPadding, hideOnMobile, hideOnDesktop } = element2.props ?? {};
|
|
2157
|
+
Rendered[KEPT] = React4.createElement(Html_default, {
|
|
2158
|
+
html,
|
|
2159
|
+
...containerPadding !== void 0 ? { containerPadding } : {},
|
|
2160
|
+
...hideOnMobile !== void 0 ? { hideOnMobile } : {},
|
|
2161
|
+
...hideOnDesktop !== void 0 ? { hideOnDesktop } : {}
|
|
2162
|
+
});
|
|
2163
|
+
return React4.createElement(Rendered, { ...element2.props });
|
|
2164
|
+
}
|
|
2165
|
+
function resolvedProps(c, props) {
|
|
2166
|
+
let out = props;
|
|
2167
|
+
for (const [key, value] of Object.entries(props)) {
|
|
2168
|
+
if (typeof value !== "string" || !value.includes(MARK)) continue;
|
|
2169
|
+
let html = value;
|
|
2170
|
+
try {
|
|
2171
|
+
if (value.includes(c.collection.nonce)) html = markup(c, parse(value, c.collection), (block) => within(null, () => renderToStaticMarkup(block)));
|
|
2172
|
+
} catch {
|
|
2173
|
+
}
|
|
2174
|
+
out = out === props ? { ...props } : out;
|
|
2175
|
+
out[key] = withoutMarkers(html, c.collection.nonce);
|
|
2176
|
+
}
|
|
2177
|
+
return out;
|
|
2178
|
+
}
|
|
2179
|
+
function withoutMarkers(html, nonce2) {
|
|
2180
|
+
const left = new RegExp(`<${MARK} data-n="${nonce2}" data-i="\\d+"(?: data-e="[ocf]")?></${MARK}>`, "g");
|
|
2181
|
+
if (!html.includes(nonce2) || !html.match(left)) return html;
|
|
2182
|
+
console.error(`[Unlayer] an Elements block rendered inside a component left a marker behind; it's taken out, and the block it stood for is missing.`);
|
|
2183
|
+
return html.replace(left, "");
|
|
2184
|
+
}
|
|
2185
|
+
function markup(c, nodes, render) {
|
|
2186
|
+
return nodes.map((node) => {
|
|
2187
|
+
switch (node.kind) {
|
|
2188
|
+
case "raw":
|
|
2189
|
+
return node.html;
|
|
2190
|
+
case "boundary":
|
|
2191
|
+
return markup(c, node.children, render);
|
|
2192
|
+
case "failed":
|
|
2193
|
+
leftOut(c, node);
|
|
2194
|
+
return "";
|
|
2195
|
+
default:
|
|
2196
|
+
return render(element(c, node));
|
|
2197
|
+
}
|
|
2198
|
+
}).join("");
|
|
2199
|
+
}
|
|
2200
|
+
var keyed = (list) => list.map((child, i) => React4.isValidElement(child) && child.key == null ? React4.cloneElement(child, { key: `k${i}` }) : child);
|
|
2201
|
+
function element(c, node) {
|
|
2202
|
+
const r = recorded(c, node);
|
|
2203
|
+
if (node.kind === "block") return React4.createElement(r.type, resolvedProps(c, r.props));
|
|
2204
|
+
if (node.kind === "column") return React4.createElement(Column_default, { ...r.props, children: keyed(elements(c, node.children, "column")) });
|
|
2205
|
+
const props = { ...r.props };
|
|
2206
|
+
const children = keyed(elements(c, node.children, "row"));
|
|
2207
|
+
if (props.layout) {
|
|
2208
|
+
const columns = children.filter((child) => React4.isValidElement(child) && child.type === Column_default).length;
|
|
2209
|
+
if (columns === props.layout.expectedColumns || React4.Children.count(props.children) === props.layout.expectedColumns) {
|
|
2210
|
+
props.cells = props.layout.cells;
|
|
2211
|
+
delete props.layout;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
return React4.createElement(Row_default, { ...props, children });
|
|
2215
|
+
}
|
|
2216
|
+
function spanned(nodes) {
|
|
2217
|
+
const out = [];
|
|
2218
|
+
const spans = [];
|
|
2219
|
+
for (const node of nodes) {
|
|
2220
|
+
if (node.kind !== "raw") {
|
|
2221
|
+
out.push({ node, inside: spans.length > 0 });
|
|
2222
|
+
continue;
|
|
2223
|
+
}
|
|
2224
|
+
const html = node.html;
|
|
2225
|
+
const open = [];
|
|
2226
|
+
let closed = spans.length ? -1 : 0;
|
|
2227
|
+
for (const m of html.matchAll(/<!--[\s\S]*?(?:-->|$)|<(\/?)([a-zA-Z][\w:-]*)\b[^>]*>/g)) {
|
|
2228
|
+
if (!m[2]) continue;
|
|
2229
|
+
const name = m[2].toLowerCase();
|
|
2230
|
+
if (m[1]) {
|
|
2231
|
+
const i = open.map((o) => o.name).lastIndexOf(name);
|
|
2232
|
+
const across = spans.lastIndexOf(name);
|
|
2233
|
+
if (i >= 0) open.length = i;
|
|
2234
|
+
else if (across >= 0) {
|
|
2235
|
+
spans.length = across;
|
|
2236
|
+
open.length = 0;
|
|
2237
|
+
if (!spans.length) closed = m.index + m[0].length;
|
|
2238
|
+
}
|
|
2239
|
+
} else if (!VOID.test(name) && !m[0].endsWith("/>")) open.push({ name, at: m.index });
|
|
2240
|
+
}
|
|
2241
|
+
const piece = (from, to, inside) => {
|
|
2242
|
+
if (to > from) out.push({ node: { kind: "raw", html: html.slice(from, to) }, inside });
|
|
2243
|
+
};
|
|
2244
|
+
if (closed < 0) piece(0, html.length, true);
|
|
2245
|
+
else {
|
|
2246
|
+
piece(0, closed, true);
|
|
2247
|
+
piece(closed, open[0]?.at ?? html.length, false);
|
|
2248
|
+
if (open.length) piece(open[0].at, html.length, true);
|
|
2249
|
+
}
|
|
2250
|
+
spans.push(...open.map((o) => o.name));
|
|
2251
|
+
}
|
|
2252
|
+
return out;
|
|
2253
|
+
}
|
|
2254
|
+
function elements(c, nodes, place, owner, inMarkup = false) {
|
|
2255
|
+
const inColumn = place === "column";
|
|
2256
|
+
return (place === "root" || inMarkup ? nodes.map((node) => ({ node, inside: inMarkup })) : spanned(nodes)).flatMap(({ node, inside }) => {
|
|
2257
|
+
switch (node.kind) {
|
|
2258
|
+
case "failed":
|
|
2259
|
+
return leftOut(c, node);
|
|
2260
|
+
case "boundary": {
|
|
2261
|
+
const kept = inColumn && !inside ? keptMarkup(c, node) : void 0;
|
|
2262
|
+
if (kept) return [kept];
|
|
2263
|
+
const Named = () => null;
|
|
2264
|
+
Named.displayName = nameOf(recorded(c, node).element);
|
|
2265
|
+
if (inColumn && !inside && !c.told.has(node.at) && node.children.some((n) => n.kind === "raw" && parts(n.html).some((p) => "tag" in p))) {
|
|
2266
|
+
c.told.add(node.at);
|
|
2267
|
+
console.warn(`Column: <${Named.displayName}> rendered markup that isn't a block, so it's left out. Put HTML in an <Html> block.`);
|
|
2268
|
+
}
|
|
2269
|
+
return elements(c, node.children, place, Named, inside);
|
|
2270
|
+
}
|
|
2271
|
+
case "raw":
|
|
2272
|
+
if (inside) {
|
|
2273
|
+
const name = /^<([a-zA-Z][\w:-]*)/.exec(node.html)?.[1];
|
|
2274
|
+
return place === "row" && name && !inMarkup ? [React4.createElement(owner ?? name)] : [];
|
|
2275
|
+
}
|
|
2276
|
+
return parts(node.html).flatMap((p) => "text" in p ? [p.text] : inColumn ? [] : [React4.createElement(owner ?? (p.tag === "#" ? "div" : p.tag))]);
|
|
2277
|
+
default:
|
|
2278
|
+
return [element(c, node)];
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
function collected(children, guarded2 = true) {
|
|
2283
|
+
const once = (markup2) => {
|
|
2284
|
+
const collection = { nonce: nonce(), recorded: [], ...markup2 ? { markup: markup2 } : {}, ...idsSeen() ? {} : { blind: true } };
|
|
2285
|
+
const html = within(collection, () => renderToStaticMarkup(/* @__PURE__ */ jsx(Fragment, { children: guarded2 ? guard(children, collection) : children }), { identifierPrefix: COLLECTING_ID }));
|
|
2286
|
+
return { nodes: parse(html, collection), collection, told: /* @__PURE__ */ new Set() };
|
|
2287
|
+
};
|
|
2288
|
+
try {
|
|
2289
|
+
return once(false);
|
|
2290
|
+
} catch (error) {
|
|
2291
|
+
if (!guarded2) throw error;
|
|
2292
|
+
return once(true);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
function idsSeen() {
|
|
2296
|
+
let seen = false;
|
|
2297
|
+
const Probe = () => {
|
|
2298
|
+
try {
|
|
2299
|
+
seen = React4.useId().slice(1).startsWith(COLLECTING_ID);
|
|
2300
|
+
} catch {
|
|
2301
|
+
}
|
|
2302
|
+
return null;
|
|
2303
|
+
};
|
|
2304
|
+
renderToStaticMarkup(/* @__PURE__ */ jsx(Probe, {}), { identifierPrefix: COLLECTING_ID });
|
|
2305
|
+
return seen;
|
|
2306
|
+
}
|
|
2307
|
+
function nonce() {
|
|
2308
|
+
const random = globalThis.crypto?.getRandomValues?.(new Uint32Array(2));
|
|
2309
|
+
return random ? Array.from(random, (n) => n.toString(36)).join("") : `${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
|
|
2310
|
+
}
|
|
2311
|
+
var collect = (children) => children !== null && typeof children === "object" ? scopeCache(children, () => collected(children)) : collected(children);
|
|
2312
|
+
function childrenHtml2(children, place) {
|
|
2313
|
+
const c = collect(children);
|
|
2314
|
+
return withoutMarkers(markup(c, c.nodes, (child) => within(null, () => renderToString(place(child)))), c.collection.nonce);
|
|
2315
|
+
}
|
|
2316
|
+
function normalizedChildren(children) {
|
|
2317
|
+
const c = collect(children);
|
|
2318
|
+
return elements(c, c.nodes, "root");
|
|
2319
|
+
}
|
|
2320
|
+
function normalizedRoot(root) {
|
|
2321
|
+
return React4.cloneElement(root, void 0, ...normalizedChildren(root.props.children));
|
|
2322
|
+
}
|
|
1516
2323
|
var DEFAULT_VALUES14 = BODY_DEFAULTS;
|
|
1517
2324
|
var MAX_PREVIEW_LENGTH = 150;
|
|
1518
2325
|
var PADDING_CHARS = [
|
|
@@ -1524,6 +2331,9 @@ var PADDING_CHARS = [
|
|
|
1524
2331
|
"\u200F",
|
|
1525
2332
|
"\uFEFF"
|
|
1526
2333
|
];
|
|
2334
|
+
function escapePreviewText(text) {
|
|
2335
|
+
return text.split(/(<%[\w.]+%>)/).map((part, i) => i % 2 ? part : part.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).join("");
|
|
2336
|
+
}
|
|
1527
2337
|
function generatePreviewHtml(text) {
|
|
1528
2338
|
if (!text || text.trim().length === 0) return "";
|
|
1529
2339
|
const truncated = text.length > MAX_PREVIEW_LENGTH ? text.slice(0, MAX_PREVIEW_LENGTH) : text;
|
|
@@ -1532,7 +2342,7 @@ function generatePreviewHtml(text) {
|
|
|
1532
2342
|
for (let i = 0; i < paddingLength; i++) {
|
|
1533
2343
|
padding += PADDING_CHARS[i % PADDING_CHARS.length];
|
|
1534
2344
|
}
|
|
1535
|
-
return `<div data-skip-in-text="true" style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">` + truncated + padding + `</div>`;
|
|
2345
|
+
return `<div data-skip-in-text="true" style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">` + escapePreviewText(truncated) + padding + `</div>`;
|
|
1536
2346
|
}
|
|
1537
2347
|
function renderBodyToHtml(innerHTML, values, mode, previewText) {
|
|
1538
2348
|
let finalInnerHtml = innerHTML;
|
|
@@ -1553,7 +2363,7 @@ function renderBodyToHtml(innerHTML, values, mode, previewText) {
|
|
|
1553
2363
|
return raw.replace("min-height: 100vh; ", "").replace("min-height: 100vh;", "");
|
|
1554
2364
|
}
|
|
1555
2365
|
var Body = (props) => {
|
|
1556
|
-
const { children, mode: modeProp, className, style, index = 0, config: configProp, previewText, ...semanticProps } = props;
|
|
2366
|
+
const { children, mode: modeProp, className, style, index = 0, config: configProp, previewText, fonts: _fonts, lang: _lang, ...semanticProps } = props;
|
|
1557
2367
|
const resolvedConfig = { ...DEFAULT_CONFIG, ...configProp };
|
|
1558
2368
|
const mode = modeProp ?? resolvedConfig.mode ?? "web";
|
|
1559
2369
|
const _config = { ...resolvedConfig, mode };
|
|
@@ -1570,22 +2380,12 @@ var Body = (props) => {
|
|
|
1570
2380
|
...values._meta || {}
|
|
1571
2381
|
}
|
|
1572
2382
|
};
|
|
1573
|
-
let enrichedChildren = children;
|
|
1574
|
-
if (children) {
|
|
1575
|
-
enrichedChildren = React.Children.map(children, (child) => {
|
|
1576
|
-
if (React.isValidElement(child)) {
|
|
1577
|
-
return React.cloneElement(child, {
|
|
1578
|
-
_config,
|
|
1579
|
-
bodyValues: values
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
return child;
|
|
1583
|
-
});
|
|
1584
|
-
}
|
|
1585
2383
|
let innerHTML = "";
|
|
1586
|
-
if (
|
|
2384
|
+
if (children) {
|
|
1587
2385
|
try {
|
|
1588
|
-
innerHTML =
|
|
2386
|
+
innerHTML = withRenderScope(() => {
|
|
2387
|
+
return childrenHtml2(children, (child) => React4.cloneElement(child, { _config, bodyValues: values }));
|
|
2388
|
+
}, true);
|
|
1589
2389
|
} catch (error) {
|
|
1590
2390
|
console.error("Body: Failed to render children:", error);
|
|
1591
2391
|
innerHTML = "";
|
|
@@ -1643,29 +2443,104 @@ UnlayerProvider.displayName = "UnlayerProvider";
|
|
|
1643
2443
|
function useUnlayerConfig() {
|
|
1644
2444
|
return useContext(getUnlayerContext());
|
|
1645
2445
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2446
|
+
|
|
2447
|
+
// src/utils/device-overrides.ts
|
|
2448
|
+
var contentOptions = {
|
|
2449
|
+
Button: ["size", "fontSize", "lineHeight", "textAlign", "padding"],
|
|
2450
|
+
Image: ["src", "textAlign"],
|
|
2451
|
+
Heading: ["fontSize", "textAlign", "lineHeight"],
|
|
2452
|
+
Paragraph: ["fontSize", "textAlign", "lineHeight"],
|
|
2453
|
+
Menu: ["fontSize", "padding"],
|
|
2454
|
+
Divider: ["width", "textAlign"]
|
|
2455
|
+
};
|
|
2456
|
+
function css(value) {
|
|
2457
|
+
const text = String(value).trim();
|
|
2458
|
+
return text && !/[!\\"'\n\r]/.test(text) && cssValueSafe(text) ? text : void 0;
|
|
2459
|
+
}
|
|
2460
|
+
function ident(id) {
|
|
2461
|
+
return id.replace(/^-?\d|^-$|[^\w\u0080-\uffff-]/g, (c) => /\d/.test(c.slice(-1)) ? `${c.slice(0, -1)}\\${c.slice(-1).charCodeAt(0).toString(16)} ` : `\\${c}`);
|
|
2462
|
+
}
|
|
2463
|
+
function size(value) {
|
|
2464
|
+
const number = parseFloat(String(value));
|
|
2465
|
+
if (!Number.isFinite(number)) return;
|
|
2466
|
+
const rounded = parseFloat(number.toFixed(2));
|
|
2467
|
+
if (typeof value === "number" || String(value).endsWith("px")) return `${rounded}px`;
|
|
2468
|
+
if (String(value).endsWith("%")) return `${rounded}%`;
|
|
2469
|
+
}
|
|
2470
|
+
function collectDeviceStyles(values, collection, name, mode, styles) {
|
|
2471
|
+
const given = safeName(values._meta?.htmlID);
|
|
2472
|
+
if (!given) return;
|
|
2473
|
+
const id = ident(given);
|
|
2474
|
+
const options = collection === "contents" ? ["containerPadding", ...contentOptions[name] || []] : collection === "columns" ? ["padding", "border"] : ["padding"];
|
|
2475
|
+
for (const device of mode === "document" ? ["desktop"] : ["desktop", "mobile"]) {
|
|
2476
|
+
const overrides = values._override?.[device];
|
|
2477
|
+
if (!overrides) continue;
|
|
2478
|
+
for (const option of options) {
|
|
2479
|
+
if (!(option in overrides)) continue;
|
|
2480
|
+
const value = overrides[option];
|
|
2481
|
+
const property = option.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
2482
|
+
const prefix = collection === "rows" ? "row-" : collection === "columns" ? "col-" : "";
|
|
2483
|
+
const selector = (attribute) => `#${id} .v-${prefix}${property === attribute ? property : `${property}-${attribute}`}`;
|
|
2484
|
+
const push = (rule) => (styles[device] || (styles[device] = [])).push(rule);
|
|
2485
|
+
if (option === "padding" || option === "containerPadding") {
|
|
2486
|
+
if (!(value || value === 0) || css(value) === void 0) continue;
|
|
2487
|
+
const direct = collection === "rows" || mode === "web" && collection === "contents" && option === "containerPadding";
|
|
2488
|
+
const target = direct ? selector("padding").replace(`#${id} `, `#${id}`) : selector("padding");
|
|
2489
|
+
push(`${target} { padding: ${css(value)} !important; }`);
|
|
2490
|
+
if (mode === "email" && collection === "rows") {
|
|
2491
|
+
const parts2 = String(value).trim().split(/\s+/);
|
|
2492
|
+
const [top, bottom] = [parts2[0], parts2[2] || parts2[0]].map((part) => size(part) ?? css(part));
|
|
2493
|
+
if (top !== void 0 && bottom !== void 0) push(`${target}--vertical { padding-top: ${top} !important; padding-bottom: ${bottom} !important; }`);
|
|
2494
|
+
}
|
|
2495
|
+
} else if (option === "border") {
|
|
2496
|
+
if (value) push(`${selector("border")} { ${["Top", "Left", "Right", "Bottom"].map((d) => {
|
|
2497
|
+
const w = css(value[`border${d}Width`] || "0px") ?? "0px";
|
|
2498
|
+
return `border-${d.toLowerCase()}: ${w} ${css(value[`border${d}Style`] || "solid") ?? "solid"} ${parseInt(w) > 0 && css(value[`border${d}Color`]) || "transparent"} !important;`;
|
|
2499
|
+
}).join("")} }`);
|
|
2500
|
+
} else if (option === "src") {
|
|
2501
|
+
if (!value) continue;
|
|
2502
|
+
const merged = { ...values.src, ...value };
|
|
2503
|
+
const maximum = merged.autoWidth === false ? size(merged.maxWidth) : void 0;
|
|
2504
|
+
push(`${selector("width")} { width: ${maximum || "100%"} !important; }`);
|
|
2505
|
+
push(`${selector("max-width")} { max-width: ${size(maximum || merged.width || "100%")} !important; }`);
|
|
2506
|
+
} else if (option === "size" || option === "width") {
|
|
2507
|
+
if (!value) continue;
|
|
2508
|
+
push(`${selector("width")} { width: ${size(value.width) && !value.autoWidth ? size(value.width) : "auto"} !important; }`);
|
|
2509
|
+
push(`${selector("max-width")} { max-width: 100% !important; }`);
|
|
2510
|
+
} else {
|
|
2511
|
+
if (!(value || option === "lineHeight" && `${value}` === "0")) continue;
|
|
2512
|
+
const rendered = css(option === "fontSize" && typeof value === "number" ? `${value}px` : value);
|
|
2513
|
+
if (rendered === void 0) continue;
|
|
2514
|
+
push(`${selector(property)} { ${property}: ${rendered} !important; }`);
|
|
2515
|
+
}
|
|
1653
2516
|
}
|
|
1654
|
-
}
|
|
2517
|
+
}
|
|
1655
2518
|
}
|
|
1656
|
-
function
|
|
1657
|
-
|
|
1658
|
-
|
|
2519
|
+
function deviceStylesCss(styles, mode) {
|
|
2520
|
+
return Object.entries(styles).filter(([, rules]) => rules.length).map(([device, rules]) => {
|
|
2521
|
+
const query = device === "mobile" ? "@media (max-width: 480px)" : `@media (min-width: ${mode === "document" ? 1 : 481}px)`;
|
|
2522
|
+
return `${query} { ${rules.join(" ")} }`;
|
|
2523
|
+
}).join("\n");
|
|
1659
2524
|
}
|
|
1660
|
-
function
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
2525
|
+
function deviceVisibilityCss(mode) {
|
|
2526
|
+
if (mode === "document") return "";
|
|
2527
|
+
const extra = mode === "email" ? "max-height: 0px; overflow: hidden; " : "";
|
|
2528
|
+
const reset = mode === "email" ? "@media (min-width: 0px) { .hide-default__display-block { display: block !important; mso-hide: unset !important; } .hide-default__display-table { display: table !important; mso-hide: unset !important; } }\n" : "";
|
|
2529
|
+
return reset + `@media (max-width: 480px) { .hide-mobile { ${extra}display: none !important; } }
|
|
2530
|
+
@media (min-width: 481px) { .hide-desktop { ${extra}display: none !important; } }`;
|
|
2531
|
+
}
|
|
2532
|
+
var schemaVersion = schemaVersion$1 ?? 24;
|
|
2533
|
+
function unwrapForJson(element2) {
|
|
2534
|
+
try {
|
|
2535
|
+
return unwrapRoot(element2);
|
|
2536
|
+
} catch (cause) {
|
|
2537
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
2538
|
+
throw new Error(
|
|
2539
|
+
`[Unlayer] renderToJson: could not unwrap <${componentName(element2) || "wrapper"}>. ${UNWRAP_ADVICE} (${detail})`
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
1668
2542
|
}
|
|
2543
|
+
var collectChildren = (node) => React4.Children.toArray(node).filter(React4.isValidElement);
|
|
1669
2544
|
function extractSemanticProps(props, extraKeys = []) {
|
|
1670
2545
|
const internalKeys = /* @__PURE__ */ new Set([
|
|
1671
2546
|
"children",
|
|
@@ -1680,6 +2555,8 @@ function extractSemanticProps(props, extraKeys = []) {
|
|
|
1680
2555
|
"_config",
|
|
1681
2556
|
"config",
|
|
1682
2557
|
"previewText",
|
|
2558
|
+
"fonts",
|
|
2559
|
+
"lang",
|
|
1683
2560
|
"layout",
|
|
1684
2561
|
"collection",
|
|
1685
2562
|
...extraKeys
|
|
@@ -1696,68 +2573,313 @@ function nextCounter(counters, key) {
|
|
|
1696
2573
|
counters[key] = (counters[key] || 0) + 1;
|
|
1697
2574
|
return counters[key];
|
|
1698
2575
|
}
|
|
1699
|
-
function
|
|
1700
|
-
if (
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
2576
|
+
function toContentType(displayName) {
|
|
2577
|
+
if (displayName === "Paragraph") return "text";
|
|
2578
|
+
return displayName.toLowerCase();
|
|
2579
|
+
}
|
|
2580
|
+
function makeId(prefix, counter) {
|
|
2581
|
+
return `${prefix}_${counter}`;
|
|
2582
|
+
}
|
|
2583
|
+
function extractTextFromTextJson(textJson) {
|
|
2584
|
+
try {
|
|
2585
|
+
const parsed = JSON.parse(textJson);
|
|
2586
|
+
if (parsed.__html) return parsed.__html;
|
|
2587
|
+
const root = parsed?.root;
|
|
2588
|
+
if (!root?.children) return "";
|
|
2589
|
+
const paragraphs = [];
|
|
2590
|
+
for (const paragraph of root.children) {
|
|
2591
|
+
if (!paragraph?.children) continue;
|
|
2592
|
+
const parts2 = [];
|
|
2593
|
+
for (const node of paragraph.children) {
|
|
2594
|
+
if (node?.text) parts2.push(node.text);
|
|
2595
|
+
}
|
|
2596
|
+
paragraphs.push(parts2.join(""));
|
|
1710
2597
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
if (js) scripts.push(js);
|
|
1716
|
-
const headTags = head.tags?.(...headArgs);
|
|
1717
|
-
if (headTags) tags.push(...headTags);
|
|
2598
|
+
return paragraphs.join("\n");
|
|
2599
|
+
} catch {
|
|
2600
|
+
return "";
|
|
2601
|
+
}
|
|
1718
2602
|
}
|
|
1719
|
-
function
|
|
1720
|
-
const componentType =
|
|
2603
|
+
function processItem(element2, counters, layout = {}, metaName) {
|
|
2604
|
+
const componentType = element2.type;
|
|
1721
2605
|
const config = componentType[UNLAYER_CONFIG_KEY];
|
|
1722
|
-
if (!config)
|
|
1723
|
-
|
|
1724
|
-
|
|
2606
|
+
if (!config && componentType?.[KEPT]) {
|
|
2607
|
+
return processItem(componentType[KEPT], counters, layout, (componentType.displayName || componentType.name || "component").toLowerCase());
|
|
2608
|
+
}
|
|
2609
|
+
if (!config) {
|
|
2610
|
+
const name2 = componentName(element2) || "Unknown";
|
|
2611
|
+
throw new Error(
|
|
2612
|
+
`[Unlayer] renderToJson: <${name2}> is not a recognized Unlayer item component. Only components created with createItemComponent are supported.`
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
const { name, defaultValues, propMapper, slug } = config;
|
|
2616
|
+
const contentType = config.contentType ?? toContentType(name);
|
|
2617
|
+
const metaBase = `u_content_${metaName ?? config.metaName ?? contentType}`;
|
|
2618
|
+
const count = nextCounter(counters, metaBase);
|
|
2619
|
+
const id = makeId(metaBase, count);
|
|
2620
|
+
const { children, ...restProps } = element2.props;
|
|
1725
2621
|
const mappedValues = propMapper({ children, ...restProps });
|
|
1726
2622
|
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
1727
|
-
|
|
2623
|
+
if (contentType === "text" && finalValues.textJson) {
|
|
2624
|
+
finalValues.text = extractTextFromTextJson(finalValues.textJson);
|
|
2625
|
+
delete finalValues.textJson;
|
|
2626
|
+
}
|
|
2627
|
+
const values = {
|
|
2628
|
+
containerPadding: "10px",
|
|
2629
|
+
...finalValues,
|
|
2630
|
+
_meta: {
|
|
2631
|
+
htmlID: id,
|
|
2632
|
+
htmlClassNames: metaBase,
|
|
2633
|
+
...finalValues._meta || {}
|
|
2634
|
+
},
|
|
2635
|
+
selectable: true,
|
|
2636
|
+
draggable: true,
|
|
2637
|
+
duplicatable: true,
|
|
2638
|
+
deletable: true,
|
|
2639
|
+
hideable: true
|
|
2640
|
+
};
|
|
2641
|
+
const itemSrc = values.src;
|
|
2642
|
+
if (itemSrc && typeof itemSrc === "object" && itemSrc.autoWidth === false) {
|
|
2643
|
+
const availableWidth = contentSlotWidth({
|
|
2644
|
+
...layout,
|
|
2645
|
+
containerPadding: values.containerPadding
|
|
2646
|
+
});
|
|
2647
|
+
values.src = pinImageSrc(itemSrc, availableWidth);
|
|
2648
|
+
}
|
|
2649
|
+
return slug ? { type: contentType, slug, values } : { type: contentType, values };
|
|
2650
|
+
}
|
|
2651
|
+
function processColumn(element2, counters, layout = {}) {
|
|
2652
|
+
const count = nextCounter(counters, "u_column");
|
|
2653
|
+
const id = makeId("u_column", count);
|
|
2654
|
+
const semanticProps = extractSemanticProps(element2.props);
|
|
2655
|
+
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
2656
|
+
const values = mergeValues(COLUMN_DEFAULTS, mapped);
|
|
2657
|
+
const valuesWithMeta = {
|
|
2658
|
+
...values,
|
|
2659
|
+
_meta: {
|
|
2660
|
+
htmlID: id,
|
|
2661
|
+
htmlClassNames: "u_column",
|
|
2662
|
+
...values._meta || {}
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
const contents = [];
|
|
2666
|
+
const children = collectChildren(element2.props.children);
|
|
2667
|
+
const itemLayout = { ...layout, columnValues: valuesWithMeta };
|
|
2668
|
+
for (const child of children) {
|
|
2669
|
+
contents.push(processItem(child, counters, itemLayout));
|
|
2670
|
+
}
|
|
2671
|
+
return { contents, values: valuesWithMeta };
|
|
2672
|
+
}
|
|
2673
|
+
function processRow(element2, counters, parentLayout = {}) {
|
|
2674
|
+
const count = nextCounter(counters, "u_row");
|
|
2675
|
+
const id = makeId("u_row", count);
|
|
2676
|
+
const { layout, cells: propsCells } = element2.props;
|
|
2677
|
+
let cells;
|
|
2678
|
+
if (layout) {
|
|
2679
|
+
cells = layout.cells;
|
|
2680
|
+
} else if (propsCells) {
|
|
2681
|
+
cells = propsCells;
|
|
2682
|
+
} else {
|
|
2683
|
+
const columnCount = Math.max(
|
|
2684
|
+
1,
|
|
2685
|
+
collectChildren(element2.props.children).filter(
|
|
2686
|
+
(child) => getDisplayName(child) === "Column"
|
|
2687
|
+
).length
|
|
2688
|
+
);
|
|
2689
|
+
cells = Array(columnCount).fill(1);
|
|
2690
|
+
}
|
|
2691
|
+
const semanticProps = extractSemanticProps(element2.props, ["layout"]);
|
|
2692
|
+
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
2693
|
+
const values = mergeValues(ROW_DEFAULTS, mapped);
|
|
2694
|
+
const valuesWithMeta = {
|
|
2695
|
+
...values,
|
|
2696
|
+
columns: values.columns ?? false,
|
|
2697
|
+
cells,
|
|
2698
|
+
_meta: {
|
|
2699
|
+
htmlID: id,
|
|
2700
|
+
htmlClassNames: "u_row",
|
|
2701
|
+
...values._meta || {}
|
|
2702
|
+
},
|
|
2703
|
+
selectable: true,
|
|
2704
|
+
draggable: true,
|
|
2705
|
+
duplicatable: true,
|
|
2706
|
+
deletable: true,
|
|
2707
|
+
hideable: true
|
|
2708
|
+
};
|
|
2709
|
+
const columns = [];
|
|
2710
|
+
const children = collectChildren(element2.props.children);
|
|
2711
|
+
const columnLayout = {
|
|
2712
|
+
bodyValues: parentLayout.bodyValues,
|
|
2713
|
+
rowValues: valuesWithMeta,
|
|
2714
|
+
rowCells: cells
|
|
2715
|
+
};
|
|
2716
|
+
let columnIndex = 0;
|
|
2717
|
+
for (const child of children) {
|
|
2718
|
+
const name = getDisplayName(child);
|
|
2719
|
+
if (name === "Column") {
|
|
2720
|
+
columns.push(
|
|
2721
|
+
processColumn(child, counters, { ...columnLayout, columnIndex })
|
|
2722
|
+
);
|
|
2723
|
+
columnIndex += 1;
|
|
2724
|
+
} else {
|
|
2725
|
+
console.warn(
|
|
2726
|
+
`[Unlayer] renderToJson: <${componentName(child) ?? String(child.type)}> is not a valid Row child. Only <Column> is allowed.`
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
return { cells, columns, values: valuesWithMeta };
|
|
2731
|
+
}
|
|
2732
|
+
function processBody(element2, counters) {
|
|
2733
|
+
const id = "u_body";
|
|
2734
|
+
const semanticProps = extractSemanticProps(element2.props);
|
|
2735
|
+
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
2736
|
+
const values = mergeValues(BODY_DEFAULTS, mapped);
|
|
2737
|
+
if ("textDirection" in values) {
|
|
2738
|
+
const direction = textDirectionOf(values.textDirection);
|
|
2739
|
+
if (direction) values.textDirection = direction;
|
|
2740
|
+
else delete values.textDirection;
|
|
2741
|
+
}
|
|
2742
|
+
const previewText = element2.props.previewText;
|
|
2743
|
+
if (previewText !== void 0) {
|
|
2744
|
+
values.preheaderText = previewText;
|
|
2745
|
+
}
|
|
2746
|
+
const valuesWithMeta = {
|
|
2747
|
+
...values,
|
|
2748
|
+
_meta: {
|
|
2749
|
+
htmlID: id,
|
|
2750
|
+
htmlClassNames: "u_body",
|
|
2751
|
+
...values._meta || {}
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
const rows = [];
|
|
2755
|
+
const children = collectChildren(element2.props.children);
|
|
2756
|
+
for (const child of children) {
|
|
2757
|
+
const name = getDisplayName(child);
|
|
2758
|
+
if (name === "Row") {
|
|
2759
|
+
rows.push(processRow(child, counters, { bodyValues: valuesWithMeta }));
|
|
2760
|
+
} else {
|
|
2761
|
+
console.warn(
|
|
2762
|
+
`[Unlayer] renderToJson: <${componentName(child) ?? String(child.type)}> is not a valid Body child. Only <Row> is allowed.`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return {
|
|
2767
|
+
rows,
|
|
2768
|
+
values: valuesWithMeta
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
function renderRowToJson(element2) {
|
|
2772
|
+
const displayName = getDisplayName(element2);
|
|
2773
|
+
if (displayName !== "Row") {
|
|
2774
|
+
throw new Error(
|
|
2775
|
+
`[Unlayer] renderRowToJson: Element must be <Row>, but got <${componentName(element2) || "unknown"}>. For full designs, use renderToJson instead.`
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
const counters = {};
|
|
2779
|
+
return withRenderScope(() => processRow(normalizedChildren(element2).find((c) => React4.isValidElement(c)) ?? element2, counters));
|
|
2780
|
+
}
|
|
2781
|
+
function renderToJson(element2) {
|
|
2782
|
+
return withRenderScope(() => designJson(element2));
|
|
2783
|
+
}
|
|
2784
|
+
function designJson(element2) {
|
|
2785
|
+
element2 = unwrapForJson(element2);
|
|
2786
|
+
const displayName = getDisplayName(element2);
|
|
2787
|
+
if (displayName && ROOT_NAMES.has(displayName)) element2 = normalizedRoot(element2);
|
|
2788
|
+
if (!displayName || !ROOT_NAMES.has(displayName)) {
|
|
2789
|
+
throw new Error(
|
|
2790
|
+
`[Unlayer] renderToJson: Root element must be <Body>, <Email>, <Page>, or <Document>, but got <${componentName(element2) || "unknown"}>. Wrap your content: <Body><Row><Column>...</Column></Row></Body>`
|
|
2791
|
+
);
|
|
2792
|
+
}
|
|
2793
|
+
const counters = {};
|
|
2794
|
+
const body = processBody(element2, counters);
|
|
2795
|
+
return {
|
|
2796
|
+
counters,
|
|
2797
|
+
body,
|
|
2798
|
+
schemaVersion
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
// src/utils/extract-head.ts
|
|
2803
|
+
function ensureMeta(values, type, index = 0) {
|
|
2804
|
+
return {
|
|
2805
|
+
...values,
|
|
2806
|
+
_meta: {
|
|
2807
|
+
htmlID: `${["row", "column"].includes(type) ? "u" : "u_content"}_${type.toLowerCase()}_${index + 1}`,
|
|
2808
|
+
htmlClassNames: `u_content_${type.toLowerCase()}`,
|
|
2809
|
+
...values._meta || {}
|
|
2810
|
+
}
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
var collectChildren2 = (node) => React4.Children.toArray(node).filter(React4.isValidElement);
|
|
2814
|
+
function callHead(head, values, bodyValues, displayMode, headConfig, styles, scripts, tags) {
|
|
2815
|
+
if (!head) return;
|
|
2816
|
+
const headArgs = [
|
|
2817
|
+
values,
|
|
2818
|
+
bodyValues,
|
|
2819
|
+
{
|
|
2820
|
+
displayMode,
|
|
2821
|
+
isViewer: false,
|
|
2822
|
+
variant: null,
|
|
2823
|
+
type: "rows",
|
|
2824
|
+
headConfig
|
|
2825
|
+
}
|
|
2826
|
+
];
|
|
2827
|
+
const css2 = head.css?.(...headArgs);
|
|
2828
|
+
if (css2) styles.push(css2);
|
|
2829
|
+
const js = head.js?.(...headArgs);
|
|
2830
|
+
if (js) scripts.push(js);
|
|
2831
|
+
const headTags = head.tags?.(...headArgs);
|
|
2832
|
+
if (headTags) tags.push(...headTags);
|
|
2833
|
+
}
|
|
2834
|
+
function walkItem(element2, bodyValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles) {
|
|
2835
|
+
const componentType = element2.type;
|
|
2836
|
+
const config = componentType[UNLAYER_CONFIG_KEY];
|
|
2837
|
+
if (!config) {
|
|
2838
|
+
if (componentType?.[KEPT]) nextCounter(counters, `u_content_${(componentType.displayName || componentType.name || "component").toLowerCase()}`);
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
const { name, defaultValues, propMapper } = config;
|
|
2842
|
+
const { children, ...restProps } = element2.props;
|
|
2843
|
+
const mappedValues = propMapper({ children, ...restProps });
|
|
2844
|
+
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
2845
|
+
const contentType = config.metaName ?? name.toLowerCase();
|
|
1728
2846
|
const count = nextCounter(counters, `u_content_${contentType}`);
|
|
1729
2847
|
const valuesWithMeta = ensureMeta(finalValues, contentType, count - 1);
|
|
2848
|
+
collectDeviceStyles(valuesWithMeta, "contents", name, displayMode, deviceStyles);
|
|
1730
2849
|
const head = config.head ?? heads[name];
|
|
1731
2850
|
callHead(head, valuesWithMeta, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1732
2851
|
}
|
|
1733
|
-
function walkColumn(
|
|
1734
|
-
const semanticProps = extractSemanticProps(
|
|
2852
|
+
function walkColumn(element2, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles) {
|
|
2853
|
+
const semanticProps = extractSemanticProps(element2.props);
|
|
1735
2854
|
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
1736
2855
|
const count = nextCounter(counters, "u_column");
|
|
1737
2856
|
const columnValues = ensureMeta(mergeValues(COLUMN_DEFAULTS, mapped), "column", count - 1);
|
|
2857
|
+
collectDeviceStyles(columnValues, "columns", "Column", displayMode, deviceStyles);
|
|
1738
2858
|
const columnHead = heads["Column"];
|
|
1739
2859
|
callHead(columnHead, columnValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1740
|
-
const children =
|
|
2860
|
+
const children = collectChildren2(element2.props.children);
|
|
1741
2861
|
for (const child of children) {
|
|
1742
|
-
walkItem(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
2862
|
+
walkItem(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles);
|
|
1743
2863
|
}
|
|
1744
2864
|
}
|
|
1745
|
-
function walkRow(
|
|
1746
|
-
const semanticProps = extractSemanticProps(
|
|
2865
|
+
function walkRow(element2, bodyValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles) {
|
|
2866
|
+
const semanticProps = extractSemanticProps(element2.props, ["layout"]);
|
|
1747
2867
|
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
1748
2868
|
const count = nextCounter(counters, "u_row");
|
|
1749
2869
|
const rowValues = ensureMeta(mergeValues(ROW_DEFAULTS, mapped), "row", count - 1);
|
|
2870
|
+
collectDeviceStyles(rowValues, "rows", "Row", displayMode, deviceStyles);
|
|
1750
2871
|
const rowHead = heads["Row"];
|
|
1751
2872
|
callHead(rowHead, rowValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1752
|
-
const children =
|
|
2873
|
+
const children = collectChildren2(element2.props.children);
|
|
1753
2874
|
for (const child of children) {
|
|
1754
2875
|
const name = getDisplayName(child);
|
|
1755
2876
|
if (name === "Column") {
|
|
1756
|
-
walkColumn(child, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
2877
|
+
walkColumn(child, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles);
|
|
1757
2878
|
}
|
|
1758
2879
|
}
|
|
1759
2880
|
}
|
|
1760
|
-
function extractHeadFromTree(
|
|
2881
|
+
function extractHeadFromTree(element2, options) {
|
|
2882
|
+
element2 = normalizedRoot(element2);
|
|
1761
2883
|
const { displayMode } = options;
|
|
1762
2884
|
const headConfig = {
|
|
1763
2885
|
hasFeature: options.headConfig?.hasFeature ?? (() => false),
|
|
@@ -1767,18 +2889,22 @@ function extractHeadFromTree(element, options) {
|
|
|
1767
2889
|
const scripts = [];
|
|
1768
2890
|
const tags = [];
|
|
1769
2891
|
const counters = {};
|
|
1770
|
-
const
|
|
2892
|
+
const deviceStyles = {};
|
|
2893
|
+
const semanticProps = extractSemanticProps(element2.props);
|
|
1771
2894
|
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1772
2895
|
const bodyValues = ensureMeta(mergeValues(BODY_DEFAULTS, mapped), "body", 0);
|
|
1773
2896
|
const bodyHead = heads["Body"];
|
|
1774
2897
|
callHead(bodyHead, bodyValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1775
|
-
const children =
|
|
2898
|
+
const children = collectChildren2(element2.props.children);
|
|
1776
2899
|
for (const child of children) {
|
|
1777
2900
|
const name = getDisplayName(child);
|
|
1778
2901
|
if (name === "Row") {
|
|
1779
|
-
walkRow(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
2902
|
+
walkRow(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags, deviceStyles);
|
|
1780
2903
|
}
|
|
1781
2904
|
}
|
|
2905
|
+
styles.unshift(deviceVisibilityCss(displayMode));
|
|
2906
|
+
const overrides = deviceStylesCss(deviceStyles, displayMode);
|
|
2907
|
+
if (overrides) styles.push(overrides);
|
|
1782
2908
|
const uniqueTags = [...new Set(tags.filter(Boolean))];
|
|
1783
2909
|
return {
|
|
1784
2910
|
css: styles.filter(Boolean).join("\n"),
|
|
@@ -1798,7 +2924,7 @@ function emailLayout(args) {
|
|
|
1798
2924
|
fontLinks && `<!--[if !mso]><!-->${fontLinks}<!--<![endif]-->`
|
|
1799
2925
|
].filter(Boolean).join("\n ");
|
|
1800
2926
|
return `<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
1801
|
-
<html${dir} xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
2927
|
+
<html${dir}${args.lang || ""} xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
1802
2928
|
<head>
|
|
1803
2929
|
<!--[if gte mso 9]>
|
|
1804
2930
|
<xml>
|
|
@@ -1822,7 +2948,7 @@ function documentLayout(args) {
|
|
|
1822
2948
|
const { dir, titleTag, tagLines, styleTag, scriptTag, fontLinks, body } = args;
|
|
1823
2949
|
const head = [titleTag, tagLines, styleTag, scriptTag, fontLinks].filter(Boolean).join("\n ");
|
|
1824
2950
|
return `<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
1825
|
-
<html${dir} xmlns="http://www.w3.org/1999/xhtml">
|
|
2951
|
+
<html${dir}${args.lang || ""} xmlns="http://www.w3.org/1999/xhtml">
|
|
1826
2952
|
<head>
|
|
1827
2953
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
1828
2954
|
${head}
|
|
@@ -1835,7 +2961,7 @@ function webLayout(args) {
|
|
|
1835
2961
|
const { dir, titleTag, tagLines, styleTag, scriptTag, fontLinks, body } = args;
|
|
1836
2962
|
const head = [titleTag, fontLinks, tagLines, styleTag, scriptTag].filter(Boolean).join("\n ");
|
|
1837
2963
|
return `<!doctype html>
|
|
1838
|
-
<html${dir}>
|
|
2964
|
+
<html${dir}${args.lang || ""}>
|
|
1839
2965
|
<head>
|
|
1840
2966
|
<meta charset="utf-8">
|
|
1841
2967
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
@@ -1856,20 +2982,25 @@ var MODE_BY_WRAPPER = {
|
|
|
1856
2982
|
Page: "web",
|
|
1857
2983
|
Document: "document"
|
|
1858
2984
|
};
|
|
1859
|
-
function
|
|
1860
|
-
const
|
|
2985
|
+
function resolveConfig(element2, config) {
|
|
2986
|
+
const props = element2.props;
|
|
2987
|
+
const textDirection = textDirectionOf(props.textDirection ?? props.values?.textDirection);
|
|
2988
|
+
return { ...DEFAULT_CONFIG, ...textDirection !== void 0 ? { textDirection } : {}, ...config };
|
|
2989
|
+
}
|
|
2990
|
+
function resolveDisplayMode(element2, mergedConfig) {
|
|
2991
|
+
const displayName = element2.type?.displayName;
|
|
1861
2992
|
const wrapperMode = displayName ? MODE_BY_WRAPPER[displayName] : void 0;
|
|
1862
|
-
return wrapperMode ??
|
|
2993
|
+
return wrapperMode ?? element2.props.mode ?? mergedConfig.mode ?? "web";
|
|
1863
2994
|
}
|
|
1864
|
-
function renderBody(
|
|
1865
|
-
const mergedConfig =
|
|
2995
|
+
function renderBody(element2, config, name = "renderToHtml") {
|
|
2996
|
+
const mergedConfig = resolveConfig(element2, config);
|
|
1866
2997
|
try {
|
|
1867
|
-
const enriched =
|
|
2998
|
+
const enriched = React4.cloneElement(element2, { config: mergedConfig });
|
|
1868
2999
|
return renderToStaticMarkup(enriched);
|
|
1869
3000
|
} catch (error) {
|
|
1870
3001
|
const message = error instanceof Error ? error.message : String(error);
|
|
1871
3002
|
throw new Error(
|
|
1872
|
-
`[Unlayer]
|
|
3003
|
+
`[Unlayer] ${name} failed: ${message}
|
|
1873
3004
|
Tip: Ensure your tree uses Body > Row > Column > Item structure.`
|
|
1874
3005
|
);
|
|
1875
3006
|
}
|
|
@@ -1881,30 +3012,41 @@ function stripOuterDiv(html) {
|
|
|
1881
3012
|
function escapeForHtml(text) {
|
|
1882
3013
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1883
3014
|
}
|
|
1884
|
-
function renderToHtml(
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
3015
|
+
function renderToHtml(element2, options) {
|
|
3016
|
+
return withRenderScope(() => htmlDocument(element2, options));
|
|
3017
|
+
}
|
|
3018
|
+
function styleText(css2) {
|
|
3019
|
+
return css2.replace(/<\/(style)/gi, "<\\/$1");
|
|
3020
|
+
}
|
|
3021
|
+
function htmlDocument(element2, options) {
|
|
3022
|
+
const { title, lang: optionLang, fonts: optionFonts = [], ...config } = options ?? {};
|
|
3023
|
+
element2 = htmlRoot(element2, "renderToHtml");
|
|
3024
|
+
const rootFonts = element2.props?.fonts ?? [];
|
|
3025
|
+
const fonts = [...rootFonts, ...optionFonts].filter((font, i, all) => font?.url && all.findIndex((f) => f?.url === font.url) === i);
|
|
3026
|
+
const lang = optionLang ?? element2.props.lang;
|
|
3027
|
+
const mergedConfig = resolveConfig(element2, config);
|
|
3028
|
+
const displayMode = resolveDisplayMode(element2, mergedConfig);
|
|
3029
|
+
const wrapperName = element2.type?.displayName;
|
|
1889
3030
|
const isUnlayerWrapper = wrapperName === "Body" || (wrapperName ? wrapperName in MODE_BY_WRAPPER : false);
|
|
1890
|
-
const rawBody = renderBody(
|
|
3031
|
+
const rawBody = renderBody(element2, config);
|
|
1891
3032
|
const body = isUnlayerWrapper ? stripOuterDiv(rawBody) : rawBody;
|
|
1892
|
-
const { css, js, tags } = extractHeadFromTree(
|
|
3033
|
+
const { css: css2, js, tags } = extractHeadFromTree(element2, {
|
|
1893
3034
|
displayMode,
|
|
1894
3035
|
headConfig: mergedConfig.headConfig
|
|
1895
3036
|
});
|
|
1896
3037
|
const layoutArgs = {
|
|
3038
|
+
lang: lang ? ` lang="${escapeForHtml(String(lang))}"` : "",
|
|
1897
3039
|
dir: mergedConfig.textDirection ? ` dir="${escapeForHtml(String(mergedConfig.textDirection))}"` : "",
|
|
1898
3040
|
titleTag: title ? `<title>${escapeForHtml(title.trim())}</title>` : "",
|
|
1899
|
-
styleTag:
|
|
1900
|
-
${
|
|
3041
|
+
styleTag: css2 ? `<style type="text/css">
|
|
3042
|
+
${styleText(css2)}
|
|
1901
3043
|
</style>` : "",
|
|
1902
3044
|
scriptTag: js ? `<script type="application/javascript">
|
|
1903
3045
|
${js}
|
|
1904
3046
|
</script>` : "",
|
|
1905
3047
|
tagLines: tags.join("\n"),
|
|
1906
3048
|
fontLinks: fonts.filter((font) => font?.url).map(
|
|
1907
|
-
(font) => `<link href="${escapeForHtml(font.url)}" rel="stylesheet" type="text/css">`
|
|
3049
|
+
(font) => `<link href="${escapeForHtml(String(font.url))}" rel="stylesheet" type="text/css">`
|
|
1908
3050
|
).join(""),
|
|
1909
3051
|
body
|
|
1910
3052
|
};
|
|
@@ -1912,21 +3054,29 @@ ${js}
|
|
|
1912
3054
|
if (displayMode === "document") return documentLayout(layoutArgs);
|
|
1913
3055
|
return webLayout(layoutArgs);
|
|
1914
3056
|
}
|
|
1915
|
-
function renderToPlainText(
|
|
1916
|
-
const html =
|
|
3057
|
+
function renderToPlainText(element2, config) {
|
|
3058
|
+
const html = withRenderScope(() => {
|
|
3059
|
+
const root = htmlRoot(element2, "renderToPlainText");
|
|
3060
|
+
normalizedRoot(root);
|
|
3061
|
+
return renderBody(root, config, "renderToPlainText");
|
|
3062
|
+
});
|
|
1917
3063
|
return htmlToPlainText(html);
|
|
1918
3064
|
}
|
|
1919
|
-
function renderToHtmlParts(
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
3065
|
+
function renderToHtmlParts(element2, config) {
|
|
3066
|
+
return withRenderScope(() => htmlParts(element2, config));
|
|
3067
|
+
}
|
|
3068
|
+
function htmlParts(element2, config) {
|
|
3069
|
+
element2 = htmlRoot(element2, "renderToHtmlParts");
|
|
3070
|
+
const body = renderBody(element2, config, "renderToHtmlParts");
|
|
3071
|
+
const mergedConfig = resolveConfig(element2, config);
|
|
3072
|
+
const displayMode = resolveDisplayMode(element2, mergedConfig);
|
|
3073
|
+
const { css: css2, js, tags } = extractHeadFromTree(element2, {
|
|
1924
3074
|
displayMode,
|
|
1925
3075
|
headConfig: mergedConfig.headConfig
|
|
1926
3076
|
});
|
|
1927
3077
|
const headParts = [];
|
|
1928
|
-
if (
|
|
1929
|
-
headParts.push(`<style>${
|
|
3078
|
+
if (css2) {
|
|
3079
|
+
headParts.push(`<style>${styleText(css2)}</style>`);
|
|
1930
3080
|
}
|
|
1931
3081
|
if (js) {
|
|
1932
3082
|
headParts.push(`<script>${js}</script>`);
|
|
@@ -1937,291 +3087,11 @@ function renderToHtmlParts(element, config) {
|
|
|
1937
3087
|
return {
|
|
1938
3088
|
head: headParts.join("\n"),
|
|
1939
3089
|
body,
|
|
1940
|
-
css,
|
|
3090
|
+
css: css2,
|
|
1941
3091
|
js,
|
|
1942
3092
|
tags
|
|
1943
3093
|
};
|
|
1944
3094
|
}
|
|
1945
|
-
var schemaVersion = schemaVersion$1 ?? 24;
|
|
1946
|
-
function getDisplayName2(element) {
|
|
1947
|
-
const type = element.type;
|
|
1948
|
-
return type?.displayName || type?.name;
|
|
1949
|
-
}
|
|
1950
|
-
var VALID_ROOTS = /* @__PURE__ */ new Set(["Body", "Email", "Page", "Document"]);
|
|
1951
|
-
function unwrapRoot(element) {
|
|
1952
|
-
let current = element;
|
|
1953
|
-
for (let depth = 0; depth < 10; depth++) {
|
|
1954
|
-
const name = getDisplayName2(current);
|
|
1955
|
-
if (name && VALID_ROOTS.has(name)) break;
|
|
1956
|
-
const type = current.type;
|
|
1957
|
-
const isPlainFunctionComponent = typeof type === "function" && !type.prototype?.isReactComponent;
|
|
1958
|
-
if (!isPlainFunctionComponent) break;
|
|
1959
|
-
let produced;
|
|
1960
|
-
try {
|
|
1961
|
-
produced = type({ ...current.props });
|
|
1962
|
-
} catch (cause) {
|
|
1963
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
1964
|
-
throw new Error(
|
|
1965
|
-
`[Unlayer] renderToJson: could not unwrap <${name || "wrapper"}>. A wrapper must be a plain component that synchronously returns a root (<Email>, <Page>, <Document>, or <Body>) and uses no React hooks. Pass the root element directly \u2014 e.g. renderToJson(<Email>\u2026</Email>). (${detail})`
|
|
1966
|
-
);
|
|
1967
|
-
}
|
|
1968
|
-
if (!React.isValidElement(produced)) break;
|
|
1969
|
-
current = produced;
|
|
1970
|
-
}
|
|
1971
|
-
return current;
|
|
1972
|
-
}
|
|
1973
|
-
function collectChildren2(node) {
|
|
1974
|
-
const result = [];
|
|
1975
|
-
React.Children.forEach(node, (child) => {
|
|
1976
|
-
if (React.isValidElement(child)) {
|
|
1977
|
-
result.push(child);
|
|
1978
|
-
}
|
|
1979
|
-
});
|
|
1980
|
-
return result;
|
|
1981
|
-
}
|
|
1982
|
-
function extractSemanticProps2(props, extraKeys = []) {
|
|
1983
|
-
const internalKeys = /* @__PURE__ */ new Set([
|
|
1984
|
-
"children",
|
|
1985
|
-
"mode",
|
|
1986
|
-
"className",
|
|
1987
|
-
"style",
|
|
1988
|
-
"index",
|
|
1989
|
-
"colIndex",
|
|
1990
|
-
"cells",
|
|
1991
|
-
"bodyValues",
|
|
1992
|
-
"rowValues",
|
|
1993
|
-
"_config",
|
|
1994
|
-
"config",
|
|
1995
|
-
"previewText",
|
|
1996
|
-
"layout",
|
|
1997
|
-
"collection",
|
|
1998
|
-
...extraKeys
|
|
1999
|
-
]);
|
|
2000
|
-
const result = {};
|
|
2001
|
-
for (const [key, value] of Object.entries(props)) {
|
|
2002
|
-
if (!internalKeys.has(key) && value !== void 0) {
|
|
2003
|
-
result[key] = value;
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
return result;
|
|
2007
|
-
}
|
|
2008
|
-
function nextCounter2(counters, key) {
|
|
2009
|
-
counters[key] = (counters[key] || 0) + 1;
|
|
2010
|
-
return counters[key];
|
|
2011
|
-
}
|
|
2012
|
-
function toContentType(displayName) {
|
|
2013
|
-
if (displayName === "Paragraph") return "text";
|
|
2014
|
-
return displayName.toLowerCase();
|
|
2015
|
-
}
|
|
2016
|
-
function makeId(prefix, counter) {
|
|
2017
|
-
return `${prefix}_${counter}`;
|
|
2018
|
-
}
|
|
2019
|
-
function extractTextFromTextJson(textJson) {
|
|
2020
|
-
try {
|
|
2021
|
-
const parsed = JSON.parse(textJson);
|
|
2022
|
-
if (parsed.__html) return parsed.__html;
|
|
2023
|
-
const root = parsed?.root;
|
|
2024
|
-
if (!root?.children) return "";
|
|
2025
|
-
const paragraphs = [];
|
|
2026
|
-
for (const paragraph of root.children) {
|
|
2027
|
-
if (!paragraph?.children) continue;
|
|
2028
|
-
const parts = [];
|
|
2029
|
-
for (const node of paragraph.children) {
|
|
2030
|
-
if (node?.text) parts.push(node.text);
|
|
2031
|
-
}
|
|
2032
|
-
paragraphs.push(parts.join(""));
|
|
2033
|
-
}
|
|
2034
|
-
return paragraphs.join("\n");
|
|
2035
|
-
} catch {
|
|
2036
|
-
return "";
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
function processItem(element, counters, layout = {}) {
|
|
2040
|
-
const componentType = element.type;
|
|
2041
|
-
const config = componentType[UNLAYER_CONFIG_KEY];
|
|
2042
|
-
if (!config) {
|
|
2043
|
-
const name2 = getDisplayName2(element) || "Unknown";
|
|
2044
|
-
throw new Error(
|
|
2045
|
-
`[Unlayer] renderToJson: <${name2}> is not a recognized Unlayer item component. Only components created with createItemComponent are supported.`
|
|
2046
|
-
);
|
|
2047
|
-
}
|
|
2048
|
-
const { name, defaultValues, propMapper, slug } = config;
|
|
2049
|
-
const contentType = config.contentType ?? toContentType(name);
|
|
2050
|
-
const metaBase = `u_content_${config.metaName ?? contentType}`;
|
|
2051
|
-
const count = nextCounter2(counters, metaBase);
|
|
2052
|
-
const id = makeId(metaBase, count);
|
|
2053
|
-
const { children, ...restProps } = element.props;
|
|
2054
|
-
const mappedValues = propMapper({ children, ...restProps });
|
|
2055
|
-
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
2056
|
-
if (contentType === "text" && finalValues.textJson) {
|
|
2057
|
-
finalValues.text = extractTextFromTextJson(finalValues.textJson);
|
|
2058
|
-
delete finalValues.textJson;
|
|
2059
|
-
}
|
|
2060
|
-
const values = {
|
|
2061
|
-
containerPadding: "10px",
|
|
2062
|
-
...finalValues,
|
|
2063
|
-
_meta: {
|
|
2064
|
-
htmlID: id,
|
|
2065
|
-
htmlClassNames: metaBase,
|
|
2066
|
-
...finalValues._meta || {}
|
|
2067
|
-
},
|
|
2068
|
-
selectable: true,
|
|
2069
|
-
draggable: true,
|
|
2070
|
-
duplicatable: true,
|
|
2071
|
-
deletable: true,
|
|
2072
|
-
hideable: true
|
|
2073
|
-
};
|
|
2074
|
-
const itemSrc = values.src;
|
|
2075
|
-
if (itemSrc && typeof itemSrc === "object" && itemSrc.autoWidth === false) {
|
|
2076
|
-
const availableWidth = contentSlotWidth({
|
|
2077
|
-
...layout,
|
|
2078
|
-
containerPadding: values.containerPadding
|
|
2079
|
-
});
|
|
2080
|
-
values.src = pinImageSrc(itemSrc, availableWidth);
|
|
2081
|
-
}
|
|
2082
|
-
return slug ? { type: contentType, slug, values } : { type: contentType, values };
|
|
2083
|
-
}
|
|
2084
|
-
function processColumn(element, counters, layout = {}) {
|
|
2085
|
-
const count = nextCounter2(counters, "u_column");
|
|
2086
|
-
const id = makeId("u_column", count);
|
|
2087
|
-
const semanticProps = extractSemanticProps2(element.props);
|
|
2088
|
-
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
2089
|
-
const values = mergeValues(COLUMN_DEFAULTS, mapped);
|
|
2090
|
-
const valuesWithMeta = {
|
|
2091
|
-
...values,
|
|
2092
|
-
_meta: {
|
|
2093
|
-
htmlID: id,
|
|
2094
|
-
htmlClassNames: "u_column",
|
|
2095
|
-
...values._meta || {}
|
|
2096
|
-
}
|
|
2097
|
-
};
|
|
2098
|
-
const contents = [];
|
|
2099
|
-
const children = collectChildren2(element.props.children);
|
|
2100
|
-
const itemLayout = { ...layout, columnValues: valuesWithMeta };
|
|
2101
|
-
for (const child of children) {
|
|
2102
|
-
contents.push(processItem(child, counters, itemLayout));
|
|
2103
|
-
}
|
|
2104
|
-
return { contents, values: valuesWithMeta };
|
|
2105
|
-
}
|
|
2106
|
-
function processRow(element, counters, parentLayout = {}) {
|
|
2107
|
-
const count = nextCounter2(counters, "u_row");
|
|
2108
|
-
const id = makeId("u_row", count);
|
|
2109
|
-
const { layout, cells: propsCells } = element.props;
|
|
2110
|
-
let cells;
|
|
2111
|
-
if (layout) {
|
|
2112
|
-
cells = layout.cells;
|
|
2113
|
-
} else if (propsCells) {
|
|
2114
|
-
cells = propsCells;
|
|
2115
|
-
} else {
|
|
2116
|
-
const columnCount = Math.max(
|
|
2117
|
-
1,
|
|
2118
|
-
collectChildren2(element.props.children).filter(
|
|
2119
|
-
(child) => getDisplayName2(child) === "Column"
|
|
2120
|
-
).length
|
|
2121
|
-
);
|
|
2122
|
-
cells = Array(columnCount).fill(1);
|
|
2123
|
-
}
|
|
2124
|
-
const semanticProps = extractSemanticProps2(element.props, ["layout"]);
|
|
2125
|
-
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
2126
|
-
const values = mergeValues(ROW_DEFAULTS, mapped);
|
|
2127
|
-
const valuesWithMeta = {
|
|
2128
|
-
...values,
|
|
2129
|
-
columns: values.columns ?? false,
|
|
2130
|
-
cells,
|
|
2131
|
-
_meta: {
|
|
2132
|
-
htmlID: id,
|
|
2133
|
-
htmlClassNames: "u_row",
|
|
2134
|
-
...values._meta || {}
|
|
2135
|
-
},
|
|
2136
|
-
selectable: true,
|
|
2137
|
-
draggable: true,
|
|
2138
|
-
duplicatable: true,
|
|
2139
|
-
deletable: true,
|
|
2140
|
-
hideable: true
|
|
2141
|
-
};
|
|
2142
|
-
const columns = [];
|
|
2143
|
-
const children = collectChildren2(element.props.children);
|
|
2144
|
-
const columnLayout = {
|
|
2145
|
-
bodyValues: parentLayout.bodyValues,
|
|
2146
|
-
rowValues: valuesWithMeta,
|
|
2147
|
-
rowCells: cells
|
|
2148
|
-
};
|
|
2149
|
-
let columnIndex = 0;
|
|
2150
|
-
for (const child of children) {
|
|
2151
|
-
const name = getDisplayName2(child);
|
|
2152
|
-
if (name === "Column") {
|
|
2153
|
-
columns.push(
|
|
2154
|
-
processColumn(child, counters, { ...columnLayout, columnIndex })
|
|
2155
|
-
);
|
|
2156
|
-
columnIndex += 1;
|
|
2157
|
-
} else {
|
|
2158
|
-
console.warn(
|
|
2159
|
-
`[Unlayer] renderToJson: <${name}> is not a valid Row child. Only <Column> is allowed.`
|
|
2160
|
-
);
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
return { cells, columns, values: valuesWithMeta };
|
|
2164
|
-
}
|
|
2165
|
-
function processBody(element, counters) {
|
|
2166
|
-
const id = "u_body";
|
|
2167
|
-
const semanticProps = extractSemanticProps2(element.props);
|
|
2168
|
-
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
2169
|
-
const values = mergeValues(BODY_DEFAULTS, mapped);
|
|
2170
|
-
const previewText = element.props.previewText;
|
|
2171
|
-
if (previewText !== void 0) {
|
|
2172
|
-
values.preheaderText = previewText;
|
|
2173
|
-
}
|
|
2174
|
-
const valuesWithMeta = {
|
|
2175
|
-
...values,
|
|
2176
|
-
_meta: {
|
|
2177
|
-
htmlID: id,
|
|
2178
|
-
htmlClassNames: "u_body",
|
|
2179
|
-
...values._meta || {}
|
|
2180
|
-
}
|
|
2181
|
-
};
|
|
2182
|
-
const rows = [];
|
|
2183
|
-
const children = collectChildren2(element.props.children);
|
|
2184
|
-
for (const child of children) {
|
|
2185
|
-
const name = getDisplayName2(child);
|
|
2186
|
-
if (name === "Row") {
|
|
2187
|
-
rows.push(processRow(child, counters, { bodyValues: valuesWithMeta }));
|
|
2188
|
-
} else {
|
|
2189
|
-
console.warn(
|
|
2190
|
-
`[Unlayer] renderToJson: <${name}> is not a valid Body child. Only <Row> is allowed.`
|
|
2191
|
-
);
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
return {
|
|
2195
|
-
rows,
|
|
2196
|
-
values: valuesWithMeta
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
function renderRowToJson(element) {
|
|
2200
|
-
const displayName = getDisplayName2(element);
|
|
2201
|
-
if (displayName !== "Row") {
|
|
2202
|
-
throw new Error(
|
|
2203
|
-
`[Unlayer] renderRowToJson: Element must be <Row>, but got <${displayName || "unknown"}>. For full designs, use renderToJson instead.`
|
|
2204
|
-
);
|
|
2205
|
-
}
|
|
2206
|
-
const counters = {};
|
|
2207
|
-
return processRow(element, counters);
|
|
2208
|
-
}
|
|
2209
|
-
function renderToJson(element) {
|
|
2210
|
-
element = unwrapRoot(element);
|
|
2211
|
-
const displayName = getDisplayName2(element);
|
|
2212
|
-
if (!displayName || !VALID_ROOTS.has(displayName)) {
|
|
2213
|
-
throw new Error(
|
|
2214
|
-
`[Unlayer] renderToJson: Root element must be <Body>, <Email>, <Page>, or <Document>, but got <${displayName || "unknown"}>. Wrap your content: <Body><Row><Column>...</Column></Row></Body>`
|
|
2215
|
-
);
|
|
2216
|
-
}
|
|
2217
|
-
const counters = {};
|
|
2218
|
-
const body = processBody(element, counters);
|
|
2219
|
-
return {
|
|
2220
|
-
counters,
|
|
2221
|
-
body,
|
|
2222
|
-
schemaVersion
|
|
2223
|
-
};
|
|
2224
|
-
}
|
|
2225
3095
|
|
|
2226
3096
|
// src/utils/register-tool.tsx
|
|
2227
3097
|
function initialValuesFromOptions(options) {
|