Feat Modo Oscuro y Otras Estéticas
This commit is contained in:
		
							
								
								
									
										58
									
								
								frontend/src/context/ThemeContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								frontend/src/context/ThemeContext.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| // frontend/src/context/ThemeContext.tsx | ||||
|  | ||||
| import React, { createContext, useState, useEffect, useContext, useMemo } from 'react'; | ||||
|  | ||||
| type Theme = 'light' | 'dark'; | ||||
|  | ||||
| interface ThemeContextType { | ||||
|   theme: Theme; | ||||
|   toggleTheme: () => void; | ||||
| } | ||||
|  | ||||
| // Creamos el contexto con un valor por defecto. | ||||
| const ThemeContext = createContext<ThemeContextType | undefined>(undefined); | ||||
|  | ||||
| // Creamos el proveedor del contexto. | ||||
| export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { | ||||
|   const [theme, setTheme] = useState<Theme>(() => { | ||||
|     // 1. Intentamos leer el tema desde localStorage. | ||||
|     const savedTheme = localStorage.getItem('theme') as Theme | null; | ||||
|     if (savedTheme) return savedTheme; | ||||
|  | ||||
|     // 2. Si no hay nada, respetamos la preferencia del sistema operativo. | ||||
|     if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { | ||||
|       return 'dark'; | ||||
|     } | ||||
|  | ||||
|     // 3. Como última opción, usamos el tema claro por defecto. | ||||
|     return 'light'; | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     // Aplicamos el tema al body y lo guardamos en localStorage cada vez que cambia. | ||||
|     document.body.dataset.theme = theme; | ||||
|     localStorage.setItem('theme', theme); | ||||
|   }, [theme]); | ||||
|  | ||||
|   const toggleTheme = () => { | ||||
|     setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light')); | ||||
|   }; | ||||
|    | ||||
|   // Usamos useMemo para evitar que el valor del contexto se recalcule en cada render. | ||||
|   const value = useMemo(() => ({ theme, toggleTheme }), [theme]); | ||||
|  | ||||
|   return ( | ||||
|     <ThemeContext.Provider value={value}> | ||||
|       {children} | ||||
|     </ThemeContext.Provider> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| // Hook personalizado para usar el contexto de forma sencilla. | ||||
| export const useTheme = (): ThemeContextType => { | ||||
|   const context = useContext(ThemeContext); | ||||
|   if (!context) { | ||||
|     throw new Error('useTheme debe ser utilizado dentro de un ThemeProvider'); | ||||
|   } | ||||
|   return context; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user