diff options
Diffstat (limited to 'frontend/src/components/shared/ThemeToggle.tsx')
-rw-r--r-- | frontend/src/components/shared/ThemeToggle.tsx | 53 |
1 files changed, 43 insertions, 10 deletions
diff --git a/frontend/src/components/shared/ThemeToggle.tsx b/frontend/src/components/shared/ThemeToggle.tsx index 679bbc5..b54bda9 100644 --- a/frontend/src/components/shared/ThemeToggle.tsx +++ b/frontend/src/components/shared/ThemeToggle.tsx @@ -1,36 +1,69 @@ 'use client'; +import { useState, useEffect } from 'react'; import { Moon, Sun } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useTheme } from './ThemeProvider'; export function ThemeToggle() { const { theme, setTheme } = useTheme(); + const [isAnimating, setIsAnimating] = useState(false); + + // Track initial mount to prevent animation on first render + const [isMounted, setIsMounted] = useState(false); + useEffect(() => { + setIsMounted(true); + }, []); const toggleTheme = () => { - if (theme === 'dark') { - setTheme('light'); - } else { - setTheme('dark'); - } + if (isAnimating) return; + + setIsAnimating(true); + + // Set new theme after a small delay for animation + const newTheme = theme === 'dark' ? 'light' : 'dark'; + setTimeout(() => { + setTheme(newTheme); + setTimeout(() => { + setIsAnimating(false); + }, 300); + }, 200); }; + if (!isMounted) { + return ( + <Button + variant="ghost" + size="icon" + className="opacity-0" + > + <Sun className="h-[1.2rem] w-[1.2rem]" /> + </Button> + ); + } + return ( <Button variant="ghost" size="icon" onClick={toggleTheme} + className={`relative overflow-hidden hover:bg-muted/80 hover:text-foreground transition-all duration-300 ${isAnimating ? 'cursor-wait' : ''}`} aria-label={ theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme' } + disabled={isAnimating} > - {theme === 'dark' ? ( - <Sun className="h-[1.2rem] w-[1.2rem]" /> - ) : ( - <Moon className="h-[1.2rem] w-[1.2rem]" /> - )} + <div className={`absolute inset-0 bg-primary/5 rounded-full transition-all duration-500 ${isAnimating ? 'scale-[5] opacity-0' : 'scale-0 opacity-0'}`}></div> + + <div className="relative"> + {theme === 'dark' ? ( + <Sun className={`h-5 w-5 transition-all duration-300 ${isAnimating ? 'rotate-90 scale-50 opacity-0' : 'rotate-0 scale-100 opacity-100'}`} /> + ) : ( + <Moon className={`h-5 w-5 transition-all duration-300 ${isAnimating ? 'rotate-90 scale-50 opacity-0' : 'rotate-0 scale-100 opacity-100'}`} /> + )} + </div> </Button> ); }
\ No newline at end of file |