feat(web): тёмная тема (system/manual) и фикс логотипа в icon-режиме сайдбара
- ThemeProvider (next-themes, attribute=class, defaultTheme=system) - ModeToggle в header: светлая / тёмная / системная - suppressHydrationWarning на <html> против FOUC - логотип в SidebarHeader через SidebarMenuButton size="lg" (корректное схлопывание в icon-режиме, aspect-square для иконки) Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -37,6 +37,8 @@ import { Separator } from '@cfdm/ui/components/separator'
|
||||
import { Link, useRouterState } from '@tanstack/react-router'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
import { ModeToggle } from '@/components/mode-toggle'
|
||||
|
||||
interface NavItem {
|
||||
to: string
|
||||
label: string
|
||||
@@ -68,15 +70,22 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
<SidebarProvider>
|
||||
<Sidebar collapsible="icon">
|
||||
<SidebarHeader>
|
||||
<div className="flex items-center gap-2 px-2 py-1.5">
|
||||
<div className="flex size-8 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
||||
<Server className="size-4" />
|
||||
</div>
|
||||
<div className="flex flex-col text-left text-sm leading-tight group-data-[collapsible=icon]:hidden">
|
||||
<span className="font-semibold">VPS Tracker</span>
|
||||
<span className="text-xs text-muted-foreground">Учёт виртуальных серверов</span>
|
||||
</div>
|
||||
</div>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" render={<Link to="/dashboard" />}>
|
||||
<div
|
||||
className="flex aspect-square size-8 items-center justify-center rounded-md bg-primary text-primary-foreground"
|
||||
aria-hidden
|
||||
>
|
||||
<Server className="size-4" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 leading-none">
|
||||
<span className="font-semibold">VPS Tracker</span>
|
||||
<span className="text-xs text-muted-foreground">Учёт виртуальных серверов</span>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<SidebarGroup>
|
||||
@@ -116,6 +125,9 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<div className="ml-auto">
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex flex-1 flex-col gap-4 p-4 md:gap-6 md:p-6">{children}</main>
|
||||
</SidebarInset>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Moon, Sun } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
import { Button } from '@cfdm/ui/components/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@cfdm/ui/components/dropdown-menu'
|
||||
|
||||
export function ModeToggle() {
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger render={<Button variant="ghost" size="icon" />}>
|
||||
<Sun className="size-5 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
||||
<Moon className="absolute size-5 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
||||
<span className="sr-only">Сменить тему</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme('light')}>Светлая</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('dark')}>Тёмная</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme('system')}>Системная</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ThemeProvider as NextThemesProvider } from 'next-themes'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export function ThemeProvider({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<NextThemesProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user