import { forwardRef } from 'react'; import type { ButtonHTMLAttributes, HTMLAttributes, KeyboardEvent, LabelHTMLAttributes, ReactNode, Ref, } from 'react'; type PlatformIconButtonBaseProps = { label: string; icon: ReactNode; children?: ReactNode; variant?: PlatformIconButtonVariant; }; export type PlatformIconButtonVariant = | 'platformIcon' | 'surfaceFloating' | 'darkMini'; type PlatformIconButtonButtonProps = Omit< ButtonHTMLAttributes, 'aria-label' | 'children' > & PlatformIconButtonBaseProps & { asChild?: false; }; type PlatformIconButtonLabelProps = Omit< LabelHTMLAttributes, 'aria-label' | 'children' > & PlatformIconButtonBaseProps & { asChild: 'label'; }; type PlatformIconButtonSpanButtonProps = Omit< HTMLAttributes, 'aria-label' | 'children' | 'role' > & PlatformIconButtonBaseProps & { asChild: 'spanButton'; disabled?: boolean; }; type PlatformIconButtonProps = | PlatformIconButtonButtonProps | PlatformIconButtonLabelProps | PlatformIconButtonSpanButtonProps; /** * 平台通用图标动作按钮。 * 统一承接纯图标动作、图标上传 label 和带短标签的浮动图标动作。 */ export const PlatformIconButton = forwardRef< HTMLButtonElement | HTMLLabelElement | HTMLSpanElement, PlatformIconButtonProps >(function PlatformIconButton( { label, icon, children, variant = 'platformIcon', title, className, asChild, ...actionProps }, ref, ) { const variantClassName = { platformIcon: 'platform-icon-button', surfaceFloating: 'inline-flex items-center justify-center rounded-full border border-white/80 bg-white/94 text-[var(--platform-text-strong)] shadow-sm backdrop-blur transition hover:text-[var(--platform-accent)] disabled:cursor-not-allowed disabled:opacity-55', darkMini: 'inline-flex items-center justify-center rounded-full border border-white/16 bg-black/55 text-white transition-colors hover:bg-black/70 disabled:cursor-not-allowed disabled:opacity-55', }[variant]; const actionClassName = [variantClassName, className] .filter(Boolean) .join(' '); if (asChild === 'label') { return ( ); } if (asChild === 'spanButton') { const { disabled, onClick, onKeyDown, tabIndex, ...spanProps } = actionProps as HTMLAttributes & { disabled?: boolean; }; const handleKeyDown = (event: KeyboardEvent) => { onKeyDown?.(event); if (event.defaultPrevented || disabled) { return; } if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); event.currentTarget.click(); } }; return ( } aria-disabled={disabled} aria-label={label} className={actionClassName} onClick={disabled ? undefined : onClick} onKeyDown={handleKeyDown} role="button" tabIndex={disabled ? -1 : (tabIndex ?? 0)} title={title} > {icon} {children} ); } const { type = 'button', ...buttonProps } = actionProps as ButtonHTMLAttributes; return ( ); });