import type { Color } from "@yaakapp-internal/plugins"; import classNames from "classnames"; import type { HTMLAttributes, ReactNode } from "react"; import { forwardRef, useImperativeHandle, useRef } from "react"; import type { HotkeyAction } from "../../hooks/useHotKey"; import { useFormattedHotkey, useHotKey } from "../../hooks/useHotKey"; import { Icon } from "./Icon"; import { LoadingIcon } from "./LoadingIcon"; export type ButtonProps = Omit, "color" | "onChange"> & { innerClassName?: string; color?: Color | "custom" | "default"; variant?: "border" | "solid"; isLoading?: boolean; size?: "2xs" | "xs" | "sm" | "md" | "auto"; justify?: "start" | "center"; type?: "button" | "submit"; forDropdown?: boolean; disabled?: boolean; title?: string; leftSlot?: ReactNode; rightSlot?: ReactNode; hotkeyAction?: HotkeyAction; hotkeyLabelOnly?: boolean; hotkeyPriority?: number; }; export const Button = forwardRef(function Button( { isLoading, className, innerClassName, children, forDropdown, color = "default", type = "button", justify = "center", size = "md", variant = "solid", leftSlot, rightSlot, disabled, hotkeyAction, hotkeyPriority, hotkeyLabelOnly, title, onClick, ...props }: ButtonProps, ref, ) { const hotkeyTrigger = useFormattedHotkey(hotkeyAction ?? null)?.join(""); const fullTitle = hotkeyTrigger ? `${title ?? ""} ${hotkeyTrigger}`.trim() : title; if (isLoading) { disabled = true; } const classes = classNames( className, "x-theme-button", `x-theme-button--${variant}`, `x-theme-button--${variant}--${color}`, "border", // They all have borders to ensure the same width "max-w-full min-w-0", // Help with truncation "hocus:opacity-100", // Force opacity for certain hover effects "whitespace-nowrap outline-none", "flex-shrink-0 flex items-center", "outline-0", disabled ? "pointer-events-none opacity-disabled" : "pointer-events-auto", justify === "start" && "justify-start", justify === "center" && "justify-center", size === "md" && "h-md px-3 rounded-md", size === "sm" && "h-sm px-2.5 rounded-md", size === "xs" && "h-xs px-2 text-sm rounded-md", size === "2xs" && "h-2xs px-2 text-xs rounded", // Solids variant === "solid" && "border-transparent", variant === "solid" && color === "custom" && "focus-visible:outline-2 outline-border-focus", variant === "solid" && color !== "custom" && "text-text enabled:hocus:text-text enabled:hocus:bg-surface-highlight outline-border-subtle", variant === "solid" && color !== "custom" && color !== "default" && "bg-surface", // Borders variant === "border" && "border", variant === "border" && color !== "custom" && "border-border-subtle text-text-subtle enabled:hocus:border-border " + "enabled:hocus:bg-surface-highlight enabled:hocus:text-text outline-border-subtler", ); const buttonRef = useRef(null); useImperativeHandle( ref, () => buttonRef.current, ); useHotKey( hotkeyAction ?? null, () => { buttonRef.current?.click(); }, { priority: hotkeyPriority, enable: !hotkeyLabelOnly }, ); return ( ); });