Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 27x 27x 64x 64x 64x 64x 64x 64x 27x 64x 27x 27x 27x 27x 27x 27x 64x 64x 27x 64x 64x 64x | import { useState, useEffect, useCallback } from "react";
import type { ThemeSetting } from "../types";
import { isSSR } from "../utils/environment";
/**
* Resolved theme (actual theme being applied)
*/
export type ResolvedTheme = "light" | "dark";
/**
* Options for useTheme hook
*/
export interface UseThemeOptions {
/** Theme setting (light, dark, or system) */
theme?: ThemeSetting;
/** Callback when resolved theme changes */
onThemeChange?: (theme: ResolvedTheme) => void;
}
/**
* Return type for useTheme hook
*/
export interface UseThemeReturn {
/** Current theme setting */
themeSetting: ThemeSetting;
/** Resolved theme (light or dark) */
resolvedTheme: ResolvedTheme;
/** Whether system prefers dark mode */
systemPrefersDark: boolean;
/** Set the theme */
setTheme: (theme: ThemeSetting) => void;
/** Toggle between light and dark */
toggleTheme: () => void;
}
/**
* Get system color scheme preference
*/
function getSystemPreference(): boolean {
Iif (isSSR()) {
return false;
}
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
}
/**
* Resolve theme setting to actual theme
*/
function resolveTheme(
setting: ThemeSetting,
systemPrefersDark: boolean
): ResolvedTheme {
Eif (setting === "system") {
return systemPrefersDark ? "dark" : "light";
}
return setting;
}
/**
* Hook to manage theme with system preference detection
*
* @param options - Configuration options
* @returns Theme state and control functions
*
* @example
* ```tsx
* const { resolvedTheme, toggleTheme } = useTheme({
* theme: 'system',
* });
*
* return (
* <div className={resolvedTheme === 'dark' ? 'dark' : ''}>
* <button onClick={toggleTheme}>Toggle Theme</button>
* </div>
* );
* ```
*/
export function useTheme(options: UseThemeOptions = {}): UseThemeReturn {
const { theme: themeProp = "system", onThemeChange } = options;
const [themeSetting, setThemeSetting] = useState<ThemeSetting>(themeProp);
const [systemPrefersDark, setSystemPrefersDark] = useState(getSystemPreference);
// Sync with external theme prop changes
useEffect(() => {
setThemeSetting(themeProp);
}, [themeProp]);
// Listen for system preference changes
useEffect(() => {
Iif (isSSR()) {
return;
}
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = (event: MediaQueryListEvent) => {
setSystemPrefersDark(event.matches);
};
// Modern browsers
Eif (mediaQuery.addEventListener) {
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}
// Legacy browsers
mediaQuery.addListener(handleChange);
return () => mediaQuery.removeListener(handleChange);
}, []);
// Resolve current theme
const resolvedTheme = resolveTheme(themeSetting, systemPrefersDark);
// Notify on theme change
useEffect(() => {
onThemeChange?.(resolvedTheme);
}, [resolvedTheme, onThemeChange]);
const setTheme = useCallback((theme: ThemeSetting) => {
setThemeSetting(theme);
}, []);
const toggleTheme = useCallback(() => {
setThemeSetting((prev) => {
if (prev === "system") {
// If system, toggle to opposite of current resolved
return systemPrefersDark ? "light" : "dark";
}
return prev === "light" ? "dark" : "light";
});
}, [systemPrefersDark]);
return {
themeSetting,
resolvedTheme,
systemPrefersDark,
setTheme,
toggleTheme,
};
}
|