| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- "use client";
- import { createContext, useContext, useEffect, useState } from "react";
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
- type Theme = "dark" | "light" | "system";
- type ThemeProviderProps = {
- children: React.ReactNode;
- defaultTheme?: Theme;
- storageKey?: string;
- };
- type ThemeProviderState = {
- theme: Theme;
- setTheme: (theme: Theme) => void;
- };
- const initialState: ThemeProviderState = {
- theme: "system",
- setTheme: () => null,
- };
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
- export function ThemeProvider({
- children,
- defaultTheme = "system",
- storageKey = "vtorio-theme",
- ...props
- }: ThemeProviderProps) {
- const [theme, setTheme] = useState<Theme>(defaultTheme);
- const [mounted, setMounted] = useState(false);
- // Handle hydration mismatch
- useEffect(() => {
- setMounted(true);
- const stored = localStorage.getItem(storageKey) as Theme;
- if (stored) {
- setTheme(stored);
- }
- }, [storageKey]);
- useEffect(() => {
- if (!mounted) return;
- const root = window.document.documentElement;
- root.classList.remove("light", "dark");
- if (theme === "system") {
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
- .matches
- ? "dark"
- : "light";
- root.classList.add(systemTheme);
- return;
- }
- root.classList.add(theme);
- }, [theme, mounted]);
- const value = {
- theme,
- setTheme: (theme: Theme) => {
- localStorage.setItem(storageKey, theme);
- setTheme(theme);
- },
- };
- // Prevent hydration mismatch
- if (!mounted) {
- return <>{children}</>;
- }
- return (
- <ThemeProviderContext.Provider {...props} value={value}>
- {children}
- </ThemeProviderContext.Provider>
- );
- }
- export const useTheme = () => {
- const context = useContext(ThemeProviderContext);
- if (context === undefined)
- throw new Error("useTheme must be used within a ThemeProvider");
- return context;
- };
- // Create a client
- const queryClient = new QueryClient();
- export function Providers({ children }: { children: React.ReactNode }) {
- return (
- <QueryClientProvider client={queryClient}>
- <ThemeProvider>
- {children}
- </ThemeProvider>
- </QueryClientProvider>
- );
- }
|