59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import { Monitor, Moon, Sun } from "lucide-react"
|
|
import { Button } from "@/shared/ui/button"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/shared/ui/dropdown-menu"
|
|
import { useThemeStore, type Theme } from "@/shared/store/themeStore"
|
|
|
|
interface ThemeToggleProps {
|
|
/** 트리거에 라벨 텍스트 함께 보일지. 기본 false (아이콘만). */
|
|
showLabel?: boolean
|
|
}
|
|
|
|
const ITEMS: { value: Theme; label: string; icon: typeof Sun }[] = [
|
|
{ value: "light", label: "라이트", icon: Sun },
|
|
{ value: "dark", label: "다크", icon: Moon },
|
|
{ value: "system", label: "시스템", icon: Monitor },
|
|
]
|
|
|
|
export function ThemeToggle({ showLabel = false }: ThemeToggleProps) {
|
|
const theme = useThemeStore((s) => s.theme)
|
|
const resolved = useThemeStore((s) => s.resolved)
|
|
const setTheme = useThemeStore((s) => s.setTheme)
|
|
|
|
const TriggerIcon = resolved === "dark" ? Moon : Sun
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size={showLabel ? "default" : "icon"}
|
|
aria-label="모드 변경"
|
|
aria-haspopup="menu"
|
|
>
|
|
<TriggerIcon className="h-4 w-4" aria-hidden="true" />
|
|
{showLabel ? <span className="ml-2 text-sm">모드</span> : null}
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-32">
|
|
{ITEMS.map(({ value, label, icon: Icon }) => (
|
|
<DropdownMenuItem
|
|
key={value}
|
|
onClick={() => setTheme(value)}
|
|
aria-current={theme === value ? "true" : undefined}
|
|
className="gap-2"
|
|
>
|
|
<Icon className="h-4 w-4" aria-hidden="true" />
|
|
<span>{label}</span>
|
|
{theme === value ? <span className="ml-auto text-xs">●</span> : null}
|
|
</DropdownMenuItem>
|
|
))}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|