@unlayer/react-elements 0.1.12 → 0.1.14

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 CHANGED
@@ -12,6 +12,81 @@ var ReactDOMServer__default = /*#__PURE__*/_interopDefault(ReactDOMServer);
12
12
 
13
13
  // src/components/Button.tsx
14
14
 
15
+ // src/utils/image-sizing.ts
16
+ function toPx(value) {
17
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
18
+ if (typeof value !== "string") return void 0;
19
+ const m = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(value.trim());
20
+ return m ? parseFloat(m[1]) : void 0;
21
+ }
22
+ function edges(value) {
23
+ if (value == null) return { left: 0, right: 0 };
24
+ if (typeof value === "number") return { left: value, right: value };
25
+ const parts = String(value).trim().split(/\s+/).map((p) => parseFloat(p) || 0);
26
+ if (parts.length === 1) return { left: parts[0], right: parts[0] };
27
+ if (parts.length === 2 || parts.length === 3)
28
+ return { left: parts[1], right: parts[1] };
29
+ return { left: parts[3] || 0, right: parts[1] || 0 };
30
+ }
31
+ function borderEdges(border) {
32
+ if (!border || typeof border !== "object") return { left: 0, right: 0 };
33
+ const b = border;
34
+ const width = (v) => parseFloat(`${v ?? ""}`) || 0;
35
+ return {
36
+ left: width(b.borderLeftWidth),
37
+ right: width(b.borderRightWidth)
38
+ };
39
+ }
40
+ function fixedContentWidth(contentWidth) {
41
+ if (typeof contentWidth === "number")
42
+ return Number.isFinite(contentWidth) ? contentWidth : void 0;
43
+ if (typeof contentWidth === "string") {
44
+ const m = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(contentWidth.trim());
45
+ if (m) return parseFloat(m[1]);
46
+ }
47
+ return void 0;
48
+ }
49
+ var FALLBACK_BODY_CONTENT_WIDTH = 500;
50
+ var DEFAULT_CONTAINER_PADDING = "10px";
51
+ function bodyContentWidthPx(contentWidth, fallback = FALLBACK_BODY_CONTENT_WIDTH) {
52
+ return fixedContentWidth(contentWidth) ?? fallback;
53
+ }
54
+ function contentSlotWidth(ctx) {
55
+ const { bodyValues = {}, rowValues = {}, columnValues = {} } = ctx;
56
+ const rowCells = ctx.rowCells && ctx.rowCells.length ? ctx.rowCells : [1];
57
+ const columnIndex = ctx.columnIndex ?? 0;
58
+ const bodyWidth = bodyContentWidthPx(bodyValues.contentWidth);
59
+ const bp = edges(bodyValues.padding);
60
+ const bb = borderEdges(bodyValues.border);
61
+ const bodyAvail = bodyWidth - bp.left - bp.right - bb.left - bb.right;
62
+ const rp = edges(rowValues.padding);
63
+ const rb = borderEdges(rowValues.border);
64
+ const rowAvail = bodyAvail - rp.left - rp.right - rb.left - rb.right;
65
+ const rowSpan = rowCells.reduce((a, b) => a + b, 0) || 1;
66
+ const colSpan = rowCells[columnIndex] || 1;
67
+ const colWidth = colSpan / rowSpan * rowAvail;
68
+ const cp = edges(columnValues.padding);
69
+ const cb = borderEdges(columnValues.border);
70
+ const colAvail = colWidth - cp.left - cp.right - cb.left - cb.right;
71
+ const ip = edges(ctx.containerPadding ?? DEFAULT_CONTAINER_PADDING);
72
+ return colAvail - ip.left - ip.right;
73
+ }
74
+ var PERCENT = /^\d+(?:\.\d+)?%$/;
75
+ function round2(n) {
76
+ return Math.round(n * 100) / 100;
77
+ }
78
+ function pinImageSrc(src, availableWidth) {
79
+ if (!src || typeof src !== "object") return src;
80
+ if (src.autoWidth !== false) return src;
81
+ const maxWidth = src.maxWidth;
82
+ if (typeof maxWidth === "string" && PERCENT.test(maxWidth.trim())) return src;
83
+ const pinPx = toPx(maxWidth);
84
+ if (pinPx == null) return src;
85
+ const avail = availableWidth && availableWidth > 0 ? availableWidth : void 0;
86
+ const pct = avail ? pinPx >= avail ? 100 : round2(pinPx / avail * 100) : 100;
87
+ return { ...src, autoWidth: false, maxWidth: `${pct}%` };
88
+ }
89
+
15
90
  // ../shared/dist/index.js
