@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,10 @@
1
+ import type { Theme } from "../../theme/themeTypes.js";
2
+ interface GenerateResponsiveTextCssOptions {
3
+ /** Selector for text style overrides, given a variant name. */
4
+ styleSelector: (variantName: string) => string;
5
+ /** Selector for bottomSpacing overrides, given a variant name. */
6
+ spacingSelector: (variantName: string) => string;
7
+ }
8
+ /** Generates responsive CSS media queries for text variant overrides. */
9
+ export declare function generateResponsiveTextCss(theme: Theme, options: GenerateResponsiveTextCssOptions): string;
10
+ export {};
@@ -0,0 +1,24 @@
1
+ import { generateResponsiveVariantCss } from "../../styles/generateResponsiveVariantCss.js";
2
+ const textStyleProperties = [
3
+ "fontFamily",
4
+ "fontSize",
5
+ "fontWeight",
6
+ "fontStyle",
7
+ "lineHeight",
8
+ "letterSpacing",
9
+ "textDecoration",
10
+ "textTransform",
11
+ "color",
12
+ ];
13
+ const spacingProperties = [{ themeKey: "bottomSpacing", cssProperties: "padding-bottom" }];
14
+ /** Generates responsive CSS media queries for text variant overrides. */
15
+ export function generateResponsiveTextCss(theme, options) {
16
+ return generateResponsiveVariantCss({
17
+ breakpoints: theme.breakpoints,
18
+ variants: theme.text.variants,
19
+ groups: [
20
+ { selector: options.styleSelector, properties: textStyleProperties },
21
+ { selector: options.spacingSelector, properties: spacingProperties },
22
+ ],
23
+ });
24
+ }
@@ -0,0 +1,3 @@
1
+ export declare const InsideMjmlWrapperContext: import("react").Context<boolean>;
2
+ /** Returns `true` when rendered inside a custom `MjmlWrapper` subtree. Internal use only. */
3
+ export declare function useIsInsideMjmlWrapper(): boolean;
@@ -0,0 +1,6 @@
1
+ import { createContext, useContext } from "react";
2
+ export const InsideMjmlWrapperContext = createContext(false);
3
+ /** Returns `true` when rendered inside a custom `MjmlWrapper` subtree. Internal use only. */
4
+ export function useIsInsideMjmlWrapper() {
5
+ return useContext(InsideMjmlWrapperContext);
6
+ }
@@ -0,0 +1,14 @@
1
+ import { type IMjmlWrapperProps } from "@faire/mjml-react";
2
+ import type { ReactNode } from "react";
3
+ export type MjmlWrapperProps = Omit<IMjmlWrapperProps, "backgroundColor"> & {
4
+ /**
5
+ * Background color of the wrapper.
6
+ *
7
+ * @defaultValue The theme's `colors.background.content`
8
+ */
9
+ backgroundColor?: IMjmlWrapperProps["backgroundColor"];
10
+ };
11
+ /**
12
+ * A wrapper that groups multiple sections sharing a background. Must be a direct child of `MjmlBody`.
13
+ */
14
+ export declare function MjmlWrapper({ children, ...restProps }: MjmlWrapperProps): ReactNode;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MjmlWrapper as BaseMjmlWrapper } from "@faire/mjml-react";
3
+ import { useOptionalTheme } from "../../theme/ThemeProvider.js";
4
+ import { InsideMjmlWrapperContext } from "./InsideMjmlWrapperContext.js";
5
+ /**
6
+ * A wrapper that groups multiple sections sharing a background. Must be a direct child of `MjmlBody`.
7
+ */
8
+ export function MjmlWrapper({ children, ...restProps }) {
9
+ const theme = useOptionalTheme();
10
+ const themeBackgroundProps = theme ? { backgroundColor: theme.colors.background.content } : {};
11
+ return (_jsx(BaseMjmlWrapper, { ...themeBackgroundProps, ...restProps, children: _jsx(InsideMjmlWrapperContext.Provider, { value: true, children: children }) }));
12
+ }
@@ -0,0 +1,43 @@
1
+ import { type PropsWithChildren, type ReactNode } from "react";
2
+ export interface PixelImageBlockConfig {
3
+ /**
4
+ * Image widths supported by the API, used to pick a render width.
5
+ * Generally derived from `cometConfig.images.imageSizes` and `cometConfig.images.deviceSizes`.
6
+ */
7
+ validSizes: number[];
8
+ /**
9
+ * Origin to prefix relative image URLs with, e.g. `http://localhost:3000`.
10
+ */
11
+ baseUrl: string;
12
+ }
13
+ /**
14
+ * Configuration context for mails.
15
+ *
16
+ * Add custom keys via TypeScript interface declaration merging:
17
+ *
18
+ * ```ts
19
+ * declare module "@dextinity/mail-react" {
20
+ * interface Config {
21
+ * myKey?: { foo: string };
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ export interface Config {
27
+ /**
28
+ * Configuration consumed by `HtmlPixelImageBlock` and `MjmlPixelImageBlock`.
29
+ * Required when those components are rendered.
30
+ */
31
+ pixelImageBlock?: PixelImageBlockConfig;
32
+ }
33
+ /**
34
+ * Places a `Config` value into a React context, making it available to all
35
+ * descendants via `useConfig`.
36
+ */
37
+ export declare function ConfigProvider({ config, children }: PropsWithChildren<{
38
+ config: Config;
39
+ }>): ReactNode;
40
+ /**
41
+ * Returns the nearest `Config` from context, defined by `ConfigProvider` or by the `config` prop on `MjmlMailRoot`.
42
+ */
43
+ export declare function useConfig(): Config;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const ConfigContext = createContext({});
4
+ /**
5
+ * Places a `Config` value into a React context, making it available to all
6
+ * descendants via `useConfig`.
7
+ */
8
+ export function ConfigProvider({ config, children }) {
9
+ return _jsx(ConfigContext.Provider, { value: config, children: children });
10
+ }
11
+ /**
12
+ * Returns the nearest `Config` from context, defined by `ConfigProvider` or by the `config` prop on `MjmlMailRoot`.
13
+ */
14
+ export function useConfig() {
15
+ return useContext(ConfigContext);
16
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ export { BlocksBlock } from "./blocks/factories/BlocksBlock.js";
2
+ export { ListBlock } from "./blocks/factories/ListBlock.js";
3
+ export { OneOfBlock } from "./blocks/factories/OneOfBlock.js";
4
+ export { OptionalBlock } from "./blocks/factories/OptionalBlock.js";
5
+ export type { SupportedBlocks } from "./blocks/factories/types.js";
6
+ export type { PropsWithData } from "./blocks/helpers/PropsWithData.js";
7
+ export type { HtmlPixelImageBlockProps } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
8
+ export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
9
+ export type { MjmlPixelImageBlockProps } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
10
+ export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
11
+ export type { CreateRichTextBlockOptions, RichTextBlockProps } from "./blocks/richText/common.js";
12
+ export { createRichTextBlock } from "./blocks/richText/createRichTextBlock.js";
13
+ export type { HtmlButtonProps } from "./components/button/HtmlButton.js";
14
+ export { HtmlButton } from "./components/button/HtmlButton.js";
15
+ export type { MjmlButtonProps } from "./components/button/MjmlButton.js";
16
+ export { MjmlButton } from "./components/button/MjmlButton.js";
17
+ export type { HtmlDividerProps } from "./components/divider/HtmlDivider.js";
18
+ export { HtmlDivider } from "./components/divider/HtmlDivider.js";
19
+ export type { MjmlDividerProps } from "./components/divider/MjmlDivider.js";
20
+ export { MjmlDivider } from "./components/divider/MjmlDivider.js";
21
+ export type { HtmlImageProps } from "./components/image/HtmlImage.js";
22
+ export { HtmlImage } from "./components/image/HtmlImage.js";
23
+ export type { MjmlImageProps } from "./components/image/MjmlImage.js";
24
+ export { MjmlImage } from "./components/image/MjmlImage.js";
25
+ export type { HtmlInlineLinkProps } from "./components/inlineLink/HtmlInlineLink.js";
26
+ export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
27
+ export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
28
+ export type { MjmlSectionProps } from "./components/section/MjmlSection.js";
29
+ export { MjmlSection } from "./components/section/MjmlSection.js";
30
+ export type { HtmlTextProps } from "./components/text/HtmlText.js";
31
+ export { HtmlText } from "./components/text/HtmlText.js";
32
+ export type { MjmlTextProps } from "./components/text/MjmlText.js";
33
+ export { MjmlText } from "./components/text/MjmlText.js";
34
+ export type { MjmlWrapperProps } from "./components/wrapper/MjmlWrapper.js";
35
+ export { MjmlWrapper } from "./components/wrapper/MjmlWrapper.js";
36
+ export { type Config, ConfigProvider, type PixelImageBlockConfig, useConfig } from "./config/ConfigProvider.js";
37
+ export { registerStyles } from "./styles/registerStyles.js";
38
+ export { createBreakpoint } from "./theme/createBreakpoint.js";
39
+ export { createTheme } from "./theme/createTheme.js";
40
+ export type { ResponsiveValue } from "./theme/responsiveValue.js";
41
+ export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
42
+ export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
43
+ export type { ButtonStyles, ButtonVariants, ButtonVariantStyles, DividerStyles, DividerVariants, DividerVariantStyles, TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
44
+ export { css } from "./utils/css.js";
45
+ export { Mjml, MjmlAccordion, MjmlAccordionElement, type IMjmlAccordionElementProps as MjmlAccordionElementProps, type IMjmlAccordionProps as MjmlAccordionProps, MjmlAccordionText, type IMjmlAccordionTextProps as MjmlAccordionTextProps, MjmlAccordionTitle, type IMjmlAccordionTitleProps as MjmlAccordionTitleProps, MjmlAll, type IMjmlAllProps as MjmlAllProps, MjmlAttributes, type IMjmlAttributesProps as MjmlAttributesProps, MjmlBody, type IMjmlBodyProps as MjmlBodyProps, MjmlBreakpoint, type IMjmlBreakpointProps as MjmlBreakpointProps, MjmlCarousel, MjmlCarouselImage, type IMjmlCarouselImageProps as MjmlCarouselImageProps, type IMjmlCarouselProps as MjmlCarouselProps, MjmlClass, type IMjmlClassProps as MjmlClassProps, MjmlColumn, type IMjmlColumnProps as MjmlColumnProps, MjmlFont, type IMjmlFontProps as MjmlFontProps, MjmlGroup, type IMjmlGroupProps as MjmlGroupProps, MjmlHead, type IMjmlHeadProps as MjmlHeadProps, MjmlHero, type IMjmlHeroProps as MjmlHeroProps, MjmlHtmlAttribute, type IMjmlHtmlAttributeProps as MjmlHtmlAttributeProps, MjmlHtmlAttributes, type IMjmlHtmlAttributesProps as MjmlHtmlAttributesProps, MjmlInclude, type IMjmlIncludeProps as MjmlIncludeProps, MjmlNavbar, MjmlNavbarLink, type IMjmlNavbarLinkProps as MjmlNavbarLinkProps, type IMjmlNavbarProps as MjmlNavbarProps, MjmlPreview, type IMjmlPreviewProps as MjmlPreviewProps, type IMjmlProps as MjmlProps, MjmlRaw, type IMjmlRawProps as MjmlRawProps, MjmlSelector, type IMjmlSelectorProps as MjmlSelectorProps, MjmlSocial, MjmlSocialElement, type IMjmlSocialElementProps as MjmlSocialElementProps, type IMjmlSocialProps as MjmlSocialProps, MjmlSpacer, type IMjmlSpacerProps as MjmlSpacerProps, MjmlStyle, type IMjmlStyleProps as MjmlStyleProps, MjmlTable, type IMjmlTableProps as MjmlTableProps, MjmlTitle, type IMjmlTitleProps as MjmlTitleProps, } from "@faire/mjml-react";
46
+ export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
47
+ export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
package/lib/index.js ADDED
@@ -0,0 +1,29 @@
1
+ export { BlocksBlock } from "./blocks/factories/BlocksBlock.js";
2
+ export { ListBlock } from "./blocks/factories/ListBlock.js";
3
+ export { OneOfBlock } from "./blocks/factories/OneOfBlock.js";
4
+ export { OptionalBlock } from "./blocks/factories/OptionalBlock.js";
5
+ export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
6
+ export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
7
+ export { createRichTextBlock } from "./blocks/richText/createRichTextBlock.js";
8
+ export { HtmlButton } from "./components/button/HtmlButton.js";
9
+ export { MjmlButton } from "./components/button/MjmlButton.js";
10
+ export { HtmlDivider } from "./components/divider/HtmlDivider.js";
11
+ export { MjmlDivider } from "./components/divider/MjmlDivider.js";
12
+ export { HtmlImage } from "./components/image/HtmlImage.js";
13
+ export { MjmlImage } from "./components/image/MjmlImage.js";
14
+ export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
15
+ export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
16
+ export { MjmlSection } from "./components/section/MjmlSection.js";
17
+ export { HtmlText } from "./components/text/HtmlText.js";
18
+ export { MjmlText } from "./components/text/MjmlText.js";
19
+ export { MjmlWrapper } from "./components/wrapper/MjmlWrapper.js";
20
+ export { ConfigProvider, useConfig } from "./config/ConfigProvider.js";
21
+ export { registerStyles } from "./styles/registerStyles.js";
22
+ export { createBreakpoint } from "./theme/createBreakpoint.js";
23
+ export { createTheme } from "./theme/createTheme.js";
24
+ export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
25
+ export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
26
+ export { css } from "./utils/css.js";
27
+ export { Mjml, MjmlAccordion, MjmlAccordionElement, MjmlAccordionText, MjmlAccordionTitle, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlCarousel, MjmlCarouselImage, MjmlClass, MjmlColumn, MjmlFont, MjmlGroup, MjmlHead, MjmlHero, MjmlHtmlAttribute, MjmlHtmlAttributes, MjmlInclude, MjmlNavbar, MjmlNavbarLink, MjmlPreview, MjmlRaw, MjmlSelector, MjmlSocial, MjmlSocialElement, MjmlSpacer, MjmlStyle, MjmlTable, MjmlTitle, } from "@faire/mjml-react";
28
+ export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
29
+ export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
@@ -0,0 +1 @@
1
+ export { renderMailHtml } from "./renderMailHtml.js";
@@ -0,0 +1 @@
1
+ export { renderMailHtml } from "./renderMailHtml.js";
@@ -0,0 +1,9 @@
1
+ import mjml2html from "mjml";
2
+ import type { ReactElement } from "react";
3
+ type MjmlOptions = Parameters<typeof mjml2html>[1];
4
+ type MjmlWarning = ReturnType<typeof mjml2html>["errors"][number];
5
+ export declare function renderMailHtml(element: ReactElement, options?: MjmlOptions): {
6
+ html: string;
7
+ mjmlWarnings: MjmlWarning[];
8
+ };
9
+ export {};
@@ -0,0 +1,7 @@
1
+ import { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
2
+ import mjml2html from "mjml";
3
+ export function renderMailHtml(element, options) {
4
+ const mjmlString = renderToMjml(element);
5
+ const { html, errors: mjmlWarnings } = mjml2html(mjmlString, { validationLevel: "soft", ...options });
6
+ return { html, mjmlWarnings };
7
+ }
@@ -0,0 +1,4 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React from "react";
4
+ export declare function CopyMailHtmlButton(): React.JSX.Element;
@@ -0,0 +1,33 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React, { useCallback, useEffect, useRef, useState } from "react";
4
+ import { Button } from "storybook/internal/components";
5
+ import { useChannel } from "storybook/manager-api";
6
+ const RENDER_RESULT_EVENT = "comet-mail-render-result";
7
+ export function CopyMailHtmlButton() {
8
+ const htmlRef = useRef("");
9
+ const [copied, setCopied] = useState(false);
10
+ const copiedTimeoutRef = useRef(undefined);
11
+ useChannel({
12
+ [RENDER_RESULT_EVENT]: ({ html }) => {
13
+ htmlRef.current = html;
14
+ setCopied(false);
15
+ },
16
+ });
17
+ useEffect(() => {
18
+ return () => {
19
+ if (copiedTimeoutRef.current) {
20
+ clearTimeout(copiedTimeoutRef.current);
21
+ }
22
+ };
23
+ }, []);
24
+ const handleClick = useCallback(async () => {
25
+ await navigator.clipboard.writeText(htmlRef.current);
26
+ setCopied(true);
27
+ if (copiedTimeoutRef.current) {
28
+ clearTimeout(copiedTimeoutRef.current);
29
+ }
30
+ copiedTimeoutRef.current = setTimeout(() => setCopied(false), 2000);
31
+ }, []);
32
+ return (React.createElement(Button, { size: "small", onClick: handleClick }, copied ? "Copied to clipboard!" : "Copy Mail HTML"));
33
+ }
@@ -0,0 +1,7 @@
1
+ import type { Theme } from "../theme/themeTypes.js";
2
+ export declare function MailRendererDecorator(Story: () => React.JSX.Element, context: {
3
+ parameters: {
4
+ mailRoot?: boolean;
5
+ theme?: Theme;
6
+ };
7
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useChannel, useGlobals } from "storybook/preview-api";
3
+ import { renderMailHtml } from "../client/renderMailHtml.js";
4
+ import { MjmlMailRoot } from "../components/mailRoot/MjmlMailRoot.js";
5
+ import { replaceImagesWithPublicUrl } from "./replaceImagesWithPublicUrl.js";
6
+ const RENDER_RESULT_EVENT = "comet-mail-render-result";
7
+ export function MailRendererDecorator(Story, context) {
8
+ const [globals] = useGlobals();
9
+ const emit = useChannel({});
10
+ const { html: rawHtml, mjmlWarnings } = renderMailHtml(context.parameters.mailRoot === false ? (_jsx(Story, {})) : (_jsx(MjmlMailRoot, { theme: context.parameters.theme, children: _jsx(Story, {}) })));
11
+ for (const warning of mjmlWarnings) {
12
+ console.warn("MJML warning:", warning);
13
+ }
14
+ const html = globals.usePublicImageUrls ? replaceImagesWithPublicUrl(rawHtml) : rawHtml;
15
+ emit(RENDER_RESULT_EVENT, { html, mjmlWarnings });
16
+ return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
17
+ }
@@ -0,0 +1,7 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React from "react";
4
+ export declare function MjmlWarningsPanel({ active }: {
5
+ active: boolean;
6
+ }): React.JSX.Element;
7
+ export declare function MjmlWarningsPanelTitle(): React.JSX.Element;
@@ -0,0 +1,33 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React, { useState } from "react";
4
+ import { AddonPanel, Badge } from "storybook/internal/components";
5
+ import { useChannel } from "storybook/manager-api";
6
+ const RENDER_RESULT_EVENT = "comet-mail-render-result";
7
+ export function MjmlWarningsPanel({ active }) {
8
+ const [renderResult, setRenderResult] = useState(null);
9
+ useChannel({
10
+ [RENDER_RESULT_EVENT]: (result) => {
11
+ setRenderResult(result);
12
+ },
13
+ });
14
+ return (React.createElement(AddonPanel, { active: active }, renderResult && (React.createElement("div", { style: { padding: 16 } }, renderResult.mjmlWarnings.length === 0 ? (React.createElement("p", { style: { color: "green" } }, "\u2713 No MJML warnings")) : (React.createElement("ul", { style: { margin: 0, padding: 0, listStyle: "none" } }, renderResult.mjmlWarnings.map((warning, index) => (React.createElement("li", { key: index, style: { marginBottom: 8 } },
15
+ React.createElement("strong", null, warning.tagName),
16
+ " (line ",
17
+ warning.line,
18
+ "): ",
19
+ warning.message)))))))));
20
+ }
21
+ export function MjmlWarningsPanelTitle() {
22
+ const [renderResult, setRenderResult] = useState(null);
23
+ useChannel({
24
+ [RENDER_RESULT_EVENT]: (result) => {
25
+ setRenderResult(result);
26
+ },
27
+ });
28
+ const warningCount = renderResult?.mjmlWarnings.length ?? 0;
29
+ const hasWarnings = warningCount > 0;
30
+ return (React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6 } },
31
+ React.createElement("span", null, "MJML Warnings"),
32
+ React.createElement(Badge, { compact: true, status: hasWarnings ? "negative" : "positive" }, hasWarnings ? warningCount : "✓")));
33
+ }
@@ -0,0 +1,4 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React from "react";
4
+ export declare function UsePublicImageUrlsToggle(): React.JSX.Element;
@@ -0,0 +1,14 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React from "react";
4
+ import { IconButton, TooltipNote, WithTooltip } from "storybook/internal/components";
5
+ import { useGlobals } from "storybook/manager-api";
6
+ export function UsePublicImageUrlsToggle() {
7
+ const [globals, updateGlobals] = useGlobals();
8
+ const isActive = Boolean(globals.usePublicImageUrls);
9
+ return (React.createElement(WithTooltip, { tooltip: React.createElement(TooltipNote, { note: "Helpful to test with real images on external devices that cannot access localhost, e.g. Email on Acid." }), trigger: "hover" },
10
+ React.createElement(IconButton, { size: "small", active: isActive, onClick: () => updateGlobals({ usePublicImageUrls: !isActive }) },
11
+ React.createElement("input", { type: "checkbox", checked: isActive, readOnly: true, style: { pointerEvents: "none", marginLeft: 4 } }),
12
+ "Use public image URLs",
13
+ React.createElement("span", { role: "img", "aria-label": "info" }, "\u2139\uFE0F"))));
14
+ }
@@ -0,0 +1,2 @@
1
+ export declare function managerEntries(existing?: string[]): string[];
2
+ export declare function previewAnnotations(input?: string[]): string[];
@@ -0,0 +1,9 @@
1
+ import { dirname, resolve } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const directoryOfThisFile = dirname(fileURLToPath(import.meta.url));
4
+ export function managerEntries(existing = []) {
5
+ return [...existing, resolve(directoryOfThisFile, "manager.js")];
6
+ }
7
+ export function previewAnnotations(input = []) {
8
+ return [...input, resolve(directoryOfThisFile, "preview.js")];
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx React.createElement */
3
+ import React from "react";
4
+ import { addons, types } from "storybook/manager-api";
5
+ import { CopyMailHtmlButton } from "./CopyMailHtmlButton.js";
6
+ import { MjmlWarningsPanel, MjmlWarningsPanelTitle } from "./MjmlWarningsPanel.js";
7
+ import { UsePublicImageUrlsToggle } from "./UsePublicImageUrlsToggle.js";
8
+ const ADDON_ID = "comet-mail-react";
9
+ addons.register(ADDON_ID, () => {
10
+ addons.add(`${ADDON_ID}/copy-html`, {
11
+ type: types.TOOL,
12
+ title: "Copy Mail HTML",
13
+ render: () => React.createElement(CopyMailHtmlButton, null),
14
+ });
15
+ addons.add(`${ADDON_ID}/public-urls`, {
16
+ type: types.TOOL,
17
+ title: "Use public image URLs",
18
+ render: () => React.createElement(UsePublicImageUrlsToggle, null),
19
+ });
20
+ addons.add(`${ADDON_ID}/mjml-warnings`, {
21
+ type: types.PANEL,
22
+ title: () => React.createElement(MjmlWarningsPanelTitle, null),
23
+ render: ({ active }) => React.createElement(MjmlWarningsPanel, { active: Boolean(active) }),
24
+ });
25
+ });
@@ -0,0 +1,39 @@
1
+ import { MailRendererDecorator } from "./MailRendererDecorator.js";
2
+ export declare const decorators: (typeof MailRendererDecorator)[];
3
+ export declare const initialGlobals: {
4
+ usePublicImageUrls: boolean;
5
+ };
6
+ export declare const parameters: {
7
+ viewport: {
8
+ options: {
9
+ mobile: {
10
+ name: string;
11
+ styles: {
12
+ width: string;
13
+ height: string;
14
+ };
15
+ };
16
+ tablet: {
17
+ name: string;
18
+ styles: {
19
+ width: string;
20
+ height: string;
21
+ };
22
+ };
23
+ emailWidth: {
24
+ name: string;
25
+ styles: {
26
+ width: string;
27
+ height: string;
28
+ };
29
+ };
30
+ desktop: {
31
+ name: string;
32
+ styles: {
33
+ width: string;
34
+ height: string;
35
+ };
36
+ };
37
+ };
38
+ };
39
+ };
@@ -0,0 +1,27 @@
1
+ import { MailRendererDecorator } from "./MailRendererDecorator.js";
2
+ export const decorators = [MailRendererDecorator];
3
+ export const initialGlobals = {
4
+ usePublicImageUrls: false,
5
+ };
6
+ export const parameters = {
7
+ viewport: {
8
+ options: {
9
+ mobile: {
10
+ name: "Mobile (375px)",
11
+ styles: { width: "375px", height: "1024px" },
12
+ },
13
+ tablet: {
14
+ name: "Tablet (500px)",
15
+ styles: { width: "500px", height: "1024px" },
16
+ },
17
+ emailWidth: {
18
+ name: "Email max-width (600px)",
19
+ styles: { width: "600px", height: "1024px" },
20
+ },
21
+ desktop: {
22
+ name: "Desktop (768px)",
23
+ styles: { width: "768px", height: "1024px" },
24
+ },
25
+ },
26
+ },
27
+ };
@@ -0,0 +1 @@
1
+ export declare function replaceImagesWithPublicUrl(html: string): string;
@@ -0,0 +1,15 @@
1
+ export function replaceImagesWithPublicUrl(html) {
2
+ let seedCounter = 0;
3
+ return html.replace(/<img\b[^>]*>/gi, (imgTag) => {
4
+ const widthMatch = imgTag.match(/\bwidth="(\d+)"/);
5
+ const heightMatch = imgTag.match(/\bheight="(\d+)"/);
6
+ if (!widthMatch || !heightMatch) {
7
+ return imgTag;
8
+ }
9
+ const retinaWidth = parseInt(widthMatch[1], 10) * 2;
10
+ const retinaHeight = parseInt(heightMatch[1], 10) * 2;
11
+ const seed = seedCounter++;
12
+ const publicUrl = `https://picsum.photos/seed/${seed}/${retinaWidth}/${retinaHeight}`;
13
+ return imgTag.replace(/\bsrc="[^"]*"/, `src="${publicUrl}"`);
14
+ });
15
+ }
@@ -0,0 +1,6 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Internal component that iterates the styles registry and renders one
4
+ * `<MjmlStyle>` element per entry.
5
+ */
6
+ export declare function Styles(): ReactNode;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { MjmlStyle } from "@faire/mjml-react";
3
+ import { useTheme } from "../theme/ThemeProvider.js";
4
+ import { getRegisteredStyles } from "./registerStyles.js";
5
+ /**
6
+ * Internal component that iterates the styles registry and renders one
7
+ * `<MjmlStyle>` element per entry.
8
+ */
9
+ export function Styles() {
10
+ const theme = useTheme();
11
+ const entries = Array.from(getRegisteredStyles().values());
12
+ return (_jsx(_Fragment, { children: entries.map((entry, index) => {
13
+ const cssString = typeof entry.styles === "function" ? entry.styles(theme) : entry.styles;
14
+ return (_jsx(MjmlStyle, { ...entry.mjmlStyleProps, children: cssString }, index));
15
+ }) }));
16
+ }
@@ -0,0 +1,26 @@
1
+ import { type ResponsiveValue } from "../theme/responsiveValue.js";
2
+ import type { ThemeBreakpoints } from "../theme/themeTypes.js";
3
+ type VariantStylesConstraint = Record<string, ResponsiveValue<string | number> | undefined>;
4
+ export interface ResponsiveVariantProperty<TVariantStyles> {
5
+ themeKey: keyof TVariantStyles;
6
+ /** CSS property name(s); defaults to the kebab-case form of `themeKey`. */
7
+ cssProperties?: string | readonly string[];
8
+ unit?: string;
9
+ }
10
+ /** A variant property is either a bare theme key (CSS name derived) or a descriptor for the exceptions. */
11
+ export type ResponsiveVariantProperties<TVariantStyles> = ReadonlyArray<keyof TVariantStyles | ResponsiveVariantProperty<TVariantStyles>>;
12
+ interface ResponsiveVariantGroup<TVariantStyles> {
13
+ selector: (variantName: string) => string;
14
+ properties: ResponsiveVariantProperties<TVariantStyles>;
15
+ }
16
+ interface GenerateResponsiveVariantCssOptions<TVariantStyles> {
17
+ breakpoints: ThemeBreakpoints;
18
+ variants: Record<string, TVariantStyles | undefined> | undefined;
19
+ groups: ReadonlyArray<ResponsiveVariantGroup<TVariantStyles>>;
20
+ }
21
+ /**
22
+ * Generates `max-width` media-query CSS for a themed component's variant overrides.
23
+ * Returns an empty string when no variant has breakpoint overrides.
24
+ */
25
+ export declare function generateResponsiveVariantCss<TVariantStyles extends VariantStylesConstraint>({ breakpoints, variants, groups, }: GenerateResponsiveVariantCssOptions<TVariantStyles>): string;
26
+ export {};
@@ -0,0 +1,66 @@
1
+ import { getResponsiveOverrides } from "../theme/responsiveValue.js";
2
+ import { css } from "../utils/css.js";
3
+ const toKebabCase = (value) => value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
4
+ /**
5
+ * Generates `max-width` media-query CSS for a themed component's variant overrides.
6
+ * Returns an empty string when no variant has breakpoint overrides.
7
+ */
8
+ export function generateResponsiveVariantCss({ breakpoints, variants, groups, }) {
9
+ if (!variants) {
10
+ return "";
11
+ }
12
+ const cssChunks = [];
13
+ for (const [variantName, variantStyles] of Object.entries(variants)) {
14
+ if (!variantStyles) {
15
+ continue;
16
+ }
17
+ for (const group of groups) {
18
+ const overridesByBreakpoint = collectOverridesByBreakpoint(variantStyles, group.properties);
19
+ cssChunks.push(renderMediaQueries({ breakpoints, selector: group.selector(variantName), overridesByBreakpoint }));
20
+ }
21
+ }
22
+ return cssChunks.filter(Boolean).join("\n");
23
+ }
24
+ function collectOverridesByBreakpoint(variantStyles, properties) {
25
+ const overridesByBreakpoint = new Map();
26
+ for (const entry of properties) {
27
+ const property = typeof entry === "object" ? entry : { themeKey: entry };
28
+ const value = variantStyles[property.themeKey];
29
+ if (value === undefined) {
30
+ continue;
31
+ }
32
+ const cssProperties = resolveCssProperties(property);
33
+ for (const { breakpointKey, value: breakpointValue } of getResponsiveOverrides(value)) {
34
+ const declarations = overridesByBreakpoint.get(breakpointKey) ?? [];
35
+ for (const cssProperty of cssProperties) {
36
+ declarations.push(formatDeclaration({ cssProperty, value: breakpointValue, unit: property.unit }));
37
+ }
38
+ overridesByBreakpoint.set(breakpointKey, declarations);
39
+ }
40
+ }
41
+ return overridesByBreakpoint;
42
+ }
43
+ function resolveCssProperties(property) {
44
+ const cssProperties = property.cssProperties ?? toKebabCase(String(property.themeKey));
45
+ return typeof cssProperties === "string" ? [cssProperties] : cssProperties;
46
+ }
47
+ function formatDeclaration({ cssProperty, value, unit }) {
48
+ return `${cssProperty}: ${String(value)}${unit ?? ""} !important`;
49
+ }
50
+ function renderMediaQueries({ breakpoints, selector, overridesByBreakpoint, }) {
51
+ const cssChunks = [];
52
+ for (const [breakpointKey, declarations] of overridesByBreakpoint) {
53
+ const breakpoint = breakpoints[breakpointKey];
54
+ if (!breakpoint) {
55
+ continue;
56
+ }
57
+ cssChunks.push(css `
58
+ ${breakpoint.belowMediaQuery} {
59
+ ${selector} {
60
+ ${declarations.join(";\n")}
61
+ }
62
+ }
63
+ `);
64
+ }
65
+ return cssChunks.join("\n");
66
+ }