import clsx from 'clsx'; import type { CSSProperties, HTMLAttributes, MouseEvent, ReactElement, } from 'react'; import { cloneElement, forwardRef, useCallback } from 'react'; import { useAutoFocus } from '../../hooks'; import { Loading } from '../loading'; import { Tooltip, type TooltipProps } from '../tooltip'; import * as styles from './button.css'; export type ButtonType = | 'primary' | 'secondary' | 'plain' | 'error' | 'success' | 'custom'; export type ButtonSize = 'default' | 'large' | 'extraLarge' | 'custom'; export interface ButtonProps extends Omit, 'type' | 'prefix'> { /** * Preset color scheme * @default 'secondary' */ variant?: ButtonType; disabled?: boolean; /** * By default, the button is `inline-flex`, set to `true` to make it `flex` * @default false */ block?: boolean; /** * Preset size, will be overridden by `style` or `className` * @default 'default' */ size?: ButtonSize; /** * Will show a loading spinner at `prefix` position */ loading?: boolean; /** No hover state */ withoutHover?: boolean; /** * By default, it is considered as an icon with preset size and color, * can be overridden by `prefixClassName` and `prefixStyle`. * * If `loading` is true, will be replaced by a spinner.(`prefixClassName` and `prefixStyle` still work) * */ prefix?: ReactElement; prefixClassName?: string; prefixStyle?: CSSProperties; contentClassName?: string; contentStyle?: CSSProperties; /** * By default, it is considered as an icon with preset size and color, * can be overridden by `suffixClassName` and `suffixStyle`. * */ suffix?: ReactElement; suffixClassName?: string; suffixStyle?: CSSProperties; tooltip?: TooltipProps['content']; tooltipShortcut?: TooltipProps['shortcut']; tooltipOptions?: Partial>; } const IconSlot = ({ icon, loading, className, ...attrs }: { icon?: ReactElement; loading?: boolean; } & HTMLAttributes) => { const showLoadingHere = loading !== undefined; const visible = icon || loading; return visible ? (
{showLoadingHere && loading ? : null} {icon && !loading ? cloneElement(icon, { width: '100%', height: '100%', ...icon.props, }) : null}
) : null; }; export const Button = forwardRef( ( { variant = 'secondary', size = 'default', children, disabled, block, loading, className, withoutHover, prefix, prefixClassName, prefixStyle, suffix, suffixClassName, suffixStyle, contentClassName, contentStyle, tooltip, tooltipShortcut, tooltipOptions, autoFocus, onClick, ...otherProps }, upstreamRef ) => { const ref = useAutoFocus(autoFocus); const handleClick = useCallback( (e: MouseEvent) => { if (loading || disabled) return; onClick?.(e); }, [disabled, loading, onClick] ); const buttonRef = (el: HTMLButtonElement | null) => { ref.current = el; if (upstreamRef) { if (typeof upstreamRef === 'function') { upstreamRef(el); } else { upstreamRef.current = el; } } }; return ( ); } ); Button.displayName = 'Button'; export default Button;