16
91
  var DEFAULT_CONFIG = {
17
92
  cdnBaseUrl: "https://cdn.tools.unlayer.com",
@@ -473,6 +548,12 @@ var ErrorFallback = ({
473
548
  ]
474
549
  }
475
550
  ) });
551
+ function nextHtmlId(config, prefix) {
552
+ if (!config) return `${prefix}_1`;
553
+ const ids = config.__ids ?? (config.__ids = {});
554
+ ids[prefix] = (ids[prefix] || 0) + 1;
555
+ return `${prefix}_${ids[prefix]}`;
556
+ }
476
557
  function ensureMeta(values, type, index = 0) {
477
558
  return {
478
559
  ...values,
@@ -484,7 +565,7 @@ function ensureMeta(values, type, index = 0) {
484
565
  };
485
566
  }
486
567
  function renderComponent(config) {
487
- const { type, values, className, style, args = [], innerHTML, _config, exporter } = config;
568
+ const { type, values, className, style, args = [], innerHTML, _config, exporter, metaContext } = config;
488
569
  try {
489
570
  const cfg = _config ?? DEFAULT_CONFIG;
490
571
  const exporterConfig = {
@@ -499,7 +580,8 @@ function renderComponent(config) {
499
580
  } else {
500
581
  const meta = {
501
582
  exporterConfig,
502
- mergeTagState: cfg.mergeTagState
583
+ mergeTagState: cfg.mergeTagState,
584
+ ...metaContext ?? {}
503
585
  };
504
586
  html = exporter(values, ...args, void 0, meta);
505
587
  }
@@ -538,6 +620,7 @@ function createItemComponent(config) {
538
620
  cells = [],
539
621
  bodyValues = {},
540
622
  rowValues = {},
623
+ columnValues = {},
541
624
  _config,
542
625
  // Children
543
626
  children,
@@ -556,13 +639,25 @@ function createItemComponent(config) {
556
639
  index
557
640
  );
558
641
  const safeBodyValues = {
559
- contentWidth: 600,
642
+ contentWidth: "500px",
560
643
  ...bodyValues
561
644
  };
562
645
  const valuesForExporter = normalizeValuesForExporter(
563
646
  valuesWithMeta,
564
647
  config.name
565
648
  );
649
+ const exportSrc = valuesForExporter.src;
650
+ if (exportSrc && typeof exportSrc === "object" && exportSrc.autoWidth === false) {
651
+ const availableWidth = contentSlotWidth({
652
+ bodyValues: safeBodyValues,
653
+ rowValues,
654
+ rowCells: cells,
655
+ columnIndex: colIndex,
656
+ columnValues,
657
+ containerPadding: props.containerPadding ?? props.values?.containerPadding
658
+ });
659
+ valuesForExporter.src = pinImageSrc(exportSrc, availableWidth);
660
+ }
566
661
  const exporter = config.exporters[mode] || config.exporters.web;
567
662
  return renderComponent({
568
663
  type: config.name,
@@ -570,6 +665,15 @@ function createItemComponent(config) {
570
665
  className,
571
666
  style,
572
667
  args: [index, colIndex, cells, safeBodyValues, rowValues],
668
+ // The 8th arg the exporters actually read: column/body context for
669
+ // width-aware rendering (Image's available-width calc), mirroring the editor.
670
+ metaContext: {
671
+ columnIndex: colIndex,
672
+ columnValues,
673
+ rowCells: cells,
674
+ rowValues,
675
+ bodyValues: safeBodyValues
676
+ },
573
677
  _config,
574
678
  exporter
575
679
  });
@@ -679,7 +783,7 @@ var Image = createItemComponent({
679
783
  name: "Image",
680
784
  defaultValues: DEFAULT_VALUES5,
681
785
  propMapper: (props) => {
682
- const { alt, src, ...rest } = props;
786
+ const { alt, src, width: widthProp, maxWidth: maxWidthProp, ...rest } = props;
683
787
  const restValues = rest.values;
684
788
  const normalizedRest = restValues && typeof restValues.src === "string" ? { ...rest, values: { ...restValues, src: { url: restValues.src } } } : rest;
685
789
  const base = mapSemanticProps(
@@ -699,21 +803,39 @@ var Image = createItemComponent({
699
803
  const start = isStringUrl ? { autoWidth: true, maxWidth: "100%" } : { ...DEFAULT_VALUES5.src };
700
804
  const merged = { ...start, ...userSrc };
701
805
  const pctRe = /^\d+(?:\.\d+)?%$/;
702
- if (typeof merged.width === "string") {
703
- const t = merged.width.trim();
704
- if (pctRe.test(t)) {
705
- if (userSrc.maxWidth === void 0) merged.maxWidth = t;
706
- delete merged.width;
707
- } else {
708
- const px = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(t);
709
- if (px) merged.width = parseFloat(px[1]);
806
+ const asPercent = (v) => typeof v === "string" && pctRe.test(v.trim()) ? v.trim() : void 0;
807
+ const asPx = (v) => {
808
+ if (typeof v === "number" && Number.isFinite(v)) return v;
809
+ if (typeof v === "string") {
810
+ const t = v.trim();
811
+ if (pctRe.test(t)) return void 0;
812
+ const m = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(t);
813
+ if (m) return parseFloat(m[1]);
814
+ }
815
+ return void 0;
816
+ };
817
+ let displayPct;
818
+ let displayPx;
819
+ for (const candidate of [widthProp, maxWidthProp, userSrc.maxWidth]) {
820
+ if (candidate === void 0) continue;
821
+ const pct = asPercent(candidate);
822
+ if (pct) {
823
+ displayPct = pct;
824
+ break;
825
+ }
826
+ const px = asPx(candidate);
827
+ if (px != null) {
828
+ displayPx = px;
829
+ break;
710
830
  }
711
831
  }
712
- const displayPct = typeof merged.maxWidth === "string" && pctRe.test(merged.maxWidth.trim()) ? merged.maxWidth.trim() : void 0;
713
832
  if (userSrc.autoWidth === void 0) {
714
833
  if (displayPct && displayPct !== "100%") {
715
834
  merged.autoWidth = false;
716
835
  merged.maxWidth = displayPct;
836
+ } else if (displayPx != null) {
837
+ merged.autoWidth = false;
838
+ merged.maxWidth = displayPx;
717
839
  } else {
718
840
  merged.autoWidth = true;
719
841
  merged.maxWidth = "100%";
@@ -1022,13 +1144,7 @@ ${widths.map(({ value, className }) => ` .no-stack .u-col-${className} { width:
1022
1144
  return baseCSS + "\n" + columnCSS + "\n" + responsiveCSS;
1023
1145
  }
1024
1146
  function toContentWidthPx(bodyValues, fallback = 500) {
1025
- const raw = bodyValues?.contentWidth;
1026
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
1027
- if (typeof raw === "string") {
1028
- const n = parseInt(raw, 10);
1029
- if (Number.isFinite(n)) return n;
1030
- }
1031
- return fallback;
1147
+ return bodyContentWidthPx(bodyValues?.contentWidth, fallback);
1032
1148
  }
1033
1149
  function renderRowToHtml(innerHTML, values, bodyValues, mode, cells, collection = "rows") {
1034
1150
  const rowExporter = exporters.RowExporters[mode] || exporters.RowExporters.web;
@@ -1105,7 +1221,7 @@ var Row = (props) => {
1105
1221
  ...values,
1106
1222
  cells,
1107
1223
  _meta: {
1108
- htmlID: `u_row_${index + 1}`,
1224
+ htmlID: nextHtmlId(_config, "u_row"),
1109
1225
  htmlClassNames: "u_row",
1110
1226
  ...values._meta || {}
1111
1227
  }
@@ -1135,7 +1251,7 @@ var Row = (props) => {
1135
1251
  };
1136
1252
  Row.displayName = "Row";
1137
1253
  var Row_default = Row;
1138
- var DEFAULT_CONTAINER_PADDING = "10px";
1254
+ var DEFAULT_CONTAINER_PADDING2 = "10px";
1139
1255
  var DEFAULT_VALUES12 = COLUMN_DEFAULTS;
1140
1256
  function renderColumnToHtml(innerHTML, values, index, cells, bodyValues, rowValues, mode) {
1141
1257
  const columnExporter = exporters.ColumnExporters[mode] || exporters.ColumnExporters.web;
@@ -1167,7 +1283,7 @@ var Column = (props) => {
1167
1283
  const valuesWithMeta = {
1168
1284
  ...values,
1169
1285
  _meta: {
1170
- htmlID: `u_column_${index + 1}`,
1286
+ htmlID: nextHtmlId(_config, "u_column"),
1171
1287
  htmlClassNames: "u_column",
1172
1288
  ...values._meta || {}
1173
1289
  }
@@ -1183,17 +1299,26 @@ var Column = (props) => {
1183
1299
  if (typeof child.type === "function") {
1184
1300
  const ComponentType = child.type;
1185
1301
  const renderFn = ComponentType[UNLAYER_RENDER_KEY] || ComponentType;
1186
- const rendered = renderFn({ ...child.props, _config });
1302
+ const rendered = renderFn({
1303
+ ...child.props,
1304
+ _config,
1305
+ colIndex: index,
1306
+ cells,
1307
+ bodyValues,
1308
+ rowValues,
1309
+ columnValues: valuesWithMeta
1310
+ });
1187
1311
  if (rendered && typeof rendered === "object" && rendered.props && rendered.props.dangerouslySetInnerHTML) {
1188
1312
  const componentHTML = rendered.props.dangerouslySetInnerHTML.__html;
1189
1313
  const componentType = child.type;
1190
1314
  const componentName = (componentType?.displayName || componentType?.name || "component").toLowerCase();
1191
1315
  const childProps = child.props;
1192
- const containerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING;
1316
+ const rawContainerPadding = childProps.containerPadding ?? childProps.values?.containerPadding ?? DEFAULT_CONTAINER_PADDING2;
1317
+ const containerPadding = typeof rawContainerPadding === "number" ? `${rawContainerPadding}px` : rawContainerPadding;
1193
1318
  const contentValues = {
1194
1319
  containerPadding,
1195
1320
  _meta: {
1196
- htmlID: `u_content_${componentName}_${childIndex + 1}`,
1321
+ htmlID: nextHtmlId(_config, `u_content_${componentName}`),
1197
1322
  htmlClassNames: `u_content_${componentName}`
1198
1323
  }
1199
1324
  };
@@ -1278,6 +1403,7 @@ var Body = (props) => {
1278
1403
  const resolvedConfig = { ...DEFAULT_CONFIG, ...configProp };
1279
1404
  const mode = modeProp ?? resolvedConfig.mode ?? "web";
1280
1405
  const _config = { ...resolvedConfig, mode };
1406
+ _config.__ids = {};
1281
1407
  const values = mergeValues(
1282
1408
  DEFAULT_VALUES13,
1283
1409
  mapSemanticProps(semanticProps, DEFAULT_VALUES13, "Body")
@@ -1285,7 +1411,7 @@ var Body = (props) => {
1285
1411
  const valuesWithMeta = {
1286
1412
  ...values,
1287
1413
  _meta: {
1288
- htmlID: `u_body_${index + 1}`,
1414
+ htmlID: nextHtmlId(_config, "u_body"),
1289
1415
  htmlClassNames: "u_body",
1290
1416
  ...values._meta || {}
1291
1417
  }
@@ -1642,7 +1768,7 @@ function extractTextFromTextJson(textJson) {
1642
1768
  return "";
1643
1769
  }
1644
1770
  }
1645
- function processItem(element, counters) {
1771
+ function processItem(element, counters, layout = {}) {
1646
1772
  const componentType = element.type;
1647
1773
  const config = componentType[UNLAYER_CONFIG_KEY];
1648
1774
  if (!config) {
@@ -1676,9 +1802,17 @@ function processItem(element, counters) {
1676
1802
  deletable: true,
1677
1803
  hideable: true
1678
1804
  };
1805
+ const itemSrc = values.src;
1806
+ if (itemSrc && typeof itemSrc === "object" && itemSrc.autoWidth === false) {
1807
+ const availableWidth = contentSlotWidth({
1808
+ ...layout,
1809
+ containerPadding: values.containerPadding
1810
+ });
1811
+ values.src = pinImageSrc(itemSrc, availableWidth);
1812
+ }
1679
1813
  return { type: contentType, values };
1680
1814
  }
1681
- function processColumn(element, counters) {
1815
+ function processColumn(element, counters, layout = {}) {
1682
1816
  const count = nextCounter2(counters, "u_column");
1683
1817
  const id = makeId("u_column", count);
1684
1818
  const semanticProps = extractSemanticProps2(element.props);
@@ -1694,12 +1828,13 @@ function processColumn(element, counters) {
1694
1828
  };
1695
1829
  const contents = [];
1696
1830
  const children = collectChildren2(element.props.children);
1831
+ const itemLayout = { ...layout, columnValues: valuesWithMeta };
1697
1832
  for (const child of children) {
1698
- contents.push(processItem(child, counters));
1833
+ contents.push(processItem(child, counters, itemLayout));
1699
1834
  }
1700
1835
  return { contents, values: valuesWithMeta };
1701
1836
  }
1702
- function processRow(element, counters) {
1837
+ function processRow(element, counters, parentLayout = {}) {
1703
1838
  const count = nextCounter2(counters, "u_row");
1704
1839
  const id = makeId("u_row", count);
1705
1840
  const { layout, cells: propsCells } = element.props;
@@ -1711,7 +1846,9 @@ function processRow(element, counters) {
1711
1846
  } else {
1712
1847
  const columnCount = Math.max(
1713
1848
  1,
1714
- collectChildren2(element.props.children).length
1849
+ collectChildren2(element.props.children).filter(
1850
+ (child) => getDisplayName2(child) === "Column"
1851
+ ).length
1715
1852
  );
1716
1853
  cells = Array(columnCount).fill(1);
1717
1854
  }
@@ -1735,10 +1872,19 @@ function processRow(element, counters) {
1735
1872
  };
1736
1873
  const columns = [];
1737
1874
  const children = collectChildren2(element.props.children);
1875
+ const columnLayout = {
1876
+ bodyValues: parentLayout.bodyValues,
1877
+ rowValues: valuesWithMeta,
1878
+ rowCells: cells
1879
+ };
1880
+ let columnIndex = 0;
1738
1881
  for (const child of children) {
1739
1882
  const name = getDisplayName2(child);
1740
1883
  if (name === "Column") {
1741
- columns.push(processColumn(child, counters));
1884
+ columns.push(
1885
+ processColumn(child, counters, { ...columnLayout, columnIndex })
1886
+ );
1887
+ columnIndex += 1;
1742
1888
  } else {
1743
1889
  console.warn(
1744
1890
  `[Unlayer] renderToJson: <${name}> is not a valid Row child. Only <Column> is allowed.`
@@ -1769,7 +1915,7 @@ function processBody(element, counters) {
1769
1915
  for (const child of children) {
1770
1916
  const name = getDisplayName2(child);
1771
1917
  if (name === "Row") {
1772
- rows.push(processRow(child, counters));
1918
+ rows.push(processRow(child, counters, { bodyValues: valuesWithMeta }));
1773
1919
  } else {
1774
1920
  console.warn(
1775
1921
  `[Unlayer] renderToJson: <${name}> is not a valid Body child. Only <Row> is allowed.`