142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
/**
|
|
* CTA button rendered as a table-wrapped `<a>` with variant + size
|
|
* recipes drawn from the theme. Outlook-on-Windows honours the table
|
|
* geometry, which is why this is not a `<button>` element.
|
|
*/
|
|
|
|
import {
|
|
EMAIL_TEMPLATES_CODES,
|
|
EmailTemplatesError,
|
|
} from "../../error-codes/error-codes.js";
|
|
import { getEmailTemplatesInstruments } from "../../telemetry/telemetry.js";
|
|
import { defaultTheme } from "../../theme/theme.js";
|
|
import type { EmailTheme } from "../../theme/theme.types.js";
|
|
import { h } from "../../vnode/vnode.js";
|
|
import type { StyleObject, VNode } from "../../vnode/vnode.types.js";
|
|
import type {
|
|
ButtonSize,
|
|
ButtonVariant,
|
|
EmailButtonProps,
|
|
} from "./email-button.types.js";
|
|
|
|
const VARIANTS: ReadonlySet<ButtonVariant> = new Set([
|
|
"primary",
|
|
"secondary",
|
|
"outline",
|
|
]);
|
|
|
|
const SIZES: ReadonlySet<ButtonSize> = new Set(["sm", "md", "lg"]);
|
|
|
|
const SIZE_PADDING: Record<ButtonSize, string> = {
|
|
sm: "8px 16px",
|
|
md: "12px 24px",
|
|
lg: "16px 32px",
|
|
};
|
|
|
|
const SIZE_FONT: Record<ButtonSize, string> = {
|
|
sm: "13px",
|
|
md: "15px",
|
|
lg: "17px",
|
|
};
|
|
|
|
function variantStyle(variant: ButtonVariant, theme: EmailTheme): StyleObject {
|
|
switch (variant) {
|
|
case "primary":
|
|
return {
|
|
"background-color": theme.colors.primary,
|
|
color: theme.colors.primaryForeground,
|
|
border: `2px solid ${theme.colors.primary}`,
|
|
};
|
|
case "secondary":
|
|
return {
|
|
"background-color": theme.colors.secondary,
|
|
color: theme.colors.secondaryForeground,
|
|
border: `2px solid ${theme.colors.secondary}`,
|
|
};
|
|
case "outline":
|
|
return {
|
|
"background-color": "transparent",
|
|
color: theme.colors.primary,
|
|
border: `2px solid ${theme.colors.primary}`,
|
|
};
|
|
}
|
|
}
|
|
|
|
export function EmailButton(props: EmailButtonProps): VNode {
|
|
const {
|
|
href,
|
|
children,
|
|
theme = defaultTheme,
|
|
variant = "primary",
|
|
size = "md",
|
|
fullWidth = false,
|
|
align = "center",
|
|
style,
|
|
} = props;
|
|
|
|
if (!VARIANTS.has(variant)) {
|
|
getEmailTemplatesInstruments().validationFailureCounter.add(1, {
|
|
reason: "invalid_button_variant",
|
|
});
|
|
throw new EmailTemplatesError(
|
|
EMAIL_TEMPLATES_CODES.INVALID_OPTIONS,
|
|
"email_templates.invalid_options.button_variant"
|
|
);
|
|
}
|
|
if (!SIZES.has(size)) {
|
|
getEmailTemplatesInstruments().validationFailureCounter.add(1, {
|
|
reason: "invalid_button_size",
|
|
});
|
|
throw new EmailTemplatesError(
|
|
EMAIL_TEMPLATES_CODES.INVALID_OPTIONS,
|
|
"email_templates.invalid_options.button_size"
|
|
);
|
|
}
|
|
|
|
const v = variantStyle(variant, theme);
|
|
return h(
|
|
"table",
|
|
{
|
|
role: "presentation",
|
|
cellpadding: 0,
|
|
cellspacing: 0,
|
|
align,
|
|
style: {
|
|
margin: `0 0 ${theme.spacing.md} 0`,
|
|
width: fullWidth ? "100%" : undefined,
|
|
},
|
|
},
|
|
h(
|
|
"tr",
|
|
{},
|
|
h(
|
|
"td",
|
|
{ align: "center" },
|
|
h(
|
|
"a",
|
|
{
|
|
href,
|
|
target: "_blank",
|
|
rel: "noopener noreferrer",
|
|
style: {
|
|
display: "inline-block",
|
|
"text-decoration": "none",
|
|
"font-family": theme.typography.fontFamily,
|
|
"font-size": SIZE_FONT[size],
|
|
"font-weight": 600,
|
|
padding: SIZE_PADDING[size],
|
|
"border-radius": theme.borderRadius,
|
|
"text-align": "center",
|
|
width: fullWidth ? "100%" : undefined,
|
|
"box-sizing": "border-box",
|
|
...v,
|
|
...(style ?? {}),
|
|
},
|
|
},
|
|
children
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|