Initial Commit
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user