react-email 6.9.3 → 6.9.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # react-email
2
2
 
3
+ ## 6.9.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 0250981: fix responsive padding not applying to inner td for Container and Section
8
+
9
+ ## 6.9.4
10
+
3
11
  ## 6.9.3
4
12
 
5
13
  ## 6.9.2
@@ -6533,7 +6533,7 @@ const getTracingRootDir = async (usersProjectLocation) => {
6533
6533
  //#region package.json
6534
6534
  var package_default = {
6535
6535
  name: "react-email",
6536
- version: "6.9.3",
6536
+ version: "6.9.5",
6537
6537
  description: "A live preview of your emails right in your browser.",
6538
6538
  bin: { "email": "./dist/cli/index.mjs" },
6539
6539
  type: "module",
@@ -4,14 +4,14 @@ let react = require("react");
4
4
  react = require_runtime.__toESM(react);
5
5
  let react_jsx_runtime = require("react/jsx-runtime");
6
6
  //#region src/components/container/container.tsx
7
- const Container = react.forwardRef(({ children, style = {}, ...props }, ref) => {
7
+ const Container = react.forwardRef(({ children, style = {}, tdClassName, ...props }, ref) => {
8
8
  const tdStyle = {};
9
9
  const tableStyle = {};
10
10
  const styleRecord = style;
11
11
  for (const key in styleRecord) {
12
12
  if (!Object.hasOwn(styleRecord, key)) continue;
13
13
  const value = styleRecord[key];
14
- if (key === "padding" || key === "paddingTop" || key === "paddingRight" || key === "paddingBottom" || key === "paddingLeft") tdStyle[key] = value;
14
+ if (key.startsWith("padding")) tdStyle[key] = value;
15
15
  else tableStyle[key] = value;
16
16
  }
17
17
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
@@ -30,6 +30,7 @@ const Container = react.forwardRef(({ children, style = {}, ...props }, ref) =>
30
30
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
31
31
  style: { width: "100%" },
32
32
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
33
+ className: tdClassName,
33
34
  style: tdStyle,
34
35
  children
35
36
  })
@@ -37,6 +38,20 @@ const Container = react.forwardRef(({ children, style = {}, ...props }, ref) =>
37
38
  });
38
39
  });
39
40
  Container.displayName = "Container";
40
- require_element_marker.markAsElement(Container);
41
+ require_element_marker.markAsElement(Container, { resolveTailwind(props, { style, className, classProperties }) {
42
+ const tableClasses = [];
43
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
44
+ for (const name of className?.split(" ") ?? []) {
45
+ const properties = classProperties[name];
46
+ if (properties && properties.length > 0 && properties.every((property) => property.startsWith("padding"))) tdClasses.push(name);
47
+ else tableClasses.push(name);
48
+ }
49
+ return {
50
+ ...props,
51
+ style,
52
+ className: tableClasses.length > 0 ? tableClasses.join(" ") : void 0,
53
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(" ") : void 0
54
+ };
55
+ } });
41
56
  //#endregion
42
57
  exports.Container = Container;
@@ -2,6 +2,8 @@ import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/container/container.d.ts
4
4
  type ContainerProps = Readonly<React$1.ComponentPropsWithoutRef<'table'>>;
5
- declare const Container: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & React$1.RefAttributes<HTMLTableElement>>;
5
+ declare const Container: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & {
6
+ tdClassName?: string;
7
+ } & React$1.RefAttributes<HTMLTableElement>>;
6
8
  //#endregion
7
9
  export { Container, ContainerProps };
@@ -2,6 +2,8 @@ import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/container/container.d.ts
4
4
  type ContainerProps = Readonly<React$1.ComponentPropsWithoutRef<'table'>>;
5
- declare const Container: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & React$1.RefAttributes<HTMLTableElement>>;
5
+ declare const Container: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & {
6
+ tdClassName?: string;
7
+ } & React$1.RefAttributes<HTMLTableElement>>;
6
8
  //#endregion
7
9
  export { Container, ContainerProps };
@@ -2,14 +2,14 @@ import { markAsElement } from "../element-marker.mjs";
2
2
  import * as React$1 from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  //#region src/components/container/container.tsx
5
- const Container = React$1.forwardRef(({ children, style = {}, ...props }, ref) => {
5
+ const Container = React$1.forwardRef(({ children, style = {}, tdClassName, ...props }, ref) => {
6
6
  const tdStyle = {};
7
7
  const tableStyle = {};
8
8
  const styleRecord = style;
9
9
  for (const key in styleRecord) {
10
10
  if (!Object.hasOwn(styleRecord, key)) continue;
11
11
  const value = styleRecord[key];
12
- if (key === "padding" || key === "paddingTop" || key === "paddingRight" || key === "paddingBottom" || key === "paddingLeft") tdStyle[key] = value;
12
+ if (key.startsWith("padding")) tdStyle[key] = value;
13
13
  else tableStyle[key] = value;
14
14
  }
15
15
  return /* @__PURE__ */ jsx("table", {
@@ -28,6 +28,7 @@ const Container = React$1.forwardRef(({ children, style = {}, ...props }, ref) =
28
28
  children: /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", {
29
29
  style: { width: "100%" },
30
30
  children: /* @__PURE__ */ jsx("td", {
31
+ className: tdClassName,
31
32
  style: tdStyle,
32
33
  children
33
34
  })
@@ -35,6 +36,20 @@ const Container = React$1.forwardRef(({ children, style = {}, ...props }, ref) =
35
36
  });
36
37
  });
37
38
  Container.displayName = "Container";
38
- markAsElement(Container);
39
+ markAsElement(Container, { resolveTailwind(props, { style, className, classProperties }) {
40
+ const tableClasses = [];
41
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
42
+ for (const name of className?.split(" ") ?? []) {
43
+ const properties = classProperties[name];
44
+ if (properties && properties.length > 0 && properties.every((property) => property.startsWith("padding"))) tdClasses.push(name);
45
+ else tableClasses.push(name);
46
+ }
47
+ return {
48
+ ...props,
49
+ style,
50
+ className: tableClasses.length > 0 ? tableClasses.join(" ") : void 0,
51
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(" ") : void 0
52
+ };
53
+ } });
39
54
  //#endregion
40
55
  export { Container };
@@ -1,8 +1,14 @@
1
1
  //#region src/components/element-marker.ts
2
2
  const elementMarker = Symbol.for("react-email.element");
3
- const markAsElement = (component) => {
4
- component[elementMarker] = true;
3
+ const markAsElement = (component, options = {}) => {
4
+ component[elementMarker] = options;
5
+ };
6
+ const getElementOptions = (type) => {
7
+ if (typeof type !== "function" && typeof type !== "object") return void 0;
8
+ if (type === null) return void 0;
9
+ return type[elementMarker];
5
10
  };
6
11
  //#endregion
7
12
  exports.elementMarker = elementMarker;
13
+ exports.getElementOptions = getElementOptions;
8
14
  exports.markAsElement = markAsElement;
@@ -0,0 +1,14 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/components/element-marker.d.ts
4
+ interface ResolvedTailwind {
5
+ style: React$1.CSSProperties;
6
+ className: string | undefined;
7
+ classProperties: Record<string, string[]>;
8
+ }
9
+ interface ElementOptions<Props = object> {
10
+ resolveTailwind?: (props: Props, resolved: ResolvedTailwind) => Props;
11
+ }
12
+ declare const markAsElement: <Props>(component: unknown, options?: ElementOptions<Props>) => void;
13
+ //#endregion
14
+ export { ElementOptions, ResolvedTailwind, markAsElement };
@@ -0,0 +1,14 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/components/element-marker.d.ts
4
+ interface ResolvedTailwind {
5
+ style: React$1.CSSProperties;
6
+ className: string | undefined;
7
+ classProperties: Record<string, string[]>;
8
+ }
9
+ interface ElementOptions<Props = object> {
10
+ resolveTailwind?: (props: Props, resolved: ResolvedTailwind) => Props;
11
+ }
12
+ declare const markAsElement: <Props>(component: unknown, options?: ElementOptions<Props>) => void;
13
+ //#endregion
14
+ export { ElementOptions, ResolvedTailwind, markAsElement };
@@ -1,7 +1,12 @@
1
1
  //#region src/components/element-marker.ts
2
2
  const elementMarker = Symbol.for("react-email.element");
3
- const markAsElement = (component) => {
4
- component[elementMarker] = true;
3
+ const markAsElement = (component, options = {}) => {
4
+ component[elementMarker] = options;
5
+ };
6
+ const getElementOptions = (type) => {
7
+ if (typeof type !== "function" && typeof type !== "object") return void 0;
8
+ if (type === null) return void 0;
9
+ return type[elementMarker];
5
10
  };
6
11
  //#endregion
7
- export { elementMarker, markAsElement };
12
+ export { elementMarker, getElementOptions, markAsElement };
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./code-block/code-block.cjs";
6
6
  import { CodeInline, CodeInlineProps } from "./code-inline/code-inline.cjs";
7
7
  import { Column, ColumnProps } from "./column/column.cjs";
8
8
  import { Container, ContainerProps } from "./container/container.cjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./element-marker.cjs";
9
10
  import { Font, FontProps } from "./font/font.cjs";
10
11
  import { Head, HeadProps } from "./head/head.cjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./heading/heading.cjs";
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./code-block/code-block.mjs";
6
6
  import { CodeInline, CodeInlineProps } from "./code-inline/code-inline.mjs";
7
7
  import { Column, ColumnProps } from "./column/column.mjs";
8
8
  import { Container, ContainerProps } from "./container/container.mjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./element-marker.mjs";
9
10
  import { Font, FontProps } from "./font/font.mjs";
10
11
  import { Head, HeadProps } from "./head/head.mjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./heading/heading.mjs";
@@ -4,14 +4,14 @@ let react = require("react");
4
4
  react = require_runtime.__toESM(react);
5
5
  let react_jsx_runtime = require("react/jsx-runtime");
6
6
  //#region src/components/section/section.tsx
7
- const Section = react.forwardRef(({ children, style = {}, ...props }, ref) => {
7
+ const Section = react.forwardRef(({ children, style = {}, tdClassName, ...props }, ref) => {
8
8
  const tdStyle = {};
9
9
  const tableStyle = {};
10
10
  const styleRecord = style;
11
11
  for (const key in styleRecord) {
12
12
  if (!Object.hasOwn(styleRecord, key)) continue;
13
13
  const value = styleRecord[key];
14
- if (key === "padding" || key === "paddingTop" || key === "paddingRight" || key === "paddingBottom" || key === "paddingLeft") tdStyle[key] = value;
14
+ if (key.startsWith("padding")) tdStyle[key] = value;
15
15
  else tableStyle[key] = value;
16
16
  }
17
17
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
@@ -25,12 +25,27 @@ const Section = react.forwardRef(({ children, style = {}, ...props }, ref) => {
25
25
  ref,
26
26
  style: tableStyle,
27
27
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
28
+ className: tdClassName,
28
29
  style: tdStyle,
29
30
  children
30
31
  }) }) })
31
32
  });
32
33
  });
33
34
  Section.displayName = "Section";
34
- require_element_marker.markAsElement(Section);
35
+ require_element_marker.markAsElement(Section, { resolveTailwind(props, { style, className, classProperties }) {
36
+ const tableClasses = [];
37
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
38
+ for (const name of className?.split(" ") ?? []) {
39
+ const properties = classProperties[name];
40
+ if (properties && properties.length > 0 && properties.every((property) => property.startsWith("padding"))) tdClasses.push(name);
41
+ else tableClasses.push(name);
42
+ }
43
+ return {
44
+ ...props,
45
+ style,
46
+ className: tableClasses.length > 0 ? tableClasses.join(" ") : void 0,
47
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(" ") : void 0
48
+ };
49
+ } });
35
50
  //#endregion
36
51
  exports.Section = Section;
@@ -2,6 +2,8 @@ import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/section/section.d.ts
4
4
  type SectionProps = Readonly<React$1.ComponentPropsWithoutRef<'table'>>;
5
- declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & React$1.RefAttributes<HTMLTableElement>>;
5
+ declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & {
6
+ tdClassName?: string;
7
+ } & React$1.RefAttributes<HTMLTableElement>>;
6
8
  //#endregion
7
9
  export { Section, SectionProps };
@@ -2,6 +2,8 @@ import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/section/section.d.ts
4
4
  type SectionProps = Readonly<React$1.ComponentPropsWithoutRef<'table'>>;
5
- declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & React$1.RefAttributes<HTMLTableElement>>;
5
+ declare const Section: React$1.ForwardRefExoticComponent<Readonly<Omit<React$1.DetailedHTMLProps<React$1.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "ref">> & {
6
+ tdClassName?: string;
7
+ } & React$1.RefAttributes<HTMLTableElement>>;
6
8
  //#endregion
7
9
  export { Section, SectionProps };
@@ -2,14 +2,14 @@ import { markAsElement } from "../element-marker.mjs";
2
2
  import * as React$1 from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  //#region src/components/section/section.tsx
5
- const Section = React$1.forwardRef(({ children, style = {}, ...props }, ref) => {
5
+ const Section = React$1.forwardRef(({ children, style = {}, tdClassName, ...props }, ref) => {
6
6
  const tdStyle = {};
7
7
  const tableStyle = {};
8
8
  const styleRecord = style;
9
9
  for (const key in styleRecord) {
10
10
  if (!Object.hasOwn(styleRecord, key)) continue;
11
11
  const value = styleRecord[key];
12
- if (key === "padding" || key === "paddingTop" || key === "paddingRight" || key === "paddingBottom" || key === "paddingLeft") tdStyle[key] = value;
12
+ if (key.startsWith("padding")) tdStyle[key] = value;
13
13
  else tableStyle[key] = value;
14
14
  }
15
15
  return /* @__PURE__ */ jsx("table", {
@@ -23,12 +23,27 @@ const Section = React$1.forwardRef(({ children, style = {}, ...props }, ref) =>
23
23
  ref,
24
24
  style: tableStyle,
25
25
  children: /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", {
26
+ className: tdClassName,
26
27
  style: tdStyle,
27
28
  children
28
29
  }) }) })
29
30
  });
30
31
  });
31
32
  Section.displayName = "Section";
32
- markAsElement(Section);
33
+ markAsElement(Section, { resolveTailwind(props, { style, className, classProperties }) {
34
+ const tableClasses = [];
35
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
36
+ for (const name of className?.split(" ") ?? []) {
37
+ const properties = classProperties[name];
38
+ if (properties && properties.length > 0 && properties.every((property) => property.startsWith("padding"))) tdClasses.push(name);
39
+ else tableClasses.push(name);
40
+ }
41
+ return {
42
+ ...props,
43
+ style,
44
+ className: tableClasses.length > 0 ? tableClasses.join(" ") : void 0,
45
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(" ") : void 0
46
+ };
47
+ } });
33
48
  //#endregion
34
49
  export { Section };
@@ -1,35 +1,48 @@
1
1
  const require_runtime = require("../../../../_virtual/_rolldown/runtime.cjs");
2
+ const require_element_marker = require("../../../element-marker.cjs");
2
3
  const require_make_inline_styles_for = require("../css/make-inline-styles-for.cjs");
3
4
  const require_sanitize_class_name = require("../compatibility/sanitize-class-name.cjs");
4
5
  const require_is_component = require("../react/is-component.cjs");
5
6
  let react = require("react");
6
7
  react = require_runtime.__toESM(react, 1);
8
+ let css_tree = require("css-tree");
7
9
  //#region src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts
8
10
  function cloneElementWithInlinedStyles(element, inlinableRules, nonInlinableRules, customProperties) {
9
- const propsToOverwrite = {};
10
- if (element.props.className && !require_is_component.isComponent(element)) {
11
- const classes = element.props.className.trim().split(/\s+/);
12
- const residualClasses = [];
13
- const rules = [];
14
- for (const className of classes) {
15
- const classRules = inlinableRules.get(className);
16
- if (classRules) rules.push(...classRules);
17
- if (nonInlinableRules.has(className)) residualClasses.push(className);
18
- else if (!classRules) residualClasses.push(className);
19
- }
20
- propsToOverwrite.style = {
11
+ if (!element.props.className || require_is_component.isComponent(element)) return react.default.cloneElement(element, element.props, element.props.children);
12
+ const classes = element.props.className.trim().split(/\s+/);
13
+ const residualClasses = [];
14
+ const classProperties = Object.create(null);
15
+ const rules = [];
16
+ for (const className of classes) {
17
+ const classRules = inlinableRules.get(className);
18
+ if (classRules) rules.push(...classRules);
19
+ const nonInlinable = nonInlinableRules.get(className);
20
+ if (nonInlinable) {
21
+ const sanitized = require_sanitize_class_name.sanitizeClassName(className);
22
+ residualClasses.push(sanitized);
23
+ const properties = [];
24
+ for (const rule of nonInlinable) (0, css_tree.walk)(rule, {
25
+ visit: "Declaration",
26
+ enter(declaration) {
27
+ properties.push(declaration.property);
28
+ }
29
+ });
30
+ classProperties[sanitized] = properties;
31
+ } else if (!classRules) residualClasses.push(className);
32
+ }
33
+ const resolved = {
34
+ style: {
21
35
  ...require_make_inline_styles_for.makeInlineStylesFor(rules, customProperties),
22
36
  ...element.props.style
23
- };
24
- if (residualClasses.length > 0) propsToOverwrite.className = residualClasses.map((className) => {
25
- if (nonInlinableRules.has(className)) return require_sanitize_class_name.sanitizeClassName(className);
26
- return className;
27
- }).join(" ");
28
- else propsToOverwrite.className = void 0;
29
- }
30
- const newProps = {
37
+ },
38
+ className: residualClasses.length > 0 ? residualClasses.join(" ") : void 0,
39
+ classProperties
40
+ };
41
+ const resolveTailwind = require_element_marker.getElementOptions(element.type)?.resolveTailwind;
42
+ const newProps = resolveTailwind ? resolveTailwind(element.props, resolved) : {
31
43
  ...element.props,
32
- ...propsToOverwrite
44
+ style: resolved.style,
45
+ className: resolved.className
33
46
  };
34
47
  return react.default.cloneElement(element, newProps, newProps.children);
35
48
  }
@@ -1,33 +1,46 @@
1
+ import { getElementOptions } from "../../../element-marker.mjs";
1
2
  import { makeInlineStylesFor } from "../css/make-inline-styles-for.mjs";
2
3
  import { sanitizeClassName } from "../compatibility/sanitize-class-name.mjs";
3
4
  import { isComponent } from "../react/is-component.mjs";
4
5
  import React from "react";
6
+ import { walk } from "css-tree/dist/csstree.esm";
5
7
  //#region src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts
6
8
  function cloneElementWithInlinedStyles(element, inlinableRules, nonInlinableRules, customProperties) {
7
- const propsToOverwrite = {};
8
- if (element.props.className && !isComponent(element)) {
9
- const classes = element.props.className.trim().split(/\s+/);
10
- const residualClasses = [];
11
- const rules = [];
12
- for (const className of classes) {
13
- const classRules = inlinableRules.get(className);
14
- if (classRules) rules.push(...classRules);
15
- if (nonInlinableRules.has(className)) residualClasses.push(className);
16
- else if (!classRules) residualClasses.push(className);
17
- }
18
- propsToOverwrite.style = {
9
+ if (!element.props.className || isComponent(element)) return React.cloneElement(element, element.props, element.props.children);
10
+ const classes = element.props.className.trim().split(/\s+/);
11
+ const residualClasses = [];
12
+ const classProperties = Object.create(null);
13
+ const rules = [];
14
+ for (const className of classes) {
15
+ const classRules = inlinableRules.get(className);
16
+ if (classRules) rules.push(...classRules);
17
+ const nonInlinable = nonInlinableRules.get(className);
18
+ if (nonInlinable) {
19
+ const sanitized = sanitizeClassName(className);
20
+ residualClasses.push(sanitized);
21
+ const properties = [];
22
+ for (const rule of nonInlinable) walk(rule, {
23
+ visit: "Declaration",
24
+ enter(declaration) {
25
+ properties.push(declaration.property);
26
+ }
27
+ });
28
+ classProperties[sanitized] = properties;
29
+ } else if (!classRules) residualClasses.push(className);
30
+ }
31
+ const resolved = {
32
+ style: {
19
33
  ...makeInlineStylesFor(rules, customProperties),
20
34
  ...element.props.style
21
- };
22
- if (residualClasses.length > 0) propsToOverwrite.className = residualClasses.map((className) => {
23
- if (nonInlinableRules.has(className)) return sanitizeClassName(className);
24
- return className;
25
- }).join(" ");
26
- else propsToOverwrite.className = void 0;
27
- }
28
- const newProps = {
35
+ },
36
+ className: residualClasses.length > 0 ? residualClasses.join(" ") : void 0,
37
+ classProperties
38
+ };
39
+ const resolveTailwind = getElementOptions(element.type)?.resolveTailwind;
40
+ const newProps = resolveTailwind ? resolveTailwind(element.props, resolved) : {
29
41
  ...element.props,
30
- ...propsToOverwrite
42
+ style: resolved.style,
43
+ className: resolved.className
31
44
  };
32
45
  return React.cloneElement(element, newProps, newProps.children);
33
46
  }
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_element_marker = require("./components/element-marker.cjs");
2
3
  const require_body = require("./components/body/body.cjs");
3
4
  const require_button = require("./components/button/button.cjs");
4
5
  const require_code_block = require("./components/code-block/code-block.cjs");
@@ -64,6 +65,7 @@ exports.hopscotch = require_themes.hopscotch;
64
65
  exports.inlineStyles = require_inline_styles.inlineStyles;
65
66
  exports.laserwave = require_themes.laserwave;
66
67
  exports.lucario = require_themes.lucario;
68
+ exports.markAsElement = require_element_marker.markAsElement;
67
69
  exports.materialDark = require_themes.materialDark;
68
70
  exports.materialLight = require_themes.materialLight;
69
71
  exports.materialOceanic = require_themes.materialOceanic;
package/dist/index.d.cts CHANGED
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./components/code-block/code-block.cj
6
6
  import { CodeInline, CodeInlineProps } from "./components/code-inline/code-inline.cjs";
7
7
  import { Column, ColumnProps } from "./components/column/column.cjs";
8
8
  import { Container, ContainerProps } from "./components/container/container.cjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./components/element-marker.cjs";
9
10
  import { Font, FontProps } from "./components/font/font.cjs";
10
11
  import { Head, HeadProps } from "./components/head/head.cjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./components/heading/heading.cjs";
@@ -23,4 +24,4 @@ import { EmailElementProps, Tailwind, TailwindConfig, TailwindProps, pixelBasedP
23
24
  import { TailwindSetup, setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tailwind.cjs";
24
25
  import { Text, TextProps } from "./components/text/text.cjs";
25
26
  export * from "@react-email/render";
26
- export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
27
+ export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, ElementOptions, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, ResolvedTailwind, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/dist/index.d.mts CHANGED
@@ -6,6 +6,7 @@ import { CodeBlock, CodeBlockProps } from "./components/code-block/code-block.mj
6
6
  import { CodeInline, CodeInlineProps } from "./components/code-inline/code-inline.mjs";
7
7
  import { Column, ColumnProps } from "./components/column/column.mjs";
8
8
  import { Container, ContainerProps } from "./components/container/container.mjs";
9
+ import { ElementOptions, ResolvedTailwind, markAsElement } from "./components/element-marker.mjs";
9
10
  import { Font, FontProps } from "./components/font/font.mjs";
10
11
  import { Head, HeadProps } from "./components/head/head.mjs";
11
12
  import { Heading, HeadingAs, HeadingProps } from "./components/heading/heading.mjs";
@@ -23,4 +24,4 @@ import { EmailElementProps, Tailwind, TailwindConfig, TailwindProps, pixelBasedP
23
24
  import { TailwindSetup, setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tailwind.mjs";
24
25
  import { Text, TextProps } from "./components/text/text.mjs";
25
26
  export * from "@react-email/render";
26
- export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
27
+ export { Body, BodyProps, Button, ButtonProps, CodeBlock, CodeBlockProps, CodeInline, CodeInlineProps, Column, ColumnProps, Container, ContainerProps, ElementOptions, EmailElementProps, Font, FontProps, Head, HeadProps, Heading, HeadingAs, HeadingProps, Hr, HrProps, Html, HtmlProps, Img, ImgProps, Link, LinkProps, Markdown, MarkdownProps, Preview, PreviewProps, PrismLanguage, ResolvedTailwind, Row, RowProps, Section, SectionProps, Tailwind, TailwindConfig, TailwindProps, TailwindSetup, Text, TextProps, Theme, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { markAsElement } from "./components/element-marker.mjs";
1
2
  import { Body } from "./components/body/body.mjs";
2
3
  import { Button } from "./components/button/button.mjs";
3
4
  import { CodeBlock } from "./components/code-block/code-block.mjs";
@@ -22,4 +23,4 @@ import { setupTailwind } from "./components/tailwind/utils/tailwindcss/setup-tai
22
23
  import { Tailwind, pixelBasedPreset } from "./components/tailwind/tailwind.mjs";
23
24
  import { Text } from "./components/text/text.mjs";
24
25
  export * from "@react-email/render";
25
- export { Body, Button, CodeBlock, CodeInline, Column, Container, Font, Head, Heading, Hr, Html, Img, Link, Markdown, Preview, Row, Section, Tailwind, Text, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
26
+ export { Body, Button, CodeBlock, CodeInline, Column, Container, Font, Head, Heading, Hr, Html, Img, Link, Markdown, Preview, Row, Section, Tailwind, Text, a11yDark, atomDark, baseAteliersulphurpoolLight, cb, coldarkCold, coldarkDark, coyWithoutShadows, darcula, dracula, duotoneDark, duotoneEarth, duotoneForest, duotoneLight, duotoneSea, duotoneSpace, ghcolors, gruvboxDark, gruvboxLight, holiTheme, hopscotch, inlineStyles, laserwave, lucario, markAsElement, materialDark, materialLight, materialOceanic, nightOwl, nord, oneDark, oneLight, pixelBasedPreset, pojoaque, renderWhiteSpace, sanitizeStyleSheet, setupTailwind, shadesOfPurple, solarizedDarkAtom, synthwave84, vesper, vs, vscDarkPlus, xonokai, zTouch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-email",
3
- "version": "6.9.3",
3
+ "version": "6.9.5",
4
4
  "description": "A live preview of your emails right in your browser.",
5
5
  "bin": {
6
6
  "email": "./dist/cli/index.mjs"
@@ -67,7 +67,7 @@
67
67
  "@types/mime-types": "2.1.4",
68
68
  "@types/prompts": "2.4.9",
69
69
  "@types/shelljs": "0.10.0",
70
- "next": "16.3.0",
70
+ "next": "16.3.3",
71
71
  "react": "19.2.4",
72
72
  "react-dom": "19.2.4",
73
73
  "shelljs": "0.10.0",
@@ -3,57 +3,77 @@ import { markAsElement } from '../element-marker.js';
3
3
 
4
4
  export type ContainerProps = Readonly<React.ComponentPropsWithoutRef<'table'>>;
5
5
 
6
- export const Container = React.forwardRef<HTMLTableElement, ContainerProps>(
7
- ({ children, style = {}, ...props }, ref) => {
8
- // Split padding styles to improve compatibility with Klaviyo and Outlook,
9
- // while preserving user-provided style property order without allocating
10
- // entry arrays on each render.
11
- const tdStyle: React.CSSProperties = {};
12
- const tableStyle: React.CSSProperties = {};
13
-
14
- const styleRecord = style as Record<string, unknown>;
15
-
16
- for (const key in styleRecord) {
17
- if (!Object.hasOwn(styleRecord, key)) {
18
- continue;
19
- }
6
+ export const Container = React.forwardRef<
7
+ HTMLTableElement,
8
+ ContainerProps & { tdClassName?: string }
9
+ >(({ children, style = {}, tdClassName, ...props }, ref) => {
10
+ // Split padding styles to improve compatibility with Klaviyo and Outlook,
11
+ // while preserving user-provided style property order without allocating
12
+ // entry arrays on each render.
13
+ const tdStyle: React.CSSProperties = {};
14
+ const tableStyle: React.CSSProperties = {};
15
+
16
+ const styleRecord = style as Record<string, unknown>;
17
+
18
+ for (const key in styleRecord) {
19
+ if (!Object.hasOwn(styleRecord, key)) {
20
+ continue;
21
+ }
22
+
23
+ const value = styleRecord[key];
20
24
 
21
- const value = styleRecord[key];
25
+ if (key.startsWith('padding')) {
26
+ (tdStyle as Record<string, unknown>)[key] = value;
27
+ } else {
28
+ (tableStyle as Record<string, unknown>)[key] = value;
29
+ }
30
+ }
22
31
 
32
+ return (
33
+ <table
34
+ align="center"
35
+ width="100%"
36
+ {...props}
37
+ border={0}
38
+ cellPadding="0"
39
+ cellSpacing="0"
40
+ ref={ref}
41
+ role="presentation"
42
+ style={{ maxWidth: '37.5em', ...tableStyle }}
43
+ >
44
+ <tbody>
45
+ <tr style={{ width: '100%' }}>
46
+ <td className={tdClassName} style={tdStyle}>
47
+ {children}
48
+ </td>
49
+ </tr>
50
+ </tbody>
51
+ </table>
52
+ );
53
+ });
54
+
55
+ Container.displayName = 'Container';
56
+ markAsElement<ContainerProps & { tdClassName?: string }>(Container, {
57
+ resolveTailwind(props, { style, className, classProperties }) {
58
+ const tableClasses: string[] = [];
59
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
60
+ for (const name of className?.split(' ') ?? []) {
61
+ const properties = classProperties[name];
23
62
  if (
24
- key === 'padding' ||
25
- key === 'paddingTop' ||
26
- key === 'paddingRight' ||
27
- key === 'paddingBottom' ||
28
- key === 'paddingLeft'
63
+ properties &&
64
+ properties.length > 0 &&
65
+ properties.every((property) => property.startsWith('padding'))
29
66
  ) {
30
- (tdStyle as Record<string, unknown>)[key] = value;
67
+ tdClasses.push(name);
31
68
  } else {
32
- (tableStyle as Record<string, unknown>)[key] = value;
69
+ tableClasses.push(name);
33
70
  }
34
71
  }
35
-
36
- return (
37
- <table
38
- align="center"
39
- width="100%"
40
- {...props}
41
- border={0}
42
- cellPadding="0"
43
- cellSpacing="0"
44
- ref={ref}
45
- role="presentation"
46
- style={{ maxWidth: '37.5em', ...tableStyle }}
47
- >
48
- <tbody>
49
- <tr style={{ width: '100%' }}>
50
- <td style={tdStyle}>{children}</td>
51
- </tr>
52
- </tbody>
53
- </table>
54
- );
72
+ return {
73
+ ...props,
74
+ style,
75
+ className: tableClasses.length > 0 ? tableClasses.join(' ') : undefined,
76
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(' ') : undefined,
77
+ };
55
78
  },
56
- );
57
-
58
- Container.displayName = 'Container';
59
- markAsElement(Container);
79
+ });
@@ -1,5 +1,28 @@
1
+ import type * as React from 'react';
2
+
1
3
  export const elementMarker = Symbol.for('react-email.element');
2
4
 
3
- export const markAsElement = (component: unknown) => {
4
- (component as Record<symbol, boolean>)[elementMarker] = true;
5
+ export interface ResolvedTailwind {
6
+ style: React.CSSProperties;
7
+ className: string | undefined;
8
+ classProperties: Record<string, string[]>;
9
+ }
10
+
11
+ export interface ElementOptions<Props = object> {
12
+ resolveTailwind?: (props: Props, resolved: ResolvedTailwind) => Props;
13
+ }
14
+
15
+ export const markAsElement = <Props>(
16
+ component: unknown,
17
+ options: ElementOptions<Props> = {},
18
+ ) => {
19
+ (component as Record<symbol, ElementOptions<Props>>)[elementMarker] = options;
20
+ };
21
+
22
+ export const getElementOptions = (
23
+ type: unknown,
24
+ ): ElementOptions | undefined => {
25
+ if (typeof type !== 'function' && typeof type !== 'object') return undefined;
26
+ if (type === null) return undefined;
27
+ return (type as Record<symbol, ElementOptions | undefined>)[elementMarker];
5
28
  };
@@ -4,6 +4,11 @@ export * from './code-block/index.js';
4
4
  export * from './code-inline/index.js';
5
5
  export * from './column/index.js';
6
6
  export * from './container/index.js';
7
+ export {
8
+ type ElementOptions,
9
+ markAsElement,
10
+ type ResolvedTailwind,
11
+ } from './element-marker.js';
7
12
  export * from './font/index.js';
8
13
  export * from './head/index.js';
9
14
  export * from './heading/index.js';
@@ -3,57 +3,77 @@ import { markAsElement } from '../element-marker.js';
3
3
 
4
4
  export type SectionProps = Readonly<React.ComponentPropsWithoutRef<'table'>>;
5
5
 
6
- export const Section = React.forwardRef<HTMLTableElement, SectionProps>(
7
- ({ children, style = {}, ...props }, ref) => {
8
- // Split padding styles to improve compatibility with Klaviyo and Outlook,
9
- // while preserving user-provided style property order without allocating
10
- // entry arrays on each render.
11
- const tdStyle: React.CSSProperties = {};
12
- const tableStyle: React.CSSProperties = {};
13
-
14
- const styleRecord = style as Record<string, unknown>;
15
-
16
- for (const key in styleRecord) {
17
- if (!Object.hasOwn(styleRecord, key)) {
18
- continue;
19
- }
6
+ export const Section = React.forwardRef<
7
+ HTMLTableElement,
8
+ SectionProps & { tdClassName?: string }
9
+ >(({ children, style = {}, tdClassName, ...props }, ref) => {
10
+ // Split padding styles to improve compatibility with Klaviyo and Outlook,
11
+ // while preserving user-provided style property order without allocating
12
+ // entry arrays on each render.
13
+ const tdStyle: React.CSSProperties = {};
14
+ const tableStyle: React.CSSProperties = {};
15
+
16
+ const styleRecord = style as Record<string, unknown>;
17
+
18
+ for (const key in styleRecord) {
19
+ if (!Object.hasOwn(styleRecord, key)) {
20
+ continue;
21
+ }
22
+
23
+ const value = styleRecord[key];
20
24
 
21
- const value = styleRecord[key];
25
+ if (key.startsWith('padding')) {
26
+ (tdStyle as Record<string, unknown>)[key] = value;
27
+ } else {
28
+ (tableStyle as Record<string, unknown>)[key] = value;
29
+ }
30
+ }
22
31
 
32
+ return (
33
+ <table
34
+ align="center"
35
+ width="100%"
36
+ border={0}
37
+ cellPadding="0"
38
+ cellSpacing="0"
39
+ role="presentation"
40
+ {...props}
41
+ ref={ref}
42
+ style={tableStyle}
43
+ >
44
+ <tbody>
45
+ <tr>
46
+ <td className={tdClassName} style={tdStyle}>
47
+ {children}
48
+ </td>
49
+ </tr>
50
+ </tbody>
51
+ </table>
52
+ );
53
+ });
54
+
55
+ Section.displayName = 'Section';
56
+ markAsElement<SectionProps & { tdClassName?: string }>(Section, {
57
+ resolveTailwind(props, { style, className, classProperties }) {
58
+ const tableClasses: string[] = [];
59
+ const tdClasses = props.tdClassName ? [props.tdClassName] : [];
60
+ for (const name of className?.split(' ') ?? []) {
61
+ const properties = classProperties[name];
23
62
  if (
24
- key === 'padding' ||
25
- key === 'paddingTop' ||
26
- key === 'paddingRight' ||
27
- key === 'paddingBottom' ||
28
- key === 'paddingLeft'
63
+ properties &&
64
+ properties.length > 0 &&
65
+ properties.every((property) => property.startsWith('padding'))
29
66
  ) {
30
- (tdStyle as Record<string, unknown>)[key] = value;
67
+ tdClasses.push(name);
31
68
  } else {
32
- (tableStyle as Record<string, unknown>)[key] = value;
69
+ tableClasses.push(name);
33
70
  }
34
71
  }
35
-
36
- return (
37
- <table
38
- align="center"
39
- width="100%"
40
- border={0}
41
- cellPadding="0"
42
- cellSpacing="0"
43
- role="presentation"
44
- {...props}
45
- ref={ref}
46
- style={tableStyle}
47
- >
48
- <tbody>
49
- <tr>
50
- <td style={tdStyle}>{children}</td>
51
- </tr>
52
- </tbody>
53
- </table>
54
- );
72
+ return {
73
+ ...props,
74
+ style,
75
+ className: tableClasses.length > 0 ? tableClasses.join(' ') : undefined,
76
+ tdClassName: tdClasses.length > 0 ? tdClasses.join(' ') : undefined,
77
+ };
55
78
  },
56
- );
57
-
58
- Section.displayName = 'Section';
59
- markAsElement(Section);
79
+ });
@@ -275,6 +275,19 @@ describe('Tailwind component', () => {
275
275
  expect(html).not.toMatch(/<table[^>]*style="[^"]*padding:1rem/);
276
276
  });
277
277
 
278
+ it('routes responsive padding classes on <Section> to the inner <td>', async () => {
279
+ const html = await render(
280
+ <Tailwind>
281
+ <Head />
282
+ <Section className="max-sm:px-5 max-sm:bg-red-500 px-9">x</Section>
283
+ </Tailwind>,
284
+ );
285
+
286
+ expect(html).toMatchInlineSnapshot(
287
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><meta name="x-apple-disable-message-reformatting"/><style>@media (max-width:40rem){.max-sm_bg-red-500{background-color:rgb(251,44,54)!important}}@media (max-width:40rem){.max-sm_px-5{padding-right:1.25rem!important;padding-left:1.25rem!important}}</style></head><!--$--><!--head--><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" class="max-sm_bg-red-500"><tbody><tr><td class="max-sm_px-5" style="padding-right:2.25rem;padding-left:2.25rem">x</td></tr></tbody></table><!--/$-->"`,
288
+ );
289
+ });
290
+
278
291
  it('inlines Tailwind classes on <Column> onto its <td>', async () => {
279
292
  const html = await render(
280
293
  <Tailwind>
@@ -1,5 +1,6 @@
1
- import type { Rule } from 'css-tree';
1
+ import { type Rule, walk } from 'css-tree';
2
2
  import React from 'react';
3
+ import { getElementOptions } from '../../../element-marker.js';
3
4
  import type { EmailElementProps } from '../../tailwind.js';
4
5
  import { sanitizeClassName } from '../compatibility/sanitize-class-name.js';
5
6
  import type { CustomProperties } from '../css/get-custom-properties.js';
@@ -12,50 +13,58 @@ export function cloneElementWithInlinedStyles(
12
13
  nonInlinableRules: Map<string, Rule[]>,
13
14
  customProperties: CustomProperties,
14
15
  ) {
15
- const propsToOverwrite: Partial<EmailElementProps> = {};
16
+ if (!element.props.className || isComponent(element)) {
17
+ return React.cloneElement(element, element.props, element.props.children);
18
+ }
16
19
 
17
- if (element.props.className && !isComponent(element)) {
18
- const classes = element.props.className.trim().split(/\s+/);
20
+ const classes = element.props.className.trim().split(/\s+/);
19
21
 
20
- const residualClasses: string[] = [];
22
+ const residualClasses: string[] = [];
23
+ const classProperties: Record<string, string[]> = Object.create(null);
21
24
 
22
- const rules: Rule[] = [];
23
- for (const className of classes) {
24
- const classRules = inlinableRules.get(className);
25
- if (classRules) {
26
- rules.push(...classRules);
27
- }
28
- if (nonInlinableRules.has(className)) {
29
- residualClasses.push(className);
30
- } else if (!classRules) {
31
- residualClasses.push(className);
32
- }
25
+ const rules: Rule[] = [];
26
+ for (const className of classes) {
27
+ const classRules = inlinableRules.get(className);
28
+ if (classRules) {
29
+ rules.push(...classRules);
33
30
  }
34
-
35
- const styles = makeInlineStylesFor(rules, customProperties);
36
- propsToOverwrite.style = {
37
- ...styles,
38
- ...element.props.style,
39
- };
40
-
41
- if (residualClasses.length > 0) {
42
- propsToOverwrite.className = residualClasses
43
- .map((className) => {
44
- if (nonInlinableRules.has(className)) {
45
- return sanitizeClassName(className);
46
- }
47
- return className;
48
- })
49
- .join(' ');
50
- } else {
51
- propsToOverwrite.className = undefined;
31
+ const nonInlinable = nonInlinableRules.get(className);
32
+ if (nonInlinable) {
33
+ const sanitized = sanitizeClassName(className);
34
+ residualClasses.push(sanitized);
35
+ const properties: string[] = [];
36
+ for (const rule of nonInlinable) {
37
+ walk(rule, {
38
+ visit: 'Declaration',
39
+ enter(declaration) {
40
+ properties.push(declaration.property);
41
+ },
42
+ });
43
+ }
44
+ classProperties[sanitized] = properties;
45
+ } else if (!classRules) {
46
+ residualClasses.push(className);
52
47
  }
53
48
  }
54
49
 
55
- const newProps = {
56
- ...element.props,
57
- ...propsToOverwrite,
50
+ const resolved = {
51
+ style: {
52
+ ...makeInlineStylesFor(rules, customProperties),
53
+ ...element.props.style,
54
+ },
55
+ className:
56
+ residualClasses.length > 0 ? residualClasses.join(' ') : undefined,
57
+ classProperties,
58
58
  };
59
59
 
60
+ const resolveTailwind = getElementOptions(element.type)?.resolveTailwind;
61
+ const newProps = resolveTailwind
62
+ ? (resolveTailwind(element.props, resolved) as EmailElementProps)
63
+ : {
64
+ ...element.props,
65
+ style: resolved.style,
66
+ className: resolved.className,
67
+ };
68
+
60
69
  return React.cloneElement(element, newProps, newProps.children);
61
70
  }