/** * Button — one recipe, four intents. * * The weight ladder is deliberately only three deep: exactly one `primary` * per screen region, `secondary` for reversible actions, `ghost` for tertiary * controls that sit inside another surface. `danger` is separate because it * signals consequence, not rank. */ import { IconSpinner } from '../icons'; const VARIANTS = { primary: 'c-btn-primary', secondary: 'c-btn-secondary', ghost: 'c-btn-ghost', danger: 'c-btn-danger', }; const SIZES = { xs: 'c-btn-xs', sm: 'c-btn-sm', md: '', }; /** * @param {Object} props * @param {string} [props.variant] primary | secondary | ghost | danger. * @param {string} [props.size] xs | sm | md. * @param {boolean} [props.busy] Swap the leading icon for a spinner and * block interaction. The label stays, so the * button never changes width mid-action. * @param {Function} [props.icon] Leading icon component. * @param {string} [props.href] Render an anchor instead of a button. * @return {Element} Button or link. */ export default function Button( { variant = 'secondary', size = 'md', busy = false, icon: Icon, href, className = '', children, disabled, ...props } ) { const classes = [ 'c-btn', VARIANTS[ variant ] || VARIANTS.secondary, SIZES[ size ] || '', className, ] .filter( Boolean ) .join( ' ' ); const content = ( <> { busy && } { ! busy && Icon && } { children } ); if ( href ) { return ( { content } ); } return ( ); }