@unlayer/react-elements 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/index.cjs +173 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -14
- package/dist/index.d.ts +108 -14
- package/dist/index.js +174 -21
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
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, ContentExporters, ColumnExporters, BodyExporters, heads } from '@unlayer/exporters';
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import React, { useMemo, useContext, createContext } from 'react';
|
|
4
4
|
import ReactDOMServer, { renderToStaticMarkup } from 'react-dom/server';
|
|
@@ -201,16 +201,53 @@ function analyzeNestedStructure(defaultValues) {
|
|
|
201
201
|
nestedStructureCache.set(defaultValues, nestedGroups);
|
|
202
202
|
return nestedGroups;
|
|
203
203
|
}
|
|
204
|
+
var PX_SIZE_KEYS = [
|
|
205
|
+
"fontSize",
|
|
206
|
+
"padding",
|
|
207
|
+
"containerPadding",
|
|
208
|
+
"borderRadius",
|
|
209
|
+
"letterSpacing"
|
|
210
|
+
];
|
|
211
|
+
function normalizeCssProps(props) {
|
|
212
|
+
if (typeof props.fontFamily === "string") {
|
|
213
|
+
const v = props.fontFamily;
|
|
214
|
+
props.fontFamily = { label: v, value: v };
|
|
215
|
+
}
|
|
216
|
+
if (typeof props.fontWeight === "string" && /^\d+$/.test(props.fontWeight.trim())) {
|
|
217
|
+
props.fontWeight = Number(props.fontWeight.trim());
|
|
218
|
+
}
|
|
219
|
+
if (typeof props.lineHeight === "number") {
|
|
220
|
+
props.lineHeight = String(props.lineHeight);
|
|
221
|
+
}
|
|
222
|
+
for (const key of PX_SIZE_KEYS) {
|
|
223
|
+
const v = props[key];
|
|
224
|
+
if (typeof v === "number") {
|
|
225
|
+
props[key] = `${v}px`;
|
|
226
|
+
} else if (typeof v === "string" && /^\d+$/.test(v.trim())) {
|
|
227
|
+
props[key] = `${v.trim()}px`;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function flattenChildrenText(node) {
|
|
232
|
+
if (node == null || typeof node === "boolean") return "";
|
|
233
|
+
if (typeof node === "string") return node;
|
|
234
|
+
if (typeof node === "number") return String(node);
|
|
235
|
+
if (Array.isArray(node)) return node.map(flattenChildrenText).join("");
|
|
236
|
+
if (typeof node === "object" && "props" in node) {
|
|
237
|
+
return flattenChildrenText(node.props?.children);
|
|
238
|
+
}
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
204
241
|
function mapSemanticProps(props, defaultValues, componentType) {
|
|
205
242
|
const { children, values, ...restProps } = props;
|
|
206
243
|
const userProps = { ...restProps };
|
|
207
244
|
const result = values ? { ...values } : {};
|
|
208
245
|
if (children !== void 0 && !result.text && !result.textJson) {
|
|
246
|
+
const textContent = typeof children === "string" ? children : flattenChildrenText(children);
|
|
209
247
|
if (componentType === "Paragraph") {
|
|
210
|
-
const textContent = typeof children === "string" ? children : String(children);
|
|
211
248
|
result.textJson = textToTextJson(textContent);
|
|
212
249
|
} else {
|
|
213
|
-
result.text =
|
|
250
|
+
result.text = textContent;
|
|
214
251
|
}
|
|
215
252
|
}
|
|
216
253
|
const textFromEscapeHatch = result.text;
|
|
@@ -236,6 +273,7 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
236
273
|
};
|
|
237
274
|
}
|
|
238
275
|
}
|
|
276
|
+
normalizeCssProps(userProps);
|
|
239
277
|
const nestedGroups = analyzeNestedStructure(defaultValues);
|
|
240
278
|
const nested = {};
|
|
241
279
|
const flat = {};
|
|
@@ -270,6 +308,19 @@ function mapSemanticProps(props, defaultValues, componentType) {
|
|
|
270
308
|
};
|
|
271
309
|
}
|
|
272
310
|
}
|
|
311
|
+
if (defaultValues && typeof defaultValues === "object" && "border" in defaultValues) {
|
|
312
|
+
const borderSideRe = /^border(Top|Right|Bottom|Left)(Width|Style|Color)$/;
|
|
313
|
+
const collected = {};
|
|
314
|
+
for (const key of Object.keys(final)) {
|
|
315
|
+
if (borderSideRe.test(key)) {
|
|
316
|
+
collected[key] = final[key];
|
|
317
|
+
delete final[key];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (Object.keys(collected).length > 0) {
|
|
321
|
+
final.border = { ...final.border || {}, ...collected };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
273
324
|
return final;
|
|
274
325
|
}
|
|
275
326
|
function normalizeLinkValue(value) {
|
|
@@ -538,7 +589,26 @@ var DEFAULT_VALUES = {
|
|
|
538
589
|
var Button = createItemComponent({
|
|
539
590
|
name: "Button",
|
|
540
591
|
defaultValues: DEFAULT_VALUES,
|
|
541
|
-
propMapper: (props) =>
|
|
592
|
+
propMapper: (props) => {
|
|
593
|
+
const mapped = mapSemanticProps(
|
|
594
|
+
props,
|
|
595
|
+
DEFAULT_VALUES,
|
|
596
|
+
"Button"
|
|
597
|
+
);
|
|
598
|
+
const size = mapped.size;
|
|
599
|
+
if (size && typeof size === "object" && !Array.isArray(size)) {
|
|
600
|
+
const s = size;
|
|
601
|
+
if (typeof s.width === "number") {
|
|
602
|
+
s.width = `${s.width}px`;
|
|
603
|
+
} else if (typeof s.width === "string" && /^\d+(?:\.\d+)?$/.test(s.width.trim())) {
|
|
604
|
+
s.width = `${s.width.trim()}px`;
|
|
605
|
+
}
|
|
606
|
+
if (s.width !== void 0 && s.autoWidth === void 0) {
|
|
607
|
+
s.autoWidth = false;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return mapped;
|
|
611
|
+
},
|
|
542
612
|
displayName: "Button",
|
|
543
613
|
exporters: ButtonExporters
|
|
544
614
|
});
|
|
@@ -603,18 +673,46 @@ var Image = createItemComponent({
|
|
|
603
673
|
defaultValues: DEFAULT_VALUES5,
|
|
604
674
|
propMapper: (props) => {
|
|
605
675
|
const { alt, src, ...rest } = props;
|
|
676
|
+
const restValues = rest.values;
|
|
677
|
+
const normalizedRest = restValues && typeof restValues.src === "string" ? { ...rest, values: { ...restValues, src: { url: restValues.src } } } : rest;
|
|
606
678
|
const base = mapSemanticProps(
|
|
607
|
-
|
|
679
|
+
normalizedRest,
|
|
608
680
|
DEFAULT_VALUES5,
|
|
609
681
|
"Image"
|
|
610
682
|
);
|
|
611
683
|
if (alt !== void 0) {
|
|
612
684
|
base.altText = alt;
|
|
613
685
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
686
|
+
const baseSrc = base.src;
|
|
687
|
+
const fromValues = baseSrc && typeof baseSrc === "object" && !Array.isArray(baseSrc) ? baseSrc : typeof baseSrc === "string" ? { url: baseSrc } : {};
|
|
688
|
+
const fromProp = typeof src === "string" ? { url: src } : src ?? {};
|
|
689
|
+
const userSrc = { ...fromValues, ...fromProp };
|
|
690
|
+
if (src !== void 0 || baseSrc !== void 0) {
|
|
691
|
+
const isStringUrl = typeof src === "string" || src === void 0 && typeof baseSrc === "string";
|
|
692
|
+
const start = isStringUrl ? { autoWidth: true, maxWidth: "100%" } : { ...DEFAULT_VALUES5.src };
|
|
693
|
+
const merged = { ...start, ...userSrc };
|
|
694
|
+
const pctRe = /^\d+(?:\.\d+)?%$/;
|
|
695
|
+
if (typeof merged.width === "string") {
|
|
696
|
+
const t = merged.width.trim();
|
|
697
|
+
if (pctRe.test(t)) {
|
|
698
|
+
if (userSrc.maxWidth === void 0) merged.maxWidth = t;
|
|
699
|
+
delete merged.width;
|
|
700
|
+
} else {
|
|
701
|
+
const px = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(t);
|
|
702
|
+
if (px) merged.width = parseFloat(px[1]);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const displayPct = typeof merged.maxWidth === "string" && pctRe.test(merged.maxWidth.trim()) ? merged.maxWidth.trim() : void 0;
|
|
706
|
+
if (userSrc.autoWidth === void 0) {
|
|
707
|
+
if (displayPct && displayPct !== "100%") {
|
|
708
|
+
merged.autoWidth = false;
|
|
709
|
+
merged.maxWidth = displayPct;
|
|
710
|
+
} else {
|
|
711
|
+
merged.autoWidth = true;
|
|
712
|
+
merged.maxWidth = "100%";
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
base.src = merged;
|
|
618
716
|
}
|
|
619
717
|
return base;
|
|
620
718
|
},
|
|
@@ -725,25 +823,37 @@ var Table = createItemComponent({
|
|
|
725
823
|
name: "Table",
|
|
726
824
|
defaultValues: DEFAULT_VALUES9,
|
|
727
825
|
propMapper: (props) => {
|
|
728
|
-
const { headers, data, ...rest } = props;
|
|
729
|
-
if (headers || data) {
|
|
826
|
+
const { headers, data, columns, rows, ...rest } = props;
|
|
827
|
+
if (headers || data || typeof columns === "number" || typeof rows === "number") {
|
|
730
828
|
const base = mapSemanticProps(
|
|
731
829
|
rest,
|
|
732
830
|
DEFAULT_VALUES9,
|
|
733
831
|
"Table"
|
|
734
832
|
);
|
|
833
|
+
const colCount = headers ? headers.length : typeof columns === "number" ? columns : data?.[0]?.length ?? 0;
|
|
834
|
+
const blankCells = (n) => Array.from({ length: n }, () => ({ text: "", width: 0 }));
|
|
735
835
|
const tableHeaders = headers ? [{ cells: headers.map((text) => ({ text, width: 0 })), height: 0 }] : [];
|
|
736
836
|
const tableRows = data ? data.map((row) => ({
|
|
737
837
|
cells: row.map((text) => ({ text, width: 0 })),
|
|
738
838
|
height: 0
|
|
739
|
-
})) :
|
|
839
|
+
})) : typeof rows === "number" ? (
|
|
840
|
+
// No data: build an empty grid sized by `columns` × `rows`.
|
|
841
|
+
Array.from({ length: rows }, () => ({
|
|
842
|
+
cells: blankCells(colCount),
|
|
843
|
+
height: 0
|
|
844
|
+
}))
|
|
845
|
+
) : [];
|
|
740
846
|
base.table = { headers: tableHeaders, rows: tableRows, footers: [] };
|
|
847
|
+
if (headers || typeof columns === "number") {
|
|
848
|
+
base.columns = colCount;
|
|
849
|
+
}
|
|
741
850
|
if (headers) {
|
|
742
|
-
base.columns = headers.length;
|
|
743
851
|
base.enableHeader = true;
|
|
744
852
|
}
|
|
745
853
|
if (data) {
|
|
746
854
|
base.rows = data.length;
|
|
855
|
+
} else if (typeof rows === "number") {
|
|
856
|
+
base.rows = rows;
|
|
747
857
|
}
|
|
748
858
|
return base;
|
|
749
859
|
}
|
|
@@ -904,13 +1014,23 @@ ${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width:
|
|
|
904
1014
|
}`;
|
|
905
1015
|
return baseCSS + "\n" + columnCSS + "\n" + responsiveCSS;
|
|
906
1016
|
}
|
|
1017
|
+
function toContentWidthPx(bodyValues, fallback = 500) {
|
|
1018
|
+
const raw = bodyValues?.contentWidth;
|
|
1019
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
1020
|
+
if (typeof raw === "string") {
|
|
1021
|
+
const n = parseInt(raw, 10);
|
|
1022
|
+
if (Number.isFinite(n)) return n;
|
|
1023
|
+
}
|
|
1024
|
+
return fallback;
|
|
1025
|
+
}
|
|
907
1026
|
function renderRowToHtml(innerHTML, values, bodyValues, mode, cells, collection = "rows") {
|
|
908
1027
|
const rowExporter = RowExporters[mode] || RowExporters.web;
|
|
909
1028
|
const html = rowExporter(innerHTML, values, bodyValues, {
|
|
910
1029
|
collection,
|
|
911
1030
|
variant: mode
|
|
912
1031
|
});
|
|
913
|
-
const
|
|
1032
|
+
const contentWidth = toContentWidthPx(bodyValues);
|
|
1033
|
+
const css = generateGridCSS(cells, mode, contentWidth);
|
|
914
1034
|
return css ? `<style>${css}</style>${html}` : html;
|
|
915
1035
|
}
|
|
916
1036
|
function processChildren(children, cells, bodyValues, rowValues, mode, _config) {
|
|
@@ -1008,11 +1128,16 @@ var Row = (props) => {
|
|
|
1008
1128
|
};
|
|
1009
1129
|
Row.displayName = "Row";
|
|
1010
1130
|
var Row_default = Row;
|
|
1131
|
+
var DEFAULT_CONTAINER_PADDING = "10px";
|
|
1011
1132
|
var DEFAULT_VALUES12 = COLUMN_DEFAULTS;
|
|
1012
1133
|
function renderColumnToHtml(innerHTML, values, index, cells, bodyValues, rowValues, mode) {
|
|
1013
1134
|
const columnExporter = ColumnExporters[mode] || ColumnExporters.web;
|
|
1014
1135
|
return columnExporter(innerHTML, values, index, cells, bodyValues, rowValues);
|
|
1015
1136
|
}
|
|
1137
|
+
function renderContentToHtml(innerHTML, values, bodyValues, mode) {
|
|
1138
|
+
const contentExporter = ContentExporters[mode] || ContentExporters.web;
|
|
1139
|
+
return contentExporter(innerHTML, values, bodyValues, {});
|
|
1140
|
+
}
|
|
1016
1141
|
var Column = (props) => {
|
|
1017
1142
|
const {
|
|
1018
1143
|
children,
|
|
@@ -1055,10 +1180,22 @@ var Column = (props) => {
|
|
|
1055
1180
|
if (rendered && typeof rendered === "object" && rendered.props && rendered.props.dangerouslySetInnerHTML) {
|
|
1056
1181
|
const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
|
|
1057
1182
|
const componentType = child.type;
|
|
1058
|
-
const componentName = componentType?.displayName || componentType?.name || "component";
|
|
1059
|
-
const
|
|
1060
|
-
const containerPadding =
|
|
1061
|
-
|
|
1183
|
+
const componentName = (componentType?.displayName || componentType?.name || "component").toLowerCase();
|
|
1184
|
+
const childProps = child.props;
|
|
1185
|
+
const containerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING;
|
|
1186
|
+
const contentValues = {
|
|
1187
|
+
containerPadding,
|
|
1188
|
+
_meta: {
|
|
1189
|
+
htmlID: `u_content_${componentName}_${childIndex + 1}`,
|
|
1190
|
+
htmlClassNames: `u_content_${componentName}`
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
innerHTML += renderContentToHtml(
|
|
1194
|
+
componentHTML,
|
|
1195
|
+
contentValues,
|
|
1196
|
+
bodyValues,
|
|
1197
|
+
mode
|
|
1198
|
+
);
|
|
1062
1199
|
} else if (rendered) {
|
|
1063
1200
|
const name = child.type?.displayName || child.type?.name || "Unknown";
|
|
1064
1201
|
console.warn(
|
|
@@ -1120,7 +1257,13 @@ function renderBodyToHtml(innerHTML, values, mode, previewText) {
|
|
|
1120
1257
|
}
|
|
1121
1258
|
}
|
|
1122
1259
|
const bodyExporter = BodyExporters[mode] || BodyExporters.web;
|
|
1123
|
-
const raw = mode === "document" ? bodyExporter(finalInnerHtml, values, { type: "" }) :
|
|
1260
|
+
const raw = mode === "document" ? bodyExporter(finalInnerHtml, values, { type: "" }) : mode === "email" ? (
|
|
1261
|
+
// The email body exporter reads body context (contentWidth,
|
|
1262
|
+
// contentAlign) from the `bodyValues` field of its 3rd argument.
|
|
1263
|
+
// Passing `values` directly left it undefined, so the Outlook (MSO)
|
|
1264
|
+
// table fell back to 600px regardless of contentWidth.
|
|
1265
|
+
bodyExporter(finalInnerHtml, values, { bodyValues: values })
|
|
1266
|
+
) : bodyExporter(finalInnerHtml, values, values);
|
|
1124
1267
|
return raw.replace("min-height: 100vh; ", "").replace("min-height: 100vh;", "");
|
|
1125
1268
|
}
|
|
1126
1269
|
var Body = (props) => {
|
|
@@ -1128,7 +1271,10 @@ var Body = (props) => {
|
|
|
1128
1271
|
const resolvedConfig = { ...DEFAULT_CONFIG, ...configProp };
|
|
1129
1272
|
const mode = modeProp ?? resolvedConfig.mode ?? "web";
|
|
1130
1273
|
const _config = { ...resolvedConfig, mode };
|
|
1131
|
-
const values =
|
|
1274
|
+
const values = mergeValues(
|
|
1275
|
+
DEFAULT_VALUES13,
|
|
1276
|
+
mapSemanticProps(semanticProps, DEFAULT_VALUES13, "Body")
|
|
1277
|
+
);
|
|
1132
1278
|
const valuesWithMeta = {
|
|
1133
1279
|
...values,
|
|
1134
1280
|
_meta: {
|
|
@@ -1141,7 +1287,10 @@ var Body = (props) => {
|
|
|
1141
1287
|
if (children) {
|
|
1142
1288
|
enrichedChildren = React.Children.map(children, (child) => {
|
|
1143
1289
|
if (React.isValidElement(child)) {
|
|
1144
|
-
return React.cloneElement(child, {
|
|
1290
|
+
return React.cloneElement(child, {
|
|
1291
|
+
_config,
|
|
1292
|
+
bodyValues: values
|
|
1293
|
+
});
|
|
1145
1294
|
}
|
|
1146
1295
|
return child;
|
|
1147
1296
|
});
|
|
@@ -1573,6 +1722,10 @@ function processBody(element, counters) {
|
|
|
1573
1722
|
const semanticProps = extractSemanticProps2(element.props);
|
|
1574
1723
|
const mapped = mapSemanticProps(semanticProps, BODY_DEFAULTS, "Body");
|
|
1575
1724
|
const values = mergeValues(BODY_DEFAULTS, mapped);
|
|
1725
|
+
const previewText = element.props.previewText;
|
|
1726
|
+
if (previewText !== void 0) {
|
|
1727
|
+
values.preheaderText = previewText;
|
|
1728
|
+
}
|
|
1576
1729
|
const valuesWithMeta = {
|
|
1577
1730
|
...values,
|
|
1578
1731
|
_meta: {
|