refactor: improve layout navbar

This commit is contained in:
moonrailgun 2024-04-02 21:55:20 +08:00
parent 3bb2cc8715
commit 6fbf316c5d
3 changed files with 121 additions and 71 deletions

View File

@ -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 };

View File

@ -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>

View File

@ -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,39 +48,8 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
} }
); );
return ( const navbar = (
<TooltipProvider delayDuration={0}> <>
<ResizablePanelGroup
direction="horizontal"
onLayout={(sizes: number[]) => {
if (sizes.length === 3) {
setLayout(sizes as typeof defaultLayout);
} else if (sizes.length === 2) {
const listSize = layout[1];
const rest = 100 - sizes[0] - listSize;
setLayout([sizes[0], listSize, rest]);
}
}}
className="h-full items-stretch"
>
<ResizablePanel
defaultSize={layout[0]}
collapsedSize={4}
collapsible={true}
minSize={15}
maxSize={20}
onCollapse={() => {
setIsCollapsed(true);
}}
onExpand={() => {
setIsCollapsed(false);
}}
className={cn(
'flex flex-col',
isCollapsed &&
'min-w-[50px] transition-all duration-300 ease-in-out'
)}
>
<div <div
className={cn( className={cn(
'flex h-[52px] items-center justify-center', 'flex h-[52px] items-center justify-center',
@ -129,7 +98,46 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
<div className="flex-1" /> <div className="flex-1" />
<Separator /> <Separator />
<div className={cn(isCollapsed && 'm-auto')}>
<UserConfig isCollapsed={isCollapsed} /> <UserConfig isCollapsed={isCollapsed} />
</div>
</>
);
return (
<TooltipProvider delayDuration={0}>
<ResizablePanelGroup
direction="horizontal"
onLayout={(sizes: number[]) => {
if (sizes.length === 3) {
setLayout(sizes as typeof defaultLayout);
} else if (sizes.length === 2) {
const listSize = layout[1];
const rest = 100 - sizes[0] - listSize;
setLayout([sizes[0], listSize, rest]);
}
}}
className="h-full items-stretch"
>
<ResizablePanel
defaultSize={layout[0]}
collapsedSize={1}
collapsible={true}
minSize={10}
maxSize={20}
onCollapse={() => {
setIsCollapsed(true);
}}
onExpand={() => {
setIsCollapsed(false);
}}
className={cn(
'flex flex-col',
isCollapsed &&
'min-w-[50px] transition-all duration-300 ease-in-out'
)}
>
{navbar}
</ResizablePanel> </ResizablePanel>
{props.list && ( {props.list && (