Skip to content

Commit f88ea14

Browse files
authored
Create sidebar nav menu with shadcn sheet component (#102)
* Add src import alias * update import alias so shadcn cli adds components in correct directory * Add shadcn sheet component * update tsconfig import alias improve path alias * Add blank sheet component and import in Header * add sheet title * Add buttons and links to SideMenu component * format * rm unused styles * Add icons to main nav items in SideMenu * Add divider between main nav links and projects * Wrap projects links in span with flex-col and px-4 on links * Wrap menu sections in sections tags * Update button height and margin-y * Change text + hover color of project links, add more space between links * Update main nav button variant to ghost, remove asChild prop, move icons inside buttons, set button `w-full` and `justify-start` * Use map functions for nav and project links * wrap mainlinks button in <a> tag to fix link not working
1 parent ce0d5ab commit f88ea14

File tree

7 files changed

+243
-2
lines changed

7 files changed

+243
-2
lines changed

bun.lockb

944 Bytes
Binary file not shown.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@astrojs/react": "^3.1.0",
2828
"@astrojs/tailwind": "^5.1.0",
2929
"@avgvstvs96/remark-sectionize": "^2.0.0",
30+
"@radix-ui/react-dialog": "^1.0.5",
3031
"@radix-ui/react-dropdown-menu": "^2.0.6",
3132
"@radix-ui/react-icons": "^1.3.0",
3233
"@radix-ui/react-slot": "^1.0.2",

src/components/ReactHeader.astro

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Button } from '@components/ui/button';
33
import { ModeToggle } from '@components/subComponents/ModeToggle';
44
import { Icon } from 'astro-icon/components';
5+
import { SideMenu } from '@components/SideMenu';
56
---
67

78
<header class="flex items-center border-b border-border/50 px-8 py-2">
@@ -18,6 +19,9 @@ import { Icon } from 'astro-icon/components';
1819
Home
1920
</Button>
2021
</a>
21-
<ModeToggle client:load />
22+
<span class="flex gap-2 items-center">
23+
<SideMenu client:load />
24+
<ModeToggle client:load />
25+
</span>
2226
</nav>
2327
</header>

src/components/SideMenu.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
Sheet,
3+
SheetContent,
4+
SheetTitle,
5+
SheetTrigger,
6+
} from '@components/ui/sheet';
7+
import { Button } from '@components/ui/button';
8+
import { HamburgerMenuIcon } from '@radix-ui/react-icons';
9+
import { HomeIcon, User, NotebookText } from 'lucide-react';
10+
11+
export function SideMenu() {
12+
const mainLinks = [
13+
{ name: 'Home', icon: <HomeIcon className="mr-2 size-4" />, href: '/' },
14+
{ name: 'About', icon: <User className="mr-2 size-4" />, href: '/about' },
15+
{
16+
name: 'Blog',
17+
icon: <NotebookText className="mr-2 size-4" />,
18+
href: '/blog',
19+
},
20+
];
21+
22+
const projectLinks = [
23+
{ name: 'Minimal Typography', href: '/designProject' },
24+
{ name: 'Old Flask Website', href: '/flaskSite' },
25+
{ name: 'GPT Chat', href: '/gpt' },
26+
{ name: 'React + shadcn/ui', href: '/react' },
27+
];
28+
29+
return (
30+
<Sheet>
31+
<SheetTrigger asChild>
32+
<Button variant="outline" size="icon">
33+
<HamburgerMenuIcon className="size-5" />
34+
</Button>
35+
</SheetTrigger>
36+
<SheetContent side="left" className="w-64">
37+
<SheetTitle className="font-bold">bassim</SheetTitle>
38+
<nav className="mt-4 flex flex-col">
39+
<section>
40+
{mainLinks.map((link) => (
41+
<span className="flex items-center" key={link.name}>
42+
<a href={link.href} className='w-full'>
43+
<Button
44+
variant="ghost"
45+
size="lg"
46+
className="h-9 w-full justify-start px-3 hover:no-underline">
47+
{link.icon}
48+
{link.name}
49+
</Button>
50+
</a>
51+
</span>
52+
))}
53+
</section>
54+
<hr className="my-4" />
55+
<section>
56+
<h4 className="mb-2 ml-2 mt-1 font-semibold">Projects</h4>
57+
<span className="flex flex-col items-start [&_a]:my-1.5 [&_a]:h-full [&_a]:px-4">
58+
{projectLinks.map((project) => (
59+
<Button
60+
asChild
61+
variant="link"
62+
size="lg"
63+
className="text-muted-foreground w-full justify-start hover:text-foreground hover:no-underline"
64+
key={project.name}>
65+
<a href={project.href}>{project.name}</a>
66+
</Button>
67+
))}
68+
</span>
69+
</section>
70+
</nav>
71+
</SheetContent>
72+
</Sheet>
73+
);
74+
}

