refactor: improve layout navbar
This commit is contained in:
parent
3bb2cc8715
commit
6fbf316c5d
@ -1,22 +1,36 @@
|
|||||||
import * as React from "react"
|
import * as React from 'react';
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||||
|
|
||||||
import { cn } from "@/utils/style"
|
import { cn } from '@/utils/style';
|
||||||
|
import { VariantProps, cva } from 'class-variance-authority';
|
||||||
|
|
||||||
|
const avatarVariants = cva(
|
||||||
|
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
default: 'h-10 w-10',
|
||||||
|
sm: 'h-8 w-8',
|
||||||
|
lg: 'h-12 w-12',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
interface AvatarProps
|
||||||
|
extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
|
||||||
|
VariantProps<typeof avatarVariants> {}
|
||||||
const Avatar = React.forwardRef<
|
const Avatar = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
AvatarProps
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, size, ...props }, ref) => (
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(avatarVariants({ size, className }))}
|
||||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
Avatar.displayName = AvatarPrimitive.Root.displayName
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||||
|
|
||||||
const AvatarImage = React.forwardRef<
|
const AvatarImage = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||||
@ -24,11 +38,11 @@ const AvatarImage = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<AvatarPrimitive.Image
|
<AvatarPrimitive.Image
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("aspect-square h-full w-full", className)}
|
className={cn('aspect-square h-full w-full', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||||
|
|
||||||
const AvatarFallback = React.forwardRef<
|
const AvatarFallback = React.forwardRef<
|
||||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||||
@ -37,12 +51,12 @@ const AvatarFallback = React.forwardRef<
|
|||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
'bg-muted flex h-full w-full items-center justify-center rounded-full',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||||
|
|
||||||
export { Avatar, AvatarImage, AvatarFallback }
|
export { Avatar, AvatarImage, AvatarFallback };
|
||||||
|
@ -12,6 +12,8 @@ import {
|
|||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuRadioGroup,
|
DropdownMenuRadioGroup,
|
||||||
DropdownMenuRadioItem,
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuLabel,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { useEvent } from '@/hooks/useEvent';
|
import { useEvent } from '@/hooks/useEvent';
|
||||||
import { useSettingsStore } from '@/store/settings';
|
import { useSettingsStore } from '@/store/settings';
|
||||||
@ -19,6 +21,7 @@ import { useUserInfo } from '@/store/user';
|
|||||||
import { languages } from '@/utils/constants';
|
import { languages } from '@/utils/constants';
|
||||||
import { useTranslation, setLanguage } from '@i18next-toolkit/react';
|
import { useTranslation, setLanguage } from '@i18next-toolkit/react';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
|
import { version } from '@tianji/shared';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { LuMoreVertical } from 'react-icons/lu';
|
import { LuMoreVertical } from 'react-icons/lu';
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ export const UserConfig: React.FC<UserConfigProps> = React.memo((props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const avatar = (
|
const avatar = (
|
||||||
<Avatar>
|
<Avatar size={props.isCollapsed ? 'sm' : 'default'}>
|
||||||
<AvatarFallback>
|
<AvatarFallback>
|
||||||
{(userInfo?.username ?? '').substring(0, 2).toUpperCase()}
|
{(userInfo?.username ?? '').substring(0, 2).toUpperCase()}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
@ -134,6 +137,31 @@ export const UserConfig: React.FC<UserConfigProps> = React.memo((props) => {
|
|||||||
</DropdownMenuSubContent>
|
</DropdownMenuSubContent>
|
||||||
</DropdownMenuPortal>
|
</DropdownMenuPortal>
|
||||||
</DropdownMenuSub>
|
</DropdownMenuSub>
|
||||||
|
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>{t('Community')}</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuPortal>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => window.open('https://discord.gg/8Vv47wAEej')}
|
||||||
|
>
|
||||||
|
{t('Join Discord')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => window.open('https://twitter.com/moonrailgun')}
|
||||||
|
>
|
||||||
|
{t('Follow Twitter')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
|
||||||
|
<DropdownMenuLabel className="text-muted">
|
||||||
|
v{version}
|
||||||
|
</DropdownMenuLabel>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@ import { WorkspaceSwitcher } from '@/components/WorkspaceSwitcher';
|
|||||||
import { UserConfig } from './Layout/UserConfig';
|
import { UserConfig } from './Layout/UserConfig';
|
||||||
import { Outlet } from '@tanstack/react-router';
|
import { Outlet } from '@tanstack/react-router';
|
||||||
import { trpc } from '@/api/trpc';
|
import { trpc } from '@/api/trpc';
|
||||||
import { useCurrentWorkspaceId, useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
|
|
||||||
const defaultLayout: [number, number, number] = [265, 440, 655];
|
const defaultLayout: [number, number, number] = [265, 440, 655];
|
||||||
|
|
||||||
@ -48,6 +48,62 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const navbar = (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex h-[52px] items-center justify-center',
|
||||||
|
isCollapsed ? 'h-[52px]' : 'px-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<WorkspaceSwitcher isCollapsed={isCollapsed} />
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<Nav
|
||||||
|
isCollapsed={isCollapsed}
|
||||||
|
links={[
|
||||||
|
{
|
||||||
|
title: 'Website',
|
||||||
|
label: String(serviceCount?.website ?? ''),
|
||||||
|
icon: LuAreaChart,
|
||||||
|
to: '/website',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Monitor',
|
||||||
|
label: String(serviceCount?.monitor ?? ''),
|
||||||
|
icon: LuMonitorDot,
|
||||||
|
to: '/monitor',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Servers',
|
||||||
|
label: '',
|
||||||
|
icon: LuServer,
|
||||||
|
to: '/server',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Telemetry',
|
||||||
|
label: String(serviceCount?.telemetry ?? ''),
|
||||||
|
icon: LuWifi,
|
||||||
|
to: '/telemetry',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Pages',
|
||||||
|
label: String(serviceCount?.page ?? ''),
|
||||||
|
icon: LuFilePieChart,
|
||||||
|
to: '/page',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex-1" />
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<div className={cn(isCollapsed && 'm-auto')}>
|
||||||
|
<UserConfig isCollapsed={isCollapsed} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipProvider delayDuration={0}>
|
<TooltipProvider delayDuration={0}>
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
@ -65,9 +121,9 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
|
|||||||
>
|
>
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
defaultSize={layout[0]}
|
defaultSize={layout[0]}
|
||||||
collapsedSize={4}
|
collapsedSize={1}
|
||||||
collapsible={true}
|
collapsible={true}
|
||||||
minSize={15}
|
minSize={10}
|
||||||
maxSize={20}
|
maxSize={20}
|
||||||
onCollapse={() => {
|
onCollapse={() => {
|
||||||
setIsCollapsed(true);
|
setIsCollapsed(true);
|
||||||
@ -81,55 +137,7 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
|
|||||||
'min-w-[50px] transition-all duration-300 ease-in-out'
|
'min-w-[50px] transition-all duration-300 ease-in-out'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
{navbar}
|
||||||
className={cn(
|
|
||||||
'flex h-[52px] items-center justify-center',
|
|
||||||
isCollapsed ? 'h-[52px]' : 'px-2'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<WorkspaceSwitcher isCollapsed={isCollapsed} />
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
<Nav
|
|
||||||
isCollapsed={isCollapsed}
|
|
||||||
links={[
|
|
||||||
{
|
|
||||||
title: 'Website',
|
|
||||||
label: String(serviceCount?.website ?? ''),
|
|
||||||
icon: LuAreaChart,
|
|
||||||
to: '/website',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Monitor',
|
|
||||||
label: String(serviceCount?.monitor ?? ''),
|
|
||||||
icon: LuMonitorDot,
|
|
||||||
to: '/monitor',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Servers',
|
|
||||||
label: '',
|
|
||||||
icon: LuServer,
|
|
||||||
to: '/server',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Telemetry',
|
|
||||||
label: String(serviceCount?.telemetry ?? ''),
|
|
||||||
icon: LuWifi,
|
|
||||||
to: '/telemetry',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Pages',
|
|
||||||
label: String(serviceCount?.page ?? ''),
|
|
||||||
icon: LuFilePieChart,
|
|
||||||
to: '/page',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<Separator />
|
|
||||||
<div className="flex-1" />
|
|
||||||
<Separator />
|
|
||||||
|
|
||||||
<UserConfig isCollapsed={isCollapsed} />
|
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
|
||||||
{props.list && (
|
{props.list && (
|
||||||
|
Loading…
Reference in New Issue
Block a user