Просмотр исходного кода

refactor: extract redundant codes into layout component

satnaing 2 лет назад
Родитель
Сommit
1bc9368eed

+ 60 - 0
src/components/custom/layout.tsx

@@ -0,0 +1,60 @@
+import * as React from 'react'
+import { cn } from '@/lib/utils'
+
+interface LayoutProps extends React.HTMLAttributes<HTMLDivElement> {
+  fadedBelow?: boolean
+  fixedHeight?: boolean
+}
+
+const Layout = React.forwardRef<HTMLDivElement, LayoutProps>(
+  ({ className, fadedBelow = false, fixedHeight = false, ...props }, ref) => (
+    <div
+      ref={ref}
+      className={cn(
+        'relative flex h-full w-full flex-col',
+        fadedBelow &&
+          'after:pointer-events-none after:absolute after:bottom-0 after:left-0 after:hidden after:h-32 after:w-full after:bg-[linear-gradient(180deg,_transparent_10%,_hsl(var(--background))_70%)] after:md:block',
+        fixedHeight && 'md:h-svh',
+        className
+      )}
+      {...props}
+    />
+  )
+)
+Layout.displayName = 'Layout'
+
+const LayoutHeader = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+  <div
+    ref={ref}
+    className={cn(
+      'flex h-[var(--header-height)] flex-none items-center gap-4 bg-background p-4 md:px-8',
+      className
+    )}
+    {...props}
+  />
+))
+LayoutHeader.displayName = 'LayoutHeader'
+
+interface LayoutBodyProps extends React.HTMLAttributes<HTMLDivElement> {
+  fixedHeight?: boolean
+}
+
+const LayoutBody = React.forwardRef<HTMLDivElement, LayoutBodyProps>(
+  ({ className, fixedHeight, ...props }, ref) => (
+    <div
+      ref={ref}
+      className={cn(
+        'flex-1 overflow-hidden px-4 py-6 md:px-8',
+        fixedHeight && 'h-[calc(100%-var(--header-height))]',
+        className
+      )}
+      {...props}
+    />
+  )
+)
+LayoutBody.displayName = 'LayoutBody'
+
+export { Layout, LayoutHeader, LayoutBody }

+ 7 - 6
src/components/sidebar.tsx

@@ -1,5 +1,6 @@
 import { useEffect, useState } from 'react'
 import { IconChevronsLeft, IconMenu2, IconX } from '@tabler/icons-react'
+import { Layout, LayoutHeader } from './custom/layout'
 import { Button } from './ui/button'
 import Nav from './nav'
 import { cn } from '@/lib/utils'
