import type { ControlSize, IntentName, } from "@charcuterie/tokens" import type { ComponentPropsWithRef, ReactNode, } from "react" import { getControlClassName } from "../controlStyles.ts" import type { IntentAppearance } from "../intentStyles.ts" import { DISABLED_CLASS } from "../intentStyles.ts" import { Spinner } from "../Spinner/Spinner.tsx" export type ButtonProps = Omit< ComponentPropsWithRef<"button">, "disabled" > & { appearance?: IntentAppearance iconEnd?: ReactNode iconStart?: ReactNode intent?: IntentName isDisabled?: boolean isFullWidth?: boolean isLoading?: boolean /** What the spinner announces. Not shown. */ loadingLabel?: string size?: ControlSize /** * Two sizing modes rather than a second component's worth of * markup. `icon` is square on the control height with no inline * padding, which is what lines an `IconButton` up with the text * button beside it in a toolbar. * * It lives here rather than in `IconButton` because appending a * competing `size-*`/`px-0` to this component's `className` would * be a specificity coin-flip: two utilities setting the same * property, and the winner decided by Tailwind's internal * ordering rather than by the caller. */ sizing?: "control" | "icon" } /** * Hand-rolled in **seven repos**, each with its own * primary/secondary/danger/ghost, and this is the component that * erases the most duplication in the fleet. * * Four decisions worth knowing, all of them fixing something an * inventoried repo gets wrong: * * - **`type="button"` by default.** A ``, which * is fine. */ export const Button = ({ appearance = "solid", children, className, iconEnd, iconStart, intent = "accent", isDisabled = false, isFullWidth = false, isLoading = false, loadingLabel = "Loading…", size = "md", sizing = "control", type = "button", ...buttonProps }: ButtonProps): ReactNode => ( )