Skip to content

Commit

Permalink
refactor(admin layout): change layout style
Browse files Browse the repository at this point in the history
  • Loading branch information
liony823 committed Apr 24, 2024
1 parent 9744076 commit 243a154
Show file tree
Hide file tree
Showing 17 changed files with 378 additions and 144 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.3",
"react-resizable-panels": "^2.0.18",
"react-type-animation": "^3.2.0",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
Expand Down
127 changes: 127 additions & 0 deletions src/app/admin/_components/ResizableLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
'use client';
import { Button, Resizable, ResizableHandle, Separator } from '~/components';
import { Nav } from '~/app/admin/_components/nav';
import { Header } from '~/app/admin/_components/header';
import * as React from 'react';
import { ReactNode } from 'react';
import { cn } from '~/lib/utils';
import {
DashboardIcon,
MailboxIcon,
NotebookIcon,
RSSIcon,
SettingIcon
} from '~/assets';
import { ThemeSwitcher } from '~/app/_components/ThemeSwitcher';
import Link from 'next/link';
import Image from 'next/image';
import { DOMAIN, GITHUB } from '~/config/constants';
interface Props {
defaultLayout: number[] | undefined;
defaultCollapsed?: boolean;
navCollapsedSize: number;
children?: ReactNode;
}

export interface IMenu {
label: string;
icon: ReactNode;
link: string;
suffix?: ReactNode;
}

export const CommonMenus: IMenu[] = [
{ label: '数据看板', icon: <DashboardIcon />, link: '/admin/dashboard' },
{ label: '博客管理', icon: <RSSIcon />, link: '/admin/blog' },
{ label: '笔记管理', icon: <NotebookIcon />, link: '/admin/notebook' },
{ label: '留言管理', icon: <MailboxIcon />, link: '/admin/mailbox' }
];

export const SettingMenus: IMenu[] = [
{
label: '后台设置',
icon: <SettingIcon />,
link: '/admin/setting',
suffix: <ThemeSwitcher />
}
];

export function ResizableLayout({
children,
defaultLayout = [265, 1095],
defaultCollapsed = false,
navCollapsedSize
}: Props) {
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed);

return (
<Resizable.PanelGroup
direction="horizontal"
className="min-h-screen w-full bg-muted/40"
onLayout={(sizes: number[]) => {
document.cookie = `react-resizable-panels:layout=${JSON.stringify(
sizes
)}`;
}}
>
<Resizable.Panel
defaultSize={defaultLayout[0]}
collapsedSize={navCollapsedSize}
collapsible={true}
minSize={15}
maxSize={17.5}
onExpand={() => {
setIsCollapsed(false);
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(
false
)}`;
}}
onCollapse={() => {
setIsCollapsed(true);
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(
true
)}`;
}}
className={cn(
'hidden sm:block',
isCollapsed && 'min-w-[50px] transition-all duration-300 ease-in-out'
)}
>
<div className="flex items-center h-[56px] px-2">
<Button className="w-10 h-10 relative" size="icon" variant="outline">
<Image
className="h-full object-cover"
src={GITHUB.DEFAULLT_AVATAR}
alt={GITHUB.DEFAULLT_NAME}
width={40}
height={40}
priority
></Image>
</Button>

{!isCollapsed && (
<span className="font-bold tracking-wide text-2xl shrink text-primary italic ml-4">
{DOMAIN}
</span>
)}
</div>
<Separator />
<Nav isCollapsed={isCollapsed} menus={CommonMenus} />
<Separator />
<Nav isCollapsed={isCollapsed} menus={SettingMenus} />
</Resizable.Panel>
<ResizableHandle className="hidden sm:flex" withHandle />

<Resizable.Panel
defaultSize={defaultLayout[1]}
minSize={30}
className="transition-all duration-300 ease-in-out"
>
<div className="w-full h-full flex flex-col">
<Header />
<main className="p-4">{children}</main>
</div>
</Resizable.Panel>
</Resizable.PanelGroup>
);
}
37 changes: 21 additions & 16 deletions src/app/admin/_components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import { SheetNavbar } from '~/app/admin/_components/sheetNavbar';
import { HeaderBreadcrumb } from '~/app/admin/_components/headerBreadcrumb';
import { SearchIcon } from '~/assets';
import { Input } from '~/components/ui';
import { Input, Separator } from '~/components/ui';
import { UserInfo } from '~/app/_components/UserInfo';
import * as React from 'react';
import { ClientOnly } from '~/components';
import { ThemeSwitcher } from '~/app/_components/ThemeSwitcher';