src/components/ui/sheet.tsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import * as React from "react"
2+
import * as SheetPrimitive from "@radix-ui/react-dialog"
3+
import { Cross2Icon } from "@radix-ui/react-icons"
4+
import { cva, type VariantProps } from "class-variance-authority"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const Sheet = SheetPrimitive.Root
9+
10+
const SheetTrigger = SheetPrimitive.Trigger
11+
12+
const SheetClose = SheetPrimitive.Close
13+
14+
const SheetPortal = SheetPrimitive.Portal
15+
16+
const SheetOverlay = React.forwardRef<
17+
React.ElementRef<typeof SheetPrimitive.Overlay>,
18+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
19+
>(({ className, ...props }, ref) => (
20+
<SheetPrimitive.Overlay
21+
className={cn(
22+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
23+
className
24+
)}
25+
{...props}
26+
ref={ref}
27+
/>
28+
))
29+
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
30+
31+
const sheetVariants = cva(
32+
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
33+
{
34+
variants: {
35+
side: {
36+
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
37+
bottom:
38+
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
39+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
40+
right:
41+
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
42+
},
43+
},
44+
defaultVariants: {
45+
side: "right",
46+
},
47+
}
48+
)
49+
50+
interface SheetContentProps
51+
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
52+
VariantProps<typeof sheetVariants> {}
53+
54+
const SheetContent = React.forwardRef<
55+
React.ElementRef<typeof SheetPrimitive.Content>,
56+
SheetContentProps
57+
>(({ side = "right", className, children, ...props }, ref) => (
58+
<SheetPortal>
59+
<SheetOverlay />
60+
<SheetPrimitive.Content
61+
ref={ref}
62+
className={cn(sheetVariants({ side }), className)}
63+
{...props}
64+
>
65+
{children}
66+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
67+
<Cross2Icon className="h-4 w-4" />
68+
<span className="sr-only">Close</span>
69+
</SheetPrimitive.Close>
70+
</SheetPrimitive.Content>
71+
</SheetPortal>
72+
))
73+
SheetContent.displayName = SheetPrimitive.Content.displayName
74+
75+
const SheetHeader = ({
76+
className,
77+
...props
78+
}: React.HTMLAttributes<HTMLDivElement>) => (
79+
<div
80+
className={cn(
81+
"flex flex-col space-y-2 text-center sm:text-left",
82+
className
83+
)}
84+
{...props}
85+
/>
86+
)
87+
SheetHeader.displayName = "SheetHeader"
88+
89+
const SheetFooter = ({
90+
className,
91+
...props
92+
}: React.HTMLAttributes<HTMLDivElement>) => (
93+
<div
94+
className={cn(
95+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
96+
className
97+
)}
98+
{...props}
99+
/>
100+
)
101+
SheetFooter.displayName = "SheetFooter"
102+
103+
const SheetTitle = React.forwardRef<
104+
React.ElementRef<typeof SheetPrimitive.Title>,
105+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
106+
>(({ className, ...props }, ref) => (
107+
<SheetPrimitive.Title
108+
ref={ref}
109+
className={cn("text-lg font-semibold text-foreground", className)}
110+
{...props}
111+
/>
112+
))
113+
SheetTitle.displayName = SheetPrimitive.Title.displayName
114+
115+
const SheetDescription = React.forwardRef<
116+
React.ElementRef<typeof SheetPrimitive.Description>,
117+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
118+
>(({ className, ...props }, ref) => (
119+
<SheetPrimitive.Description
120+
ref={ref}
121+
className={cn("text-sm text-muted-foreground", className)}
122+
{...props}
123+
/>
124+
))
125+
SheetDescription.displayName = SheetPrimitive.Description.displayName
126+
127+
export {
128+
Sheet,
129+
SheetPortal,
130+
SheetOverlay,
131+
SheetTrigger,
132+
SheetClose,
133+
SheetContent,
134+
SheetHeader,
135+
SheetFooter,
136+
SheetTitle,
137+
SheetDescription,
138+
}

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
],
2323
"@utils/*": [
2424
"src/utils/*"
25+
],
26+
"@/*" : [
27+
"./src/*"
2528
]
2629
},
2730
"jsx": "react-jsx",

yarn.lock

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
22
# yarn lockfile v1
3-
# bun ./bun.lockb --hash: 8346B7533A29A703-80b767fed1250594-7B9E4E5A1F1C70BE-4eb6a922344826d6
3+
# bun ./bun.lockb --hash: 02F83925EA061AAF-da5527d0351ef716-8A84F3D0EFCECE7B-c8c84cc248e1b2f4
44

55

66
"@alloc/quick-lru@^5.2.0":
@@ -1448,6 +1448,27 @@
14481448
dependencies:
14491449
"@babel/runtime" "^7.13.10"
14501450

1451+
"@radix-ui/react-dialog@^1.0.5":
1452+
version "1.0.5"
1453+
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz"
1454+
integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==
1455+
dependencies:
1456+
"@babel/runtime" "^7.13.10"
1457+
"@radix-ui/primitive" "1.0.1"
1458+
"@radix-ui/react-compose-refs" "1.0.1"
1459+
"@radix-ui/react-context" "1.0.1"
1460+
"@radix-ui/react-dismissable-layer" "1.0.5"
1461+
"@radix-ui/react-focus-guards" "1.0.1"
1462+
"@radix-ui/react-focus-scope" "1.0.4"
1463+
"@radix-ui/react-id" "1.0.1"
1464+
"@radix-ui/react-portal" "1.0.4"
1465+
"@radix-ui/react-presence" "1.0.1"
1466+
"@radix-ui/react-primitive" "1.0.3"
1467+
"@radix-ui/react-slot" "1.0.2"
1468+
"@radix-ui/react-use-controllable-state" "1.0.1"
1469+
aria-hidden "^1.1.1"
1470+
react-remove-scroll "2.5.5"
1471+
14511472
"@radix-ui/react-direction@1.0.1":
14521473
version "1.0.1"
14531474
resolved "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz"

0 commit comments

Comments
 (0)