// /components/ui/Button.tsx import * as React from 'react'; import clsx from 'clsx'; export type ButtonVariant = 'primary' | 'secondary' | 'soft'; export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; export type ButtonShape = 'default' | 'pill' | 'circle'; export type ButtonTone = 'indigo' | 'gray' | 'rose'; export interface ButtonProps extends React.ButtonHTMLAttributes { variant?: ButtonVariant; size?: ButtonSize; shape?: ButtonShape; // "default" (normal), "pill" (rounded-full), "circle" (nur Icon) icon?: React.ReactNode; iconPosition?: 'leading' | 'trailing'; fullWidth?: boolean; tone?: ButtonTone; // NEU: Farbton } const baseClasses = 'inline-flex items-center justify-center font-semibold shadow-xs ' + 'focus-visible:outline-2 focus-visible:outline-offset-2 ' + 'disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150'; // Farb-Kombinationen pro Variant + Tone const variantToneClasses: Record> = { primary: { indigo: 'bg-indigo-600 text-white hover:bg-indigo-500 ' + 'focus-visible:outline-indigo-600 ' + 'dark:bg-indigo-500 dark:text-white dark:hover:bg-indigo-400 dark:shadow-none dark:focus-visible:outline-indigo-500', gray: 'bg-gray-900 text-white hover:bg-gray-800 ' + 'focus-visible:outline-gray-900 ' + 'dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 dark:shadow-none dark:focus-visible:outline-gray-700', rose: 'bg-rose-600 text-white hover:bg-rose-500 ' + 'focus-visible:outline-rose-600 ' + 'dark:bg-rose-500 dark:text-white dark:hover:bg-rose-400 dark:shadow-none dark:focus-visible:outline-rose-500', }, secondary: { indigo: 'bg-white text-gray-900 inset-ring inset-ring-gray-300 hover:bg-gray-50 ' + 'focus-visible:outline-indigo-600 ' + 'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20 dark:focus-visible:outline-indigo-500', gray: 'bg-white text-gray-900 inset-ring inset-ring-gray-300 hover:bg-gray-50 ' + 'focus-visible:outline-gray-900 ' + 'dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20 dark:focus-visible:outline-gray-700', rose: 'bg-white text-rose-600 inset-ring inset-ring-rose-200 hover:bg-rose-50 ' + 'focus-visible:outline-rose-600 ' + 'dark:bg-white/10 dark:text-rose-300 dark:shadow-none dark:inset-ring-rose-500/40 dark:hover:bg-rose-500/10 dark:focus-visible:outline-rose-500', }, soft: { indigo: 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100 ' + 'focus-visible:outline-indigo-600 ' + 'dark:bg-indigo-500/20 dark:text-indigo-400 dark:shadow-none dark:hover:bg-indigo-500/30 dark:focus-visible:outline-indigo-500', gray: 'bg-gray-100 text-gray-800 hover:bg-gray-200 ' + 'focus-visible:outline-gray-900 ' + 'dark:bg-gray-700/40 dark:text-gray-100 dark:shadow-none dark:hover:bg-gray-700/60 dark:focus-visible:outline-gray-600', rose: 'bg-rose-50 text-rose-600 hover:bg-rose-100 ' + 'focus-visible:outline-rose-600 ' + 'dark:bg-rose-500/20 dark:text-rose-300 dark:shadow-none dark:hover:bg-rose-500/30 dark:focus-visible:outline-rose-500', }, }; // Größen wie in deinen Snippets (rectangular) const sizeClasses: Record = { xs: 'rounded-sm px-2 py-1 text-xs', sm: 'rounded-sm px-2 py-1 text-sm', md: 'rounded-md px-2.5 py-1.5 text-sm', lg: 'rounded-md px-3 py-2 text-sm', xl: 'rounded-md px-3.5 py-2.5 text-sm', }; // Pill / "rounded primary/secondary buttons" Größen const pillSizeClasses: Record = { xs: 'rounded-full px-2.5 py-1 text-xs', sm: 'rounded-full px-2.5 py-1 text-sm', md: 'rounded-full px-3 py-1.5 text-sm', lg: 'rounded-full px-3.5 py-2 text-sm', xl: 'rounded-full px-4 py-2.5 text-sm', }; // Circular Buttons – nur Icon const circleSizeClasses: Record = { xs: 'rounded-full p-1', sm: 'rounded-full p-1.5', md: 'rounded-full p-2', lg: 'rounded-full p-2', xl: 'rounded-full p-2.5', }; function getSizeClasses(size: ButtonSize, shape: ButtonShape): string { switch (shape) { case 'pill': return pillSizeClasses[size]; case 'circle': return circleSizeClasses[size]; default: return sizeClasses[size]; } } const Button = React.forwardRef( ( { variant = 'primary', tone = 'indigo', // NEU: Default size = 'md', shape = 'default', icon, iconPosition = 'leading', fullWidth, className, children, type = 'button', ...props }, ref ) => { const hasIcon = !!icon; const hasLabel = React.Children.count(children) > 0; const gapClasses = hasIcon && hasLabel ? size === 'xl' ? 'gap-x-2' : 'gap-x-1.5' : ''; return ( ); } ); Button.displayName = 'Button'; export default Button;