export function Header() {
return (
<header className="sticky top-0 z-10 h-14 border-b bg-background flex items-center gap-4 px-4 sm:static sm:px-6 sm:h-auto sm:border-transparent sm:bg-transparent transition-colors">
<SheetNavbar />
<ClientOnly>
<HeaderBreadcrumb />
</ClientOnly>
<div>
<header className="sticky top-0 z-10 h-14 border-b bg-background flex items-center gap-4 px-4 sm:static sm:px-6 sm:16 sm:border-transparent sm:bg-transparent transition-colors">
<SheetNavbar />
<ClientOnly>
<HeaderBreadcrumb />
</ClientOnly>

<div className="relative flex-1 ml-auto sm:grow-0">
<SearchIcon className="absolute text-muted-foreground left-2.5 top-2.5" />
<Input
type="search"
placeholder="Search..."
className="rounded-lg sm:w-[200px] md:w-[280px] lg:w-[320px] bg-background pl-8"
/>
</div>
<UserInfo />
</header>
<div className="relative flex-1 ml-auto sm:grow-0">
<SearchIcon className="absolute text-muted-foreground left-2.5 top-2.5" />
<Input
type="search"
placeholder="Search..."
className="rounded-lg sm:w-[200px] md:w-[280px] lg:w-[320px] bg-background pl-8"
/>
</div>
<UserInfo />
<ThemeSwitcher side="bottom" className="hidden sm:flex" />
</header>
<Separator className="hidden sm:block" />
</div>
);
}
76 changes: 76 additions & 0 deletions src/app/admin/_components/nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client';
import Link from 'next/link';
import { buttonVariants, Tooltip } from '~/components/ui';
import * as React from 'react';
import { cn } from '~/lib/utils';
import { usePathname } from 'next/navigation';
import { IMenu } from '~/app/admin/_components/ResizableLayout';

export function Nav({
isCollapsed,
menus
}: {
isCollapsed: boolean;
menus: IMenu[];
}) {
const pathname = usePathname();
return (
<div
data-collapsed={isCollapsed}
className="group flex flex-col gap-4 py-2 data-[collapsed=true]:py-2"
>
<nav className="grid gap-1 px-2 group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2">
{menus.map((menu) =>
isCollapsed ? (
<Tooltip.Provider key={menu.link} delayDuration={0}>
<Tooltip>
<Tooltip.Trigger asChild>
<Link
href={menu.link}
className={cn(
buttonVariants({
variant: pathname.includes(menu.link)
? 'default'
: 'ghost',
size: 'icon'
}),
'h-9 w-9',
pathname.includes(menu.link) &&
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white'
)}
>
<span className="text-base">{menu.icon}</span>
<span className="sr-only">{menu.label}</span>
</Link>
</Tooltip.Trigger>
<Tooltip.Content
side="right"
className="flex items-center gap-4"
custom={false}
>
{menu.label}
</Tooltip.Content>
</Tooltip>
</Tooltip.Provider>
) : (
<Link
key={menu.link}
href={menu.link}
className={cn(
buttonVariants({
variant: pathname.includes(menu.link) ? 'default' : 'ghost'
}),
pathname.includes(menu.link) &&
'dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white',
'justify-start'
)}
>
<span className="text-base mr-2">{menu.icon}</span>
{menu.label}
</Link>
)
)}
</nav>
</div>
);
}
7 changes: 5 additions & 2 deletions src/app/admin/_components/sheetNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import Link from 'next/link';
import Image from 'next/image';
import { GITHUB } from '~/config/constants';
import * as React from 'react';
import { CommonMenus, SettingMenu } from '~/app/admin/_components/sidebar';
import {
SettingMenus,
CommonMenus
} from '~/app/admin/_components/ResizableLayout';

export function SheetNavbar() {
return (
Expand All @@ -28,7 +31,7 @@ export function SheetNavbar() {
priority
></Image>
</Link>
{[...CommonMenus, SettingMenu].map((menu) => (
{[...CommonMenus, ...SettingMenus].map((menu) => (
<div key={menu.link} className="flex justify-between">
<Link
href={menu.link}
Expand Down
102 changes: 0 additions & 102 deletions src/app/admin/_components/sidebar.tsx

This file was deleted.

Loading

0 comments on commit 243a154

Please sign in to comment.