2024-11-10 11:01:59 +01:00
|
|
|
"use client"
|
2026-02-12 15:48:51 +01:00
|
|
|
import { Moon, Sun, Check, SunMoon } from "lucide-react"
|
2026-02-10 17:02:06 +01:00
|
|
|
import { useTheme } from "next-themes"
|
2026-05-28 20:50:05 +02:00
|
|
|
import { useEffect, useState } from "react"
|
2026-02-12 15:48:51 +01:00
|
|
|
import { authClient } from "@/lib/auth/auth-client"
|
|
|
|
|
|
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
import {
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuItem,
|
|
|
|
|
DropdownMenuTrigger,
|
|
|
|
|
} from "@/components/ui/dropdown-menu"
|
2026-02-10 17:02:06 +01:00
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
|
|
|
|
|
|
const themes = [
|
|
|
|
|
{ id: "light", icon: Sun, label: "Light" },
|
|
|
|
|
{ id: "dark", icon: Moon, label: "Dark" },
|
2026-02-12 15:48:51 +01:00
|
|
|
{ id: "system", icon: SunMoon, label: "System" },
|
2026-02-10 17:02:06 +01:00
|
|
|
] as const
|
2024-11-10 11:01:59 +01:00
|
|
|
|
|
|
|
|
export function ModeToggle() {
|
2026-02-10 17:02:06 +01:00
|
|
|
const { theme } = useTheme()
|
2026-05-28 20:50:05 +02:00
|
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
|
useEffect(() => setMounted(true), [])
|
2025-12-22 20:32:18 +01:00
|
|
|
|
2026-02-10 17:02:06 +01:00
|
|
|
const handleThemeChange = async (newTheme: "light" | "system" | "dark") => {
|
|
|
|
|
await authClient.updateUser({ theme: newTheme })
|
|
|
|
|
}
|
2024-11-10 11:01:59 +01:00
|
|
|
|
|
|
|
|
return (
|
2026-02-12 15:48:51 +01:00
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
<Button variant="ghost" size="icon" className="size-8 rounded-full border border-input bg-transparent shadow-xs transition-transform active:scale-95">
|
2026-05-28 20:50:05 +02:00
|
|
|
{!mounted || theme === "system" ? <SunMoon className="size-4" /> : theme === "light" ? <Sun className="size-4" /> : <Moon className="size-4" />}
|
2026-02-12 15:48:51 +01:00
|
|
|
<span className="sr-only">Toggle theme</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent align="end" className="min-w-37.5 rounded-xl border-2 border-border p-1 shadow-none">
|
2026-02-10 17:02:06 +01:00
|
|
|
{themes.map((t) => {
|
2026-02-12 15:48:51 +01:00
|
|
|
const ThemeIcon = t.icon
|
2026-02-10 17:02:06 +01:00
|
|
|
const isActive = theme === t.id
|
|
|
|
|
return (
|
2026-02-12 15:48:51 +01:00
|
|
|
<DropdownMenuItem
|
2026-02-10 17:02:06 +01:00
|
|
|
key={t.id}
|
|
|
|
|
onClick={() => handleThemeChange(t.id)}
|
|
|
|
|
className={cn(
|
2026-02-12 15:48:51 +01:00
|
|
|
"group gap-2 p-2 cursor-pointer rounded-lg mb-1 last:mb-0 transition-colors",
|
|
|
|
|
isActive
|
|
|
|
|
? "bg-primary/10 text-primary border border-primary/20"
|
|
|
|
|
: "focus:bg-accent hover:bg-accent/50 border border-transparent"
|
2026-02-10 17:02:06 +01:00
|
|
|
)}
|
|
|
|
|
>
|
2026-02-12 15:48:51 +01:00
|
|
|
<div className={cn(
|
|
|
|
|
"flex size-7 shrink-0 items-center justify-center rounded-md border shadow-sm transition-all group-hover:shadow-md",
|
|
|
|
|
isActive ? "bg-primary text-primary-foreground border-primary/30" : "bg-muted/50 border-border"
|
|
|
|
|
)}>
|
|
|
|
|
<ThemeIcon className={cn(
|
|
|
|
|
"size-4",
|
|
|
|
|
isActive ? "text-primary-foreground" : "text-muted-foreground"
|
|
|
|
|
)} />
|
|
|
|
|
</div>
|
|
|
|
|
<span className={cn(
|
|
|
|
|
"text-sm font-medium leading-none flex-1",
|
|
|
|
|
isActive ? "text-primary" : ""
|
|
|
|
|
)}>
|
|
|
|
|
{t.label}
|
|
|
|
|
</span>
|
|
|
|
|
{isActive && (
|
|
|
|
|
<div className="flex size-4 items-center justify-center rounded-full bg-primary shadow-sm ml-auto">
|
|
|
|
|
<Check className="size-2.5 text-primary-foreground" strokeWidth={3} />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</DropdownMenuItem>
|
2026-02-10 17:02:06 +01:00
|
|
|
)
|
|
|
|
|
})}
|
2026-02-12 15:48:51 +01:00
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
2024-11-10 11:01:59 +01:00
|
|
|
)
|
2026-02-10 17:02:06 +01:00
|
|
|
}
|