@unlayer/react-elements 0.1.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 +432 -0
- package/dist/index.cjs +1621 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +852 -0
- package/dist/index.d.ts +852 -0
- package/dist/index.js +1588 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1621 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var exporters = require('@unlayer/exporters');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var ReactDOMServer = require('react-dom/server');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
11
|
+
var ReactDOMServer__default = /*#__PURE__*/_interopDefault(ReactDOMServer);
|
|
12
|
+
|
|
13
|
+
// src/components/Button.tsx
|
|
14
|
+
|
|
15
|
+
// ../shared/dist/index.js
|
|
16
|
+
var DEFAULT_CONFIG = {
|
|
17
|
+
cdnBaseUrl: "https://cdn.tools.unlayer.com",
|
|
18
|
+
mode: "web"
|
|
19
|
+
};
|
|
20
|
+
var ColumnLayouts = {
|
|
21
|
+
OneColumn: {
|
|
22
|
+
name: "OneColumn",
|
|
23
|
+
description: "One full column (100%)",
|
|
24
|
+
cells: [1],
|
|
25
|
+
expectedColumns: 1,
|
|
26
|
+
widths: ["100%"]
|
|
27
|
+
},
|
|
28
|
+
TwoEqual: {
|
|
29
|
+
name: "TwoEqual",
|
|
30
|
+
description: "Two equal columns (50% + 50%)",
|
|
31
|
+
cells: [1, 1],
|
|
32
|
+
expectedColumns: 2,
|
|
33
|
+
widths: ["50%", "50%"]
|
|
34
|
+
},
|
|
35
|
+
TwoWideNarrow: {
|
|
36
|
+
name: "TwoWideNarrow",
|
|
37
|
+
description: "Two columns: wide + narrow (67% + 33%)",
|
|
38
|
+
cells: [2, 1],
|
|
39
|
+
expectedColumns: 2,
|
|
40
|
+
widths: ["66.67%", "33.33%"]
|
|
41
|
+
},
|
|
42
|
+
TwoNarrowWide: {
|
|
43
|
+
name: "TwoNarrowWide",
|
|
44
|
+
description: "Two columns: narrow + wide (33% + 67%)",
|
|
45
|
+
cells: [1, 2],
|
|
46
|
+
expectedColumns: 2,
|
|
47
|
+
widths: ["33.33%", "66.67%"]
|
|
48
|
+
},
|
|
49
|
+
ThreeEqual: {
|
|
50
|
+
name: "ThreeEqual",
|
|
51
|
+
description: "Three equal columns (33% + 33% + 33%)",
|
|
52
|
+
cells: [1, 1, 1],
|
|
53
|
+
expectedColumns: 3,
|
|
54
|
+
widths: ["33.33%", "33.33%", "33.33%"]
|
|
55
|
+
},
|
|
56
|
+
ThreeNarrowWideNarrow: {
|
|
57
|
+
name: "ThreeNarrowWideNarrow",
|
|
58
|
+
description: "Three columns: narrow + wide + narrow (25% + 50% + 25%)",
|
|
59
|
+
cells: [1, 2, 1],
|
|
60
|
+
expectedColumns: 3,
|
|
61
|
+
widths: ["25%", "50%", "25%"]
|
|
62
|
+
},
|
|
63
|
+
FourEqual: {
|
|
64
|
+
name: "FourEqual",
|
|
65
|
+
description: "Four equal columns (25% + 25% + 25% + 25%)",
|
|
66
|
+
cells: [1, 1, 1, 1],
|
|
67
|
+
expectedColumns: 4,
|
|
68
|
+
widths: ["25%", "25%", "25%", "25%"]
|
|
69
|
+
},
|
|
70
|
+
FiveEqual: {
|
|
71
|
+
name: "FiveEqual",
|
|
72
|
+
description: "Five equal columns (20% + 20% + 20% + 20% + 20%)",
|
|
73
|
+
cells: [1, 1, 1, 1, 1],
|
|
74
|
+
expectedColumns: 5,
|
|
75
|
+
widths: ["20%", "20%", "20%", "20%", "20%"]
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function validateColumnLayout(layout, childrenCount) {
|
|
79
|
+
if (layout.expectedColumns !== childrenCount) {
|
|
80
|
+
throw new Error(`${layout.name} layout expects ${layout.expectedColumns} <Column> components, but got ${childrenCount}. Layout: ${layout.description}. Make sure you have exactly ${layout.expectedColumns} <Column> children.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var EMPTY_TEXT_JSON = '{"root":{"children":[{"children":[],"format":"","indent":0,"type":"extended-paragraph","version":1,"textFormat":0,"textStyle":""}],"format":"","indent":0,"type":"root","version":1}}';
|
|
84
|
+
function textToTextJson(text) {
|
|
85
|
+
if (!text) return EMPTY_TEXT_JSON;
|
|
86
|
+
const escaped = JSON.stringify(text);
|
|
87
|
+
return `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":${escaped},"type":"extended-text","version":1}],"format":"","indent":0,"type":"extended-paragraph","version":1,"textFormat":0,"textStyle":""}],"format":"","indent":0,"type":"root","version":1}}`;
|
|
88
|
+
}
|
|
89
|
+
var FORMAT_TAGS = [
|
|
90
|
+
[1, "<strong>", "</strong>"],
|
|
91
|
+
// bold
|
|
92
|
+
[2, "<em>", "</em>"],
|
|
93
|
+
// italic
|
|
94
|
+
[4, "<s>", "</s>"],
|
|
95
|
+
// strikethrough
|
|
96
|
+
[8, "<u>", "</u>"],
|
|
97
|
+
// underline
|
|
98
|
+
[16, "<code>", "</code>"],
|
|
99
|
+
// code
|
|
100
|
+
[32, "<sub>", "</sub>"],
|
|
101
|
+
// subscript
|
|
102
|
+
[64, "<sup>", "</sup>"]
|
|
103
|
+
// superscript
|
|
104
|
+
];
|
|
105
|
+
function escapeHtml(text) {
|
|
106
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
107
|
+
}
|
|
108
|
+
function textNodeToHtml(node) {
|
|
109
|
+
let html = escapeHtml(node.text || "");
|
|
110
|
+
const style = node.style;
|
|
111
|
+
const format = node.format || 0;
|
|
112
|
+
let openTags = "";
|
|
113
|
+
let closeTags = "";
|
|
114
|
+
for (const [bit, open, close] of FORMAT_TAGS) {
|
|
115
|
+
if (format & bit) {
|
|
116
|
+
openTags += open;
|
|
117
|
+
closeTags = close + closeTags;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
html = openTags + html + closeTags;
|
|
121
|
+
if (style) {
|
|
122
|
+
html = `<span style="${escapeHtml(style)}">${html}</span>`;
|
|
123
|
+
}
|
|
124
|
+
return html;
|
|
125
|
+
}
|
|
126
|
+
function nodeToHtml(node) {
|
|
127
|
+
if (!node || typeof node !== "object") return "";
|
|
128
|
+
const type = node.type;
|
|
129
|
+
if (type === "text" || type === "extended-text") {
|
|
130
|
+
return textNodeToHtml(node);
|
|
131
|
+
}
|
|
132
|
+
if (type === "linebreak") {
|
|
133
|
+
return "<br>";
|
|
134
|
+
}
|
|
135
|
+
const childrenHtml = (node.children || []).map(nodeToHtml).join("");
|
|
136
|
+
switch (type) {
|
|
137
|
+
case "root":
|
|
138
|
+
return childrenHtml;
|
|
139
|
+
case "paragraph":
|
|
140
|
+
case "extended-paragraph": {
|
|
141
|
+
if (!childrenHtml) return "<p><br></p>";
|
|
142
|
+
const dir = node.direction ? ` dir="${node.direction}"` : "";
|
|
143
|
+
return `<p${dir}>${childrenHtml}</p>`;
|
|
144
|
+
}
|
|
145
|
+
case "heading": {
|
|
146
|
+
const tag = node.tag || "h1";
|
|
147
|
+
const dir = node.direction ? ` dir="${node.direction}"` : "";
|
|
148
|
+
return `<${tag}${dir}>${childrenHtml}</${tag}>`;
|
|
149
|
+
}
|
|
150
|
+
case "link": {
|
|
151
|
+
const href = node.url ? ` href="${escapeHtml(node.url)}"` : "";
|
|
152
|
+
const target = node.target ? ` target="${escapeHtml(node.target)}"` : "";
|
|
153
|
+
const rel = node.rel ? ` rel="${escapeHtml(node.rel)}"` : "";
|
|
154
|
+
return `<a${href}${target}${rel}>${childrenHtml}</a>`;
|
|
155
|
+
}
|
|
156
|
+
case "list": {
|
|
157
|
+
const tag = node.listType === "number" ? "ol" : "ul";
|
|
158
|
+
return `<${tag}>${childrenHtml}</${tag}>`;
|
|
159
|
+
}
|
|
160
|
+
case "listitem":
|
|
161
|
+
return `<li>${childrenHtml}</li>`;
|
|
162
|
+
default:
|
|
163
|
+
return childrenHtml;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function generateHtmlFromTextJson(textJson) {
|
|
167
|
+
if (!textJson) return "";
|
|
168
|
+
try {
|
|
169
|
+
const parsed = JSON.parse(textJson);
|
|
170
|
+
if (parsed.__html) return parsed.__html;
|
|
171
|
+
return nodeToHtml(parsed.root);
|
|
172
|
+
} catch {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function htmlToTextJson(html) {
|
|
177
|
+
if (!html) return EMPTY_TEXT_JSON;
|
|
178
|
+
return JSON.stringify({ __html: html });
|
|
179
|
+
}
|
|
180
|
+
function mergeValues(defaults, userValues) {
|
|
181
|
+
if (!userValues) return defaults;
|
|
182
|
+
const result = { ...defaults };
|
|
183
|
+
for (const key of Object.keys(userValues)) {
|
|
184
|
+
const userVal = userValues[key];
|
|
185
|
+
const defaultVal = defaults[key];
|
|
186
|
+
if (userVal !== null && defaultVal !== null && typeof userVal === "object" && typeof defaultVal === "object" && !Array.isArray(userVal) && !Array.isArray(defaultVal)) {
|
|
187
|
+
result[key] = mergeValues(defaultVal, userVal);
|
|
188
|
+
} else {
|
|
189
|
+
result[key] = userVal;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
var nestedStructureCache = /* @__PURE__ */ new WeakMap();
|
|
195
|
+
function analyzeNestedStructure(defaultValues) {
|
|
196
|
+
const cached = nestedStructureCache.get(defaultValues);
|
|
197
|
+
if (cached) return cached;
|
|
198
|
+
const nestedGroups = /* @__PURE__ */ new Map();
|
|
199
|
+
for (const [key, value] of Object.entries(defaultValues)) {
|
|
200
|
+
if (key === "_meta" || key === "text" || key === "textJson" || key === "children") {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0) {
|
|
204
|
+
const props = new Set(Object.keys(value));
|
|
205
|
+
nestedGroups.set(key, props);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
nestedStructureCache.set(defaultValues, nestedGroups);
|
|
209
|
+
return nestedGroups;
|
|
210
|
+
}
|
|
211
|
+
function mapSemanticProps(props, defaultValues, componentType) {
|
|
212
|
+
const { children, values, ...restProps } = props;
|
|
213
|
+
const userProps = { ...restProps };
|
|
214
|
+
const result = values ? { ...values } : {};
|
|
215
|
+
if (children !== void 0 && !result.text && !result.textJson) {
|
|
216
|
+
if (componentType === "Paragraph") {
|
|
217
|
+
const textContent = typeof children === "string" ? children : String(children);
|
|
218
|
+
result.textJson = textToTextJson(textContent);
|
|
219
|
+
} else {
|
|
220
|
+
result.text = children;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const textFromEscapeHatch = result.text;
|
|
224
|
+
const textFromProp = userProps.text;
|
|
225
|
+
const textProp = textFromEscapeHatch || textFromProp;
|
|
226
|
+
if (componentType === "Paragraph" && textProp && !result.textJson) {
|
|
227
|
+
result.textJson = textFromEscapeHatch ? htmlToTextJson(String(textProp)) : textToTextJson(String(textProp));
|
|
228
|
+
delete result.text;
|
|
229
|
+
delete userProps.text;
|
|
230
|
+
}
|
|
231
|
+
const htmlProp = userProps.html || result.html;
|
|
232
|
+
if (componentType === "Paragraph" && htmlProp && !result.textJson) {
|
|
233
|
+
result.textJson = htmlToTextJson(String(htmlProp));
|
|
234
|
+
delete userProps.html;
|
|
235
|
+
delete result.html;
|
|
236
|
+
}
|
|
237
|
+
const href = userProps.href;
|
|
238
|
+
if (href !== void 0) {
|
|
239
|
+
if (typeof href === "string") {
|
|
240
|
+
userProps.href = {
|
|
241
|
+
name: "web",
|
|
242
|
+
values: { href, target: "_blank" }
|
|
243
|
+
};
|
|
244
|
+
} else {
|
|
245
|
+
userProps.href = href;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const nestedGroups = analyzeNestedStructure(defaultValues);
|
|
249
|
+
const nested = {};
|
|
250
|
+
const flat = {};
|
|
251
|
+
for (const [key, value] of Object.entries(userProps)) {
|
|
252
|
+
if (value === void 0) continue;
|
|
253
|
+
if (nestedGroups.has(key)) {
|
|
254
|
+
nested[key] = value;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
let belongsToGroup = false;
|
|
258
|
+
for (const [groupName, groupProps] of nestedGroups.entries()) {
|
|
259
|
+
if (groupProps.has(key)) {
|
|
260
|
+
nested[groupName] = nested[groupName] || {};
|
|
261
|
+
nested[groupName][key] = value;
|
|
262
|
+
belongsToGroup = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!belongsToGroup) {
|
|
267
|
+
flat[key] = value;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const final = {
|
|
271
|
+
...result,
|
|
272
|
+
...flat
|
|
273
|
+
};
|
|
274
|
+
for (const [groupName, groupValues] of Object.entries(nested)) {
|
|
275
|
+
if (Object.keys(groupValues).length > 0) {
|
|
276
|
+
final[groupName] = {
|
|
277
|
+
...result[groupName],
|
|
278
|
+
...groupValues
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return final;
|
|
283
|
+
}
|
|
284
|
+
var HTML_ENTITIES = {
|
|
285
|
+
"&": "&",
|
|
286
|
+
"<": "<",
|
|
287
|
+
">": ">",
|
|
288
|
+
""": '"',
|
|
289
|
+
"'": "'",
|
|
290
|
+
"'": "'",
|
|
291
|
+
" ": " ",
|
|
292
|
+
"—": "\u2014",
|
|
293
|
+
"–": "\u2013",
|
|
294
|
+
"«": "\xAB",
|
|
295
|
+
"»": "\xBB",
|
|
296
|
+
"•": "\u2022",
|
|
297
|
+
"·": "\xB7",
|
|
298
|
+
"…": "\u2026",
|
|
299
|
+
"©": "\xA9",
|
|
300
|
+
"®": "\xAE",
|
|
301
|
+
"™": "\u2122"
|
|
302
|
+
};
|
|
303
|
+
function decodeEntities(text) {
|
|
304
|
+
let result = text.replace(/&\w+;/g, (match) => HTML_ENTITIES[match] ?? match);
|
|
305
|
+
result = result.replace(
|
|
306
|
+
/&#(\d+);/g,
|
|
307
|
+
(_, code) => String.fromCharCode(parseInt(code, 10))
|
|
308
|
+
);
|
|
309
|
+
result = result.replace(
|
|
310
|
+
/&#x([0-9a-fA-F]+);/g,
|
|
311
|
+
(_, code) => String.fromCharCode(parseInt(code, 16))
|
|
312
|
+
);
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
function htmlToPlainText(html) {
|
|
316
|
+
if (!html) return "";
|
|
317
|
+
let text = html;
|
|
318
|
+
text = text.replace(/<[^>]+data-skip-in-text="true"[^>]*>[\s\S]*?<\/[^>]+>/gi, "");
|
|
319
|
+
text = text.replace(/<head[\s\S]*?<\/head>/gi, "");
|
|
320
|
+
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
321
|
+
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
322
|
+
text = text.replace(/<h[12][^>]*>([\s\S]*?)<\/h[12]>/gi, (_, content) => {
|
|
323
|
+
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
324
|
+
return "\n" + clean.toUpperCase() + "\n";
|
|
325
|
+
});
|
|
326
|
+
text = text.replace(/<h[3-6][^>]*>([\s\S]*?)<\/h[3-6]>/gi, (_, content) => {
|
|
327
|
+
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
328
|
+
return "\n" + clean + "\n";
|
|
329
|
+
});
|
|
330
|
+
text = text.replace(/<a[^>]+href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, content) => {
|
|
331
|
+
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
332
|
+
if (clean === href || clean === decodeEntities(href)) {
|
|
333
|
+
return clean;
|
|
334
|
+
}
|
|
335
|
+
return `${clean} (${href})`;
|
|
336
|
+
});
|
|
337
|
+
text = text.replace(/<img[^>]+alt=["']([^"']+)["'][^>]*\/?>/gi, (_, alt) => {
|
|
338
|
+
return `[${alt}]`;
|
|
339
|
+
});
|
|
340
|
+
text = text.replace(/<img[^>]*\/?>/gi, "");
|
|
341
|
+
text = text.replace(/<hr[^>]*\/?>/gi, "\n---\n");
|
|
342
|
+
text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_, content) => {
|
|
343
|
+
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
344
|
+
return "- " + clean + "\n";
|
|
345
|
+
});
|
|
346
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
347
|
+
text = text.replace(/<\/(?:p|div|tr|blockquote|section|article|header|footer|main|nav|aside)>/gi, "\n");
|
|
348
|
+
text = text.replace(/<\/(?:ul|ol|table|thead|tbody|tfoot)>/gi, "\n");
|
|
349
|
+
text = text.replace(/<\/(?:td|th)>/gi, " ");
|
|
350
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
351
|
+
text = decodeEntities(text);
|
|
352
|
+
text = text.replace(/\t/g, " ");
|
|
353
|
+
text = text.replace(/[^\S\n]+/g, " ");
|
|
354
|
+
text = text.split("\n").map((line) => line.trim()).join("\n");
|
|
355
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
356
|
+
text = text.trim();
|
|
357
|
+
return text;
|
|
358
|
+
}
|
|
359
|
+
var UNLAYER_RENDER_KEY = "__unlayerRender";
|
|
360
|
+
var UNLAYER_CONFIG_KEY = "__unlayerItemConfig";
|
|
361
|
+
var ErrorFallback = ({
|
|
362
|
+
type,
|
|
363
|
+
className,
|
|
364
|
+
style
|
|
365
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
366
|
+
"div",
|
|
367
|
+
{
|
|
368
|
+
style: {
|
|
369
|
+
padding: "20px",
|
|
370
|
+
backgroundColor: "#fee",
|
|
371
|
+
border: "1px solid #fcc",
|
|
372
|
+
borderRadius: "4px",
|
|
373
|
+
color: "#c33",
|
|
374
|
+
textAlign: "center",
|
|
375
|
+
fontFamily: "system-ui, sans-serif"
|
|
376
|
+
},
|
|
377
|
+
children: [
|
|
378
|
+
/* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
|
|
379
|
+
type,
|
|
380
|
+
" failed to render."
|
|
381
|
+
] }),
|
|
382
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsx("small", { children: "Check console for details." })
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
) });
|
|
387
|
+
function ensureMeta(values, type, index = 0) {
|
|
388
|
+
return {
|
|
389
|
+
...values,
|
|
390
|
+
_meta: {
|
|
391
|
+
htmlID: `u_content_${type.toLowerCase()}_${index + 1}`,
|
|
392
|
+
htmlClassNames: `u_content_${type.toLowerCase()}`,
|
|
393
|
+
...values._meta || {}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function renderComponent(config) {
|
|
398
|
+
const { type, values, className, style, args = [], innerHTML, _config, exporter } = config;
|
|
399
|
+
try {
|
|
400
|
+
const cfg = _config ?? DEFAULT_CONFIG;
|
|
401
|
+
const exporterConfig = {
|
|
402
|
+
generateHtmlFromTextJson,
|
|
403
|
+
toSafeHtml: cfg.toSafeHtml,
|
|
404
|
+
textDirection: cfg.textDirection,
|
|
405
|
+
cdnBaseUrl: cfg.cdnBaseUrl
|
|
406
|
+
};
|
|
407
|
+
let html;
|
|
408
|
+
if (innerHTML !== void 0) {
|
|
409
|
+
html = exporter(innerHTML, values, ...args);
|
|
410
|
+
} else {
|
|
411
|
+
const meta = {
|
|
412
|
+
exporterConfig,
|
|
413
|
+
mergeTagState: cfg.mergeTagState
|
|
414
|
+
};
|
|
415
|
+
html = exporter(values, ...args, void 0, meta);
|
|
416
|
+
}
|
|
417
|
+
html = typeof html === "string" ? html : String(html);
|
|
418
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
+
"div",
|
|
420
|
+
{
|
|
421
|
+
className,
|
|
422
|
+
style,
|
|
423
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.error(`${type} rendering failed:`, error);
|
|
428
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
429
|
+
ErrorFallback,
|
|
430
|
+
{
|
|
431
|
+
type,
|
|
432
|
+
error,
|
|
433
|
+
className,
|
|
434
|
+
style
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function createItemComponent(config) {
|
|
440
|
+
function renderFn(props) {
|
|
441
|
+
const {
|
|
442
|
+
// Base props
|
|
443
|
+
mode: modeProp,
|
|
444
|
+
className,
|
|
445
|
+
style,
|
|
446
|
+
// Internal props
|
|
447
|
+
index = 0,
|
|
448
|
+
colIndex = 0,
|
|
449
|
+
cells = [],
|
|
450
|
+
bodyValues = {},
|
|
451
|
+
rowValues = {},
|
|
452
|
+
_config,
|
|
453
|
+
// Children
|
|
454
|
+
children,
|
|
455
|
+
// Rest are semantic props
|
|
456
|
+
...restProps
|
|
457
|
+
} = props;
|
|
458
|
+
const mode = modeProp ?? _config?.mode ?? "web";
|
|
459
|
+
const mappedValues = config.propMapper({
|
|
460
|
+
children,
|
|
461
|
+
...restProps
|
|
462
|
+
});
|
|
463
|
+
const finalValues = mergeValues(config.defaultValues, mappedValues);
|
|
464
|
+
const valuesWithMeta = ensureMeta(
|
|
465
|
+
finalValues,
|
|
466
|
+
config.name.toLowerCase(),
|
|
467
|
+
index
|
|
468
|
+
);
|
|
469
|
+
const safeBodyValues = {
|
|
470
|
+
contentWidth: 600,
|
|
471
|
+
...bodyValues
|
|
472
|
+
};
|
|
473
|
+
const exporter = config.exporters[mode] || config.exporters.web;
|
|
474
|
+
return renderComponent({
|
|
475
|
+
type: config.name,
|
|
476
|
+
values: valuesWithMeta,
|
|
477
|
+
className,
|
|
478
|
+
style,
|
|
479
|
+
args: [index, colIndex, cells, safeBodyValues, rowValues],
|
|
480
|
+
_config,
|
|
481
|
+
exporter
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
const ItemComponent = (props) => {
|
|
485
|
+
return renderFn(props);
|
|
486
|
+
};
|
|
487
|
+
ItemComponent.displayName = config.displayName || config.name;
|
|
488
|
+
ItemComponent[UNLAYER_RENDER_KEY] = renderFn;
|
|
489
|
+
ItemComponent[UNLAYER_CONFIG_KEY] = {
|
|
490
|
+
name: config.name,
|
|
491
|
+
defaultValues: config.defaultValues,
|
|
492
|
+
propMapper: config.propMapper
|
|
493
|
+
};
|
|
494
|
+
return ItemComponent;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/components/Button.tsx
|
|
498
|
+
var DEFAULT_VALUES = {
|
|
499
|
+
...exporters.ButtonDefaults,
|
|
500
|
+
text: "Button"
|
|
501
|
+
// React convenience — not in schema
|
|
502
|
+
};
|
|
503
|
+
var Button = createItemComponent({
|
|
504
|
+
name: "Button",
|
|
505
|
+
defaultValues: DEFAULT_VALUES,
|
|
506
|
+
propMapper: (props) => mapSemanticProps(props, DEFAULT_VALUES, "Button"),
|
|
507
|
+
displayName: "Button",
|
|
508
|
+
exporters: exporters.ButtonExporters
|
|
509
|
+
});
|
|
510
|
+
var Button_default = Button;
|
|
511
|
+
var DEFAULT_VALUES2 = {
|
|
512
|
+
...exporters.DividerDefaults
|
|
513
|
+
};
|
|
514
|
+
var Divider = createItemComponent({
|
|
515
|
+
name: "Divider",
|
|
516
|
+
defaultValues: DEFAULT_VALUES2,
|
|
517
|
+
propMapper: (props) => mapSemanticProps(props, DEFAULT_VALUES2, "Divider"),
|
|
518
|
+
displayName: "Divider",
|
|
519
|
+
exporters: exporters.DividerExporters
|
|
520
|
+
});
|
|
521
|
+
var Divider_default = Divider;
|
|
522
|
+
var DEFAULT_VALUES3 = {
|
|
523
|
+
...exporters.HeadingDefaults,
|
|
524
|
+
color: "#000000",
|
|
525
|
+
// Not in schema options but used by renderer
|
|
526
|
+
fontWeight: 400,
|
|
527
|
+
// Not in schema options but used by renderer
|
|
528
|
+
text: "Heading"
|
|
529
|
+
// React convenience — not in schema
|
|
530
|
+
};
|
|
531
|
+
var Heading = createItemComponent({
|
|
532
|
+
name: "Heading",
|
|
533
|
+
defaultValues: DEFAULT_VALUES3,
|
|
534
|
+
propMapper: (props) => {
|
|
535
|
+
const { level, ...rest } = props;
|
|
536
|
+
const result = mapSemanticProps(rest, DEFAULT_VALUES3, "Heading");
|
|
537
|
+
if (level && !result.headingType) {
|
|
538
|
+
result.headingType = level;
|
|
539
|
+
}
|
|
540
|
+
return result;
|
|
541
|
+
},
|
|
542
|
+
displayName: "Heading",
|
|
543
|
+
exporters: exporters.HeadingExporters
|
|
544
|
+
});
|
|
545
|
+
var Heading_default = Heading;
|
|
546
|
+
var DEFAULT_VALUES4 = {
|
|
547
|
+
...exporters.HtmlDefaults
|
|
548
|
+
};
|
|
549
|
+
var Html = createItemComponent({
|
|
550
|
+
name: "Html",
|
|
551
|
+
defaultValues: DEFAULT_VALUES4,
|
|
552
|
+
propMapper: (props) => mapSemanticProps(props, DEFAULT_VALUES4, "Html"),
|
|
553
|
+
displayName: "Html",
|
|
554
|
+
exporters: exporters.HtmlExporters
|
|
555
|
+
});
|
|
556
|
+
var Html_default = Html;
|
|
557
|
+
var DEFAULT_VALUES5 = {
|
|
558
|
+
...exporters.ImageDefaults,
|
|
559
|
+
// Override src with autoWidth/maxWidth for responsive rendering
|
|
560
|
+
src: {
|
|
561
|
+
...exporters.ImageDefaults.src,
|
|
562
|
+
autoWidth: true,
|
|
563
|
+
maxWidth: "100%"
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var Image = createItemComponent({
|
|
567
|
+
name: "Image",
|
|
568
|
+
defaultValues: DEFAULT_VALUES5,
|
|
569
|
+
propMapper: (props) => {
|
|
570
|
+
const { alt, src, ...rest } = props;
|
|
571
|
+
const base = mapSemanticProps(
|
|
572
|
+
rest,
|
|
573
|
+
DEFAULT_VALUES5,
|
|
574
|
+
"Image"
|
|
575
|
+
);
|
|
576
|
+
if (alt !== void 0) {
|
|
577
|
+
base.altText = alt;
|
|
578
|
+
}
|
|
579
|
+
if (typeof src === "string") {
|
|
580
|
+
base.src = { url: src, autoWidth: true, maxWidth: "100%" };
|
|
581
|
+
} else if (src !== void 0) {
|
|
582
|
+
base.src = { ...DEFAULT_VALUES5.src, ...src };
|
|
583
|
+
}
|
|
584
|
+
return base;
|
|
585
|
+
},
|
|
586
|
+
displayName: "Image",
|
|
587
|
+
exporters: exporters.ImageExporters
|
|
588
|
+
});
|
|
589
|
+
var Image_default = Image;
|
|
590
|
+
exporters.MenuDefaults.menu ?? { };
|
|
591
|
+
var DEFAULT_VALUES6 = {
|
|
592
|
+
...exporters.MenuDefaults
|
|
593
|
+
};
|
|
594
|
+
var Menu = createItemComponent({
|
|
595
|
+
name: "Menu",
|
|
596
|
+
defaultValues: DEFAULT_VALUES6,
|
|
597
|
+
propMapper: (props) => {
|
|
598
|
+
const { items, ...rest } = props;
|
|
599
|
+
if (Array.isArray(items)) {
|
|
600
|
+
const mapped = items.map((item, i) => ({
|
|
601
|
+
key: String(i + 1),
|
|
602
|
+
text: item.text,
|
|
603
|
+
link: {
|
|
604
|
+
name: "web",
|
|
605
|
+
values: { href: item.href, target: item.target ?? "_blank" }
|
|
606
|
+
}
|
|
607
|
+
}));
|
|
608
|
+
const base = mapSemanticProps(
|
|
609
|
+
rest,
|
|
610
|
+
DEFAULT_VALUES6,
|
|
611
|
+
"Menu"
|
|
612
|
+
);
|
|
613
|
+
base.menu = { items: mapped };
|
|
614
|
+
return base;
|
|
615
|
+
}
|
|
616
|
+
return mapSemanticProps(
|
|
617
|
+
props,
|
|
618
|
+
DEFAULT_VALUES6,
|
|
619
|
+
"Menu"
|
|
620
|
+
);
|
|
621
|
+
},
|
|
622
|
+
displayName: "Menu",
|
|
623
|
+
exporters: exporters.MenuExporters
|
|
624
|
+
});
|
|
625
|
+
var Menu_default = Menu;
|
|
626
|
+
var DEFAULT_VALUES7 = {
|
|
627
|
+
...exporters.ParagraphDefaults,
|
|
628
|
+
color: "#000000"
|
|
629
|
+
// Not in schema options but used by renderer
|
|
630
|
+
};
|
|
631
|
+
var Paragraph = createItemComponent({
|
|
632
|
+
name: "Paragraph",
|
|
633
|
+
defaultValues: DEFAULT_VALUES7,
|
|
634
|
+
propMapper: (props) => mapSemanticProps(props, DEFAULT_VALUES7, "Paragraph"),
|
|
635
|
+
displayName: "Paragraph",
|
|
636
|
+
exporters: exporters.ParagraphExporters
|
|
637
|
+
});
|
|
638
|
+
var Paragraph_default = Paragraph;
|
|
639
|
+
var DEFAULT_ICONS = exporters.SocialDefaults.icons ?? { iconType: "circle", icons: [] };
|
|
640
|
+
var DEFAULT_VALUES8 = {
|
|
641
|
+
...exporters.SocialDefaults
|
|
642
|
+
};
|
|
643
|
+
var Social = createItemComponent({
|
|
644
|
+
name: "Social",
|
|
645
|
+
defaultValues: DEFAULT_VALUES8,
|
|
646
|
+
propMapper: (props) => {
|
|
647
|
+
const { icons, iconType, ...rest } = props;
|
|
648
|
+
if (Array.isArray(icons)) {
|
|
649
|
+
const mapped = icons.map((icon) => ({
|
|
650
|
+
name: icon.name,
|
|
651
|
+
url: icon.url
|
|
652
|
+
}));
|
|
653
|
+
const base = mapSemanticProps(
|
|
654
|
+
rest,
|
|
655
|
+
DEFAULT_VALUES8,
|
|
656
|
+
"Social"
|
|
657
|
+
);
|
|
658
|
+
base.icons = {
|
|
659
|
+
iconType: iconType ?? base.icons?.iconType ?? "circle",
|
|
660
|
+
icons: mapped
|
|
661
|
+
};
|
|
662
|
+
return base;
|
|
663
|
+
}
|
|
664
|
+
if (iconType !== void 0) {
|
|
665
|
+
const base = mapSemanticProps(
|
|
666
|
+
rest,
|
|
667
|
+
DEFAULT_VALUES8,
|
|
668
|
+
"Social"
|
|
669
|
+
);
|
|
670
|
+
base.icons = { ...DEFAULT_ICONS, ...base.icons, iconType };
|
|
671
|
+
return base;
|
|
672
|
+
}
|
|
673
|
+
return mapSemanticProps(
|
|
674
|
+
props,
|
|
675
|
+
DEFAULT_VALUES8,
|
|
676
|
+
"Social"
|
|
677
|
+
);
|
|
678
|
+
},
|
|
679
|
+
displayName: "Social",
|
|
680
|
+
exporters: exporters.SocialExporters
|
|
681
|
+
});
|
|
682
|
+
var Social_default = Social;
|
|
683
|
+
var DEFAULT_TABLE = { headers: [], rows: [], footers: [] };
|
|
684
|
+
var DEFAULT_VALUES9 = {
|
|
685
|
+
...exporters.TableDefaults,
|
|
686
|
+
table: DEFAULT_TABLE
|
|
687
|
+
// Schema doesn't include table data structure
|
|
688
|
+
};
|
|
689
|
+
var Table = createItemComponent({
|
|
690
|
+
name: "Table",
|
|
691
|
+
defaultValues: DEFAULT_VALUES9,
|
|
692
|
+
propMapper: (props) => {
|
|
693
|
+
const { headers, data, ...rest } = props;
|
|
694
|
+
if (headers || data) {
|
|
695
|
+
const base = mapSemanticProps(
|
|
696
|
+
rest,
|
|
697
|
+
DEFAULT_VALUES9,
|
|
698
|
+
"Table"
|
|
699
|
+
);
|
|
700
|
+
const tableHeaders = headers ? [{ cells: headers.map((text) => ({ text, width: 0 })), height: 0 }] : [];
|
|
701
|
+
const tableRows = data ? data.map((row) => ({
|
|
702
|
+
cells: row.map((text) => ({ text, width: 0 })),
|
|
703
|
+
height: 0
|
|
704
|
+
})) : [];
|
|
705
|
+
base.table = { headers: tableHeaders, rows: tableRows, footers: [] };
|
|
706
|
+
if (headers) {
|
|
707
|
+
base.columns = headers.length;
|
|
708
|
+
base.enableHeader = true;
|
|
709
|
+
}
|
|
710
|
+
if (data) {
|
|
711
|
+
base.rows = data.length;
|
|
712
|
+
}
|
|
713
|
+
return base;
|
|
714
|
+
}
|
|
715
|
+
return mapSemanticProps(
|
|
716
|
+
props,
|
|
717
|
+
DEFAULT_VALUES9,
|
|
718
|
+
"Table"
|
|
719
|
+
);
|
|
720
|
+
},
|
|
721
|
+
displayName: "Table",
|
|
722
|
+
exporters: exporters.TableExporters
|
|
723
|
+
});
|
|
724
|
+
var Table_default = Table;
|
|
725
|
+
var DEFAULT_VIDEO = {
|
|
726
|
+
type: "youtube",
|
|
727
|
+
videoId: "",
|
|
728
|
+
thumbnail: "",
|
|
729
|
+
loading: false,
|
|
730
|
+
...exporters.VideoDefaults.video
|
|
731
|
+
};
|
|
732
|
+
var DEFAULT_VALUES10 = {
|
|
733
|
+
...exporters.VideoDefaults,
|
|
734
|
+
video: DEFAULT_VIDEO
|
|
735
|
+
};
|
|
736
|
+
function parseVideoUrl(url) {
|
|
737
|
+
const ytMatch = url.match(
|
|
738
|
+
/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
|
|
739
|
+
);
|
|
740
|
+
if (ytMatch) {
|
|
741
|
+
const videoId = ytMatch[1];
|
|
742
|
+
return {
|
|
743
|
+
type: "youtube",
|
|
744
|
+
videoId,
|
|
745
|
+
thumbnail: `https://img.youtube.com/vi/${videoId}/0.jpg`
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
|
|
749
|
+
if (vimeoMatch) {
|
|
750
|
+
return {
|
|
751
|
+
type: "vimeo",
|
|
752
|
+
videoId: vimeoMatch[1],
|
|
753
|
+
thumbnail: ""
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
var Video = createItemComponent({
|
|
759
|
+
name: "Video",
|
|
760
|
+
defaultValues: DEFAULT_VALUES10,
|
|
761
|
+
propMapper: (props) => {
|
|
762
|
+
const { videoUrl, ...rest } = props;
|
|
763
|
+
if (typeof videoUrl === "string") {
|
|
764
|
+
const parsed = parseVideoUrl(videoUrl);
|
|
765
|
+
const base = mapSemanticProps(
|
|
766
|
+
rest,
|
|
767
|
+
DEFAULT_VALUES10,
|
|
768
|
+
"Video"
|
|
769
|
+
);
|
|
770
|
+
if (parsed) {
|
|
771
|
+
base.video = { ...DEFAULT_VIDEO, ...parsed };
|
|
772
|
+
}
|
|
773
|
+
return base;
|
|
774
|
+
}
|
|
775
|
+
return mapSemanticProps(
|
|
776
|
+
props,
|
|
777
|
+
DEFAULT_VALUES10,
|
|
778
|
+
"Video"
|
|
779
|
+
);
|
|
780
|
+
},
|
|
781
|
+
displayName: "Video",
|
|
782
|
+
exporters: exporters.VideoExporters
|
|
783
|
+
});
|
|
784
|
+
var Video_default = Video;
|
|
785
|
+
var allBodyDefaults = exporters.BodyDefaults;
|
|
786
|
+
var {
|
|
787
|
+
popupPosition: _p1,
|
|
788
|
+
popupDisplayDelay: _p2,
|
|
789
|
+
popupWidth: _p3,
|
|
790
|
+
popupHeight: _p4,
|
|
791
|
+
popupBackgroundColor: _p5,
|
|
792
|
+
popupBackgroundImage: _p6,
|
|
793
|
+
popupOverlay_backgroundColor: _p7,
|
|
794
|
+
popupCloseButton_position: _p8,
|
|
795
|
+
popupCloseButton_backgroundColor: _p9,
|
|
796
|
+
popupCloseButton_iconColor: _p10,
|
|
797
|
+
popupCloseButton_borderRadius: _p11,
|
|
798
|
+
popupCloseButton_margin: _p12,
|
|
799
|
+
popupCloseButton_action: _p13,
|
|
800
|
+
...bodyRest
|
|
801
|
+
} = allBodyDefaults;
|
|
802
|
+
var BODY_DEFAULTS = bodyRest;
|
|
803
|
+
var ROW_DEFAULTS = { ...exporters.RowDefaults };
|
|
804
|
+
var COLUMN_DEFAULTS = { ...exporters.ColumnDefaults };
|
|
805
|
+
var DEFAULT_VALUES11 = ROW_DEFAULTS;
|
|
806
|
+
var DEFAULT_BODY_VALUES = BODY_DEFAULTS;
|
|
807
|
+
function getWidthPercentages(cells) {
|
|
808
|
+
if (cells.length === 0) return [];
|
|
809
|
+
const total = cells.reduce((a, b) => a + b, 0);
|
|
810
|
+
if (total <= 0) return [];
|
|
811
|
+
return cells.map((span) => {
|
|
812
|
+
const value = Math.round(span / total * 100 * 100) / 100;
|
|
813
|
+
const className = `${value}`.replace(/\./g, "p");
|
|
814
|
+
return { value, className };
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function generateGridCSS(cells, mode, contentWidth = 600, mobileBreakpoint = 620) {
|
|
818
|
+
const widths = getWidthPercentages(cells);
|
|
819
|
+
if (mode === "email") {
|
|
820
|
+
const minQuery = `@media only screen and (min-width: ${contentWidth + 20}px)`;
|
|
821
|
+
const maxQuery = `@media only screen and (max-width: ${contentWidth + 20}px)`;
|
|
822
|
+
return `
|
|
823
|
+
${minQuery} {
|
|
824
|
+
.u-row { width: ${contentWidth}px !important; }
|
|
825
|
+
.u-row .u-col { vertical-align: top; }
|
|
826
|
+
${widths.map(({ value, className }) => ` .u-row .u-col-${className} { width: ${Math.round(contentWidth * value / 100)}px !important; }`).join("\n")}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
${maxQuery} {
|
|
830
|
+
.u-row-container { max-width: 100% !important; padding-left: 0px !important; padding-right: 0px !important; }
|
|
831
|
+
.u-row { width: 100% !important; }
|
|
832
|
+
.u-row .u-col { display: block !important; width: 100% !important; min-width: 320px !important; max-width: 100% !important; }
|
|
833
|
+
.u-row .u-col > div { margin: 0 auto; }
|
|
834
|
+
.no-stack .u-col { min-width: 0 !important; display: table-cell !important; }
|
|
835
|
+
${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width: ${value}% !important; }`).join("\n")}
|
|
836
|
+
}`;
|
|
837
|
+
}
|
|
838
|
+
const baseCSS = `
|
|
839
|
+
.u-row {
|
|
840
|
+
display: flex;
|
|
841
|
+
flex-wrap: nowrap;
|
|
842
|
+
margin-left: 0;
|
|
843
|
+
margin-right: 0;
|
|
844
|
+
}
|
|
845
|
+
.u-row .u-col {
|
|
846
|
+
position: relative;
|
|
847
|
+
width: 100%;
|
|
848
|
+
padding-right: 0;
|
|
849
|
+
padding-left: 0;
|
|
850
|
+
}`;
|
|
851
|
+
const columnCSS = widths.map(
|
|
852
|
+
({ value, className }) => `.u-row .u-col.u-col-${className} { flex: 0 0 ${value}%; max-width: ${value}%; }`
|
|
853
|
+
).join("\n");
|
|
854
|
+
const responsiveCSS = `
|
|
855
|
+
@media only screen and (max-width: ${mobileBreakpoint}px) {
|
|
856
|
+
.u-row { width: 100% !important; }
|
|
857
|
+
.u-row .u-col {
|
|
858
|
+
display: block !important;
|
|
859
|
+
width: 100% !important;
|
|
860
|
+
min-width: 320px !important;
|
|
861
|
+
max-width: 100% !important;
|
|
862
|
+
}
|
|
863
|
+
.u-row .u-col > div { margin: 0 auto; }
|
|
864
|
+
.no-stack .u-col {
|
|
865
|
+
min-width: 0 !important;
|
|
866
|
+
display: table-cell !important;
|
|
867
|
+
}
|
|
868
|
+
${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width: ${value}% !important; }`).join("\n")}
|
|
869
|
+
}`;
|
|
870
|
+
return baseCSS + "\n" + columnCSS + "\n" + responsiveCSS;
|
|
871
|
+
}
|
|
872
|
+
function renderRowToHtml(innerHTML, values, bodyValues, mode, cells, collection = "rows") {
|
|
873
|
+
const rowExporter = exporters.RowExporters[mode] || exporters.RowExporters.web;
|
|
874
|
+
const html = rowExporter(innerHTML, values, bodyValues, {
|
|
875
|
+
collection,
|
|
876
|
+
variant: mode
|
|
877
|
+
});
|
|
878
|
+
const css = generateGridCSS(cells, mode);
|
|
879
|
+
return css ? `<style>${css}</style>${html}` : html;
|
|
880
|
+
}
|
|
881
|
+
function processChildren(children, cells, bodyValues, rowValues, mode, _config) {
|
|
882
|
+
if (!children) return "";
|
|
883
|
+
let innerHTML = "";
|
|
884
|
+
const childrenArray = React__default.default.Children.toArray(children);
|
|
885
|
+
childrenArray.forEach((child, index) => {
|
|
886
|
+
if (!React__default.default.isValidElement(child)) {
|
|
887
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
888
|
+
innerHTML += String(child);
|
|
889
|
+
}
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const componentType = child.type;
|
|
893
|
+
const isColumn = componentType?.displayName === "Column" || componentType?.name === "Column";
|
|
894
|
+
if (isColumn && typeof child.type === "function") {
|
|
895
|
+
const rendered = child.type({
|
|
896
|
+
...child.props,
|
|
897
|
+
index,
|
|
898
|
+
cells,
|
|
899
|
+
bodyValues,
|
|
900
|
+
rowValues,
|
|
901
|
+
mode,
|
|
902
|
+
_config
|
|
903
|
+
});
|
|
904
|
+
if (rendered?.props?.dangerouslySetInnerHTML?.__html) {
|
|
905
|
+
innerHTML += rendered.props.dangerouslySetInnerHTML.__html;
|
|
906
|
+
}
|
|
907
|
+
} else if (React__default.default.isValidElement(child)) {
|
|
908
|
+
const name = child.type?.displayName || child.type?.name || "Unknown";
|
|
909
|
+
console.warn(
|
|
910
|
+
`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>`
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
return innerHTML;
|
|
915
|
+
}
|
|
916
|
+
var Row = (props) => {
|
|
917
|
+
const {
|
|
918
|
+
layout,
|
|
919
|
+
cells: propsCells,
|
|
920
|
+
children,
|
|
921
|
+
mode: modeProp,
|
|
922
|
+
className,
|
|
923
|
+
style,
|
|
924
|
+
index = 0,
|
|
925
|
+
bodyValues = {},
|
|
926
|
+
collection = "rows",
|
|
927
|
+
_config,
|
|
928
|
+
...semanticProps
|
|
929
|
+
} = props;
|
|
930
|
+
const mode = modeProp ?? _config?.mode ?? "web";
|
|
931
|
+
let cells = propsCells || [1];
|
|
932
|
+
if (layout) {
|
|
933
|
+
validateColumnLayout(layout, React__default.default.Children.count(children));
|
|
934
|
+
cells = layout.cells;
|
|
935
|
+
}
|
|
936
|
+
const safeBodyValues = { ...DEFAULT_BODY_VALUES, ...bodyValues };
|
|
937
|
+
const values = mapSemanticProps(
|
|
938
|
+
semanticProps,
|
|
939
|
+
DEFAULT_VALUES11,
|
|
940
|
+
"Row"
|
|
941
|
+
);
|
|
942
|
+
const valuesWithMeta = {
|
|
943
|
+
...values,
|
|
944
|
+
cells,
|
|
945
|
+
_meta: {
|
|
946
|
+
htmlID: `u_row_${index + 1}`,
|
|
947
|
+
htmlClassNames: "u_row",
|
|
948
|
+
...values._meta || {}
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
const innerHTML = processChildren(
|
|
952
|
+
children,
|
|
953
|
+
cells,
|
|
954
|
+
safeBodyValues,
|
|
955
|
+
valuesWithMeta,
|
|
956
|
+
mode,
|
|
957
|
+
_config
|
|
958
|
+
);
|
|
959
|
+
try {
|
|
960
|
+
const html = renderRowToHtml(innerHTML, valuesWithMeta, safeBodyValues, mode, cells, collection);
|
|
961
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
962
|
+
"div",
|
|
963
|
+
{
|
|
964
|
+
dangerouslySetInnerHTML: { __html: html },
|
|
965
|
+
className,
|
|
966
|
+
style
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
} catch (error) {
|
|
970
|
+
console.error("Row rendering failed:", error);
|
|
971
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children });
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
Row.displayName = "Row";
|
|
975
|
+
var Row_default = Row;
|
|
976
|
+
var DEFAULT_VALUES12 = COLUMN_DEFAULTS;
|
|
977
|
+
function renderColumnToHtml(innerHTML, values, index, cells, bodyValues, rowValues, mode) {
|
|
978
|
+
const columnExporter = exporters.ColumnExporters[mode] || exporters.ColumnExporters.web;
|
|
979
|
+
return columnExporter(innerHTML, values, index, cells, bodyValues, rowValues);
|
|
980
|
+
}
|
|
981
|
+
var Column = (props) => {
|
|
982
|
+
const {
|
|
983
|
+
children,
|
|
984
|
+
index = 0,
|
|
985
|
+
cells = [1],
|
|
986
|
+
bodyValues = {},
|
|
987
|
+
rowValues = {},
|
|
988
|
+
mode: modeProp,
|
|
989
|
+
className,
|
|
990
|
+
style,
|
|
991
|
+
_config,
|
|
992
|
+
...semanticProps
|
|
993
|
+
} = props;
|
|
994
|
+
const mode = modeProp ?? _config?.mode ?? "web";
|
|
995
|
+
const values = mapSemanticProps(
|
|
996
|
+
semanticProps,
|
|
997
|
+
DEFAULT_VALUES12,
|
|
998
|
+
"Column"
|
|
999
|
+
);
|
|
1000
|
+
const valuesWithMeta = {
|
|
1001
|
+
...values,
|
|
1002
|
+
_meta: {
|
|
1003
|
+
htmlID: `u_column_${index + 1}`,
|
|
1004
|
+
htmlClassNames: "u_column",
|
|
1005
|
+
...values._meta || {}
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
let innerHTML = "";
|
|
1009
|
+
if (children) {
|
|
1010
|
+
try {
|
|
1011
|
+
const childrenArray = React__default.default.Children.toArray(children);
|
|
1012
|
+
childrenArray.forEach((child, childIndex) => {
|
|
1013
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
1014
|
+
innerHTML += String(child);
|
|
1015
|
+
} else if (React__default.default.isValidElement(child)) {
|
|
1016
|
+
if (typeof child.type === "function") {
|
|
1017
|
+
const ComponentType = child.type;
|
|
1018
|
+
const renderFn = ComponentType[UNLAYER_RENDER_KEY] || ComponentType;
|
|
1019
|
+
const rendered = renderFn({ ...child.props, _config });
|
|
1020
|
+
if (rendered && typeof rendered === "object" && rendered.props && rendered.props.dangerouslySetInnerHTML) {
|
|
1021
|
+
const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
|
|
1022
|
+
const componentType = child.type;
|
|
1023
|
+
const componentName = componentType?.displayName || componentType?.name || "component";
|
|
1024
|
+
const componentProps = child.props;
|
|
1025
|
+
const containerPadding = componentProps.values?.containerPadding || DEFAULT_VALUES12.padding || "10px";
|
|
1026
|
+
innerHTML += `<div id="u_content_${componentName.toLowerCase()}_${childIndex + 1}" class="u_content_${componentName.toLowerCase()}" style="padding: ${containerPadding};">${componentHTML}</div>`;
|
|
1027
|
+
} else if (rendered) {
|
|
1028
|
+
const name = child.type?.displayName || child.type?.name || "Unknown";
|
|
1029
|
+
console.warn(
|
|
1030
|
+
`Column: <${name}> did not produce renderable HTML. Ensure it is an Unlayer component (Button, Text, Image, etc.).`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
console.error("Column: Failed to render children:", error);
|
|
1038
|
+
innerHTML = "";
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
try {
|
|
1042
|
+
const html = renderColumnToHtml(innerHTML, valuesWithMeta, index, cells, bodyValues, rowValues, mode);
|
|
1043
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1044
|
+
"div",
|
|
1045
|
+
{
|
|
1046
|
+
dangerouslySetInnerHTML: { __html: html },
|
|
1047
|
+
className,
|
|
1048
|
+
style
|
|
1049
|
+
}
|
|
1050
|
+
);
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
console.error("Column rendering failed:", error);
|
|
1053
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children });
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
Column.displayName = "Column";
|
|
1057
|
+
var Column_default = Column;
|
|
1058
|
+
var DEFAULT_VALUES13 = BODY_DEFAULTS;
|
|
1059
|
+
var MAX_PREVIEW_LENGTH = 150;
|
|
1060
|
+
var PADDING_CHARS = [
|
|
1061
|
+
"\xA0",
|
|
1062
|
+
"\u200C",
|
|
1063
|
+
"\u200B",
|
|
1064
|
+
"\u200D",
|
|
1065
|
+
"\u200E",
|
|
1066
|
+
"\u200F",
|
|
1067
|
+
"\uFEFF"
|
|
1068
|
+
];
|
|
1069
|
+
function generatePreviewHtml(text) {
|
|
1070
|
+
if (!text || text.trim().length === 0) return "";
|
|
1071
|
+
const truncated = text.length > MAX_PREVIEW_LENGTH ? text.slice(0, MAX_PREVIEW_LENGTH) : text;
|
|
1072
|
+
const paddingLength = Math.max(0, MAX_PREVIEW_LENGTH - truncated.length);
|
|
1073
|
+
let padding = "";
|
|
1074
|
+
for (let i = 0; i < paddingLength; i++) {
|
|
1075
|
+
padding += PADDING_CHARS[i % PADDING_CHARS.length];
|
|
1076
|
+
}
|
|
1077
|
+
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>`;
|
|
1078
|
+
}
|
|
1079
|
+
function renderBodyToHtml(innerHTML, values, mode, previewText) {
|
|
1080
|
+
let finalInnerHtml = innerHTML;
|
|
1081
|
+
if (mode === "email" && previewText) {
|
|
1082
|
+
const previewHtml = generatePreviewHtml(previewText);
|
|
1083
|
+
if (previewHtml) {
|
|
1084
|
+
finalInnerHtml = previewHtml + innerHTML;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
const bodyExporter = exporters.BodyExporters[mode] || exporters.BodyExporters.web;
|
|
1088
|
+
const raw = mode === "document" ? bodyExporter(finalInnerHtml, values, { type: "" }) : bodyExporter(finalInnerHtml, values, values);
|
|
1089
|
+
return raw.replace("min-height: 100vh; ", "").replace("min-height: 100vh;", "");
|
|
1090
|
+
}
|
|
1091
|
+
var Body = (props) => {
|
|
1092
|
+
const { children, mode: modeProp, className, style, index = 0, config: configProp, previewText, ...semanticProps } = props;
|
|
1093
|
+
const resolvedConfig = { ...DEFAULT_CONFIG, ...configProp };
|
|
1094
|
+
const mode = modeProp ?? resolvedConfig.mode ?? "web";
|
|
1095
|
+
const _config = { ...resolvedConfig, mode };
|
|
1096
|
+
const values = mapSemanticProps(semanticProps, DEFAULT_VALUES13, "Body");
|
|
1097
|
+
const valuesWithMeta = {
|
|
1098
|
+
...values,
|
|
1099
|
+
_meta: {
|
|
1100
|
+
htmlID: `u_body_${index + 1}`,
|
|
1101
|
+
htmlClassNames: "u_body",
|
|
1102
|
+
...values._meta || {}
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
let enrichedChildren = children;
|
|
1106
|
+
if (children) {
|
|
1107
|
+
enrichedChildren = React__default.default.Children.map(children, (child) => {
|
|
1108
|
+
if (React__default.default.isValidElement(child)) {
|
|
1109
|
+
return React__default.default.cloneElement(child, { _config });
|
|
1110
|
+
}
|
|
1111
|
+
return child;
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
let innerHTML = "";
|
|
1115
|
+
if (enrichedChildren) {
|
|
1116
|
+
try {
|
|
1117
|
+
innerHTML = ReactDOMServer__default.default.renderToString(enrichedChildren);
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
console.error("Body: Failed to render children:", error);
|
|
1120
|
+
innerHTML = "";
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
try {
|
|
1124
|
+
const html = renderBodyToHtml(innerHTML, valuesWithMeta, mode, previewText);
|
|
1125
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1126
|
+
"div",
|
|
1127
|
+
{
|
|
1128
|
+
dangerouslySetInnerHTML: { __html: html },
|
|
1129
|
+
className,
|
|
1130
|
+
style
|
|
1131
|
+
}
|
|
1132
|
+
);
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
console.error("Body rendering failed:", error);
|
|
1135
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children });
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
Body.displayName = "Body";
|
|
1139
|
+
var Body_default = Body;
|
|
1140
|
+
function Email(props) {
|
|
1141
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Body_default, { ...props, mode: "email" });
|
|
1142
|
+
}
|
|
1143
|
+
Email.displayName = "Email";
|
|
1144
|
+
function Page(props) {
|
|
1145
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Body_default, { ...props, mode: "web" });
|
|
1146
|
+
}
|
|
1147
|
+
Page.displayName = "Page";
|
|
1148
|
+
function Document(props) {
|
|
1149
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Body_default, { ...props, mode: "document" });
|
|
1150
|
+
}
|
|
1151
|
+
Document.displayName = "Document";
|
|
1152
|
+
var PROVIDER_ACTIVE_KEY = "__unlayerProviderActive";
|
|
1153
|
+
var _UnlayerContext = null;
|
|
1154
|
+
function getUnlayerContext() {
|
|
1155
|
+
if (!_UnlayerContext) {
|
|
1156
|
+
_UnlayerContext = React.createContext(DEFAULT_CONFIG);
|
|
1157
|
+
}
|
|
1158
|
+
return _UnlayerContext;
|
|
1159
|
+
}
|
|
1160
|
+
var UnlayerProvider = ({
|
|
1161
|
+
config,
|
|
1162
|
+
children
|
|
1163
|
+
}) => {
|
|
1164
|
+
const merged = React.useMemo(
|
|
1165
|
+
() => ({ ...DEFAULT_CONFIG, ...config, [PROVIDER_ACTIVE_KEY]: true }),
|
|
1166
|
+
[config]
|
|
1167
|
+
);
|
|
1168
|
+
const Context = getUnlayerContext();
|
|
1169
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value: merged, children });
|
|
1170
|
+
};
|
|
1171
|
+
UnlayerProvider.displayName = "UnlayerProvider";
|
|
1172
|
+
function useUnlayerConfig() {
|
|
1173
|
+
return React.useContext(getUnlayerContext());
|
|
1174
|
+
}
|
|
1175
|
+
function ensureMeta2(values, type, index = 0) {
|
|
1176
|
+
return {
|
|
1177
|
+
...values,
|
|
1178
|
+
_meta: {
|
|
1179
|
+
htmlID: `u_content_${type.toLowerCase()}_${index + 1}`,
|
|
1180
|
+
htmlClassNames: `u_content_${type.toLowerCase()}`,
|
|
1181
|
+
...values._meta || {}
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
function getDisplayName(element) {
|
|
1186
|
+
const type = element.type;
|
|
1187
|
+
return type?.displayName || type?.name;
|
|
1188
|
+
}
|
|
1189
|
+
function collectChildren(node) {
|
|
1190
|
+
const result = [];
|
|
1191
|
+
React__default.default.Children.forEach(node, (child) => {
|
|
1192
|
+
if (React__default.default.isValidElement(child)) {
|
|
1193
|
+
result.push(child);
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
return result;
|
|
1197
|
+
}
|
|
1198
|
+
function extractSemanticProps(props, extraKeys = []) {
|
|
1199
|
+
const internalKeys = /* @__PURE__ */ new Set([
|
|
1200
|
+
"children",
|
|
1201
|
+
"mode",
|
|
1202
|
+
"className",
|
|
1203
|
+
"style",
|
|
1204
|
+
"index",
|
|
1205
|
+
"colIndex",
|
|
1206
|
+
"cells",
|
|
1207
|
+
"bodyValues",
|
|
1208
|
+
"rowValues",
|
|
1209
|
+
"_config",
|
|
1210
|
+
"config",
|
|
1211
|
+
"previewText",
|
|
1212
|
+
"layout",
|
|
1213
|
+
"collection",
|
|
1214
|
+
...extraKeys
|
|
1215
|
+
]);
|
|
1216
|
+
const result = {};
|
|
1217
|
+
for (const [key, value] of Object.entries(props)) {
|
|
1218
|
+
if (!internalKeys.has(key) && value !== void 0) {
|
|
1219
|
+
result[key] = value;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return result;
|
|
1223
|
+
}
|
|
1224
|
+
function nextCounter(counters, key) {
|
|
1225
|
+
counters[key] = (counters[key] || 0) + 1;
|
|
1226
|
+
return counters[key];
|
|
1227
|
+
}
|
|
1228
|
+
function callHead(head, values, bodyValues, displayMode, headConfig, styles, scripts, tags) {
|
|
1229
|
+
if (!head) return;
|
|
1230
|
+
const headArgs = [
|
|
1231
|
+
values,
|
|
1232
|
+
bodyValues,
|
|
1233
|
+
{
|
|
1234
|
+
displayMode,
|
|
1235
|
+
isViewer: false,
|
|
1236
|
+
variant: null,
|
|
1237
|
+
type: "rows",
|
|
1238
|
+
headConfig
|
|
1239
|
+
}
|
|
1240
|
+
];
|
|
1241
|
+
const css = head.css?.(...headArgs);
|
|
1242
|
+
if (css) styles.push(css);
|
|
1243
|
+
const js = head.js?.(...headArgs);
|
|
1244
|
+
if (js) scripts.push(js);
|
|
1245
|
+
const headTags = head.tags?.(...headArgs);
|
|
1246
|
+
if (headTags) tags.push(...headTags);
|
|
1247
|
+
}
|
|
1248
|
+
function walkItem(element, bodyValues, displayMode, headConfig, counters, styles, scripts, tags) {
|
|
1249
|
+
const componentType = element.type;
|
|
1250
|
+
const config = componentType[UNLAYER_CONFIG_KEY];
|
|
1251
|
+
if (!config) return;
|
|
1252
|
+
const { name, defaultValues, propMapper } = config;
|
|
1253
|
+
const { children, ...restProps } = element.props;
|
|
1254
|
+
const mappedValues = propMapper({ children, ...restProps });
|
|
1255
|
+
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
1256
|
+
const contentType = name.toLowerCase();
|
|
1257
|
+
const count = nextCounter(counters, `u_content_${contentType}`);
|
|
1258
|
+
const valuesWithMeta = ensureMeta2(finalValues, contentType, count - 1);
|
|
1259
|
+
const head = exporters.heads[name];
|
|
1260
|
+
callHead(head, valuesWithMeta, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1261
|
+
}
|
|
1262
|
+
function walkColumn(element, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags) {
|
|
1263
|
+
const semanticProps = extractSemanticProps(element.props);
|
|
1264
|
+
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
1265
|
+
const count = nextCounter(counters, "u_column");
|
|
1266
|
+
const columnValues = ensureMeta2(mergeValues(COLUMN_DEFAULTS, mapped), "column", count - 1);
|
|
1267
|
+
const columnHead = exporters.heads["Column"];
|
|
1268
|
+
callHead(columnHead, columnValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1269
|
+
const children = collectChildren(element.props.children);
|
|
1270
|
+
for (const child of children) {
|
|
1271
|
+
walkItem(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function walkRow(element, bodyValues, displayMode, headConfig, counters, styles, scripts, tags) {
|
|
1275
|
+
const semanticProps = extractSemanticProps(element.props, ["layout"]);
|
|
1276
|
+
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
1277
|
+
const count = nextCounter(counters, "u_row");
|
|
1278
|
+
const rowValues = ensureMeta2(mergeValues(ROW_DEFAULTS, mapped), "row", count - 1);
|
|
1279
|
+
const rowHead = exporters.heads["Row"];
|
|
1280
|
+
callHead(rowHead, rowValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1281
|
+
const children = collectChildren(element.props.children);
|
|
1282
|
+
for (const child of children) {
|
|
1283
|
+
const name = getDisplayName(child);
|
|
1284
|
+
if (name === "Column") {
|
|
1285
|
+
walkColumn(child, bodyValues, rowValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function extractHeadFromTree(element, options) {
|
|
1290
|
+
const { displayMode } = options;
|
|
1291
|
+
const headConfig = {
|
|
1292
|
+
hasFeature: options.headConfig?.hasFeature ?? (() => false),
|
|
1293
|
+
getInitialValues: options.headConfig?.getInitialValues ?? (() => ({}))
|
|
1294
|
+
};
|
|
1295
|
+
const styles = [];
|
|
1296
|
+
const scripts = [];
|
|
1297
|
+
const tags = [];
|
|
1298
|
+
const counters = {};
|
|
1299
|
+
const semanticProps = extractSemanticProps(element.props);
|
|
1300
|
+
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1301
|
+
const bodyValues = ensureMeta2(mergeValues(BODY_DEFAULTS, mapped), "body", 0);
|
|
1302
|
+
const bodyHead = exporters.heads["Body"];
|
|
1303
|
+
callHead(bodyHead, bodyValues, bodyValues, displayMode, headConfig, styles, scripts, tags);
|
|
1304
|
+
const children = collectChildren(element.props.children);
|
|
1305
|
+
for (const child of children) {
|
|
1306
|
+
const name = getDisplayName(child);
|
|
1307
|
+
if (name === "Row") {
|
|
1308
|
+
walkRow(child, bodyValues, displayMode, headConfig, counters, styles, scripts, tags);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
const uniqueTags = [...new Set(tags.filter(Boolean))];
|
|
1312
|
+
return {
|
|
1313
|
+
css: styles.filter(Boolean).join("\n"),
|
|
1314
|
+
js: scripts.filter(Boolean).join("\n"),
|
|
1315
|
+
tags: uniqueTags
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// src/utils/render-to-html.tsx
|
|
1320
|
+
function renderToHtml(element, config) {
|
|
1321
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
1322
|
+
try {
|
|
1323
|
+
const enriched = React__default.default.cloneElement(element, { config: mergedConfig });
|
|
1324
|
+
return ReactDOMServer.renderToStaticMarkup(enriched);
|
|
1325
|
+
} catch (error) {
|
|
1326
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1327
|
+
throw new Error(
|
|
1328
|
+
`[Unlayer] renderToHtml failed: ${message}
|
|
1329
|
+
Tip: Ensure your tree uses Body > Row > Column > Item structure.`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function renderToPlainText(element, config) {
|
|
1334
|
+
const html = renderToHtml(element, config);
|
|
1335
|
+
return htmlToPlainText(html);
|
|
1336
|
+
}
|
|
1337
|
+
function renderToHtmlParts(element, config) {
|
|
1338
|
+
const body = renderToHtml(element, config);
|
|
1339
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
1340
|
+
const displayMode = element.props.mode ?? mergedConfig.mode ?? "web";
|
|
1341
|
+
const { css, js, tags } = extractHeadFromTree(element, {
|
|
1342
|
+
displayMode,
|
|
1343
|
+
headConfig: mergedConfig.headConfig
|
|
1344
|
+
});
|
|
1345
|
+
const headParts = [];
|
|
1346
|
+
if (css) {
|
|
1347
|
+
headParts.push(`<style>${css}</style>`);
|
|
1348
|
+
}
|
|
1349
|
+
if (js) {
|
|
1350
|
+
headParts.push(`<script>${js}</script>`);
|
|
1351
|
+
}
|
|
1352
|
+
if (tags.length > 0) {
|
|
1353
|
+
headParts.push(...tags);
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
head: headParts.join("\n"),
|
|
1357
|
+
body
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
var schemaVersion = exporters.schemaVersion ?? 24;
|
|
1361
|
+
function getDisplayName2(element) {
|
|
1362
|
+
const type = element.type;
|
|
1363
|
+
return type?.displayName || type?.name;
|
|
1364
|
+
}
|
|
1365
|
+
function collectChildren2(node) {
|
|
1366
|
+
const result = [];
|
|
1367
|
+
React__default.default.Children.forEach(node, (child) => {
|
|
1368
|
+
if (React__default.default.isValidElement(child)) {
|
|
1369
|
+
result.push(child);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
return result;
|
|
1373
|
+
}
|
|
1374
|
+
function extractSemanticProps2(props, extraKeys = []) {
|
|
1375
|
+
const internalKeys = /* @__PURE__ */ new Set([
|
|
1376
|
+
"children",
|
|
1377
|
+
"mode",
|
|
1378
|
+
"className",
|
|
1379
|
+
"style",
|
|
1380
|
+
"index",
|
|
1381
|
+
"colIndex",
|
|
1382
|
+
"cells",
|
|
1383
|
+
"bodyValues",
|
|
1384
|
+
"rowValues",
|
|
1385
|
+
"_config",
|
|
1386
|
+
"config",
|
|
1387
|
+
"previewText",
|
|
1388
|
+
"layout",
|
|
1389
|
+
"collection",
|
|
1390
|
+
...extraKeys
|
|
1391
|
+
]);
|
|
1392
|
+
const result = {};
|
|
1393
|
+
for (const [key, value] of Object.entries(props)) {
|
|
1394
|
+
if (!internalKeys.has(key) && value !== void 0) {
|
|
1395
|
+
result[key] = value;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return result;
|
|
1399
|
+
}
|
|
1400
|
+
function nextCounter2(counters, key) {
|
|
1401
|
+
counters[key] = (counters[key] || 0) + 1;
|
|
1402
|
+
return counters[key];
|
|
1403
|
+
}
|
|
1404
|
+
function toContentType(displayName) {
|
|
1405
|
+
if (displayName === "Paragraph") return "text";
|
|
1406
|
+
return displayName.toLowerCase();
|
|
1407
|
+
}
|
|
1408
|
+
function makeId(prefix, counter) {
|
|
1409
|
+
return `${prefix}_${counter}`;
|
|
1410
|
+
}
|
|
1411
|
+
function extractTextFromTextJson(textJson) {
|
|
1412
|
+
try {
|
|
1413
|
+
const parsed = JSON.parse(textJson);
|
|
1414
|
+
if (parsed.__html) return parsed.__html;
|
|
1415
|
+
const root = parsed?.root;
|
|
1416
|
+
if (!root?.children) return "";
|
|
1417
|
+
const paragraphs = [];
|
|
1418
|
+
for (const paragraph of root.children) {
|
|
1419
|
+
if (!paragraph?.children) continue;
|
|
1420
|
+
const parts = [];
|
|
1421
|
+
for (const node of paragraph.children) {
|
|
1422
|
+
if (node?.text) parts.push(node.text);
|
|
1423
|
+
}
|
|
1424
|
+
paragraphs.push(parts.join(""));
|
|
1425
|
+
}
|
|
1426
|
+
return paragraphs.join("\n");
|
|
1427
|
+
} catch {
|
|
1428
|
+
return "";
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
function processItem(element, counters) {
|
|
1432
|
+
const componentType = element.type;
|
|
1433
|
+
const config = componentType[UNLAYER_CONFIG_KEY];
|
|
1434
|
+
if (!config) {
|
|
1435
|
+
const name2 = getDisplayName2(element) || "Unknown";
|
|
1436
|
+
throw new Error(
|
|
1437
|
+
`[Unlayer] renderToJson: <${name2}> is not a recognized Unlayer item component. Only components created with createItemComponent are supported.`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
const { name, defaultValues, propMapper } = config;
|
|
1441
|
+
const contentType = toContentType(name);
|
|
1442
|
+
const count = nextCounter2(counters, `u_content_${contentType}`);
|
|
1443
|
+
const id = makeId(`u_content_${contentType}`, count);
|
|
1444
|
+
const { children, ...restProps } = element.props;
|
|
1445
|
+
const mappedValues = propMapper({ children, ...restProps });
|
|
1446
|
+
const finalValues = mergeValues(defaultValues, mappedValues);
|
|
1447
|
+
if (contentType === "text" && finalValues.textJson) {
|
|
1448
|
+
finalValues.text = extractTextFromTextJson(finalValues.textJson);
|
|
1449
|
+
delete finalValues.textJson;
|
|
1450
|
+
}
|
|
1451
|
+
const values = {
|
|
1452
|
+
containerPadding: "10px",
|
|
1453
|
+
...finalValues,
|
|
1454
|
+
_meta: {
|
|
1455
|
+
htmlID: id,
|
|
1456
|
+
htmlClassNames: `u_content_${contentType}`,
|
|
1457
|
+
...finalValues._meta || {}
|
|
1458
|
+
},
|
|
1459
|
+
selectable: true,
|
|
1460
|
+
draggable: true,
|
|
1461
|
+
duplicatable: true,
|
|
1462
|
+
deletable: true,
|
|
1463
|
+
hideable: true
|
|
1464
|
+
};
|
|
1465
|
+
return { type: contentType, values };
|
|
1466
|
+
}
|
|
1467
|
+
function processColumn(element, counters) {
|
|
1468
|
+
const count = nextCounter2(counters, "u_column");
|
|
1469
|
+
const id = makeId("u_column", count);
|
|
1470
|
+
const semanticProps = extractSemanticProps2(element.props);
|
|
1471
|
+
const mapped = mapSemanticProps(semanticProps, COLUMN_DEFAULTS, "Column");
|
|
1472
|
+
const values = mergeValues(COLUMN_DEFAULTS, mapped);
|
|
1473
|
+
const valuesWithMeta = {
|
|
1474
|
+
...values,
|
|
1475
|
+
_meta: {
|
|
1476
|
+
htmlID: id,
|
|
1477
|
+
htmlClassNames: "u_column",
|
|
1478
|
+
...values._meta || {}
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const contents = [];
|
|
1482
|
+
const children = collectChildren2(element.props.children);
|
|
1483
|
+
for (const child of children) {
|
|
1484
|
+
contents.push(processItem(child, counters));
|
|
1485
|
+
}
|
|
1486
|
+
return { contents, values: valuesWithMeta };
|
|
1487
|
+
}
|
|
1488
|
+
function processRow(element, counters) {
|
|
1489
|
+
const count = nextCounter2(counters, "u_row");
|
|
1490
|
+
const id = makeId("u_row", count);
|
|
1491
|
+
const { layout, cells: propsCells } = element.props;
|
|
1492
|
+
let cells;
|
|
1493
|
+
if (layout) {
|
|
1494
|
+
cells = layout.cells;
|
|
1495
|
+
} else if (propsCells) {
|
|
1496
|
+
cells = propsCells;
|
|
1497
|
+
} else {
|
|
1498
|
+
const columnCount = Math.max(
|
|
1499
|
+
1,
|
|
1500
|
+
collectChildren2(element.props.children).length
|
|
1501
|
+
);
|
|
1502
|
+
cells = Array(columnCount).fill(1);
|
|
1503
|
+
}
|
|
1504
|
+
const semanticProps = extractSemanticProps2(element.props, ["layout"]);
|
|
1505
|
+
const mapped = mapSemanticProps(semanticProps, ROW_DEFAULTS, "Row");
|
|
1506
|
+
const values = mergeValues(ROW_DEFAULTS, mapped);
|
|
1507
|
+
const valuesWithMeta = {
|
|
1508
|
+
...values,
|
|
1509
|
+
columns: values.columns ?? false,
|
|
1510
|
+
cells,
|
|
1511
|
+
_meta: {
|
|
1512
|
+
htmlID: id,
|
|
1513
|
+
htmlClassNames: "u_row",
|
|
1514
|
+
...values._meta || {}
|
|
1515
|
+
},
|
|
1516
|
+
selectable: true,
|
|
1517
|
+
draggable: true,
|
|
1518
|
+
duplicatable: true,
|
|
1519
|
+
deletable: true,
|
|
1520
|
+
hideable: true
|
|
1521
|
+
};
|
|
1522
|
+
const columns = [];
|
|
1523
|
+
const children = collectChildren2(element.props.children);
|
|
1524
|
+
for (const child of children) {
|
|
1525
|
+
const name = getDisplayName2(child);
|
|
1526
|
+
if (name === "Column") {
|
|
1527
|
+
columns.push(processColumn(child, counters));
|
|
1528
|
+
} else {
|
|
1529
|
+
console.warn(
|
|
1530
|
+
`[Unlayer] renderToJson: <${name}> is not a valid Row child. Only <Column> is allowed.`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return { cells, columns, values: valuesWithMeta };
|
|
1535
|
+
}
|
|
1536
|
+
function processBody(element, counters) {
|
|
1537
|
+
const id = "u_body";
|
|
1538
|
+
const semanticProps = extractSemanticProps2(element.props);
|
|
1539
|
+
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1540
|
+
const values = mergeValues(BODY_DEFAULTS, mapped);
|
|
1541
|
+
const valuesWithMeta = {
|
|
1542
|
+
...values,
|
|
1543
|
+
_meta: {
|
|
1544
|
+
htmlID: id,
|
|
1545
|
+
htmlClassNames: "u_body",
|
|
1546
|
+
...values._meta || {}
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
const rows = [];
|
|
1550
|
+
const children = collectChildren2(element.props.children);
|
|
1551
|
+
for (const child of children) {
|
|
1552
|
+
const name = getDisplayName2(child);
|
|
1553
|
+
if (name === "Row") {
|
|
1554
|
+
rows.push(processRow(child, counters));
|
|
1555
|
+
} else {
|
|
1556
|
+
console.warn(
|
|
1557
|
+
`[Unlayer] renderToJson: <${name}> is not a valid Body child. Only <Row> is allowed.`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return {
|
|
1562
|
+
rows,
|
|
1563
|
+
values: valuesWithMeta
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function renderRowToJson(element) {
|
|
1567
|
+
const displayName = getDisplayName2(element);
|
|
1568
|
+
if (displayName !== "Row") {
|
|
1569
|
+
throw new Error(
|
|
1570
|
+
`[Unlayer] renderRowToJson: Element must be <Row>, but got <${displayName || "unknown"}>. For full designs, use renderToJson instead.`
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
const counters = {};
|
|
1574
|
+
return processRow(element, counters);
|
|
1575
|
+
}
|
|
1576
|
+
function renderToJson(element) {
|
|
1577
|
+
const displayName = getDisplayName2(element);
|
|
1578
|
+
const validRoots = /* @__PURE__ */ new Set(["Body", "Email", "Page", "Document"]);
|
|
1579
|
+
if (!displayName || !validRoots.has(displayName)) {
|
|
1580
|
+
throw new Error(
|
|
1581
|
+
`[Unlayer] renderToJson: Root element must be <Body>, <Email>, <Page>, or <Document>, but got <${displayName || "unknown"}>. Wrap your content: <Body><Row><Column>...</Column></Row></Body>`
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
const counters = {};
|
|
1585
|
+
const body = processBody(element, counters);
|
|
1586
|
+
return {
|
|
1587
|
+
counters,
|
|
1588
|
+
body,
|
|
1589
|
+
schemaVersion
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
exports.Body = Body_default;
|
|
1594
|
+
exports.Button = Button_default;
|
|
1595
|
+
exports.Column = Column_default;
|
|
1596
|
+
exports.ColumnLayouts = ColumnLayouts;
|
|
1597
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
1598
|
+
exports.Divider = Divider_default;
|
|
1599
|
+
exports.Document = Document;
|
|
1600
|
+
exports.Email = Email;
|
|
1601
|
+
exports.Heading = Heading_default;
|
|
1602
|
+
exports.Html = Html_default;
|
|
1603
|
+
exports.Image = Image_default;
|
|
1604
|
+
exports.Menu = Menu_default;
|
|
1605
|
+
exports.Page = Page;
|
|
1606
|
+
exports.Paragraph = Paragraph_default;
|
|
1607
|
+
exports.Row = Row_default;
|
|
1608
|
+
exports.Social = Social_default;
|
|
1609
|
+
exports.Table = Table_default;
|
|
1610
|
+
exports.UnlayerProvider = UnlayerProvider;
|
|
1611
|
+
exports.Video = Video_default;
|
|
1612
|
+
exports.htmlToTextJson = htmlToTextJson;
|
|
1613
|
+
exports.renderRowToJson = renderRowToJson;
|
|
1614
|
+
exports.renderToHtml = renderToHtml;
|
|
1615
|
+
exports.renderToHtmlParts = renderToHtmlParts;
|
|
1616
|
+
exports.renderToJson = renderToJson;
|
|
1617
|
+
exports.renderToPlainText = renderToPlainText;
|
|
1618
|
+
exports.useUnlayerConfig = useUnlayerConfig;
|
|
1619
|
+
exports.validateColumnLayout = validateColumnLayout;
|
|
1620
|
+
//# sourceMappingURL=index.cjs.map
|
|
1621
|
+
//# sourceMappingURL=index.cjs.map
|