@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/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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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
+ "&amp;": "&",
286
+ "&lt;": "<",
287
+ "&gt;": ">",
288
+ "&quot;": '"',
289
+ "&#39;": "'",
290
+ "&apos;": "'",
291
+ "&nbsp;": " ",
292
+ "&mdash;": "\u2014",
293
+ "&ndash;": "\u2013",
294
+ "&laquo;": "\xAB",
295
+ "&raquo;": "\xBB",
296
+ "&bull;": "\u2022",
297
+ "&middot;": "\xB7",
298
+ "&hellip;": "\u2026",
299
+ "&copy;": "\xA9",
300
+ "&reg;": "\xAE",
301
+ "&trade;": "\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