/* eslint-disable react/button-has-type */ import React, { ReactNode, CSSProperties, ComponentType, ButtonHTMLAttributes, createElement, useCallback } from 'react' import classnames from 'classnames' import { useFormInfo } from '../Form/defs' import { concat } from '../utils' import { ComponentSize, usePrefix } from '../ConfigProvider' import { LoadingThinIcon } from '../icons' import { useAnimationCls } from '../utils/hooks' import { WithRef } from '../utils/type' export type ButtonType = 'primary' | 'reverse' | 'default' | 'secondary' | 'link' | 'text' | 'dashed' export type ButtonHtmlType = 'button' | 'submit' | 'reset' export interface ButtonProps { /** * 类型 * * @default 'default' */ type?: ButtonType /** * 是否告警按钮 * * @description 该属性对 type 为 default 和 reverse 无效 * @default false */ danger?: boolean /** * 图标 * * @description 该按钮会出现在文字前面 */ icon?: ReactNode /** * 图标 * * @description 该按钮会出现在文字后面 */ endIcon?: ReactNode /** * 原生 button 标签的 type 属性 * * @description 可选属性 'button', 'submit', 'reset' * @default 'button' */ htmlType?: ButtonHtmlType /** * 按钮大小 * * @default 'default' */ size?: ComponentSize /** * 是否显示 loading 状态 */ loading?: boolean onClick?(event: React.MouseEvent): void disabled?: boolean className?: string style?: CSSProperties children?: ReactNode /** 指定组件实现替换 HTML button */ as?: ComponentType> /** * 根元素的 html 属性 */ rootHtmlProps?: WithRef, T> } export default function Button(props: ButtonProps) { const { children, type = 'default', danger = false, size, icon, endIcon, htmlType = 'button', loading, onClick, disabled, className, as, rootHtmlProps, style, ...rest } = props const formInfo = useFormInfo() const finalDisabled = disabled || loading // loading 的时候按钮也应该被禁用 const prefix = usePrefix('btn') const typePrefix = concat(prefix, type) const dangerPrefix = concat(prefix, 'danger') const sizePrefix = concat(typePrefix, size ?? formInfo.size) const loadingPrefix = concat(typePrefix, 'loading') const contentPrefix = concat(prefix, 'content') const iconPrefix = concat(contentPrefix, 'icon') const endIconPrefix = concat(contentPrefix, 'endicon') const classname = classnames( prefix, typePrefix, sizePrefix, danger && dangerPrefix, loading && loadingPrefix, finalDisabled && concat(prefix, 'disabled'), className, rootHtmlProps?.className ) const { spinCls } = useAnimationCls() const startIcon = loading ? : icon && {icon} const handleClick = useCallback((e: React.MouseEvent) => { if (finalDisabled) return rootHtmlProps?.onClick?.(e) if (e.defaultPrevented) return onClick?.(e) }, [rootHtmlProps, onClick, finalDisabled]) const rootProps: ButtonHTMLAttributes = { ...rootHtmlProps, type: htmlType, className: classname, style: { ...style, ...rootHtmlProps?.style }, onClick: (rootHtmlProps?.onClick || onClick) && handleClick, disabled: finalDisabled, ...rest } return createElement(as ?? 'button', rootProps, ( {startIcon} {children && {children}} {endIcon && {endIcon}} )) }