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