@comet/mail-react 9.0.0-beta.0 → 9.0.0-beta.2
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/README.md +26 -0
- package/lib/__stories__/examples/CustomNestedFooterTheme.stories.d.ts +5 -0
- package/lib/__stories__/examples/CustomNestedFooterTheme.stories.js +32 -0
- package/lib/__stories__/examples/NotificationEmail.stories.d.ts +5 -0
- package/lib/__stories__/examples/NotificationEmail.stories.js +25 -0
- package/lib/__stories__/examples/TextWithImageEmail.stories.d.ts +5 -0
- package/lib/__stories__/examples/TextWithImageEmail.stories.js +56 -0
- package/lib/client/index.d.ts +1 -0
- package/lib/client/index.js +1 -0
- package/lib/client/renderMailHtml.d.ts +9 -0
- package/lib/client/renderMailHtml.js +7 -0
- package/lib/components/inlineLink/HtmlInlineLink.d.ts +10 -0
- package/lib/components/inlineLink/HtmlInlineLink.js +35 -0
- package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.d.ts +12 -0
- package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.js +56 -0
- package/lib/components/mailRoot/MjmlMailRoot.d.ts +22 -0
- package/lib/components/mailRoot/MjmlMailRoot.js +20 -0
- package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.d.ts +7 -0
- package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.js +18 -0
- package/lib/components/section/MjmlSection.d.ts +15 -0
- package/lib/components/section/MjmlSection.js +44 -0
- package/lib/components/section/__stories__/MjmlSection.stories.d.ts +9 -0
- package/lib/components/section/__stories__/MjmlSection.stories.js +30 -0
- package/lib/components/text/HtmlText.d.ts +48 -0
- package/lib/components/text/HtmlText.js +47 -0
- package/lib/components/text/MjmlText.d.ts +37 -0
- package/lib/components/text/MjmlText.js +65 -0
- package/lib/components/text/OutlookTextStyleContext.d.ts +9 -0
- package/lib/components/text/OutlookTextStyleContext.js +10 -0
- package/lib/components/text/__stories__/HtmlText.stories.d.ts +12 -0
- package/lib/components/text/__stories__/HtmlText.stories.js +77 -0
- package/lib/components/text/__stories__/MjmlText.stories.d.ts +10 -0
- package/lib/components/text/__stories__/MjmlText.stories.js +71 -0
- package/lib/components/text/__tests__/HtmlText.test.d.ts +1 -0
- package/lib/components/text/__tests__/HtmlText.test.js +157 -0
- package/lib/components/text/__tests__/MjmlText.test.d.ts +1 -0
- package/lib/components/text/__tests__/MjmlText.test.js +112 -0
- package/lib/components/text/textStyles.d.ts +12 -0
- package/lib/components/text/textStyles.js +69 -0
- package/lib/index.d.ts +17 -1
- package/lib/index.js +11 -1
- package/lib/server/index.d.ts +1 -0
- package/lib/server/index.js +1 -0
- package/lib/server/renderMailHtml.d.ts +9 -0
- package/lib/server/renderMailHtml.js +7 -0
- package/lib/server/renderMailHtml.test.d.ts +1 -0
- package/lib/server/renderMailHtml.test.js +43 -0
- package/lib/storybook/CopyMailHtmlButton.d.ts +4 -0
- package/lib/storybook/CopyMailHtmlButton.js +33 -0
- package/lib/storybook/MailRendererDecorator.d.ts +7 -0
- package/lib/storybook/MailRendererDecorator.js +17 -0
- package/lib/storybook/MjmlWarningsPanel.d.ts +7 -0
- package/lib/storybook/MjmlWarningsPanel.js +33 -0
- package/lib/storybook/UsePublicImageUrlsToggle.d.ts +4 -0
- package/lib/storybook/UsePublicImageUrlsToggle.js +14 -0
- package/lib/storybook/index.d.ts +2 -0
- package/lib/storybook/index.js +9 -0
- package/lib/storybook/manager.d.ts +1 -0
- package/lib/storybook/manager.js +25 -0
- package/lib/storybook/preview.d.ts +5 -0
- package/lib/storybook/preview.js +5 -0
- package/lib/storybook/replaceImagesWithPublicUrl.d.ts +1 -0
- package/lib/storybook/replaceImagesWithPublicUrl.js +15 -0
- package/lib/storybook/replaceImagesWithPublicUrl.test.d.ts +1 -0
- package/lib/storybook/replaceImagesWithPublicUrl.test.js +55 -0
- package/lib/styles/Styles.d.ts +6 -0
- package/lib/styles/Styles.js +16 -0
- package/lib/styles/registerStyles.d.ts +19 -0
- package/lib/styles/registerStyles.js +14 -0
- package/lib/theme/ThemeProvider.d.ts +8 -0
- package/lib/theme/ThemeProvider.js +17 -0
- package/lib/theme/__stories__/ThemeProvider.stories.d.ts +7 -0
- package/lib/theme/__stories__/ThemeProvider.stories.js +23 -0
- package/lib/theme/createBreakpoint.d.ts +7 -0
- package/lib/theme/createBreakpoint.js +11 -0
- package/lib/theme/createTheme.d.ts +22 -0
- package/lib/theme/createTheme.js +30 -0
- package/lib/theme/createTheme.test.d.ts +1 -0
- package/lib/theme/createTheme.test.js +52 -0
- package/lib/theme/defaultTheme.d.ts +2 -0
- package/lib/theme/defaultTheme.js +23 -0
- package/lib/theme/responsiveValue.d.ts +31 -0
- package/lib/theme/responsiveValue.js +33 -0
- package/lib/theme/responsiveValue.test.d.ts +1 -0
- package/lib/theme/responsiveValue.test.js +62 -0
- package/lib/theme/themeTypes.d.ts +106 -0
- package/lib/theme/themeTypes.js +1 -0
- package/lib/utils/css.test.d.ts +1 -0
- package/lib/utils/css.test.js +26 -0
- package/package.json +35 -11
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @comet/mail-react
|
|
2
|
+
|
|
3
|
+
Utilities for building HTML emails with React and MJML.
|
|
4
|
+
|
|
5
|
+
## Spec-driven development with OpenSpec
|
|
6
|
+
|
|
7
|
+
This package uses [OpenSpec](https://github.com/Fission-AI/OpenSpec) for spec-driven development. Specs and change history live in `openspec/` and are committed to the repo.
|
|
8
|
+
|
|
9
|
+
## Getting started
|
|
10
|
+
|
|
11
|
+
- Run `./install.sh` from the project root to install dependencies and configure OpenSpec agent skills
|
|
12
|
+
- Open the `packages/mail-react/` directory directly in the IDE, as OpenSpec does not currently support monorepos
|
|
13
|
+
|
|
14
|
+
## Suggested development workflow
|
|
15
|
+
|
|
16
|
+
- Use `/opsx:explore` to think through ideas before proposing
|
|
17
|
+
- Use `/opsx:propose` to propose a change, then commit the proposal
|
|
18
|
+
- Use `/opsx:apply` to apply the change, then commit the result
|
|
19
|
+
- Use `/opsx:archive` to archive the change and merge delta specs into the main specs, then commit
|
|
20
|
+
|
|
21
|
+
Minor fixes may skip this workflow if they do not create a gap between `openspec/specs/` and the code (e.g. changing a TSDoc comment).
|
|
22
|
+
|
|
23
|
+
## Suggested review workflow
|
|
24
|
+
|
|
25
|
+
- A pull request should propose, apply, and archive a change in one go
|
|
26
|
+
- Reviewers can then focus on the code changes and resulting spec, skimming the archived implementation details
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlSpacer, MjmlTable } from "@faire/mjml-react";
|
|
3
|
+
import { HtmlInlineLink } from "../../components/inlineLink/HtmlInlineLink.js";
|
|
4
|
+
import { MjmlMailRoot } from "../../components/mailRoot/MjmlMailRoot.js";
|
|
5
|
+
import { MjmlSection } from "../../components/section/MjmlSection.js";
|
|
6
|
+
import { HtmlText } from "../../components/text/HtmlText.js";
|
|
7
|
+
import { MjmlText } from "../../components/text/MjmlText.js";
|
|
8
|
+
import { createTheme } from "../../theme/createTheme.js";
|
|
9
|
+
import { ThemeProvider } from "../../theme/ThemeProvider.js";
|
|
10
|
+
const config = {
|
|
11
|
+
title: "Examples/CustomNestedFooterTheme",
|
|
12
|
+
parameters: { mailRoot: false },
|
|
13
|
+
};
|
|
14
|
+
export default config;
|
|
15
|
+
export const Default = {
|
|
16
|
+
render: () => {
|
|
17
|
+
const theme = createTheme({
|
|
18
|
+
text: {
|
|
19
|
+
defaultVariant: "body",
|
|
20
|
+
variants: {
|
|
21
|
+
heading: { fontSize: "22px", lineHeight: "28px", fontWeight: "bold" },
|
|
22
|
+
body: { fontSize: "14px", lineHeight: "20px" },
|
|
23
|
+
legal: { fontSize: "12px", lineHeight: "18px" },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const footerTheme = structuredClone(theme);
|
|
28
|
+
footerTheme.colors.background.content = "#2d4a6e";
|
|
29
|
+
footerTheme.text.color = "#c8d8e9";
|
|
30
|
+
return (_jsxs(MjmlMailRoot, { theme: theme, children: [_jsx(MjmlSection, { backgroundColor: "#1a1a1a", indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 15 }), _jsx(MjmlText, { color: "#ffffff", fontWeight: "bold", align: "center", children: "Company Name" }), _jsx(MjmlSpacer, { height: 15 })] }) }), _jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 30 }), _jsx(MjmlText, { variant: "heading", bottomSpacing: true, children: "This is a notification email" }), _jsx(MjmlText, { bottomSpacing: true, children: "Minima ea distinctio quisquam. Illo reiciendis non officiis consectetur. Ratione perferendis distinctio sapiente est. Dolor consequatur qui excepturi natus." }), _jsx(MjmlText, { children: "Numquam aut voluptas numquam aspernatur. Consequatur quidem omnis dolorem natus quis soluta. Est recusandae delectus sed sed deserunt velit quia. Occaecati vel possimus similique reiciendis possimus iure rerum sit architecto." }), _jsx(MjmlSpacer, { height: 30 })] }) }), _jsxs(ThemeProvider, { theme: footerTheme, children: [_jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 20 }), _jsx(MjmlText, { align: "center", bottomSpacing: true, children: "\u00A9 2026 Company Name \u2013 All rights reserved" }), _jsx(MjmlText, { variant: "legal", align: "center", bottomSpacing: true, children: "Legal text, corporis eos et quia. Assumenda eum maiores esse. Voluptas laudantium cupiditate aut repudiandae iste fugiat nam. Quas in debitis. Sed laudantium illum aut occaecati excepturi veniam harum reprehenderit." })] }) }), _jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlTable, { width: "auto", align: "center", children: _jsx("tbody", { children: _jsxs("tr", { children: [_jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com/privacy", children: "Privacy Policy" }) }), _jsx("td", { width: "20px" }), _jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com/imprint", children: "Imprint" }) }), _jsx("td", { width: "20px" }), _jsx(HtmlText, { align: "center", children: _jsx(HtmlInlineLink, { href: "https://example.com", children: "Website" }) })] }) }) }), _jsx(MjmlSpacer, { height: 20 })] }) })] })] }));
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlSpacer } from "@faire/mjml-react";
|
|
3
|
+
import { MjmlMailRoot } from "../../components/mailRoot/MjmlMailRoot.js";
|
|
4
|
+
import { MjmlSection } from "../../components/section/MjmlSection.js";
|
|
5
|
+
import { MjmlText } from "../../components/text/MjmlText.js";
|
|
6
|
+
import { createTheme } from "../../theme/createTheme.js";
|
|
7
|
+
const config = {
|
|
8
|
+
title: "Examples/NotificationEmail",
|
|
9
|
+
parameters: { mailRoot: false },
|
|
10
|
+
};
|
|
11
|
+
export default config;
|
|
12
|
+
export const Default = {
|
|
13
|
+
render: () => {
|
|
14
|
+
const theme = createTheme({
|
|
15
|
+
text: {
|
|
16
|
+
defaultVariant: "body",
|
|
17
|
+
variants: {
|
|
18
|
+
heading: { fontSize: "22px", lineHeight: "28px", fontWeight: "bold" },
|
|
19
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return (_jsxs(MjmlMailRoot, { theme: theme, children: [_jsx(MjmlSection, { backgroundColor: "#1a1a1a", indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 15 }), _jsx(MjmlText, { color: "#ffffff", fontWeight: "bold", align: "center", children: "Company Name" }), _jsx(MjmlSpacer, { height: 15 })] }) }), _jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 30 }), _jsx(MjmlText, { variant: "heading", bottomSpacing: true, children: "This is a notification email" }), _jsx(MjmlText, { bottomSpacing: true, children: "Minima ea distinctio quisquam. Illo reiciendis non officiis consectetur. Ratione perferendis distinctio sapiente est. Dolor consequatur qui excepturi natus." }), _jsx(MjmlText, { children: "Numquam aut voluptas numquam aspernatur. Consequatur quidem omnis dolorem natus quis soluta. Est recusandae delectus sed sed deserunt velit quia. Occaecati vel possimus similique reiciendis possimus iure rerum sit architecto." }), _jsx(MjmlSpacer, { height: 30 })] }) })] }));
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlImage, MjmlSpacer } from "@faire/mjml-react";
|
|
3
|
+
import { MjmlMailRoot } from "../../components/mailRoot/MjmlMailRoot.js";
|
|
4
|
+
import { MjmlSection } from "../../components/section/MjmlSection.js";
|
|
5
|
+
import { MjmlText } from "../../components/text/MjmlText.js";
|
|
6
|
+
import { registerStyles } from "../../styles/registerStyles.js";
|
|
7
|
+
import { createTheme } from "../../theme/createTheme.js";
|
|
8
|
+
import { getDefaultFromResponsiveValue } from "../../theme/responsiveValue.js";
|
|
9
|
+
import { css } from "../../utils/css.js";
|
|
10
|
+
const config = {
|
|
11
|
+
title: "Examples/TextWithImageEmail",
|
|
12
|
+
parameters: { mailRoot: false },
|
|
13
|
+
};
|
|
14
|
+
export default config;
|
|
15
|
+
export const Default = {
|
|
16
|
+
render: () => {
|
|
17
|
+
const theme = createTheme({
|
|
18
|
+
text: {
|
|
19
|
+
defaultVariant: "body",
|
|
20
|
+
variants: {
|
|
21
|
+
heading: { fontSize: "22px", lineHeight: "28px", fontWeight: "bold" },
|
|
22
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const IMAGE_WIDTH = 120;
|
|
27
|
+
const IMAGE_TEXT_GAP = 20;
|
|
28
|
+
registerStyles((theme) => css `
|
|
29
|
+
${theme.breakpoints.default.belowMediaQuery} {
|
|
30
|
+
.textWithImageEmail__textColumn {
|
|
31
|
+
width: calc(100% - ${IMAGE_WIDTH}px) !important;
|
|
32
|
+
max-width: calc(100% - ${IMAGE_WIDTH}px) !important;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
${theme.breakpoints.mobile.belowMediaQuery} {
|
|
37
|
+
.textWithImageEmail__imageColumn {
|
|
38
|
+
margin-bottom: 10px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.textWithImageEmail__textColumn {
|
|
42
|
+
width: 100% !important;
|
|
43
|
+
max-width: 100% !important;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.textWithImageEmail__textColumn > table > tbody > tr > td {
|
|
47
|
+
padding-left: 0 !important;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`);
|
|
51
|
+
const sectionIndent = getDefaultFromResponsiveValue(theme.sizes.contentIndentation);
|
|
52
|
+
const sectionInnerWidth = theme.sizes.bodyWidth - 2 * sectionIndent;
|
|
53
|
+
const textColumnWidth = sectionInnerWidth - IMAGE_WIDTH;
|
|
54
|
+
return (_jsxs(MjmlMailRoot, { theme: theme, children: [_jsx(MjmlSection, { backgroundColor: "#1a1a1a", indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlSpacer, { height: 15 }), _jsx(MjmlText, { color: "#ffffff", fontWeight: "bold", align: "center", children: "Company Name" }), _jsx(MjmlSpacer, { height: 15 })] }) }), _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlSpacer, { height: 30 }) }) }), _jsxs(MjmlSection, { indent: true, children: [_jsx(MjmlColumn, { className: "textWithImageEmail__imageColumn", width: `${IMAGE_WIDTH}px`, verticalAlign: "middle", children: _jsx(MjmlImage, { src: `https://picsum.photos/seed/1/${IMAGE_WIDTH}/150`, alt: "Featured image", align: "center", width: IMAGE_WIDTH }) }), _jsxs(MjmlColumn, { className: "textWithImageEmail__textColumn", width: `${textColumnWidth}px`, paddingLeft: `${IMAGE_TEXT_GAP}px`, verticalAlign: "middle", children: [_jsx(MjmlText, { variant: "heading", bottomSpacing: true, children: "Responsive Text-Image" }), _jsx(MjmlText, { children: "Demonstrates a responsive text-image layout with a fixed-width image column and a text column taking up remaining space." })] })] }), _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlSpacer, { height: 30 }) }) })] }));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -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-browser";
|
|
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-browser";
|
|
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,10 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
export type HtmlInlineLinkProps = ComponentProps<"a">;
|
|
3
|
+
/**
|
|
4
|
+
* Inline link styled to match the surrounding text, for use inside `HtmlText` or `MjmlText`.
|
|
5
|
+
*
|
|
6
|
+
* Applies explicit text styles from the parent text component's context so that
|
|
7
|
+
* Outlook Desktop (which overrides `<a>` tags with its built-in "Hyperlink" style)
|
|
8
|
+
* renders the link with the correct font and color.
|
|
9
|
+
*/
|
|
10
|
+
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,12 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { HtmlInlineLink } from "../HtmlInlineLink.js";
|
|
3
|
+
type Story = StoryObj<typeof HtmlInlineLink>;
|
|
4
|
+
declare const config: Meta<typeof HtmlInlineLink>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const InText: Story;
|
|
7
|
+
/**
|
|
8
|
+
* Using `!important` overrides the component's responsive `inherit !important`
|
|
9
|
+
* reset, ensuring the custom color persists on both desktop and mobile.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CustomColorOverride: Story;
|
|
12
|
+
export declare const WithTextVariants: Story;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
4
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
5
|
+
import { MjmlText } from "../../text/MjmlText.js";
|
|
6
|
+
import { HtmlInlineLink } from "../HtmlInlineLink.js";
|
|
7
|
+
const config = {
|
|
8
|
+
title: "Components/HtmlInlineLink",
|
|
9
|
+
component: HtmlInlineLink,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
};
|
|
12
|
+
export default config;
|
|
13
|
+
export const InText = {
|
|
14
|
+
parameters: {
|
|
15
|
+
theme: createTheme({
|
|
16
|
+
text: {
|
|
17
|
+
fontFamily: "Arial, sans-serif",
|
|
18
|
+
fontSize: "16px",
|
|
19
|
+
lineHeight: "24px",
|
|
20
|
+
color: "#333333",
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsxs(MjmlText, { children: ["Visit our ", _jsx(HtmlInlineLink, { href: "https://example.com", children: "website" }), " for more information."] }) }) })),
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Using `!important` overrides the component's responsive `inherit !important`
|
|
28
|
+
* reset, ensuring the custom color persists on both desktop and mobile.
|
|
29
|
+
*/
|
|
30
|
+
export const CustomColorOverride = {
|
|
31
|
+
parameters: {
|
|
32
|
+
theme: createTheme({
|
|
33
|
+
text: {
|
|
34
|
+
fontFamily: "Arial, sans-serif",
|
|
35
|
+
fontSize: "16px",
|
|
36
|
+
color: "#333333",
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
},
|
|
40
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsxs(MjmlText, { children: ["Click", " ", _jsx(HtmlInlineLink, { href: "https://example.com", style: { color: "#0066cc !important" }, children: "here" }), " ", "to continue."] }) }) })),
|
|
41
|
+
};
|
|
42
|
+
export const WithTextVariants = {
|
|
43
|
+
parameters: {
|
|
44
|
+
theme: createTheme({
|
|
45
|
+
text: {
|
|
46
|
+
fontFamily: "Arial, sans-serif",
|
|
47
|
+
color: "#333333",
|
|
48
|
+
variants: {
|
|
49
|
+
heading: { fontSize: "32px", fontWeight: 700, lineHeight: "40px" },
|
|
50
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
render: () => (_jsx(MjmlSection, { children: _jsxs(MjmlColumn, { children: [_jsxs(MjmlText, { variant: "heading", bottomSpacing: true, children: ["Welcome to ", _jsx(HtmlInlineLink, { href: "https://example.com", children: "our platform" })] }), _jsxs(MjmlText, { variant: "body", children: ["Explore our ", _jsx(HtmlInlineLink, { href: "https://example.com/features", children: "features" }), " and start building today."] })] }) })),
|
|
56
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PropsWithChildren, ReactNode } from "react";
|
|
2
|
+
import type { Theme } from "../../theme/themeTypes.js";
|
|
3
|
+
type MjmlMailRootProps = PropsWithChildren<{
|
|
4
|
+
/**
|
|
5
|
+
* Theme to use for the email. When omitted, the default theme
|
|
6
|
+
* (equivalent to `createTheme()`) is used.
|
|
7
|
+
*/
|
|
8
|
+
theme?: Theme;
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* The root element for email templates. Renders the standard MJML email skeleton
|
|
12
|
+
* (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
|
|
13
|
+
* default attribute so all components start with zero padding.
|
|
14
|
+
*
|
|
15
|
+
* Accepts an optional `theme` prop that controls the body width and responsive
|
|
16
|
+
* breakpoints. The theme is made available to all descendant components via
|
|
17
|
+
* `useTheme()`.
|
|
18
|
+
*
|
|
19
|
+
* Direct children should be section-level components (e.g. `MjmlSection`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function MjmlMailRoot({ theme: themeProp, children }: MjmlMailRootProps): ReactNode;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { Styles } from "../../styles/Styles.js";
|
|
4
|
+
import { createTheme } from "../../theme/createTheme.js";
|
|
5
|
+
import { ThemeProvider } from "../../theme/ThemeProvider.js";
|
|
6
|
+
/**
|
|
7
|
+
* The root element for email templates. Renders the standard MJML email skeleton
|
|
8
|
+
* (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
|
|
9
|
+
* default attribute so all components start with zero padding.
|
|
10
|
+
*
|
|
11
|
+
* Accepts an optional `theme` prop that controls the body width and responsive
|
|
12
|
+
* breakpoints. The theme is made available to all descendant components via
|
|
13
|
+
* `useTheme()`.
|
|
14
|
+
*
|
|
15
|
+
* Direct children should be section-level components (e.g. `MjmlSection`).
|
|
16
|
+
*/
|
|
17
|
+
export function MjmlMailRoot({ theme: themeProp, children }) {
|
|
18
|
+
const theme = themeProp ?? createTheme();
|
|
19
|
+
return (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Mjml, { children: [_jsxs(MjmlHead, { children: [_jsx(MjmlAttributes, { children: _jsx(MjmlAll, { padding: "0", fontFamily: theme.text.fontFamily }) }), _jsx(MjmlBreakpoint, { width: `${theme.breakpoints.mobile.value}px` }), _jsx(Styles, {})] }), _jsx(MjmlBody, { width: theme.sizes.bodyWidth, backgroundColor: theme.colors.background.body, children: children })] }) }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { MjmlMailRoot } from "../MjmlMailRoot.js";
|
|
3
|
+
type Story = StoryObj<typeof MjmlMailRoot>;
|
|
4
|
+
declare const config: Meta<typeof MjmlMailRoot>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Basic: Story;
|
|
7
|
+
export declare const CustomBodyBackground: Story;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
4
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
5
|
+
import { MjmlMailRoot } from "../MjmlMailRoot.js";
|
|
6
|
+
const config = {
|
|
7
|
+
title: "Components/MjmlMailRoot",
|
|
8
|
+
component: MjmlMailRoot,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: { mailRoot: false },
|
|
11
|
+
};
|
|
12
|
+
export default config;
|
|
13
|
+
export const Basic = {
|
|
14
|
+
render: () => (_jsx(MjmlMailRoot, { children: _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Hello from MjmlMailRoot" }) }) }) })),
|
|
15
|
+
};
|
|
16
|
+
export const CustomBodyBackground = {
|
|
17
|
+
render: () => (_jsx(MjmlMailRoot, { theme: createTheme({ colors: { background: { body: "#EAEAEA" } } }), children: _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Custom body background color" }) }) }) })),
|
|
18
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type IMjmlGroupProps, type IMjmlSectionProps } from "@faire/mjml-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export type MjmlSectionProps = IMjmlSectionProps & {
|
|
4
|
+
/** Applies theme-based content indentation with responsive overrides. */
|
|
5
|
+
indent?: boolean;
|
|
6
|
+
/** When true, child columns remain side-by-side on mobile instead of stacking vertically. */
|
|
7
|
+
disableResponsiveBehavior?: boolean;
|
|
8
|
+
/** Props forwarded to internal sub-components. */
|
|
9
|
+
slotProps?: {
|
|
10
|
+
/** Props passed to the wrapping `MjmlGroup` when `disableResponsiveBehavior` is enabled. */
|
|
11
|
+
group?: Partial<IMjmlGroupProps>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/** A section wrapper for email layouts. Must be a direct child of `MjmlBody`. */
|
|
15
|
+
export declare function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }: MjmlSectionProps): ReactNode;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
/** A section wrapper for email layouts. Must be a direct child of `MjmlBody`. */
|
|
9
|
+
export function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }) {
|
|
10
|
+
const theme = useOptionalTheme();
|
|
11
|
+
const indentProps = indent ? getIndentProps(theme) : {};
|
|
12
|
+
const resolvedClassName = clsx("mjmlSection", indent && "mjmlSection--indented", className);
|
|
13
|
+
const themeBackgroundProps = theme ? { backgroundColor: theme.colors.background.content } : {};
|
|
14
|
+
return (_jsx(BaseMjmlSection, { className: resolvedClassName, ...themeBackgroundProps, ...indentProps, ...restProps, children: disableResponsiveBehavior ? _jsx(MjmlGroup, { ...slotProps?.group, children: children }) : _jsx(_Fragment, { children: children }) }));
|
|
15
|
+
}
|
|
16
|
+
function getIndentProps(theme) {
|
|
17
|
+
if (theme === null) {
|
|
18
|
+
throw new Error("The `indent` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
paddingLeft: getDefaultFromResponsiveValue(theme.sizes.contentIndentation),
|
|
22
|
+
paddingRight: getDefaultFromResponsiveValue(theme.sizes.contentIndentation),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
registerStyles((theme) => {
|
|
26
|
+
const overrides = getResponsiveOverrides(theme.sizes.contentIndentation);
|
|
27
|
+
if (overrides.length === 0)
|
|
28
|
+
return css ``;
|
|
29
|
+
return overrides
|
|
30
|
+
.map((override) => {
|
|
31
|
+
const breakpoint = theme.breakpoints[override.breakpointKey];
|
|
32
|
+
if (!breakpoint)
|
|
33
|
+
return "";
|
|
34
|
+
return css `
|
|
35
|
+
${breakpoint.belowMediaQuery} {
|
|
36
|
+
.mjmlSection--indented > table > tbody > tr > td {
|
|
37
|
+
padding-left: ${override.value}px !important;
|
|
38
|
+
padding-right: ${override.value}px !important;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
})
|
|
43
|
+
.join("\n");
|
|
44
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { MjmlSection } from "../MjmlSection.js";
|
|
3
|
+
type Story = StoryObj<typeof MjmlSection>;
|
|
4
|
+
declare const config: Meta<typeof MjmlSection>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Primary: Story;
|
|
7
|
+
export declare const Indented: Story;
|
|
8
|
+
export declare const ExplicitBackgroundColor: Story;
|
|
9
|
+
export declare const DisabledResponsiveBehavior: Story;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
|
|
3
|
+
import { MjmlSection } from "../MjmlSection.js";
|
|
4
|
+
const config = {
|
|
5
|
+
title: "Components/MjmlSection",
|
|
6
|
+
component: MjmlSection,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
};
|
|
9
|
+
export default config;
|
|
10
|
+
export const Primary = {
|
|
11
|
+
render: (args) => (_jsx(MjmlSection, { ...args, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Section content" }) }) })),
|
|
12
|
+
};
|
|
13
|
+
export const Indented = {
|
|
14
|
+
args: {
|
|
15
|
+
indent: true,
|
|
16
|
+
},
|
|
17
|
+
render: (args) => (_jsx(MjmlSection, { ...args, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Indented section content" }) }) })),
|
|
18
|
+
};
|
|
19
|
+
export const ExplicitBackgroundColor = {
|
|
20
|
+
args: {
|
|
21
|
+
backgroundColor: "#FF0000",
|
|
22
|
+
},
|
|
23
|
+
render: (args) => (_jsx(MjmlSection, { ...args, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Explicit backgroundColor overrides theme default" }) }) })),
|
|
24
|
+
};
|
|
25
|
+
export const DisabledResponsiveBehavior = {
|
|
26
|
+
args: {
|
|
27
|
+
disableResponsiveBehavior: true,
|
|
28
|
+
},
|
|
29
|
+
render: (args) => (_jsxs(MjmlSection, { ...args, children: [_jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "First column" }) }), _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Second column" }) })] })),
|
|
30
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef, type JSX, type ReactNode, type 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 "@comet/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
|
+
variant?: VariantName;
|
|
26
|
+
/** When true, applies spacing below the text. */
|
|
27
|
+
bottomSpacing?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type HtmlTextProps<E extends keyof JSX.IntrinsicElements = "td"> = HtmlTextOwnProps & {
|
|
30
|
+
/**
|
|
31
|
+
* The HTML element to render instead of the default `<td>`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <HtmlText element="div">Rendered as a div</HtmlText>
|
|
36
|
+
* <HtmlText element="a" href="/link">Rendered as an anchor</HtmlText>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
element?: E;
|
|
40
|
+
} & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">;
|
|
41
|
+
/**
|
|
42
|
+
* Themed text component for use inside MJML ending tags or outside of the MJML context.
|
|
43
|
+
*/
|
|
44
|
+
export declare function HtmlText<E extends keyof JSX.IntrinsicElements>(props: HtmlTextOwnProps & {
|
|
45
|
+
element: E;
|
|
46
|
+
} & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">): ReactNode;
|
|
47
|
+
export declare function HtmlText(props: HtmlTextOwnProps & Omit<TdHTMLAttributes<HTMLTableCellElement>, keyof HtmlTextOwnProps>): ReactNode;
|
|
48
|
+
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,37 @@
|
|
|
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 "@comet/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
|
+
variant?: VariantName;
|
|
27
|
+
/** When true, applies spacing below the text. */
|
|
28
|
+
bottomSpacing?: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Text component that can be styled using the theme, optionally using a variant.
|
|
32
|
+
*
|
|
33
|
+
* Works without a `ThemeProvider` as a plain pass-through to the base MJML text component.
|
|
34
|
+
* The `variant` and `bottomSpacing` props require a `ThemeProvider` (or `MjmlMailRoot`).
|
|
35
|
+
*/
|
|
36
|
+
export declare function MjmlText({ variant: variantProp, bottomSpacing, className, children, ...restProps }: MjmlTextProps): ReactNode;
|
|
37
|
+
export declare function generateTextStyles(theme: Theme): string;
|