import * as stylex from "@stylexjs/stylex"; import { type PropsWithChildren, createElement, memo } from "react"; import { fontSize, headingMargin, headingWeight } from "./tokens.stylex"; export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6; //#region Styles const styles = stylex.create({ heading: { textWrap: "balance", }, truncate: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", }, centered: { textAlign: "center", }, upperCased: { textTransform: "uppercase", }, "1": { fontSize: fontSize.h1, marginTop: headingMargin.h1Top, marginBottom: headingMargin.h1Bottom, fontWeight: headingWeight.h1, }, "2": { fontSize: fontSize.h2, marginTop: headingMargin.h2Top, marginBottom: headingMargin.h2Bottom, fontWeight: headingWeight.h2, }, "3": { fontSize: fontSize.h3, marginTop: headingMargin.h3Top, marginBottom: headingMargin.h3Bottom, fontWeight: headingWeight.h3, }, "4": { fontSize: fontSize.h4, marginTop: headingMargin.h4Top, marginBottom: headingMargin.h4Bottom, fontWeight: headingWeight.h4, }, "5": { fontSize: fontSize.h5, marginTop: headingMargin.h5Top, marginBottom: headingMargin.h5Bottom, fontWeight: headingWeight.h5, }, "6": { fontSize: fontSize.h6, marginTop: headingMargin.h6Top, marginBottom: headingMargin.h6Bottom, fontWeight: headingWeight.h6, }, noTopMargin: { marginTop: 0, }, noBottomMargin: { marginBottom: 0, }, }); //#endregion /** * Based on this heading component: * https://react.docs.search.io/components/heading * https://mui.com/material-ui/react-typography/ */ export interface HeadingProps extends PropsWithChildren { level: HeadingLevel; /** Use this to make the level smaller/larger than it would be according to the HTML tag. Used for a11y to not break the document outline. */ styleLike?: HeadingLevel; /** * If true, the heading will be upper-cased (using CSS for a11y reasons). */ upperCased?: boolean; noTopMargin?: boolean; noBottomMargin?: boolean; centered?: boolean; /** * If true, the heading will be truncated with an ellipsis if it overflows. */ truncate?: boolean; } export default memo(function Heading(props: HeadingProps) { const elementProps = stylex.props( styles.heading, styles[props.styleLike ?? props.level], props.centered && styles.centered, props.upperCased && styles.upperCased, props.truncate && styles.truncate, props.noTopMargin && styles.noTopMargin, props.noBottomMargin && styles.noBottomMargin, ); // We used to have a switch-case here. But we can call createElement directly to avoid the code duplication // biome-ignore lint/style/useTemplate: nope return createElement("h" + props.level, elementProps, props.children); });