@dextinity/mail-react 2.0.0-canary-20260729062014

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.
Files changed (123) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +112 -0
  3. package/lib/blocks/factories/BlocksBlock.d.ts +14 -0
  4. package/lib/blocks/factories/BlocksBlock.js +15 -0
  5. package/lib/blocks/factories/ListBlock.d.ts +13 -0
  6. package/lib/blocks/factories/ListBlock.js +5 -0
  7. package/lib/blocks/factories/OneOfBlock.d.ts +14 -0
  8. package/lib/blocks/factories/OneOfBlock.js +15 -0
  9. package/lib/blocks/factories/OptionalBlock.d.ts +10 -0
  10. package/lib/blocks/factories/OptionalBlock.js +7 -0
  11. package/lib/blocks/factories/types.d.ts +4 -0
  12. package/lib/blocks/factories/types.js +1 -0
  13. package/lib/blocks/helpers/PropsWithData.d.ts +3 -0
  14. package/lib/blocks/helpers/PropsWithData.js +1 -0
  15. package/lib/blocks/pixelImage/HtmlPixelImageBlock.d.ts +11 -0
  16. package/lib/blocks/pixelImage/HtmlPixelImageBlock.js +18 -0
  17. package/lib/blocks/pixelImage/MjmlPixelImageBlock.d.ts +9 -0
  18. package/lib/blocks/pixelImage/MjmlPixelImageBlock.js +15 -0
  19. package/lib/blocks/pixelImage/common.d.ts +18 -0
  20. package/lib/blocks/pixelImage/common.js +1 -0
  21. package/lib/blocks/pixelImage/usePixelImageBlockConfig.d.ts +5 -0
  22. package/lib/blocks/pixelImage/usePixelImageBlockConfig.js +11 -0
  23. package/lib/blocks/pixelImage/usePixelImageBlockData.d.ts +16 -0
  24. package/lib/blocks/pixelImage/usePixelImageBlockData.js +80 -0
  25. package/lib/blocks/richText/common.d.ts +66 -0
  26. package/lib/blocks/richText/common.js +1 -0
  27. package/lib/blocks/richText/createRichTextBlock.d.ts +30 -0
  28. package/lib/blocks/richText/createRichTextBlock.js +81 -0
  29. package/lib/blocks/richText/createRichTextRenderers.d.ts +17 -0
  30. package/lib/blocks/richText/createRichTextRenderers.js +83 -0
  31. package/lib/blocks.generated.d.ts +140 -0
  32. package/lib/blocks.generated.js +1 -0
  33. package/lib/client/index.d.ts +1 -0
  34. package/lib/client/index.js +1 -0
  35. package/lib/client/renderMailHtml.d.ts +9 -0
  36. package/lib/client/renderMailHtml.js +7 -0
  37. package/lib/components/button/HtmlButton.d.ts +9 -0
  38. package/lib/components/button/HtmlButton.js +58 -0
  39. package/lib/components/button/MjmlButton.d.ts +10 -0
  40. package/lib/components/button/MjmlButton.js +91 -0
  41. package/lib/components/button/buttonProps.d.ts +35 -0
  42. package/lib/components/button/buttonProps.js +1 -0
  43. package/lib/components/button/defaultButtonStyles.d.ts +2 -0
  44. package/lib/components/button/defaultButtonStyles.js +9 -0
  45. package/lib/components/button/generateResponsiveButtonCss.d.ts +8 -0
  46. package/lib/components/button/generateResponsiveButtonCss.js +21 -0
  47. package/lib/components/divider/HtmlDivider.d.ts +9 -0
  48. package/lib/components/divider/HtmlDivider.js +43 -0
  49. package/lib/components/divider/MjmlDivider.d.ts +7 -0
  50. package/lib/components/divider/MjmlDivider.js +13 -0
  51. package/lib/components/divider/defaultDividerStyles.d.ts +2 -0
  52. package/lib/components/divider/defaultDividerStyles.js +4 -0
  53. package/lib/components/divider/dividerProps.d.ts +51 -0
  54. package/lib/components/divider/dividerProps.js +1 -0
  55. package/lib/components/divider/generateResponsiveDividerCss.d.ts +8 -0
  56. package/lib/components/divider/generateResponsiveDividerCss.js +15 -0
  57. package/lib/components/image/HtmlImage.d.ts +11 -0
  58. package/lib/components/image/HtmlImage.js +23 -0
  59. package/lib/components/image/MjmlImage.d.ts +10 -0
  60. package/lib/components/image/MjmlImage.js +22 -0
  61. package/lib/components/inlineLink/HtmlInlineLink.d.ts +17 -0
  62. package/lib/components/inlineLink/HtmlInlineLink.js +35 -0
  63. package/lib/components/mailRoot/MjmlMailRoot.d.ts +32 -0
  64. package/lib/components/mailRoot/MjmlMailRoot.js +25 -0
  65. package/lib/components/section/MjmlSection.d.ts +21 -0
  66. package/lib/components/section/MjmlSection.js +48 -0
  67. package/lib/components/text/HtmlText.d.ts +51 -0
  68. package/lib/components/text/HtmlText.js +47 -0
  69. package/lib/components/text/MjmlText.d.ts +40 -0
  70. package/lib/components/text/MjmlText.js +65 -0
  71. package/lib/components/text/OutlookTextStyleContext.d.ts +9 -0
  72. package/lib/components/text/OutlookTextStyleContext.js +10 -0
  73. package/lib/components/text/textStyles.d.ts +10 -0
  74. package/lib/components/text/textStyles.js +24 -0
  75. package/lib/components/wrapper/InsideMjmlWrapperContext.d.ts +3 -0
  76. package/lib/components/wrapper/InsideMjmlWrapperContext.js +6 -0
  77. package/lib/components/wrapper/MjmlWrapper.d.ts +14 -0
  78. package/lib/components/wrapper/MjmlWrapper.js +12 -0
  79. package/lib/config/ConfigProvider.d.ts +43 -0
  80. package/lib/config/ConfigProvider.js +16 -0
  81. package/lib/index.d.ts +47 -0
  82. package/lib/index.js +29 -0
  83. package/lib/server/index.d.ts +1 -0
  84. package/lib/server/index.js +1 -0
  85. package/lib/server/renderMailHtml.d.ts +9 -0
  86. package/lib/server/renderMailHtml.js +7 -0
  87. package/lib/storybook/CopyMailHtmlButton.d.ts +4 -0
  88. package/lib/storybook/CopyMailHtmlButton.js +33 -0
  89. package/lib/storybook/MailRendererDecorator.d.ts +7 -0
  90. package/lib/storybook/MailRendererDecorator.js +17 -0
  91. package/lib/storybook/MjmlWarningsPanel.d.ts +7 -0
  92. package/lib/storybook/MjmlWarningsPanel.js +33 -0
  93. package/lib/storybook/UsePublicImageUrlsToggle.d.ts +4 -0
  94. package/lib/storybook/UsePublicImageUrlsToggle.js +14 -0
  95. package/lib/storybook/index.d.ts +2 -0
  96. package/lib/storybook/index.js +9 -0
  97. package/lib/storybook/manager.d.ts +1 -0
  98. package/lib/storybook/manager.js +25 -0
  99. package/lib/storybook/preview.d.ts +39 -0
  100. package/lib/storybook/preview.js +27 -0
  101. package/lib/storybook/replaceImagesWithPublicUrl.d.ts +1 -0
  102. package/lib/storybook/replaceImagesWithPublicUrl.js +15 -0
  103. package/lib/styles/Styles.d.ts +6 -0
  104. package/lib/styles/Styles.js +16 -0
  105. package/lib/styles/generateResponsiveVariantCss.d.ts +26 -0
  106. package/lib/styles/generateResponsiveVariantCss.js +66 -0
  107. package/lib/styles/registerStyles.d.ts +19 -0
  108. package/lib/styles/registerStyles.js +14 -0
  109. package/lib/theme/ThemeProvider.d.ts +8 -0
  110. package/lib/theme/ThemeProvider.js +17 -0
  111. package/lib/theme/createBreakpoint.d.ts +7 -0
  112. package/lib/theme/createBreakpoint.js +11 -0
  113. package/lib/theme/createTheme.d.ts +24 -0
  114. package/lib/theme/createTheme.js +32 -0
  115. package/lib/theme/defaultTheme.d.ts +2 -0
  116. package/lib/theme/defaultTheme.js +27 -0
  117. package/lib/theme/responsiveValue.d.ts +31 -0
  118. package/lib/theme/responsiveValue.js +34 -0
  119. package/lib/theme/themeTypes.d.ts +204 -0
  120. package/lib/theme/themeTypes.js +1 -0
  121. package/lib/utils/css.d.ts +4 -0
  122. package/lib/utils/css.js +4 -0
  123. package/package.json +91 -0
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { registerStyles } from "../../styles/registerStyles.js";
4
+ import { getDefaultOrUndefined } from "../../theme/responsiveValue.js";
5
+ import { useOptionalTheme } from "../../theme/ThemeProvider.js";
6
+ import { defaultDividerStyles } from "./defaultDividerStyles.js";
7
+ import { generateResponsiveDividerCss } from "./generateResponsiveDividerCss.js";
8
+ // U+200B keeps the cell from collapsing in clients that drop empty <td>s,
9
+ // without contributing visible width.
10
+ const zeroWidthSpace = String.fromCharCode(0x200b);
11
+ /**
12
+ * Themed divider for use inside MJML ending tags or outside of the MJML context.
13
+ */
14
+ export function HtmlDivider({ variant: variantProp, height: heightProp, backgroundColor: backgroundColorProp, backgroundImage: backgroundImageProp, className, style, }) {
15
+ const theme = useOptionalTheme();
16
+ if (theme === null && variantProp !== undefined) {
17
+ throw new Error("The `variant` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
18
+ }
19
+ const themeDivider = theme?.divider ?? defaultDividerStyles;
20
+ const { defaultVariant, variants, ...baseStyles } = themeDivider;
21
+ const activeVariant = variantProp ?? defaultVariant;
22
+ const variantStyles = activeVariant ? variants?.[activeVariant] : undefined;
23
+ const mergedStyles = variantStyles ? { ...baseStyles, ...variantStyles } : baseStyles;
24
+ const height = heightProp ?? getDefaultOrUndefined(mergedStyles.height);
25
+ const backgroundColor = backgroundColorProp ?? getDefaultOrUndefined(mergedStyles.backgroundColor);
26
+ const backgroundImage = backgroundImageProp ?? getDefaultOrUndefined(mergedStyles.backgroundImage);
27
+ const dividerStyle = {
28
+ height,
29
+ lineHeight: height === undefined ? undefined : `${height}px`,
30
+ fontSize: 0,
31
+ backgroundColor,
32
+ backgroundImage,
33
+ ...{ msoLineHeightRule: "exactly" },
34
+ ...style,
35
+ };
36
+ return (_jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, width: "100%", className: clsx("htmlDivider", activeVariant && `htmlDivider--${activeVariant}`, className), children: _jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { bgcolor: backgroundColor, height: height, style: dividerStyle, children: zeroWidthSpace }) }) }) }));
37
+ }
38
+ export function generateHtmlDividerStyles(theme) {
39
+ return generateResponsiveDividerCss(theme, {
40
+ styleSelector: (variantName) => `.htmlDivider--${variantName} td`,
41
+ });
42
+ }
43
+ registerStyles(generateHtmlDividerStyles);
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DividerProps } from "./dividerProps.js";
3
+ export type MjmlDividerProps = DividerProps;
4
+ /**
5
+ * Themed divider for use inside an `MjmlColumn`.
6
+ */
7
+ export declare function MjmlDivider({ variant, height, backgroundColor, backgroundImage, className, style }: MjmlDividerProps): ReactNode;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MjmlRaw } from "@faire/mjml-react";
3
+ import clsx from "clsx";
4
+ import { useOptionalTheme } from "../../theme/ThemeProvider.js";
5
+ import { HtmlDivider } from "./HtmlDivider.js";
6
+ /**
7
+ * Themed divider for use inside an `MjmlColumn`.
8
+ */
9
+ export function MjmlDivider({ variant, height, backgroundColor, backgroundImage, className, style }) {
10
+ const theme = useOptionalTheme();
11
+ const activeVariant = variant ?? theme?.divider.defaultVariant;
12
+ return (_jsx(MjmlRaw, { children: _jsx(HtmlDivider, { variant: variant, height: height, backgroundColor: backgroundColor, backgroundImage: backgroundImage, className: clsx("mjmlDivider", activeVariant && `mjmlDivider--${activeVariant}`, className), style: style }) }));
13
+ }
@@ -0,0 +1,2 @@
1
+ import type { DividerStyles } from "../../theme/themeTypes.js";
2
+ export declare const defaultDividerStyles: DividerStyles;
@@ -0,0 +1,4 @@
1
+ export const defaultDividerStyles = {
2
+ height: 4,
3
+ backgroundColor: "#000000",
4
+ };
@@ -0,0 +1,51 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { DividerVariantName } from "../../theme/themeTypes.js";
3
+ export interface DividerProps {
4
+ /**
5
+ * The component's variant to apply, as defined in the theme. Requires a
6
+ * theme (`ThemeProvider` or `MjmlMailRoot`) when set.
7
+ *
8
+ * Custom variants should be defined in the theme through module augmentation:
9
+ *
10
+ * ```ts
11
+ * declare module "@dextinity/mail-react" {
12
+ * interface DividerVariants { thin: true; thick: true }
13
+ * }
14
+ * ```
15
+ *
16
+ * ```ts
17
+ * const theme = createTheme({
18
+ * divider: {
19
+ * variants: {
20
+ * thin: { height: 1 },
21
+ * thick: { height: 8 },
22
+ * },
23
+ * },
24
+ * });
25
+ * ```
26
+ *
27
+ * @defaultValue The theme's `divider.defaultVariant`, when set
28
+ */
29
+ variant?: DividerVariantName;
30
+ /**
31
+ * Height of the divider in pixels.
32
+ *
33
+ * @defaultValue The theme's `divider` height for the active variant
34
+ */
35
+ height?: number;
36
+ /**
37
+ * Background color of the divider (e.g. `"#FF0000"`).
38
+ *
39
+ * @defaultValue The theme's `divider` background color for the active variant
40
+ */
41
+ backgroundColor?: string;
42
+ /**
43
+ * Background image for the divider — typically a gradient. Clients that
44
+ * don't render `background-image` fall back to the solid `backgroundColor`.
45
+ *
46
+ * @defaultValue The theme's `divider` background image for the active variant
47
+ */
48
+ backgroundImage?: string;
49
+ className?: string;
50
+ style?: CSSProperties;
51
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { Theme } from "../../theme/themeTypes.js";
2
+ interface GenerateResponsiveDividerCssOptions {
3
+ /** Selector for divider style overrides, given a variant name. */
4
+ styleSelector: (variantName: string) => string;
5
+ }
6
+ /** Generates responsive CSS media queries for divider variant overrides. */
7
+ export declare function generateResponsiveDividerCss(theme: Theme, options: GenerateResponsiveDividerCssOptions): string;
8
+ export {};
@@ -0,0 +1,15 @@
1
+ import { generateResponsiveVariantCss } from "../../styles/generateResponsiveVariantCss.js";
2
+ const dividerProperties = [
3
+ // line-height matches height so Outlook honors the declared cell height.
4
+ { themeKey: "height", cssProperties: ["height", "line-height"], unit: "px" },
5
+ "backgroundColor",
6
+ "backgroundImage",
7
+ ];
8
+ /** Generates responsive CSS media queries for divider variant overrides. */
9
+ export function generateResponsiveDividerCss(theme, options) {
10
+ return generateResponsiveVariantCss({
11
+ breakpoints: theme.breakpoints,
12
+ variants: theme.divider.variants,
13
+ groups: [{ selector: options.styleSelector, properties: dividerProperties }],
14
+ });
15
+ }
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ export type HtmlImageProps = ComponentProps<"img">;
3
+ /**
4
+ * Renders an `<img>` tag that adapts to its container width below the default
5
+ * breakpoint.
6
+ *
7
+ * Use within raw HTML context — HTML-only emails or
8
+ * [MJML ending tags](https://documentation.mjml.io/#ending-tags) like `MjmlRaw`.
9
+ * For MJML context, use `MjmlImage`.
10
+ */
11
+ export declare function HtmlImage({ className, ...restProps }: HtmlImageProps): ReactNode;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { registerStyles } from "../../styles/registerStyles.js";
4
+ import { css } from "../../utils/css.js";
5
+ /**
6
+ * Renders an `<img>` tag that adapts to its container width below the default
7
+ * breakpoint.
8
+ *
9
+ * Use within raw HTML context — HTML-only emails or
10
+ * [MJML ending tags](https://documentation.mjml.io/#ending-tags) like `MjmlRaw`.
11
+ * For MJML context, use `MjmlImage`.
12
+ */
13
+ export function HtmlImage({ className, ...restProps }) {
14
+ return _jsx("img", { className: clsx("htmlImage", className), ...restProps });
15
+ }
16
+ registerStyles((theme) => css `
17
+ ${theme.breakpoints.default.belowMediaQuery} {
18
+ .htmlImage {
19
+ width: 100%;
20
+ height: auto;
21
+ }
22
+ }
23
+ `);
@@ -0,0 +1,10 @@
1
+ import { type IMjmlImageProps } from "@faire/mjml-react";
2
+ import type { ReactNode } from "react";
3
+ export type MjmlImageProps = IMjmlImageProps;
4
+ /**
5
+ * Renders an MJML image that adapts to the viewport width below the default breakpoint.
6
+ *
7
+ * Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
8
+ * use `HtmlImage` instead.
9
+ */
10
+ export declare function MjmlImage({ className, ...restProps }: MjmlImageProps): ReactNode;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MjmlImage as BaseMjmlImage } from "@faire/mjml-react";
3
+ import clsx from "clsx";
4
+ import { registerStyles } from "../../styles/registerStyles.js";
5
+ import { css } from "../../utils/css.js";
6
+ /**
7
+ * Renders an MJML image that adapts to the viewport width below the default breakpoint.
8
+ *
9
+ * Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
10
+ * use `HtmlImage` instead.
11
+ */
12
+ export function MjmlImage({ className, ...restProps }) {
13
+ return _jsx(BaseMjmlImage, { className: clsx("mjmlImage", className), ...restProps });
14
+ }
15
+ // MJML inlines a fixed `height` on the inner <img>; !important overrides it for responsive scaling.
16
+ registerStyles((theme) => css `
17
+ ${theme.breakpoints.default.belowMediaQuery} {
18
+ .mjmlImage img {
19
+ height: auto !important;
20
+ }
21
+ }
22
+ `);
@@ -0,0 +1,17 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ export type HtmlInlineLinkProps = Omit<ComponentProps<"a">, "target"> & {
3
+ /**
4
+ * Where to open the linked document.
5
+ *
6
+ * @defaultValue `"_blank"`
7
+ */
8
+ target?: ComponentProps<"a">["target"];
9
+ };
10
+ /**
11
+ * Inline link styled to match the surrounding text, for use inside `HtmlText` or `MjmlText`.
12
+ *
13
+ * Applies explicit text styles from the parent text component's context so that
14
+ * Outlook Desktop (which overrides `<a>` tags with its built-in "Hyperlink" style)
15
+ * renders the link with the correct font and color.
16
+ */
17
+ export declare function HtmlInlineLink({ className, style, target, ...restProps }: HtmlInlineLinkProps): ReactNode;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { registerStyles } from "../../styles/registerStyles.js";
4
+ import { css } from "../../utils/css.js";
5
+ import { useOutlookTextStyle } from "../text/OutlookTextStyleContext.js";
6
+ /**
7
+ * Inline link styled to match the surrounding text, for use inside `HtmlText` or `MjmlText`.
8
+ *
9
+ * Applies explicit text styles from the parent text component's context so that
10
+ * Outlook Desktop (which overrides `<a>` tags with its built-in "Hyperlink" style)
11
+ * renders the link with the correct font and color.
12
+ */
13
+ export function HtmlInlineLink({ className, style, target = "_blank", ...restProps }) {
14
+ const outlookTextStyle = useOutlookTextStyle();
15
+ const baseStyle = {
16
+ fontFamily: outlookTextStyle?.fontFamily ?? "inherit",
17
+ fontSize: outlookTextStyle?.fontSize ?? "inherit",
18
+ lineHeight: outlookTextStyle?.lineHeight ?? "inherit",
19
+ fontWeight: outlookTextStyle?.fontWeight ?? "inherit",
20
+ color: outlookTextStyle?.color ?? "inherit",
21
+ textDecoration: "underline",
22
+ };
23
+ return _jsx("a", { className: clsx("htmlInlineLink", className), style: { ...baseStyle, ...style }, target: target, ...restProps });
24
+ }
25
+ registerStyles((theme) => css `
26
+ ${theme.breakpoints.default.belowMediaQuery} {
27
+ .htmlInlineLink {
28
+ font-family: inherit !important;
29
+ font-size: inherit !important;
30
+ line-height: inherit !important;
31
+ font-weight: inherit !important;
32
+ color: inherit !important;
33
+ }
34
+ }
35
+ `);
@@ -0,0 +1,32 @@
1
+ import type { PropsWithChildren, ReactNode } from "react";
2
+ import { type Config } from "../../config/ConfigProvider.js";
3
+ import type { Theme } from "../../theme/themeTypes.js";
4
+ type MjmlMailRootProps = PropsWithChildren<{
5
+ /**
6
+ * Theme to use for the email.
7
+ *
8
+ * @defaultValue `createTheme()`
9
+ */
10
+ theme?: Theme;
11
+ /** Extra content appended inside the built-in `<MjmlAttributes>`, after the default `<MjmlAll>`. */
12
+ attributes?: ReactNode;
13
+ /** Extra content appended inside `<MjmlHead>`, after the registered styles block. */
14
+ head?: ReactNode;
15
+ /**
16
+ * Configuration to make available to descendants via `useConfig`.
17
+ */
18
+ config?: Config;
19
+ }>;
20
+ /**
21
+ * The root element for email templates. Renders the standard MJML email skeleton
22
+ * (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
23
+ * default attribute so all components start with zero padding.
24
+ *
25
+ * Accepts an optional `theme` prop that controls the body width and responsive breakpoints. The theme is made available to all descendant components via `useTheme()`.
26
+ *
27
+ * Accepts an optional `config` prop containing configuration. When provided, the value is made available to descendants via `useConfig()`.
28
+ *
29
+ * Direct children should be section-level components (e.g. `MjmlSection`).
30
+ */
31
+ export declare function MjmlMailRoot({ theme: themeProp, attributes, head, config, children }: MjmlMailRootProps): ReactNode;
32
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Mjml, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlHead } from "@faire/mjml-react";
3
+ import { ConfigProvider } from "../../config/ConfigProvider.js";
4
+ import { Styles } from "../../styles/Styles.js";
5
+ import { createTheme } from "../../theme/createTheme.js";
6
+ import { ThemeProvider } from "../../theme/ThemeProvider.js";
7
+ /**
8
+ * The root element for email templates. Renders the standard MJML email skeleton
9
+ * (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
10
+ * default attribute so all components start with zero padding.
11
+ *
12
+ * Accepts an optional `theme` prop that controls the body width and responsive breakpoints. The theme is made available to all descendant components via `useTheme()`.
13
+ *
14
+ * Accepts an optional `config` prop containing configuration. When provided, the value is made available to descendants via `useConfig()`.
15
+ *
16
+ * Direct children should be section-level components (e.g. `MjmlSection`).
17
+ */
18
+ export function MjmlMailRoot({ theme: themeProp, attributes, head, config, children }) {
19
+ const theme = themeProp ?? createTheme();
20
+ const content = (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Mjml, { children: [_jsxs(MjmlHead, { children: [_jsxs(MjmlAttributes, { children: [_jsx(MjmlAll, { padding: "0", fontFamily: theme.text.fontFamily }), attributes] }), _jsx(MjmlBreakpoint, { width: `${theme.breakpoints.mobile.value}px` }), _jsx(Styles, {}), head] }), _jsx(MjmlBody, { width: theme.sizes.bodyWidth, backgroundColor: theme.colors.background.body, children: children })] }) }));
21
+ if (config) {
22
+ return _jsx(ConfigProvider, { config: config, children: content });
23
+ }
24
+ return content;
25
+ }
@@ -0,0 +1,21 @@
1
+ import { type IMjmlGroupProps, type IMjmlSectionProps } from "@faire/mjml-react";
2
+ import type { ReactNode } from "react";
3
+ export type MjmlSectionProps = Omit<IMjmlSectionProps, "backgroundColor"> & {
4
+ /**
5
+ * Background color of the section.
6
+ *
7
+ * @defaultValue The theme's `colors.background.content`, unless inside an `MjmlWrapper`
8
+ */
9
+ backgroundColor?: IMjmlSectionProps["backgroundColor"];
10
+ /** Applies theme-based content indentation with responsive overrides. */
11
+ indent?: boolean;
12
+ /** When true, child columns remain side-by-side on mobile instead of stacking vertically. */
13
+ disableResponsiveBehavior?: boolean;
14
+ /** Props forwarded to internal sub-components. */
15
+ slotProps?: {
16
+ /** Props passed to the wrapping `MjmlGroup` when `disableResponsiveBehavior` is enabled. */
17
+ group?: Partial<IMjmlGroupProps>;
18
+ };
19
+ };
20
+ /** A section wrapper for email layouts. Must be a direct child of `MjmlBody` or `MjmlWrapper`. */
21
+ export declare function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }: MjmlSectionProps): ReactNode;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { MjmlGroup, MjmlSection as BaseMjmlSection } from "@faire/mjml-react";
3
+ import clsx from "clsx";
4
+ import { registerStyles } from "../../styles/registerStyles.js";
5
+ import { getDefaultFromResponsiveValue, getResponsiveOverrides } from "../../theme/responsiveValue.js";
6
+ import { useOptionalTheme } from "../../theme/ThemeProvider.js";
7
+ import { css } from "../../utils/css.js";
8
+ import { useIsInsideMjmlWrapper } from "../wrapper/InsideMjmlWrapperContext.js";
9
+ /** A section wrapper for email layouts. Must be a direct child of `MjmlBody` or `MjmlWrapper`. */
10
+ export function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }) {
11
+ const theme = useOptionalTheme();
12
+ const isInsideWrapper = useIsInsideMjmlWrapper();
13
+ const indentProps = indent ? getIndentProps(theme) : {};
14
+ const resolvedClassName = clsx("mjmlSection", indent && "mjmlSection--indented", className);
15
+ const themeBackgroundProps = theme && !isInsideWrapper ? { backgroundColor: theme.colors.background.content } : {};
16
+ return (_jsx(BaseMjmlSection, { className: resolvedClassName, ...themeBackgroundProps, ...indentProps, ...restProps, children: disableResponsiveBehavior ? _jsx(MjmlGroup, { ...slotProps?.group, children: children }) : _jsx(_Fragment, { children: children }) }));
17
+ }
18
+ function getIndentProps(theme) {
19
+ if (theme === null) {
20
+ throw new Error("The `indent` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
21
+ }
22
+ return {
23
+ paddingLeft: getDefaultFromResponsiveValue(theme.sizes.contentIndentation),
24
+ paddingRight: getDefaultFromResponsiveValue(theme.sizes.contentIndentation),
25
+ };
26
+ }
27
+ registerStyles((theme) => {
28
+ const overrides = getResponsiveOverrides(theme.sizes.contentIndentation);
29
+ if (overrides.length === 0) {
30
+ return css ``;
31
+ }
32
+ return overrides
33
+ .map((override) => {
34
+ const breakpoint = theme.breakpoints[override.breakpointKey];
35
+ if (!breakpoint) {
36
+ return "";
37
+ }
38
+ return css `
39
+ ${breakpoint.belowMediaQuery} {
40
+ .mjmlSection--indented > table > tbody > tr > td {
41
+ padding-left: ${override.value}px !important;
42
+ padding-right: ${override.value}px !important;
43
+ }
44
+ }
45
+ `;
46
+ })
47
+ .join("\n");
48
+ });
@@ -0,0 +1,51 @@
1
+ import type { ComponentPropsWithoutRef, JSX, ReactNode, TdHTMLAttributes } from "react";
2
+ import type { VariantName } from "../../theme/themeTypes.js";
3
+ interface HtmlTextOwnProps {
4
+ /**
5
+ * The component's variant to apply, as defined in the theme.
6
+ *
7
+ * Custom variants should be defined in the theme through module augmentation:
8
+ *
9
+ * ```ts
10
+ * declare module "@dextinity/mail-react" {
11
+ * interface TextVariants { heading: true; body: true }
12
+ * }
13
+ * ```
14
+ *
15
+ * ```ts
16
+ * const theme = createTheme({
17
+ * text: {
18
+ * variants: {
19
+ * heading: { fontSize: "24px" },
20
+ * body: { fontSize: "16px" },
21
+ * },
22
+ * },
23
+ * });
24
+ * ```
25
+ *
26
+ * @defaultValue The theme's `text.defaultVariant`, when set
27
+ */
28
+ variant?: VariantName;
29
+ /** When true, applies spacing below the text. */
30
+ bottomSpacing?: boolean;
31
+ }
32
+ export type HtmlTextProps<E extends keyof JSX.IntrinsicElements = "td"> = HtmlTextOwnProps & {
33
+ /**
34
+ * The HTML element to render instead of the default `<td>`.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <HtmlText element="div">Rendered as a div</HtmlText>
39
+ * <HtmlText element="a" href="/link">Rendered as an anchor</HtmlText>
40
+ * ```
41
+ */
42
+ element?: E;
43
+ } & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">;
44
+ /**
45
+ * Themed text component for use inside MJML ending tags or outside of the MJML context.
46
+ */
47
+ export declare function HtmlText<E extends keyof JSX.IntrinsicElements>(props: HtmlTextOwnProps & {
48
+ element: E;
49
+ } & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">): ReactNode;
50
+ export declare function HtmlText(props: HtmlTextOwnProps & Omit<TdHTMLAttributes<HTMLTableCellElement>, keyof HtmlTextOwnProps>): ReactNode;
51
+ export {};
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import clsx from "clsx";
3
+ import { registerStyles } from "../../styles/registerStyles.js";
4
+ import { getDefaultOrUndefined } from "../../theme/responsiveValue.js";
5
+ import { useTheme } from "../../theme/ThemeProvider.js";
6
+ import { OutlookTextStyleProvider } from "./OutlookTextStyleContext.js";
7
+ import { generateResponsiveTextCss } from "./textStyles.js";
8
+ export function HtmlText({ element: Element = "td", variant: variantProp, bottomSpacing, className, style, children, ...restProps }) {
9
+ const theme = useTheme();
10
+ const { defaultVariant, variants, ...baseStyles } = theme.text;
11
+ const activeVariant = variantProp ?? defaultVariant;
12
+ const variantStyles = activeVariant ? variants?.[activeVariant] : undefined;
13
+ const mergedStyles = variantStyles ? { ...baseStyles, ...variantStyles } : baseStyles;
14
+ const themeStyle = {
15
+ fontFamily: getDefaultOrUndefined(mergedStyles.fontFamily),
16
+ fontSize: getDefaultOrUndefined(mergedStyles.fontSize),
17
+ fontWeight: getDefaultOrUndefined(mergedStyles.fontWeight),
18
+ fontStyle: getDefaultOrUndefined(mergedStyles.fontStyle),
19
+ lineHeight: getDefaultOrUndefined(mergedStyles.lineHeight),
20
+ letterSpacing: getDefaultOrUndefined(mergedStyles.letterSpacing),
21
+ textDecoration: getDefaultOrUndefined(mergedStyles.textDecoration),
22
+ textTransform: getDefaultOrUndefined(mergedStyles.textTransform),
23
+ color: getDefaultOrUndefined(mergedStyles.color),
24
+ ...(getDefaultOrUndefined(mergedStyles.lineHeight) !== undefined && { msoLineHeightRule: "exactly" }),
25
+ ...(bottomSpacing && { paddingBottom: getDefaultOrUndefined(mergedStyles.bottomSpacing) }),
26
+ };
27
+ const outlookTextStyleValues = {
28
+ fontFamily: themeStyle.fontFamily,
29
+ fontSize: themeStyle.fontSize,
30
+ lineHeight: themeStyle.lineHeight,
31
+ fontWeight: themeStyle.fontWeight,
32
+ color: themeStyle.color,
33
+ ...(style?.fontFamily !== undefined && { fontFamily: style.fontFamily }),
34
+ ...(style?.fontSize !== undefined && { fontSize: style.fontSize }),
35
+ ...(style?.lineHeight !== undefined && { lineHeight: style.lineHeight }),
36
+ ...(style?.fontWeight !== undefined && { fontWeight: style.fontWeight }),
37
+ ...(style?.color !== undefined && { color: style.color }),
38
+ };
39
+ return (_jsx(Element, { ...restProps, className: clsx("htmlText", activeVariant && `htmlText--${activeVariant}`, bottomSpacing && "htmlText--bottomSpacing", className), style: { ...themeStyle, ...style }, children: _jsx(OutlookTextStyleProvider, { value: outlookTextStyleValues, children: children }) }));
40
+ }
41
+ function generateHtmlTextStyles(theme) {
42
+ return generateResponsiveTextCss(theme, {
43
+ styleSelector: (variantName) => `.htmlText--${variantName}`,
44
+ spacingSelector: (variantName) => `.htmlText--bottomSpacing.htmlText--${variantName}`,
45
+ });
46
+ }
47
+ registerStyles(generateHtmlTextStyles);
@@ -0,0 +1,40 @@
1
+ import { type IMjmlTextProps } from "@faire/mjml-react";
2
+ import type { ReactNode } from "react";
3
+ import type { Theme, VariantName } from "../../theme/themeTypes.js";
4
+ export type MjmlTextProps = IMjmlTextProps & {
5
+ /**
6
+ * The component's variant to apply, as defined in the theme.
7
+ *
8
+ * Custom variants should be defined in the theme, through module augmentation.
9
+ *
10
+ * ```ts
11
+ * declare module "@dextinity/mail-react" {
12
+ * interface TextVariants { heading: true; body: true }
13
+ * }
14
+ * ```
15
+ *
16
+ * ```ts
17
+ * const theme = createTheme({
18
+ * text: {
19
+ * variants: {
20
+ * heading: { fontSize: "24px" },
21
+ * body: { fontSize: "16px" },
22
+ * },
23
+ * },
24
+ * });
25
+ * ```
26
+ *
27
+ * @defaultValue The theme's `text.defaultVariant`, when set
28
+ */
29
+ variant?: VariantName;
30
+ /** When true, applies spacing below the text. */
31
+ bottomSpacing?: boolean;
32
+ };
33
+ /**
34
+ * Text component that can be styled using the theme, optionally using a variant.
35
+ *
36
+ * Works without a `ThemeProvider` as a plain pass-through to the base MJML text component.
37
+ * The `variant` and `bottomSpacing` props require a `ThemeProvider` (or `MjmlMailRoot`).
38
+ */
39
+ export declare function MjmlText({ variant: variantProp, bottomSpacing, className, children, ...restProps }: MjmlTextProps): ReactNode;
40
+ export declare function generateTextStyles(theme: Theme): string;
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MjmlText as BaseMjmlText } from "@faire/mjml-react";
3
+ import clsx from "clsx";
4
+ import { registerStyles } from "../../styles/registerStyles.js";
5
+ import { getDefaultOrUndefined } from "../../theme/responsiveValue.js";
6
+ import { useOptionalTheme } from "../../theme/ThemeProvider.js";
7
+ import { OutlookTextStyleProvider } from "./OutlookTextStyleContext.js";
8
+ import { generateResponsiveTextCss } from "./textStyles.js";
9
+ /**
10
+ * Text component that can be styled using the theme, optionally using a variant.
11
+ *
12
+ * Works without a `ThemeProvider` as a plain pass-through to the base MJML text component.
13
+ * The `variant` and `bottomSpacing` props require a `ThemeProvider` (or `MjmlMailRoot`).
14
+ */
15
+ export function MjmlText({ variant: variantProp, bottomSpacing, className, children, ...restProps }) {
16
+ const theme = useOptionalTheme();
17
+ const themedProps = getThemedProps(theme, variantProp, bottomSpacing, restProps);
18
+ const resolvedClassName = clsx("mjmlText", themedProps.activeVariant && `mjmlText--${themedProps.activeVariant}`, bottomSpacing && "mjmlText--bottomSpacing", className);
19
+ return (_jsx(BaseMjmlText, { ...themedProps.baseProps, className: resolvedClassName, ...restProps, children: themedProps.outlookTextStyleValues !== null ? (_jsx(OutlookTextStyleProvider, { value: themedProps.outlookTextStyleValues, children: children })) : (children) }));
20
+ }
21
+ function getThemedProps(theme, variantProp, bottomSpacing, explicitProps) {
22
+ if (theme === null) {
23
+ if (variantProp !== undefined) {
24
+ throw new Error("The `variant` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
25
+ }
26
+ if (bottomSpacing) {
27
+ throw new Error("The `bottomSpacing` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
28
+ }
29
+ return { activeVariant: undefined, baseProps: {}, outlookTextStyleValues: null };
30
+ }
31
+ const { defaultVariant, variants, ...baseStyles } = theme.text;
32
+ const activeVariant = variantProp ?? defaultVariant;
33
+ const variantStyles = activeVariant ? variants?.[activeVariant] : undefined;
34
+ const mergedStyles = variantStyles ? { ...baseStyles, ...variantStyles } : baseStyles;
35
+ const fontWeightDefault = getDefaultOrUndefined(mergedStyles.fontWeight);
36
+ return {
37
+ activeVariant,
38
+ baseProps: {
39
+ fontFamily: getDefaultOrUndefined(mergedStyles.fontFamily),
40
+ fontSize: getDefaultOrUndefined(mergedStyles.fontSize),
41
+ fontWeight: fontWeightDefault !== undefined ? String(fontWeightDefault) : undefined,
42
+ fontStyle: getDefaultOrUndefined(mergedStyles.fontStyle),
43
+ lineHeight: getDefaultOrUndefined(mergedStyles.lineHeight),
44
+ letterSpacing: getDefaultOrUndefined(mergedStyles.letterSpacing),
45
+ textDecoration: getDefaultOrUndefined(mergedStyles.textDecoration),
46
+ textTransform: getDefaultOrUndefined(mergedStyles.textTransform),
47
+ color: getDefaultOrUndefined(mergedStyles.color),
48
+ paddingBottom: bottomSpacing ? getDefaultOrUndefined(mergedStyles.bottomSpacing) : undefined,
49
+ },
50
+ outlookTextStyleValues: {
51
+ fontFamily: explicitProps.fontFamily ?? getDefaultOrUndefined(mergedStyles.fontFamily),
52
+ fontSize: explicitProps.fontSize ?? getDefaultOrUndefined(mergedStyles.fontSize),
53
+ fontWeight: explicitProps.fontWeight ?? fontWeightDefault,
54
+ lineHeight: explicitProps.lineHeight ?? getDefaultOrUndefined(mergedStyles.lineHeight),
55
+ color: explicitProps.color ?? getDefaultOrUndefined(mergedStyles.color),
56
+ },
57
+ };
58
+ }
59
+ export function generateTextStyles(theme) {
60
+ return generateResponsiveTextCss(theme, {
61
+ styleSelector: (variantName) => `.mjmlText--${variantName} > div`,
62
+ spacingSelector: (variantName) => `.mjmlText--bottomSpacing.mjmlText--${variantName}`,
63
+ });
64
+ }
65
+ registerStyles(generateTextStyles);
@@ -0,0 +1,9 @@
1
+ import { type CSSProperties, type ReactNode } from "react";
2
+ type OutlookTextStyleValues = Pick<CSSProperties, "fontFamily" | "fontSize" | "lineHeight" | "fontWeight" | "color">;
3
+ declare function OutlookTextStyleProvider({ value, children }: {
4
+ value: OutlookTextStyleValues;
5
+ children: ReactNode;
6
+ }): ReactNode;
7
+ declare function useOutlookTextStyle(): OutlookTextStyleValues | null;
8
+ export { OutlookTextStyleProvider, useOutlookTextStyle };
9
+ export type { OutlookTextStyleValues };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const OutlookTextStyleContext = createContext(null);
4
+ function OutlookTextStyleProvider({ value, children }) {
5
+ return _jsx(OutlookTextStyleContext, { value: value, children: children });
6
+ }
7
+ function useOutlookTextStyle() {
8
+ return useContext(OutlookTextStyleContext);
9
+ }
10
+ export { OutlookTextStyleProvider, useOutlookTextStyle };