@@ -39,10 +40,10 @@ export default function Sidebar2({
         className={`absolute inset-0 transition-[opacity] delay-100 duration-700 ${navOpened ? 'h-svh opacity-50' : 'h-0 opacity-0'} w-full bg-black md:hidden`}
       />
 
-      <div className='relative h-full w-full'>
+      <Layout>
         {/* Header */}
-        <div className='sidebar-header sticky top-0 flex h-16 items-center justify-between bg-background px-4 py-3 shadow'>
-          <div className={` flex items-center ${!isCollapsed ? 'gap-2' : ''}`}>
+        <LayoutHeader className='sticky top-0 justify-between px-4 py-3 shadow md:px-4'>
+          <div className={`flex items-center ${!isCollapsed ? 'gap-2' : ''}`}>
             <svg
               xmlns='http://www.w3.org/2000/svg'
               viewBox='0 0 256 256'
@@ -93,12 +94,12 @@ export default function Sidebar2({
           >
             {navOpened ? <IconX /> : <IconMenu2 />}
           </Button>
-        </div>
+        </LayoutHeader>
 
         {/* Navigation links */}
         <Nav
           id='sidebar-menu'
-          className={`h-[calc(100%-4rem)] overflow-auto ${navOpened ? 'max-h-screen' : 'max-h-0 py-0 md:max-h-screen md:py-2'}`}
+          className={`h-full flex-1 overflow-auto ${navOpened ? 'max-h-screen' : 'max-h-0 py-0 md:max-h-screen md:py-2'}`}
           closeNav={() => setNavOpened(false)}
           isCollapsed={isCollapsed}
           links={sidelinks}
@@ -116,7 +117,7 @@ export default function Sidebar2({
             className={`h-5 w-5 ${isCollapsed ? 'rotate-180' : ''}`}
           />
         </Button>
-      </div>
+      </Layout>
     </aside>
   )
 }

+ 4 - 0
src/index.css

@@ -4,6 +4,10 @@
 
 @layer base {
   :root {
+    /* Custom CSS Variables */
+    --header-height: 4rem;
+
+    /* CSS Variables for light theme */
     --background: 0 0% 100%;
     --foreground: 222.2 84% 4.9%;
 

+ 7 - 6
src/pages/dashboard/index.tsx

@@ -11,24 +11,25 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
 import ThemeSwitch from '@/components/theme-switch'
 import { TopNav } from '@/components/top-nav'
 import { UserNav } from '@/components/user-nav'
+import { Layout, LayoutBody, LayoutHeader } from '@/components/custom/layout'
 import { RecentSales } from './components/recent-sales'
 import { Overview } from './components/overview'
 
 export default function Dashboard() {
   return (
-    <>
+    <Layout>
       {/* ===== Top Heading ===== */}
-      <div className='flex h-16 items-center gap-4 px-4 md:px-8'>
+      <LayoutHeader>
         <TopNav links={topNav} />
         <div className='ml-auto flex items-center space-x-4'>
           <Search />
           <ThemeSwitch />
           <UserNav />
         </div>
-      </div>
+      </LayoutHeader>
 
       {/* ===== Main ===== */}
-      <div className='space-y-4 px-4 py-6 pt-6 md:px-8'>
+      <LayoutBody className='space-y-4'>
         <div className='flex items-center justify-between space-y-2'>
           <h1 className='text-2xl font-bold tracking-tight md:text-3xl'>
             Dashboard
@@ -177,8 +178,8 @@ export default function Dashboard() {
             </div>
           </TabsContent>
         </Tabs>
-      </div>
-    </>
+      </LayoutBody>
+    </Layout>
   )
 }
 

+ 7 - 6
src/pages/settings/index.tsx

@@ -11,21 +11,22 @@ import { Search } from '@/components/search'
 import { Separator } from '@/components/ui/separator'
 import ThemeSwitch from '@/components/theme-switch'
 import { UserNav } from '@/components/user-nav'
+import { Layout, LayoutBody, LayoutHeader } from '@/components/custom/layout'
 import SidebarNav from './components/sidebar-nav'
 
 export default function Settings() {
   return (
-    <div className='relative flex flex-col after:pointer-events-none after:absolute after:bottom-0 after:left-0 after:hidden after:h-32 after:w-full after:bg-[linear-gradient(180deg,_transparent_10%,_hsl(var(--background))_70%)] md:h-svh after:md:block'>
+    <Layout fadedBelow fixedHeight>
       {/* ===== Top Heading ===== */}
-      <div className='flex h-16 flex-none items-center gap-4 bg-background p-4 md:px-8'>
+      <LayoutHeader>
         <Search />
         <div className='ml-auto flex items-center space-x-4'>
           <ThemeSwitch />
           <UserNav />
         </div>
-      </div>
+      </LayoutHeader>
 
-      <div className='flex h-[calc(100%-4rem)] flex-1 flex-col overflow-hidden px-4 pb-6 pt-6 md:px-10'>
+      <LayoutBody className='flex flex-col' fixedHeight>
         <div className='space-y-0.5'>
           <h1 className='text-2xl font-bold tracking-tight md:text-3xl'>
             Settings
@@ -45,8 +46,8 @@ export default function Settings() {
             </div>
           </div>
         </div>
-      </div>
-    </div>
+      </LayoutBody>
+    </Layout>
   )
 }
 

+ 7 - 6
src/pages/tasks/index.tsx

@@ -1,23 +1,24 @@
 import { Search } from '@/components/search'
 import ThemeSwitch from '@/components/theme-switch'
 import { UserNav } from '@/components/user-nav'
+import { Layout, LayoutBody, LayoutHeader } from '@/components/custom/layout'
 import { DataTable } from './components/data-table'
 import { columns } from './components/columns'
 import { tasks } from './data/tasks'
 
 export default function Tasks() {
   return (
-    <div className='flex flex-col md:h-svh'>
+    <Layout>
       {/* ===== Top Heading ===== */}
-      <div className='flex h-16 flex-none items-center gap-4 bg-background p-4 md:px-8'>
+      <LayoutHeader>
         <Search />
         <div className='ml-auto flex items-center space-x-4'>
           <ThemeSwitch />
           <UserNav />
         </div>
-      </div>
+      </LayoutHeader>
 
-      <div className='flex h-[calc(100%-4rem)] flex-1 flex-col overflow-hidden px-4 pb-6 pt-6 md:px-10'>
+      <LayoutBody className='flex flex-col' fixedHeight>
         <div className='mb-2 flex items-center justify-between space-y-2'>
           <div>
             <h2 className='text-2xl font-bold tracking-tight'>Welcome back!</h2>
@@ -29,7 +30,7 @@ export default function Tasks() {
         <div className='-mx-4 flex-1 overflow-auto px-4 py-1 lg:flex-row lg:space-x-12 lg:space-y-0'>
           <DataTable data={tasks} columns={columns} />
         </div>
-      </div>
-    </div>
+      </LayoutBody>
+    </Layout>
   )
 }