diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index ef92abcf..ec966b14 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/badge'; import { Link, useNavigate } from 'react-router-dom'; import { SearchDropdown } from '@/components/search/SearchDropdown'; import { AuthButtons } from '@/components/auth/AuthButtons'; +import { ThemeToggle } from '@/components/theme/ThemeToggle'; export function Header() { const navigate = useNavigate(); @@ -67,6 +68,7 @@ export function Header() { {/* User Actions */}
+ {/* Mobile Menu */} @@ -105,6 +107,9 @@ export function Header() { 🏭 Manufacturers
+
+ +
diff --git a/src/components/theme/ThemeProvider.tsx b/src/components/theme/ThemeProvider.tsx new file mode 100644 index 00000000..ee5f2dbf --- /dev/null +++ b/src/components/theme/ThemeProvider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react" + +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(initialState) + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme + ) + + useEffect(() => { + 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]) + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme) + setTheme(theme) + }, + } + + return ( + + {children} + + ) +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext) + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider") + + return context +} \ No newline at end of file diff --git a/src/components/theme/ThemeToggle.tsx b/src/components/theme/ThemeToggle.tsx new file mode 100644 index 00000000..a8622e8e --- /dev/null +++ b/src/components/theme/ThemeToggle.tsx @@ -0,0 +1,36 @@ +import { Moon, Sun } from "lucide-react" +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { useTheme } from "@/components/theme/ThemeProvider" + +export function ThemeToggle() { + const { setTheme } = useTheme() + + return ( + + + + + + setTheme("light")} className="cursor-pointer"> + Light + + setTheme("dark")} className="cursor-pointer"> + Dark + + setTheme("system")} className="cursor-pointer"> + System + + + + ) +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 68176a72..eb38f776 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,10 @@ import { createRoot } from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; +import { ThemeProvider } from "@/components/theme/ThemeProvider"; -createRoot(document.getElementById("root")!).render(); +createRoot(document.getElementById("root")!).render( + + + +);