feat: add shadcn theme switcher

This commit is contained in:
hailin
2026-07-02 15:57:35 +08:00
parent ce423bd90b
commit 32ed881732
5 changed files with 110 additions and 3 deletions

View File

@@ -0,0 +1,14 @@
import type * as React from 'react'
import { ThemeProvider } from 'next-themes'
type AppThemeProviderProps = {
children: React.ReactNode
}
export function AppThemeProvider({ children }: AppThemeProviderProps) {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
{children}
</ThemeProvider>
)
}

View File

@@ -0,0 +1,53 @@
import { Laptop, Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
const themeOptions = [
{ value: 'light', label: '亮色', icon: Sun },
{ value: 'dark', label: '暗色', icon: Moon },
{ value: 'system', label: '跟随系统', icon: Laptop }
]
export function AppThemeToggle() {
const { theme = 'system', setTheme } = useTheme()
const activeTheme = themeOptions.find((option) => option.value === theme) ?? themeOptions[2]
const ActiveIcon = activeTheme.icon
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" aria-label={`主题:${activeTheme.label}`}>
<ActiveIcon data-icon="inline-start" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuLabel>界面主题</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
{themeOptions.map((option) => {
const Icon = option.icon
return (
<DropdownMenuRadioItem key={option.value} value={option.value}>
<Icon data-icon="inline-start" />
{option.label}
</DropdownMenuRadioItem>
)
})}
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -1,5 +1,6 @@
import { Bell, ChevronRight, LogOut, Menu, UserRound } from 'lucide-react'
import { AppThemeToggle } from '@/components/app/AppThemeToggle'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
@@ -34,6 +35,7 @@ export function AppTopbar({ onMobileMenuClick }: AppTopbarProps) {
<Button variant="ghost" size="icon" aria-label="通知">
<Bell data-icon="inline-start" />
</Button>
<AppThemeToggle />
<Button variant="ghost" className="hidden gap-2 sm:inline-flex">
<UserRound data-icon="inline-start" />
<span>{user?.display_name || user?.username || '当前用户'}</span>