import type { ComponentPropsWithRef, ElementType, ReactNode, SyntheticEvent } from "react"; import "./Button.css"; export type ButtonVariant = "primary" | "secondary" | "dark" | "danger" | "ghost"; /** Figma calls these Small / Medium / Large; they resolve to 36 / 48 / 52px. */ export type ButtonSize = "sm" | "md" | "lg"; /** * Each size owns a published type role rather than a raw font-size, and the * role's line-height is what makes the button land on its designed height. */ const TYPE_ROLE: Record = { sm: "body-sm-bold", md: "body-md-bold", lg: "body-lg-bold", }; type ButtonOwnProps = { variant?: ButtonVariant; size?: ButtonSize; /** Rendered in the inline-start 24px slot - the left in LTR, the right in RTL. */ iconStart?: ReactNode; /** Rendered in the inline-end slot. Pass `` for the CTA arrow. */ iconEnd?: ReactNode; /** Keeps the label's box, overlays a spinner and sets `aria-busy`. */ isLoading?: boolean; disabled?: boolean; /** Stretch to the width of the parent (`display: grid` instead of inline). */ fullWidth?: boolean; className?: string; children?: ReactNode; }; export type ButtonProps = ButtonOwnProps & { /** * The element to render. Defaults to `button`; pass react-router's `Link` * (plus its `to`) for navigation, or `"a"` for an external href. Props of the * chosen element are type-checked, so `as={Link}` requires `to`. */ as?: E; } & Omit, keyof ButtonOwnProps | "as">; function cx(...parts: Array): string { return parts.filter(Boolean).join(" "); } function blockActivation(event: SyntheticEvent): void { event.preventDefault(); event.stopPropagation(); } /** * The SHARP-ER button - figma frame 14:487. * * Polymorphic: renders a native ` // Every variant, all three sizes. // Submitting a form: aria-busy is set and the label keeps its box. // Disabled - aria-disabled on both the native and the link form. // A "previous" control, mirrored the other way. // An external link keeps anchor semantics and prop typing. ------------------------------------------------------------------------- */