@comet/mail-react 9.3.0-canary-20260803113554 → 9.3.0
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/lib/blocks/richText/RichTextList.js +39 -11
- package/lib/index.d.ts +1 -1
- package/lib/styles/generateResponsiveVariantCss.d.ts +14 -0
- package/lib/styles/generateResponsiveVariantCss.js +15 -0
- package/lib/theme/createTheme.d.ts +2 -1
- package/lib/theme/createTheme.js +1 -0
- package/lib/theme/defaultTheme.js +5 -0
- package/lib/theme/themeTypes.d.ts +10 -0
- package/package.json +3 -3
|
@@ -2,12 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
import { useOutlookTextStyle } from "../../components/text/OutlookTextStyleContext.js";
|
|
4
4
|
import { generateResponsiveTextCss } from "../../components/text/textStyles.js";
|
|
5
|
+
import { generateResponsiveTokenCss } from "../../styles/generateResponsiveVariantCss.js";
|
|
5
6
|
import { registerStyles } from "../../styles/registerStyles.js";
|
|
6
|
-
import {
|
|
7
|
+
import { defaultTheme } from "../../theme/defaultTheme.js";
|
|
8
|
+
import { getDefaultFromResponsiveValue, getDefaultOrUndefined } from "../../theme/responsiveValue.js";
|
|
7
9
|
import { useOptionalTheme } from "../../theme/ThemeProvider.js";
|
|
8
|
-
const listIndent = 8;
|
|
9
|
-
const markerGap = 12;
|
|
10
|
-
const itemSpacing = 8;
|
|
11
10
|
const unorderedMarker = "•";
|
|
12
11
|
// Variant names come from the consumer, so the `variant` prefix keeps them apart from the package's own modifiers.
|
|
13
12
|
function variantModifier(variantName) {
|
|
@@ -17,6 +16,7 @@ function variantModifier(variantName) {
|
|
|
17
16
|
export function RichTextList({ ordered, variant, bottomSpacing, items }) {
|
|
18
17
|
const theme = useOptionalTheme();
|
|
19
18
|
const outlookTextStyle = useOutlookTextStyle();
|
|
19
|
+
const list = theme?.list ?? defaultTheme.list;
|
|
20
20
|
const themeText = theme?.text ?? {};
|
|
21
21
|
const { defaultVariant, variants, ...baseTextStyles } = themeText;
|
|
22
22
|
const activeVariant = variant ?? defaultVariant;
|
|
@@ -31,22 +31,50 @@ export function RichTextList({ ordered, variant, bottomSpacing, items }) {
|
|
|
31
31
|
...outlookTextStyle,
|
|
32
32
|
...(outlookTextStyle?.lineHeight !== undefined && { msoLineHeightRule: "exactly" }),
|
|
33
33
|
};
|
|
34
|
-
|
|
34
|
+
const itemSpacing = getDefaultFromResponsiveValue(list.itemSpacing);
|
|
35
|
+
const markerCellPadding = {
|
|
36
|
+
paddingLeft: getDefaultFromResponsiveValue(list.indent),
|
|
37
|
+
paddingRight: getDefaultFromResponsiveValue(list.markerGap),
|
|
38
|
+
};
|
|
39
|
+
return (_jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, width: "100%", className: clsx("richTextBlock__list", ordered ? "richTextBlock__list--ordered" : "richTextBlock__list--unordered", activeVariant && variantModifier(activeVariant)), style: { borderCollapse: "collapse" }, children: _jsx("tbody", { children: items.map((item, index) => {
|
|
35
40
|
const isLastItem = index === items.length - 1;
|
|
36
41
|
const spacingBelow = isLastItem ? blockSpacing : itemSpacing;
|
|
37
42
|
const cellStyle = { ...fontStyle, ...(spacingBelow !== undefined && { paddingBottom: spacingBelow }) };
|
|
38
43
|
return (_jsxs("tr", { className: clsx("richTextBlock__listItem", !isLastItem && "richTextBlock__listItem--itemSpacing", isLastItem && blockSpacing !== undefined && "richTextBlock__listItem--blockSpacing"), children: [_jsx("td", { className: "richTextBlock__listItemMarker", align: ordered ? "right" : "left", valign: "top", style: {
|
|
39
44
|
...cellStyle,
|
|
40
|
-
|
|
41
|
-
paddingRight: markerGap,
|
|
45
|
+
...markerCellPadding,
|
|
42
46
|
whiteSpace: "nowrap", // The full-width text cell squeezes this column, so markers such as `10.` must stay on one line.
|
|
43
47
|
}, children: ordered ? `${index + 1}.` : unorderedMarker }), _jsx("td", { className: "richTextBlock__listItemText", width: "100%", valign: "top", style: cellStyle, children: item.content })] }, item.key));
|
|
44
48
|
}) }) }));
|
|
45
49
|
}
|
|
46
50
|
export function generateRichTextListStyles(theme) {
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
return [
|
|
52
|
+
generateResponsiveListSpacingCss(theme),
|
|
53
|
+
generateResponsiveTextCss(theme, {
|
|
54
|
+
styleSelector: (variantName) => `.${variantModifier(variantName)} .richTextBlock__listItemMarker, .${variantModifier(variantName)} .richTextBlock__listItemText`,
|
|
55
|
+
spacingSelector: (variantName) => `.${variantModifier(variantName)} .richTextBlock__listItem--blockSpacing > td`,
|
|
56
|
+
}),
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join("\n");
|
|
60
|
+
}
|
|
61
|
+
function generateResponsiveListSpacingCss(theme) {
|
|
62
|
+
return [
|
|
63
|
+
generateResponsiveTokenCss({
|
|
64
|
+
breakpoints: theme.breakpoints,
|
|
65
|
+
selector: ".richTextBlock__listItemMarker",
|
|
66
|
+
tokens: [
|
|
67
|
+
{ value: theme.list.indent, cssProperty: "padding-left", unit: "px" },
|
|
68
|
+
{ value: theme.list.markerGap, cssProperty: "padding-right", unit: "px" },
|
|
69
|
+
],
|
|
70
|
+
}),
|
|
71
|
+
generateResponsiveTokenCss({
|
|
72
|
+
breakpoints: theme.breakpoints,
|
|
73
|
+
selector: ".richTextBlock__listItem--itemSpacing > td",
|
|
74
|
+
tokens: [{ value: theme.list.itemSpacing, cssProperty: "padding-bottom", unit: "px" }],
|
|
75
|
+
}),
|
|
76
|
+
]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join("\n");
|
|
51
79
|
}
|
|
52
80
|
registerStyles(generateRichTextListStyles);
|
package/lib/index.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export { createTheme } from "./theme/createTheme.js";
|
|
|
40
40
|
export type { ResponsiveValue } from "./theme/responsiveValue.js";
|
|
41
41
|
export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
|
|
42
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";
|
|
43
|
+
export type { ButtonStyles, ButtonVariants, ButtonVariantStyles, DividerStyles, DividerVariants, DividerVariantStyles, TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeList, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
|
|
44
44
|
export { css } from "./utils/css.js";
|
|
45
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
46
|
export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
|
|
@@ -23,4 +23,18 @@ interface GenerateResponsiveVariantCssOptions<TVariantStyles> {
|
|
|
23
23
|
* Returns an empty string when no variant has breakpoint overrides.
|
|
24
24
|
*/
|
|
25
25
|
export declare function generateResponsiveVariantCss<TVariantStyles extends VariantStylesConstraint>({ breakpoints, variants, groups, }: GenerateResponsiveVariantCssOptions<TVariantStyles>): string;
|
|
26
|
+
interface ResponsiveToken {
|
|
27
|
+
value: ResponsiveValue<string | number>;
|
|
28
|
+
cssProperty: string;
|
|
29
|
+
unit?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generates `max-width` media-query CSS for theme tokens that are not keyed by variant name.
|
|
33
|
+
* Returns an empty string when no token has breakpoint overrides.
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateResponsiveTokenCss({ breakpoints, selector, tokens, }: {
|
|
36
|
+
breakpoints: ThemeBreakpoints;
|
|
37
|
+
selector: string;
|
|
38
|
+
tokens: readonly ResponsiveToken[];
|
|
39
|
+
}): string;
|
|
26
40
|
export {};
|
|
@@ -21,6 +21,21 @@ export function generateResponsiveVariantCss({ breakpoints, variants, groups, })
|
|
|
21
21
|
}
|
|
22
22
|
return cssChunks.filter(Boolean).join("\n");
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Generates `max-width` media-query CSS for theme tokens that are not keyed by variant name.
|
|
26
|
+
* Returns an empty string when no token has breakpoint overrides.
|
|
27
|
+
*/
|
|
28
|
+
export function generateResponsiveTokenCss({ breakpoints, selector, tokens, }) {
|
|
29
|
+
const overridesByBreakpoint = new Map();
|
|
30
|
+
for (const token of tokens) {
|
|
31
|
+
for (const { breakpointKey, value } of getResponsiveOverrides(token.value)) {
|
|
32
|
+
const declarations = overridesByBreakpoint.get(breakpointKey) ?? [];
|
|
33
|
+
declarations.push(formatDeclaration({ cssProperty: token.cssProperty, value, unit: token.unit }));
|
|
34
|
+
overridesByBreakpoint.set(breakpointKey, declarations);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return renderMediaQueries({ breakpoints, selector, overridesByBreakpoint });
|
|
38
|
+
}
|
|
24
39
|
function collectOverridesByBreakpoint(variantStyles, properties) {
|
|
25
40
|
const overridesByBreakpoint = new Map();
|
|
26
41
|
for (const entry of properties) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeSizes, ThemeText } from "./themeTypes.js";
|
|
1
|
+
import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeButton, ThemeColors, ThemeDivider, ThemeList, ThemeSizes, ThemeText } from "./themeTypes.js";
|
|
2
2
|
type CreateThemeOverrides = {
|
|
3
3
|
sizes?: Partial<ThemeSizes>;
|
|
4
4
|
breakpoints?: Partial<ThemeBreakpoints>;
|
|
5
5
|
text?: Partial<ThemeText>;
|
|
6
|
+
list?: Partial<ThemeList>;
|
|
6
7
|
divider?: Partial<ThemeDivider>;
|
|
7
8
|
button?: Partial<ThemeButton>;
|
|
8
9
|
colors?: {
|
package/lib/theme/createTheme.js
CHANGED
|
@@ -21,6 +21,7 @@ export function createTheme(overrides) {
|
|
|
21
21
|
...overrides?.breakpoints,
|
|
22
22
|
},
|
|
23
23
|
text: { ...defaultTheme.text, ...overrides?.text },
|
|
24
|
+
list: { ...defaultTheme.list, ...overrides?.list },
|
|
24
25
|
divider: { ...defaultTheme.divider, ...overrides?.divider },
|
|
25
26
|
button: { ...defaultTheme.button, ...overrides?.button },
|
|
26
27
|
colors: {
|
|
@@ -46,6 +46,15 @@ export interface ThemeText extends TextStyles {
|
|
|
46
46
|
defaultVariant?: VariantName;
|
|
47
47
|
variants?: VariantsRecord;
|
|
48
48
|
}
|
|
49
|
+
/** Spacing tokens in pixels, applying to lists the RichText block renders. */
|
|
50
|
+
export interface ThemeList {
|
|
51
|
+
/** The space before the marker, measured from the list's left edge. */
|
|
52
|
+
indent: ResponsiveValue;
|
|
53
|
+
/** The space between the marker and the item's text. */
|
|
54
|
+
markerGap: ResponsiveValue;
|
|
55
|
+
/** The space between items. The space below the last one comes from the text variant's `bottomSpacing`. */
|
|
56
|
+
itemSpacing: ResponsiveValue;
|
|
57
|
+
}
|
|
49
58
|
/**
|
|
50
59
|
* Single source of truth for divider style property names and their value types.
|
|
51
60
|
* Both `DividerStyles` and `DividerVariantStyles` are derived from this interface.
|
|
@@ -197,6 +206,7 @@ export interface Theme {
|
|
|
197
206
|
sizes: ThemeSizes;
|
|
198
207
|
breakpoints: ThemeBreakpoints;
|
|
199
208
|
text: ThemeText;
|
|
209
|
+
list: ThemeList;
|
|
200
210
|
divider: ThemeDivider;
|
|
201
211
|
button: ThemeButton;
|
|
202
212
|
colors: ThemeColors;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/mail-react",
|
|
3
|
-
"version": "9.3.0
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "Utilities for building HTML emails with React",
|
|
5
5
|
"license": "BSD-2-Clause",
|
|
6
6
|
"type": "module",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"typescript": "^5.9.3",
|
|
49
49
|
"vite": "^7.3.1",
|
|
50
50
|
"vitest": "^4.1.8",
|
|
51
|
-
"@comet/cli": "9.3.0
|
|
52
|
-
"@comet/eslint-config": "9.3.0
|
|
51
|
+
"@comet/cli": "9.3.0",
|
|
52
|
+
"@comet/eslint-config": "9.3.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|