Spaces:
Sleeping
Sleeping
| // Copyright (c) 2025-2026, RTE (https://www.rte-france.com) | |
| // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. | |
| // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, | |
| // you can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| import { useCallback, useEffect, useState } from 'react'; | |
| import { interactionLogger } from '../utils/interactionLogger'; | |
| export type Theme = 'light' | 'dark'; | |
| const STORAGE_KEY = 'cs4g-theme'; | |
| // Read the persisted choice, falling back to the OS preference. Kept | |
| // pure (no DOM writes) so the same logic can run from the pre-mount | |
| // inline script in index.html and from React without diverging. | |
| export function resolveInitialTheme(): Theme { | |
| try { | |
| const stored = localStorage.getItem(STORAGE_KEY); | |
| if (stored === 'light' || stored === 'dark') return stored; | |
| } catch { | |
| // localStorage unavailable (private mode, SSR-like envs) — fall through. | |
| } | |
| if (typeof window !== 'undefined' && window.matchMedia) { | |
| return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; | |
| } | |
| return 'light'; | |
| } | |
| function applyThemeToDocument(theme: Theme): void { | |
| if (typeof document === 'undefined') return; | |
| document.documentElement.setAttribute('data-theme', theme); | |
| document.documentElement.style.colorScheme = theme; | |
| } | |
| export interface ThemeState { | |
| theme: Theme; | |
| toggleTheme: () => void; | |
| setTheme: (t: Theme) => void; | |
| } | |
| export function useTheme(): ThemeState { | |
| const [theme, setThemeState] = useState<Theme>(() => resolveInitialTheme()); | |
| useEffect(() => { | |
| applyThemeToDocument(theme); | |
| try { | |
| localStorage.setItem(STORAGE_KEY, theme); | |
| } catch { | |
| // ignore — same fallback as resolveInitialTheme. | |
| } | |
| }, [theme]); | |
| const setTheme = useCallback((t: Theme) => setThemeState(t), []); | |
| const toggleTheme = useCallback(() => { | |
| setThemeState(prev => { | |
| const next: Theme = prev === 'dark' ? 'light' : 'dark'; | |
| interactionLogger.record('theme_toggled', { theme: next }); | |
| return next; | |
| }); | |
| }, []); | |
| return { theme, toggleTheme, setTheme }; | |
| } | |