import { forwardRef } from "react"; import type { ButtonHTMLAttributes, ReactNode } from "react"; import clsx from "clsx"; import { Loader2 } from "lucide-react"; import { FocusSafeButton } from "./FocusSafeButton"; import { iconButtonClass, type IconButtonSize, type IconButtonVariant } from "./buttonStyles"; // A button that is only a glyph (docs/DESIGN.md A2): two sizes, r8, and a name it cannot ship without // (`label` is required: it is the accessible name and the tooltip). On a picture it is round and dark // translucent (`media`), readable on any thumbnail; round means "floats over something" (media, or a // pill field it sits inside — `round`), and nothing else. The glyph // is sized here, so a row of them never mixes 14 and 16px icons. // `pressed`, never a bare `aria-pressed`: the prop is what tints a toggled glyph (see Button). export interface IconButtonProps extends Omit, "children" | "aria-pressed"> { /** The accessible name and the tooltip. Required: the button renders no text. */ label: string; /** The glyph (a lucide element); the primitive sizes it. */ children: ReactNode; variant?: IconButtonVariant; size?: IconButtonSize; /** A toggle: sets aria-pressed and tints the glyph with the accent. */ pressed?: boolean; /** Not accepted: a toggle is `pressed` (JSX lets any undeclared `aria-*` through, so it is `never`). */ "aria-pressed"?: never; /** Sits on a picture or inside a pill field: round (A2). Implied by the `media` variant. */ round?: boolean; /** Busy: a spinner replaces the glyph, and the button is unavailable without losing the focus. */ loading?: boolean; /** Leave the tooltip to a richer one the caller renders (a `Tooltip` around the button). */ noTitle?: boolean; } export const IconButton = forwardRef(function IconButton( { label, children, variant = "ghost", size = "sm", pressed, round = false, loading = false, noTitle = false, type, className, ...rest }, ref, ) { return ( {loading ? : children} ); });