Files
rxminder/components/ui/ThemeSwitcher.tsx
William Valentin e48adbcb00 Initial commit: Complete NodeJS-native setup
- Migrated from Python pre-commit to NodeJS-native solution
- Reorganized documentation structure
- Set up Husky + lint-staged for efficient pre-commit hooks
- Fixed Dockerfile healthcheck issue
- Added comprehensive documentation index
2025-09-06 01:42:48 -07:00

75 lines
2.5 KiB
TypeScript

import React, { useState, useRef, useEffect } from 'react';
import { useTheme } from '../../hooks/useTheme';
import { SunIcon, MoonIcon, DesktopIcon } from '../icons/Icons';
type Theme = 'light' | 'dark' | 'system';
const themeOptions: {
value: Theme;
label: string;
icon: React.FC<React.ComponentProps<'svg'>>;
}[] = [
{ value: 'light', label: 'Light', icon: SunIcon },
{ value: 'dark', label: 'Dark', icon: MoonIcon },
{ value: 'system', label: 'System', icon: DesktopIcon },
];
const ThemeSwitcher: React.FC = () => {
const { theme, setTheme } = useTheme();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const currentTheme =
themeOptions.find(t => t.value === theme) || themeOptions[2];
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div className='relative' ref={dropdownRef}>
<button
onClick={() => setIsOpen(!isOpen)}
className='flex items-center justify-center w-10 h-10 rounded-lg bg-slate-100 hover:bg-slate-200 dark:bg-slate-700 dark:hover:bg-slate-600 transition-colors'
aria-label={`Current theme: ${currentTheme.label}. Change theme.`}
>
<SunIcon className='w-5 h-5 text-slate-700 dark:hidden' />
<MoonIcon className='w-5 h-5 text-slate-200 hidden dark:block' />
</button>
{isOpen && (
<div className='absolute right-0 mt-2 w-36 bg-white dark:bg-slate-800 rounded-md shadow-lg ring-1 ring-black ring-opacity-5 py-1 z-30 border dark:border-slate-700'>
{themeOptions.map(option => (
<button
key={option.value}
onClick={() => {
setTheme(option.value);
setIsOpen(false);
}}
className={`w-full text-left flex items-center space-x-2 px-3 py-2 text-sm ${
theme === option.value
? 'bg-indigo-600 text-white'
: 'text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700'
}`}
>
<option.icon className='w-4 h-4' />
<span>{option.label}</span>
</button>
))}
</div>
)}
</div>
);
};
export default ThemeSwitcher;