Files
portabase/src/features/theme/components/mode-toggle.tsx
T

80 lines
3.7 KiB
TypeScript
Raw Normal View History

"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
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 })
}
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>
)
2026-02-10 17:02:06 +01:00
}