import React, { useState } from 'react'; import { clsx } from 'clsx'; import { useTheme } from '../../../contexts/ThemeContext'; import { Button } from '../Button'; import { Sun, Moon, Computer } from 'lucide-react'; export interface ThemeToggleProps { className?: string; /** * Toggle style variant */ variant?: 'button' | 'dropdown' | 'switch'; /** * Size of the toggle */ size?: 'sm' | 'md' | 'lg'; /** * Show labels alongside icons */ showLabels?: boolean; /** * Position of dropdown (when variant='dropdown') */ dropdownPosition?: 'left' | 'right' | 'center'; } /** * ThemeToggle - Reusable theme switching component * * Features: * - Multiple display variants (button, dropdown, switch) * - Support for light/dark/system themes * - Configurable size and labels * - Accessible keyboard navigation * - Click outside to close dropdown */ export const ThemeToggle: React.FC = ({ className, variant = 'button', size = 'md', showLabels = false, dropdownPosition = 'right', }) => { const { theme, setTheme, resolvedTheme } = useTheme(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const themes = [ { key: 'light' as const, label: 'Claro', icon: Sun }, { key: 'dark' as const, label: 'Oscuro', icon: Moon }, { key: 'auto' as const, label: 'Sistema', icon: Computer }, ]; const currentTheme = themes.find(t => t.key === theme) || themes[0]; const CurrentIcon = currentTheme.icon; // Size mappings const sizeClasses = { sm: 'text-sm', md: 'text-base', lg: 'text-lg', }; const iconSizes = { sm: 'w-4 h-4', md: 'w-5 h-5', lg: 'w-6 h-6', }; // Handle keyboard navigation React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isDropdownOpen) { setIsDropdownOpen(false); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [isDropdownOpen]); // Close dropdown when clicking outside React.useEffect(() => { if (!isDropdownOpen) return; const handleClickOutside = (event: MouseEvent) => { const target = event.target as Element; if (!target.closest('[data-theme-toggle]')) { setIsDropdownOpen(false); } }; document.addEventListener('click', handleClickOutside); return () => document.removeEventListener('click', handleClickOutside); }, [isDropdownOpen]); // Cycle through themes for button variant const handleButtonToggle = () => { const currentIndex = themes.findIndex(t => t.key === theme); const nextIndex = (currentIndex + 1) % themes.length; setTheme(themes[nextIndex].key); }; // Handle theme selection const handleThemeSelect = (themeKey: 'light' | 'dark' | 'auto') => { setTheme(themeKey); setIsDropdownOpen(false); }; // Button variant - cycles through themes if (variant === 'button') { return ( ); } // Switch variant - simple toggle between light/dark if (variant === 'switch') { const isDark = resolvedTheme === 'dark'; return ( ); } // Dropdown variant - shows all theme options return (
{isDropdownOpen && (
{themes.map(({ key, label, icon: Icon }) => ( ))}
)}
); }; export default ThemeToggle;