// Adapted from Astryx Button's native button, variant and loading-state model. // Copyright (c) Meta Platforms, Inc. and affiliates. MIT; see THIRD_PARTY_NOTICES.md. import { createElement, type ComponentProps, type ComponentType, type ReactNode, } from "react"; import { Slot } from "./_ynrlib/radix-ui.js"; import { GlassButton } from "./glass-button.js"; import type { GlassAppearance } from "./glass-surface.js"; export type { GlassShade, GlassAppearance } from "./glass-surface.js"; import { isIconComponent, type IconProps } from "./icons.js"; export type ButtonSize = "sm" | "md" | "lg"; export type ButtonVariant = | "outline" | "link" | "glass" | "glass-icon" | "foreground" | "icon" | "icon-foreground" | "ghost" | "destructive" | "primary" | "secondary"; export interface ButtonProps extends ComponentProps<"button">, GlassAppearance { asChild?: boolean; label?: string; /** Omit for a normal button. primary/secondary remain compatible aliases. */ variant?: ButtonVariant; size?: | ButtonSize | { base: ButtonSize; sm?: ButtonSize; md?: ButtonSize; lg?: ButtonSize }; isDisabled?: boolean; isLoading?: boolean; loading?: boolean; icon?: ReactNode | ComponentType; iconOnly?: boolean; iconClassName?: string; } export function Button({ asChild = false, variant, shade, blur, refraction, dispersion, icon, label, size, children, isDisabled, isLoading, loading, disabled, iconOnly = false, iconClassName = "", type = "button", className = "", ...props }: ButtonProps) { const busy = loading || isLoading; const hiddenLabel = iconOnly || variant === "icon" || variant === "icon-foreground"; const foreground = variant === "foreground" || variant === "icon-foreground" || variant === "primary"; const compact = hiddenLabel || Boolean(icon && !children && !label); const canonicalVariant = foreground ? compact ? "icon-foreground" : "foreground" : variant === "ghost" || variant === "destructive" || variant === "outline" || variant === "link" ? variant : compact ? "icon" : "normal"; const legacyVariant = foreground ? "primary" : variant === "ghost" || variant === "destructive" || variant === "outline" || variant === "link" ? variant : "secondary"; const sizes = typeof size === "object" ? size : { base: size }; if (variant === "glass" || variant === "glass-icon") return ( {children} ); const Comp = asChild ? Slot.Root : "button"; const blocked = disabled || isDisabled || busy; return ( { props.onClickCapture?.(e); if (asChild && blocked) { e.preventDefault(); e.stopPropagation(); } }} aria-busy={busy || props["aria-busy"] || undefined} > {!asChild && icon && ( )} {asChild ? ( {children} ) : hiddenLabel ? ( {children ?? label} ) : busy ? ( Saving… ) : ( (children ?? label) )} ); }