重构布局组件,优化导航栏和头部结构;调整样式以改善响应式设计
This commit is contained in:
@@ -13,21 +13,21 @@ export default function Header(props: HeaderProps) {
|
||||
|
||||
return (
|
||||
<header className={merge(
|
||||
`flex-none h-16`,
|
||||
`flex-none h-16 overflow-hidden`,
|
||||
`flex items-stretch`,
|
||||
)}>
|
||||
{/* left */}
|
||||
<div className="flex-auto flex items-center gap-2">
|
||||
<Button
|
||||
theme="ghost"
|
||||
className="w-9 h-9"
|
||||
className="w-9 h-9 ml-4 md:ml-0"
|
||||
onClick={toggleNavbar}>
|
||||
{navbar
|
||||
? <PanelLeftCloseIcon/>
|
||||
: <PanelLeftOpenIcon/>
|
||||
}
|
||||
</Button>
|
||||
<span>
|
||||
<span className="max-md:hidden">
|
||||
欢迎来到,蓝狐代理
|
||||
</span>
|
||||
</div>
|
||||
|
||||
81
src/app/admin/_client/layout.tsx
Normal file
81
src/app/admin/_client/layout.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
'use client'
|
||||
import {ReactNode} from 'react'
|
||||
import {useLayoutStore} from '@/components/providers/StoreProvider'
|
||||
import {merge} from '@/lib/utils'
|
||||
import {Slot} from '@radix-ui/react-slot'
|
||||
|
||||
type AdminLayoutProps = {
|
||||
navbar: ReactNode
|
||||
header: ReactNode
|
||||
content: ReactNode
|
||||
}
|
||||
|
||||
export default function Layout(props: AdminLayoutProps) {
|
||||
const navbar = useLayoutStore(store => store.navbar)
|
||||
const setNevBar = useLayoutStore(store => store.setNavbar)
|
||||
|
||||
return (
|
||||
<div className="relative h-dvh overflow-hidden">
|
||||
|
||||
{/* 结构 */}
|
||||
<div
|
||||
data-expand={navbar}
|
||||
className={merge(
|
||||
`transition-[grid-template-columns] duration-300 ease-in-out`,
|
||||
`w-full h-full grid`,
|
||||
`grid-rows-[64px_1fr]`,
|
||||
`data-[expand=true]:grid-cols-[200px_1fr]`,
|
||||
`data-[expand=false]:grid-cols-[0px_1fr]`,
|
||||
`md:data-[expand=false]:grid-cols-[64px_1fr]`,
|
||||
)}
|
||||
>
|
||||
<div className="col-start-1 row-start-1 row-span-2 bg-card overflow-hidden relative z-20">
|
||||
{props.navbar}
|
||||
</div>
|
||||
|
||||
<div className="col-start-2 row-start-1 bg-card overflow-hidden relative z-20">
|
||||
{props.header}
|
||||
</div>
|
||||
|
||||
<svg className="col-start-2 row-start-2 w-full h-full z-20 pointer-events-none" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<mask id="top-left-rounded-mask">
|
||||
<rect width="100%" height="100%" fill="white"/>
|
||||
<circle cx="16" cy="16" r="16" fill="black"/>
|
||||
<rect x="16" y="0" width="100%" height="32" fill="black"/>
|
||||
<rect x="0" y="16" width="32" height="100%" fill="black"/>
|
||||
<rect x="16" y="16" width="100%" height="100%" fill="black"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" className="fill-card" mask="url(#top-left-rounded-mask)"/>
|
||||
</svg>
|
||||
|
||||
<div
|
||||
data-expand={navbar}
|
||||
className={merge(
|
||||
`md:hidden`,
|
||||
`transition-opacity duration-300 ease-in-out`,
|
||||
`col-start-1 row-start-1 col-span-2 row-span-2 bg-black/50 z-10`,
|
||||
`max-md:data-[expand=true]:opacity-100 data-[expand=false]:opacity-0`,
|
||||
`max-md:data-[expand=true]:pointer-events-auto data-[expand=false]:pointer-events-none`,
|
||||
)}
|
||||
onClick={() => setNevBar(false)}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 内容 */}
|
||||
<div
|
||||
data-expand={navbar}
|
||||
className={merge(
|
||||
`transition-[margin] duration-300 ease-in-out`,
|
||||
`absolute inset-0 overflow-hidden`,
|
||||
`mt-[64px]`,
|
||||
`md:data-[expand=true]:ml-[200px]`,
|
||||
`md:data-[expand=false]:ml-[64px]`,
|
||||
)}>
|
||||
{props.content}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import {ReactNode, useState} from 'react'
|
||||
import {ComponentProps, ReactNode, useState} from 'react'
|
||||
import {merge} from '@/lib/utils'
|
||||
import {useLayoutStore} from '@/components/providers/StoreProvider'
|
||||
import Link from 'next/link'
|
||||
@@ -19,7 +19,7 @@ import {Eye} from 'lucide-react'
|
||||
import {Archive} from 'lucide-react'
|
||||
import {ArchiveRestore} from 'lucide-react'
|
||||
|
||||
export type NavbarProps = {}
|
||||
export type NavbarProps = {} & ComponentProps<'nav'>
|
||||
|
||||
export default function Navbar(props: NavbarProps) {
|
||||
const navbar = useLayoutStore(store => store.navbar)
|
||||
@@ -29,10 +29,8 @@ export default function Navbar(props: NavbarProps) {
|
||||
data-expand={navbar}
|
||||
className={merge(
|
||||
`transition-[flex-basis] duration-300 ease-in-out`,
|
||||
`flex-none`,
|
||||
`flex flex-col overflow-hidden group`,
|
||||
`data-[expand=true]:basis-52 data-[expand=false]:basis-16`,
|
||||
` `,
|
||||
)}>
|
||||
{/* logo */}
|
||||
<Link
|
||||
@@ -56,7 +54,7 @@ export default function Navbar(props: NavbarProps) {
|
||||
{/* routes */}
|
||||
<section className={merge(
|
||||
`transition-[padding] duration-300 ease-in-out`,
|
||||
`flex-auto overflow-auto`,
|
||||
`flex-auto overflow-x-hidden overflow-y-auto`,
|
||||
`group-data-[expand=true]:px-4 group-data-[expand=false]:px-3`,
|
||||
)}>
|
||||
<TooltipProvider>
|
||||
@@ -119,6 +117,13 @@ function NavItem(props: {
|
||||
}
|
||||
}
|
||||
|
||||
const setNavbar = useLayoutStore(store => store.setNavbar)
|
||||
const closeNavBarIfMobile = () => {
|
||||
if (window.innerWidth < 768) {
|
||||
setNavbar(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip open={open} onOpenChange={handleOpenChange}>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -129,7 +134,9 @@ function NavItem(props: {
|
||||
`hover:bg-gray-100`,
|
||||
`group-data-[expand=true]:px-4`,
|
||||
)}
|
||||
href={props.href}>
|
||||
href={props.href}
|
||||
onClick={closeNavBarIfMobile}
|
||||
>
|
||||
<span className="flex-none w-10 h-10 flex items-center justify-center">{props.icon}</span>
|
||||
<span className={merge(
|
||||
`flex-auto`,
|
||||
|
||||
Reference in New Issue
Block a user