import React, { useState, useRef, useEffect } from 'react'; import styled from '@emotion/styled'; import { useTranslation } from 'react-i18next'; import { useTheme } from '../../contexts/ThemeContext'; import { spacing, borderRadius, animation, sizing } from '../../theme'; // SVG Icons (inline for no external dependencies) const SunIcon: React.FC<{ size?: number }> = ({ size = 20 }) => ( ); const MoonIcon: React.FC<{ size?: number }> = ({ size = 20 }) => ( ); const SystemIcon: React.FC<{ size?: number }> = ({ size = 20 }) => ( ); type ThemeMode = 'dark' | 'light' | 'system'; const themeOptionDefs: { value: ThemeMode; labelKey: string; icon: React.ReactNode }[] = [ { value: 'dark', labelKey: 'theme.dark', icon: }, { value: 'light', labelKey: 'theme.light', icon: }, { value: 'system', labelKey: 'theme.system', icon: }, ]; export const ThemeToggle: React.FC = () => { const { t } = useTranslation(); const { theme, setTheme, resolvedTheme } = useTheme(); const [isOpen, setIsOpen] = useState(false); const containerRef = useRef(null); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); // Close on escape key useEffect(() => { const handleEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') { setIsOpen(false); } }; if (isOpen) { document.addEventListener('keydown', handleEscape); } return () => { document.removeEventListener('keydown', handleEscape); }; }, [isOpen]); // Determine which icon to show based on theme state const getCurrentIcon = () => { if (theme === 'system') { return ; } return resolvedTheme === 'dark' ? : ; }; const handleSelect = (value: ThemeMode) => { setTheme(value); setIsOpen(false); }; return ( setIsOpen(!isOpen)} aria-label={t('theme.toggleMenu')} aria-expanded={isOpen} aria-haspopup="listbox" title={t('theme.currentTheme', { theme: theme === 'system' ? t('theme.system') : resolvedTheme })} > {getCurrentIcon()} {isOpen && ( {themeOptionDefs.map((option) => ( handleSelect(option.value)} role="option" aria-selected={theme === option.value} isActive={theme === option.value} > {option.icon} {t(option.labelKey)} ))} )} ); }; const ToggleContainer = styled.div` position: relative; display: inline-block; `; const ToggleButton = styled.button` display: flex; align-items: center; justify-content: center; width: ${sizing.button.lg}px; height: ${sizing.button.lg}px; background: var(--bg-surface); border: 1px solid var(--border-default); border-radius: ${borderRadius.md}px; cursor: pointer; color: var(--fg-primary); transition: all ${animation.duration.normal} ${animation.easing.default}; &:hover { background: var(--accent-subtle); border-color: var(--accent-border); transform: translateY(-2px); } &:focus { outline: none; box-shadow: 0 0 0 2px rgba(0, 145, 152, 0.3); } &:active { transform: translateY(0); } svg { flex-shrink: 0; } `; const Dropdown = styled.div` position: absolute; top: calc(100% + ${spacing.sm}px); right: 0; background: var(--bg-elevated); border: 1px solid var(--border-subtle); border-radius: ${borderRadius.sm}px; padding: ${spacing.xs}px; min-width: 140px; z-index: 1000; box-shadow: var(--shadow-lg); `; const DropdownItem = styled.button<{ isActive: boolean }>` display: flex; align-items: center; gap: ${spacing.sm}px; width: 100%; padding: ${spacing.sm}px ${spacing.md}px; background: ${props => props.isActive ? 'var(--accent-subtle)' : 'transparent'}; border: none; border-radius: ${borderRadius.sm}px; cursor: pointer; color: ${props => props.isActive ? 'var(--accent-text)' : 'var(--fg-primary)'}; font-size: 14px; text-align: left; transition: background ${animation.duration.fast} ${animation.easing.default}; &:hover { background: ${props => props.isActive ? 'var(--accent-subtle)' : 'var(--bg-hover)'}; } &:focus { outline: none; background: var(--accent-subtle); } `; const IconWrapper = styled.span` display: flex; align-items: center; justify-content: center; width: ${sizing.icon.sm}px; height: ${sizing.icon.sm}px; `;