@unlayer/react-elements 0.1.14 → 0.1.16

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.d.cts CHANGED
@@ -160,7 +160,7 @@ declare const DEFAULT_CONFIG: UnlayerConfig;
160
160
  * configurations that match exactly what the Unlayer editor supports.
161
161
  *
162
162
  * Usage:
163
- * import { ColumnLayouts } from '@unlayer-internal/shared-elements';
163
+ * import { ColumnLayouts } from '@unlayer/react-elements';
164
164
  * <Row layout={ColumnLayouts.TwoEqual}>
165
165
  * <Column>...</Column>
166
166
  * <Column>...</Column>
@@ -368,7 +368,8 @@ interface ButtonProps extends ItemComponentProps<ButtonSemanticProps> {
368
368
  *
369
369
  * @example Flat Props (Simple - most common)
370
370
  * ```tsx
371
- * <Button color="white" backgroundColor="#3b82f6" fontSize="16px">
371
+ * // `href` accepts a plain URL string (the ergonomic form).
372
+ * <Button href="https://example.com" color="white" backgroundColor="#3b82f6" fontSize="16px">
372
373
  * Click me
373
374
  * </Button>
374
375
  * ```
@@ -466,6 +467,13 @@ interface HtmlProps extends ItemComponentProps<SemanticProps<HtmlValues>> {
466
467
  /**
467
468
  * Html - Universal SSR/Client Component for custom HTML with Automatic Semantic Props
468
469
  *
470
+ * ⚠️ Renders the HTML verbatim — it is NOT sanitized by default. Only pass HTML
471
+ * you trust, and make sure it is valid: notably, an inline SVG inside a `url(...)`
472
+ * must be URL-encoded, because a raw `"` inside a double-quoted `style="…"`
473
+ * closes the attribute and the rest leaks out as text. To sanitize (matching the
474
+ * editor's HTML block, which strips scripts/event handlers), pass a `toSafeHtml`
475
+ * function via the `UnlayerProvider` config.
476
+ *
469
477
  * @example Flat Props
470
478
  * ```tsx
471
479
  * <Html html="<div>Custom HTML</div>" />
@@ -578,15 +586,21 @@ interface ParagraphProps extends ItemComponentProps<ParagraphSemanticProps> {
578
586
  */
579
587
  declare const Paragraph: React$1.FC<ItemComponentProps<ParagraphSemanticProps>>;
580
588
 
581
- type SocialSemanticProps = SemanticProps<SocialValues> & {
589
+ type SocialSemanticProps = Omit<SemanticProps<SocialValues>, "iconSize" | "spacing"> & {
582
590
  /** Social icons shorthand (array of {name, url}) */
583
591
  icons?: SocialIcon[];
584
592
  /** Icon shape */
585
593
  iconType?: "circle" | "rounded" | "squared";
594
+ /** Icon size in px — a number (34) or px string ("34px"). */
595
+ iconSize?: SizeInput;
596
+ /** Gap between icons in px — a number or px string. */
597
+ spacing?: SizeInput;
586
598
  };
587
- interface SocialProps extends Omit<ItemComponentProps<SemanticProps<SocialValues>>, "icons"> {
599
+ interface SocialProps extends Omit<ItemComponentProps<SemanticProps<SocialValues>>, "icons" | "iconSize" | "spacing"> {
588
600
  icons?: SocialIcon[] | SocialValues["icons"];
589
601
  iconType?: "circle" | "rounded" | "squared";
602
+ iconSize?: SizeInput;
603
+ spacing?: SizeInput;
590
604
  }
591
605
  /**
592
606
  * Social - Renders social-media icon links.
@@ -958,4 +972,4 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
958
972
  declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
959
973
  declare function renderToJson(element: React__default.ReactElement): DesignJSON;
960
974
 
961
- export { Body, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, Email, type EmailProps, Heading, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, Row, type RowProps, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
975
+ export { Body, type BorderInput, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, Email, type EmailProps, type FontFamilyInput, type FontWeightInput, Heading, type HeadingLevel, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type ImageSrcInput, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, Row, type RowProps, type SizeInput, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type TextStyleProps, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
package/dist/index.d.ts CHANGED
@@ -160,7 +160,7 @@ declare const DEFAULT_CONFIG: UnlayerConfig;
160
160
  * configurations that match exactly what the Unlayer editor supports.
161
161
  *
162
162
  * Usage:
163
- * import { ColumnLayouts } from '@unlayer-internal/shared-elements';
163
+ * import { ColumnLayouts } from '@unlayer/react-elements';
164
164
  * <Row layout={ColumnLayouts.TwoEqual}>
165
165
  * <Column>...</Column>
166
166
  * <Column>...</Column>
@@ -368,7 +368,8 @@ interface ButtonProps extends ItemComponentProps<ButtonSemanticProps> {
368
368
  *
369
369
  * @example Flat Props (Simple - most common)
370
370
  * ```tsx
371
- * <Button color="white" backgroundColor="#3b82f6" fontSize="16px">
371
+ * // `href` accepts a plain URL string (the ergonomic form).
372
+ * <Button href="https://example.com" color="white" backgroundColor="#3b82f6" fontSize="16px">
372
373
  * Click me
373
374
  * </Button>
374
375
  * ```
@@ -466,6 +467,13 @@ interface HtmlProps extends ItemComponentProps<SemanticProps<HtmlValues>> {
466
467
  /**
467
468
  * Html - Universal SSR/Client Component for custom HTML with Automatic Semantic Props
468
469
  *
470
+ * ⚠️ Renders the HTML verbatim — it is NOT sanitized by default. Only pass HTML
471
+ * you trust, and make sure it is valid: notably, an inline SVG inside a `url(...)`
472
+ * must be URL-encoded, because a raw `"` inside a double-quoted `style="…"`
473
+ * closes the attribute and the rest leaks out as text. To sanitize (matching the
474
+ * editor's HTML block, which strips scripts/event handlers), pass a `toSafeHtml`
475
+ * function via the `UnlayerProvider` config.
476
+ *
469
477
  * @example Flat Props
470
478
  * ```tsx
471
479
  * <Html html="<div>Custom HTML</div>" />
@@ -578,15 +586,21 @@ interface ParagraphProps extends ItemComponentProps<ParagraphSemanticProps> {
578
586
  */
579
587
  declare const Paragraph: React$1.FC<ItemComponentProps<ParagraphSemanticProps>>;
580
588
 
581
- type SocialSemanticProps = SemanticProps<SocialValues> & {
589
+ type SocialSemanticProps = Omit<SemanticProps<SocialValues>, "iconSize" | "spacing"> & {
582
590
  /** Social icons shorthand (array of {name, url}) */
583
591
  icons?: SocialIcon[];
584
592
  /** Icon shape */
585
593
  iconType?: "circle" | "rounded" | "squared";
594
+ /** Icon size in px — a number (34) or px string ("34px"). */
595
+ iconSize?: SizeInput;
596
+ /** Gap between icons in px — a number or px string. */
597
+ spacing?: SizeInput;
586
598
  };
587
- interface SocialProps extends Omit<ItemComponentProps<SemanticProps<SocialValues>>, "icons"> {
599
+ interface SocialProps extends Omit<ItemComponentProps<SemanticProps<SocialValues>>, "icons" | "iconSize" | "spacing"> {
588
600
  icons?: SocialIcon[] | SocialValues["icons"];
589
601
  iconType?: "circle" | "rounded" | "squared";
602
+ iconSize?: SizeInput;
603
+ spacing?: SizeInput;
590
604
  }
591
605
  /**
592
606
  * Social - Renders social-media icon links.
@@ -958,4 +972,4 @@ declare function renderToHtmlParts(element: React__default.ReactElement, config?
958
972
  declare function renderRowToJson(element: React__default.ReactElement): DesignRow;
959
973
  declare function renderToJson(element: React__default.ReactElement): DesignJSON;
960
974
 
961
- export { Body, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, Email, type EmailProps, Heading, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, Row, type RowProps, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
975
+ export { Body, type BorderInput, Button, type ButtonProps, Column, type ColumnLayout, ColumnLayouts, DEFAULT_CONFIG, type DesignBody, type DesignColumn, type DesignContent, type DesignJSON, type DesignRow, Divider, type DividerProps, Document, type DocumentProps, Email, type EmailProps, type FontFamilyInput, type FontWeightInput, Heading, type HeadingLevel, type HeadingProps, type Href, Html, type HtmlParts, type HtmlProps, type Icons, Image, type ImageProps, type ImageSrcInput, type LinkStyle, Menu, type MenuItem, type MenuProps, Page, type PageProps, Paragraph, type ParagraphProps, Row, type RowProps, type SizeInput, Social, type SocialIcon, type SocialProps, Table, type TableProps, type TextAlign, type TextStyleProps, type UnlayerConfig, UnlayerProvider, type UnlayerProviderProps, type ValidColumnLayout, Video, type VideoProps, type VideoSource, htmlToTextJson, renderRowToJson, renderToHtml, renderToHtmlParts, renderToJson, renderToPlainText, useUnlayerConfig, validateColumnLayout };
package/dist/index.js CHANGED
@@ -339,14 +339,27 @@ function mapSemanticProps(props, defaultValues, componentType) {
339
339
  delete userProps.html;
340
340
  delete result.html;
341
341
  }
342
- for (const key of ["href", "action"]) {
343
- const v = userProps[key];
342
+ const canonicalizeLink = (v) => {
344
343
  if (typeof v === "string") {
345
- userProps[key] = {
346
- name: "web",
347
- values: { href: v, target: "_blank" }
348
- };
344
+ return { name: "web", values: { href: v, target: "_blank" } };
345
+ }
346
+ if (v && typeof v === "object" && "name" in v && v.attrs && typeof v.attrs === "object") {
347
+ const { href: attrsHref, target: attrsTarget, ...customAttrs } = v.attrs;
348
+ if (attrsHref !== void 0 || attrsTarget !== void 0) {
349
+ const linkValues = { ...v.values };
350
+ if (!linkValues.href && attrsHref !== void 0) linkValues.href = attrsHref;
351
+ if (!linkValues.target && attrsTarget !== void 0) linkValues.target = attrsTarget;
352
+ const next = { ...v, values: linkValues };
353
+ if (Object.keys(customAttrs).length) next.attrs = customAttrs;
354
+ else delete next.attrs;
355
+ return next;
356
+ }
349
357
  }
358
+ return v;
359
+ };
360
+ for (const key of ["href", "action"]) {
361
+ if (userProps[key] !== void 0) userProps[key] = canonicalizeLink(userProps[key]);
362
+ if (result[key] !== void 0) result[key] = canonicalizeLink(result[key]);
350
363
  }
351
364
  normalizeCssProps(userProps);
352
365
  const nestedGroups = analyzeNestedStructure(defaultValues);
@@ -395,6 +408,16 @@ function mapSemanticProps(props, defaultValues, componentType) {
395
408
  if (Object.keys(collected).length > 0) {
396
409
  final.border = { ...final.border || {}, ...collected };
397
410
  }
411
+ if (final.border && typeof final.border === "object") {
412
+ const b = { ...final.border };
413
+ for (const key of Object.keys(b)) {
414
+ if (!/Width$/.test(key)) continue;
415
+ const v = b[key];
416
+ if (typeof v === "number") b[key] = `${v}px`;
417
+ else if (typeof v === "string" && /^\d+(?:\.\d+)?$/.test(v.trim())) b[key] = `${v.trim()}px`;
418
+ }
419
+ final.border = b;
420
+ }
398
421
  }
399
422
  return final;
400
423
  }
@@ -406,12 +429,14 @@ function normalizeLinkValue(value) {
406
429
  if (typeof value !== "object") return void 0;
407
430
  const v = value;
408
431
  if ("url" in v) return v;
409
- if ("name" in v && v.values && typeof v.values === "object") {
410
- const inner = v.values;
432
+ if ("name" in v && (v.values && typeof v.values === "object" || v.attrs && typeof v.attrs === "object")) {
433
+ const inner = v.values && typeof v.values === "object" ? v.values : {};
434
+ const attrs = v.attrs && typeof v.attrs === "object" ? v.attrs : {};
435
+ const { href: attrsHref, target: attrsTarget, ...customAttrs } = attrs;
411
436
  return {
412
- url: inner.href ?? "",
413
- target: inner.target ?? "_blank",
414
- ...v.attrs ?? {}
437
+ url: inner.href || attrsHref || "",
438
+ target: inner.target || attrsTarget || "_blank",
439
+ ...customAttrs
415
440
  };
416
441
  }
417
442
  return void 0;
@@ -763,11 +788,11 @@ var Html = createItemComponent({
763
788
  exporters: HtmlExporters
764
789
  });
765
790
  var Html_default = Html;
791
+ var { height: _placeholderHeight, ...defaultSrc } = ImageDefaults.src;
766
792
  var DEFAULT_VALUES5 = {
767
793
  ...ImageDefaults,
768
- // Override src with autoWidth/maxWidth for responsive rendering
769
794
  src: {
770
- ...ImageDefaults.src,
795
+ ...defaultSrc,
771
796
  autoWidth: true,
772
797
  maxWidth: "100%"
773
798
  }
@@ -809,7 +834,7 @@ var Image = createItemComponent({
809
834
  };
810
835
  let displayPct;
811
836
  let displayPx;
812
- for (const candidate of [widthProp, maxWidthProp, userSrc.maxWidth]) {
837
+ for (const candidate of [widthProp, maxWidthProp, userSrc.maxWidth, userSrc.width]) {
813
838
  if (candidate === void 0) continue;
814
839
  const pct = asPercent(candidate);
815
840
  if (pct) {
@@ -900,6 +925,17 @@ var Social = createItemComponent({
900
925
  defaultValues: DEFAULT_VALUES8,
901
926
  propMapper: (props) => {
902
927
  const { icons, iconType, ...rest } = props;
928
+ const coerceSizes = (base) => {
929
+ for (const key of ["iconSize", "spacing"]) {
930
+ const v = base[key];
931
+ if (typeof v === "string") {
932
+ const m = /^(\d+(?:\.\d+)?)(?:px)?$/.exec(v.trim());
933
+ if (m) base[key] = parseFloat(m[1]);
934
+ else delete base[key];
935
+ }
936
+ }
937
+ return base;
938
+ };
903
939
  if (Array.isArray(icons)) {
904
940
  const mapped = icons.map((icon) => ({
905
941
  name: icon.name,
@@ -914,7 +950,7 @@ var Social = createItemComponent({
914
950
  iconType: iconType ?? base.icons?.iconType ?? "circle",
915
951
  icons: mapped
916
952
  };
917
- return base;
953
+ return coerceSizes(base);
918
954
  }
919
955
  if (iconType !== void 0) {
920
956
  const base = mapSemanticProps(
@@ -923,12 +959,10 @@ var Social = createItemComponent({
923
959
  "Social"
924
960
  );
925
961
  base.icons = { ...DEFAULT_ICONS, ...base.icons, iconType };
926
- return base;
962
+ return coerceSizes(base);
927
963
  }
928
- return mapSemanticProps(
929
- props,
930
- DEFAULT_VALUES8,
931
- "Social"
964
+ return coerceSizes(
965
+ mapSemanticProps(props, DEFAULT_VALUES8, "Social")
932
966
  );
933
967
  },
934
968
  displayName: "Social",
@@ -1199,10 +1233,17 @@ var Row = (props) => {
1199
1233
  ...semanticProps
1200
1234
  } = props;
1201
1235
  const mode = modeProp ?? _config?.mode ?? "web";
1202
- let cells = propsCells || [1];
1236
+ let cells;
1203
1237
  if (layout) {
1204
1238
  validateColumnLayout(layout, React.Children.count(children));
1205
1239
  cells = layout.cells;
1240
+ } else if (propsCells) {
1241
+ cells = propsCells;
1242
+ } else {
1243
+ const columnCount = React.Children.toArray(children).filter(
1244
+ (c) => React.isValidElement(c) && /^Column$/.test(c.type?.displayName || c.type?.name || "")
1245
+ ).length;
1246
+ cells = Array(Math.max(1, columnCount)).fill(1);
1206
1247
  }
1207
1248
  const safeBodyValues = { ...DEFAULT_BODY_VALUES, ...bodyValues };
1208
1249
  const values = mapSemanticProps(