'use client' import { ReactNode, forwardRef, useState, useRef, useEffect, ButtonHTMLAttributes } from 'react' type ButtonProps = { title?: string children?: ReactNode onClick?: (event: React.MouseEvent) => void onToggle?: (open: boolean) => void modalId?: string color?: 'blue' | 'red' | 'gray' | 'green' | 'teal' | 'transparent' variant?: 'solid' | 'outline' | 'ghost' | 'soft' | 'white' | 'link' /** Steuert NUR Höhe/Abstände */ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' /** Optionale Schriftgröße */ textSize?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' className?: string dropDirection?: 'up' | 'down' | 'auto' disabled?: boolean loading?: boolean } & ButtonHTMLAttributes const Button = forwardRef(function Button( { title, children, onClick, onToggle, modalId, color = 'blue', variant = 'solid', size = 'md', textSize = 'sm', className, dropDirection = 'down', disabled = false, loading = false, ...rest }, ref ) { const [open, setOpen] = useState(false) const [direction, setDirection] = useState<'up' | 'down'>('down') const localRef = useRef(null) const buttonRef = (ref as React.RefObject) || localRef const modalAttributes: { [key: string]: string } = modalId ? { 'aria-haspopup': 'dialog', 'aria-expanded': 'false', 'aria-controls': modalId, 'data-hs-overlay': `#${modalId}`, } : {} // Feste Höhen sorgen für vertikale Zentrierung const sizeClasses: Record, string> = { xs: 'h-7 px-2', sm: 'h-8 px-3', md: 'h-9 px-4', lg: 'h-10 px-5', xl: 'h-12 px-6', full: 'h-12 px-6 w-full', } // Nur Textgröße const textSizeClasses: Record, string> = { xs: 'text-xs', sm: 'text-sm', base: 'text-base', lg: 'text-lg', xl: 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', } const base = ` ${sizeClasses[size] || sizeClasses['md']} inline-flex items-center gap-x-2 ${textSizeClasses[textSize] || 'text-sm'} font-medium rounded-lg leading-none focus:outline-hidden disabled:opacity-50 disabled:cursor-not-allowed ` const variants: Record> = { solid: { blue: 'bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700', red: 'bg-red-600 text-white hover:bg-red-700 focus:bg-red-700', gray: 'bg-gray-600 text-white hover:bg-gray-700 focus:bg-gray-700', teal: 'bg-teal-600 text-white hover:bg-teal-700 focus:bg-teal-700', green: 'bg-green-600 text-white hover:bg-green-700 focus:bg-green-700', transparent: 'bg-transparent-600 text-white hover:bg-transparent-700 focus:bg-transparent-700', }, outline: { blue: 'border border-gray-200 text-gray-500 hover:border-blue-600 hover:text-blue-600 focus:border-blue-600 focus:text-blue-600 dark:border-neutral-700 dark:text-neutral-400 dark:hover:text-blue-500 dark:hover:border-blue-600 dark:focus:text-blue-500 dark:focus:border-blue-600', red: 'border border-gray-200 text-gray-500 hover:border-red-600 hover:text-red-600 focus:border-red-600 focus:text-red-600 dark:border-neutral-700 dark:text-neutral-400 dark:hover:text-red-500 dark:hover:border-red-600 dark:focus:text-red-500 dark:focus:border-red-600', gray: 'border border-gray-200 text-gray-500 hover:border-gray-600 hover:text-gray-600 focus:border-gray-600 focus:text-gray-600 dark:border-neutral-700 dark:text-neutral-400 dark:hover:text-white dark:hover:border-neutral-600 dark:focus:text-white dark:focus:border-neutral-600', teal: 'border border-teal-200 text-teal-500 hover:border-teal-600 hover:text-teal-600 focus:border-teal-600 focus:text-teal-600 dark:border-neutral-700 dark:text-neutral-400 dark:hover:text-white dark:hover:border-neutral-600 dark:focus:text-white dark:focus:border-neutral-600', green: 'border border-green-200 text-green-500 hover:border-green-600 hover:text-green-600 focus:border-green-600 focus:text-green-600 dark:border-neutral-700 dark:text-neutral-400 dark:hover:text-white dark:hover:border-neutral-600 dark:focus:text-white dark:focus:border-neutral-600', transparent: 'border border-white/20 bg-transparent text-white shadow-2xs hover:bg-white/15 focus:bg-white/15 dark:bg-transparent dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700/50 dark:focus:bg-neutral-700/50', }, ghost: { blue: 'border border-transparent text-blue-600 hover:bg-blue-100 hover:text-blue-800 focus:bg-blue-100 focus:text-blue-800 dark:text-blue-500 dark:hover:bg-blue-800/30 dark:hover:text-blue-400 dark:focus:bg-blue-800/30 dark:focus:text-blue-400', red: 'border border-transparent text-red-600 hover:bg-red-100 hover:text-red-800 focus:bg-red-100 focus:text-red-800 dark:text-red-500 dark:hover:bg-red-800/30 dark:hover:text-red-400 dark:focus:bg-red-800/30 dark:focus:text-red-400', gray: 'border border-transparent text-gray-600 hover:bg-gray-100 hover:text-gray-800 focus:bg-gray-100 focus:text-gray-800 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:hover:text-white dark:focus:bg-neutral-700 dark:focus:text-white', teal: 'border border-transparent text-teal-600 hover:bg-teal-100 hover:text-teal-800 focus:bg-teal-100 focus:text-teal-800 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:hover:text-white dark:focus:bg-neutral-700 dark:focus:text-white', green: 'border border-transparent text-green-600 hover:bg-green-100 hover:text-green-800 focus:bg-green-100 focus:text-green-800 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700 dark:focus:text-white', transparent: 'border border-transparent text-white hover:bg-white/10 focus:bg-white/10 dark:text-white', }, soft: { blue: 'bg-blue-100 text-blue-800 hover:bg-blue-200 focus:bg-blue-200 dark:text-blue-400 dark:hover:bg-blue-900 dark:focus:bg-blue-900', red: 'bg-red-100 text-red-800 hover:bg-red-200 focus:bg-red-200 dark:text-red-400 dark:hover:bg-red-900 dark:focus:bg-red-900', gray: 'bg-gray-100 text-gray-800 hover:bg-gray-200 focus:bg-gray-200 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', teal: 'bg-teal-100 text-teal-800 hover:bg-teal-200 focus:bg-teal-200 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', green: 'bg-green-100 text-green-800 hover:bg-green-200 focus:bg-green-200 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', transparent: 'bg-transparent-100 text-transparent-800 hover:bg-transparent-200 focus:bg-transparent-200 dark:text-neutral-300 dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', }, white: { blue: 'border border-teal-200 bg-white text-gray-800 shadow-2xs hover:bg-gray-50 focus:bg-gray-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', red: 'border border-gray-200 bg-white text-gray-800 shadow-2xs hover:bg-gray-50 focus:bg-gray-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', gray: 'border border-gray-200 bg-white text-gray-800 shadow-2xs hover:bg-gray-50 focus:bg-gray-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', teal: 'border border-teal-200 bg-white text-teal-800 shadow-2xs hover:bg-teal-50 focus:bg-teal-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', green: 'border border-green-200 bg-white text-green-800 shadow-2xs hover:bg-green-50 focus:bg-green-50 dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700', transparent: 'border border-white/20 bg-transparent text-white shadow-2xs hover:bg-white/15 focus:bg-white/15 dark:bg-transparent dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700/50 dark:focus:bg-neutral-700/50', }, link: { blue: 'border border-transparent text-blue-600 hover:text-blue-800 focus:text-blue-800 dark:text-blue-500 dark:hover:text-blue-400 dark:focus:text-blue-400', red: 'border border-transparent text-red-600 hover:text-red-800 focus:text-red-800 dark:text-red-500 dark:hover:text-red-400 dark:focus:text-red-400', gray: 'border border-transparent text-gray-600 hover:text-gray-800 focus:text-gray-800 dark:text-neutral-400 dark:hover:text-white dark:focus:text-white', teal: 'border border-transparent text-teal-600 hover:text-teal-800 focus:text-teal-800 dark:text-neutral-400 dark:hover:text-white dark:focus:text-white', green: 'border border-transparent text-green-600 hover:text-green-800 focus:text-green-800 dark:text-neutral-400 dark:hover:text-white dark:focus:text-white', transparent: 'border border-transparent text-transparent-600 hover:text-transparent-800 focus:text-transparent-800 dark:text-neutral-400 dark:hover:text-white dark:focus:text-white', }, } const variantClasses = variants[variant]?.[color] || variants.solid.blue const stripInteractive = (cls: string) => cls .split(/\s+/) .filter(c => c && !c.includes('hover:') && !c.includes('focus:') && !c.includes('active:')) .join(' ') const safeVariantClasses = disabled || loading ? stripInteractive(variantClasses) : variantClasses const classes = ` ${base} ${safeVariantClasses} ${className || ''} ` useEffect(() => { if (open && dropDirection === 'auto' && buttonRef.current) { requestAnimationFrame(() => { const rect = buttonRef.current!.getBoundingClientRect() const dropdownHeight = 200 const spaceBelow = window.innerHeight - rect.bottom const spaceAbove = rect.top setDirection(spaceBelow < dropdownHeight && spaceAbove > dropdownHeight ? 'up' : 'down') }) } }, [open, dropDirection]) const toggle = (event: React.MouseEvent) => { const next = !open setOpen(next) onToggle?.(next) onClick?.(event) } return ( ) }) export default Button