Keep Sidebar fixed + refactor Sidebar.tsx (#38)

* Refactor Sidebar.tsx (for /resource)

Will look weird on other pages.

* Move sidebar open button into Sidebar.tsx, apply to other pages

Significantly reduces boilerplate of sidebar on pages where it is used.

* Set sidebar to be open by default

* Minor code formatting
This commit is contained in:
Andy Chan 2024-11-05 19:52:09 -05:00 committed by GitHub
parent 354946e19e
commit d0a315c365
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 117 additions and 175 deletions

View File

@ -2,7 +2,6 @@
import Sidebar from "@/components/Sidebar/Sidebar"; import Sidebar from "@/components/Sidebar/Sidebar";
import React, { useState } from "react"; import React, { useState } from "react";
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
import { createClient } from "@/utils/supabase/client"; import { createClient } from "@/utils/supabase/client";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect } from "react"; import { useEffect } from "react";
@ -14,7 +13,7 @@ export default function RootLayout({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState<User>(); const [user, setUser] = useState<User>();
@ -56,34 +55,13 @@ export default function RootLayout({
<div className="flex-row"> <div className="flex-row">
{user ? ( {user ? (
<div> <div>
{/* button to open sidebar */} <Sidebar
<button setIsSidebarOpen={setIsSidebarOpen}
onClick={() => setIsSidebarOpen(!isSidebarOpen)} isSidebarOpen={isSidebarOpen}
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`} name={user.username}
aria-label={"Open sidebar"} email={user.email}
> isAdmin={user.role === Role.ADMIN}
{ />
!isSidebarOpen && (
<ChevronDoubleRightIcon className="h-5 w-5" />
) // Icon for closing the sidebar
}
</button>
{/* sidebar */}
<div
className={`absolute inset-y-0 left-0 transform ${
isSidebarOpen
? "translate-x-0"
: "-translate-x-full"
} w-64 transition duration-300 ease-in-out`}
>
<Sidebar
setIsSidebarOpen={setIsSidebarOpen}
name={user.username}
email={user.email}
isAdmin={user.role === Role.ADMIN}
/>
</div>
{/* page ui */}
<div <div
className={`flex-1 transition duration-300 ease-in-out ${ className={`flex-1 transition duration-300 ease-in-out ${
isSidebarOpen ? "ml-64" : "ml-0" isSidebarOpen ? "ml-64" : "ml-0"

View File

@ -1,7 +1,6 @@
"use client"; "use client";
import Sidebar from "@/components/Sidebar/Sidebar"; import Sidebar from "@/components/Sidebar/Sidebar";
import React, { useState } from "react"; import React, { useState } from "react";
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
import { createClient } from "@/utils/supabase/client"; import { createClient } from "@/utils/supabase/client";
import { useEffect } from "react"; import { useEffect } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -13,7 +12,7 @@ export default function RootLayout({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [user, setUser] = useState<User>(); const [user, setUser] = useState<User>();
const router = useRouter(); const router = useRouter();
@ -45,34 +44,13 @@ export default function RootLayout({
<div className="flex-row"> <div className="flex-row">
{user ? ( {user ? (
<div> <div>
{/* button to open sidebar */} <Sidebar
<button name={user.username}
onClick={() => setIsSidebarOpen(!isSidebarOpen)} email={user.email}
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`} setIsSidebarOpen={setIsSidebarOpen}
aria-label={"Open sidebar"} isSidebarOpen={isSidebarOpen}
> isAdmin={user.role === Role.ADMIN}
{ />
!isSidebarOpen && (
<ChevronDoubleRightIcon className="h-5 w-5" />
) // Icon for closing the sidebar
}
</button>
{/* sidebar */}
<div
className={`absolute inset-y-0 left-0 transform ${
isSidebarOpen
? "translate-x-0"
: "-translate-x-full"
} w-64 transition duration-300 ease-in-out`}
>
<Sidebar
name={user.username}
email={user.email}
setIsSidebarOpen={setIsSidebarOpen}
isAdmin={user.role === Role.ADMIN}
/>
</div>
{/* page ui */}
<div <div
className={`flex-1 transition duration-300 ease-in-out ${ className={`flex-1 transition duration-300 ease-in-out ${
isSidebarOpen ? "ml-64" : "ml-0" isSidebarOpen ? "ml-64" : "ml-0"

View File

@ -2,7 +2,6 @@
import Sidebar from "@/components/Sidebar/Sidebar"; import Sidebar from "@/components/Sidebar/Sidebar";
import React, { useState } from "react"; import React, { useState } from "react";
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
import { createClient } from "@/utils/supabase/client"; import { createClient } from "@/utils/supabase/client";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect } from "react"; import { useEffect } from "react";
@ -14,7 +13,7 @@ export default function RootLayout({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState<User>(); const [user, setUser] = useState<User>();
@ -48,33 +47,14 @@ export default function RootLayout({
<div className="flex-row"> <div className="flex-row">
{user ? ( {user ? (
<div> <div>
{/* button to open sidebar */} <Sidebar
<button setIsSidebarOpen={setIsSidebarOpen}
onClick={() => setIsSidebarOpen(!isSidebarOpen)} isSidebarOpen={isSidebarOpen}
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`} name={user.username}
aria-label={"Open sidebar"} email={user.email}
> isAdmin={user.role === Role.ADMIN}
{ />
!isSidebarOpen && ( {/* </div>*/}
<ChevronDoubleRightIcon className="h-5 w-5" />
) // Icon for closing the sidebar
}
</button>
{/* sidebar */}
<div
className={`absolute inset-y-0 left-0 transform ${
isSidebarOpen
? "translate-x-0"
: "-translate-x-full"
} w-64 transition duration-300 ease-in-out`}
>
<Sidebar
setIsSidebarOpen={setIsSidebarOpen}
name={user.username}
email={user.email}
isAdmin={user.role === Role.ADMIN}
/>
</div>
{/* page ui */} {/* page ui */}
<div <div
className={`flex-1 transition duration-300 ease-in-out ${ className={`flex-1 transition duration-300 ease-in-out ${

View File

@ -2,7 +2,6 @@
import Sidebar from "@/components/Sidebar/Sidebar"; import Sidebar from "@/components/Sidebar/Sidebar";
import React, { useState } from "react"; import React, { useState } from "react";
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
import { createClient } from "@/utils/supabase/client"; import { createClient } from "@/utils/supabase/client";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect } from "react"; import { useEffect } from "react";
@ -14,7 +13,7 @@ export default function RootLayout({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const router = useRouter(); const router = useRouter();
const [user, setUser] = useState<User>(); const [user, setUser] = useState<User>();
@ -48,34 +47,13 @@ export default function RootLayout({
<div className="flex-row"> <div className="flex-row">
{user ? ( {user ? (
<div> <div>
{/* button to open sidebar */} <Sidebar
<button setIsSidebarOpen={setIsSidebarOpen}
onClick={() => setIsSidebarOpen(!isSidebarOpen)} isSidebarOpen={isSidebarOpen}
className={`fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0`} name={user.username}
aria-label={"Open sidebar"} email={user.email}
> isAdmin={user.role === Role.ADMIN}
{ />
!isSidebarOpen && (
<ChevronDoubleRightIcon className="h-5 w-5" />
) // Icon for closing the sidebar
}
</button>
{/* sidebar */}
<div
className={`absolute inset-y-0 left-0 transform ${
isSidebarOpen
? "translate-x-0"
: "-translate-x-full"
} w-64 transition duration-300 ease-in-out`}
>
<Sidebar
setIsSidebarOpen={setIsSidebarOpen}
name={user.username}
email={user.email}
isAdmin={user.role === Role.ADMIN}
/>
</div>
{/* page ui */}
<div <div
className={`flex-1 transition duration-300 ease-in-out ${ className={`flex-1 transition duration-300 ease-in-out ${
isSidebarOpen ? "ml-64" : "ml-0" isSidebarOpen ? "ml-64" : "ml-0"

View File

@ -2,6 +2,7 @@ import React from "react";
import { import {
HomeIcon, HomeIcon,
ChevronDoubleLeftIcon, ChevronDoubleLeftIcon,
ChevronDoubleRightIcon,
BookmarkIcon, BookmarkIcon,
ClipboardIcon, ClipboardIcon,
BookOpenIcon, BookOpenIcon,
@ -12,6 +13,7 @@ import { UserProfile } from "../resource/UserProfile";
interface SidebarProps { interface SidebarProps {
setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>; setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>;
isSidebarOpen: boolean;
name: string; name: string;
email: string; email: string;
isAdmin: boolean; isAdmin: boolean;
@ -19,70 +21,96 @@ interface SidebarProps {
const Sidebar: React.FC<SidebarProps> = ({ const Sidebar: React.FC<SidebarProps> = ({
setIsSidebarOpen, setIsSidebarOpen,
isSidebarOpen,
name, name,
email, email,
isAdmin: admin, isAdmin: admin,
}) => { }) => {
return ( return (
<div className="w-64 h-full border border-gray-200 bg-gray-50 px-4"> <>
{/* button to close sidebar */} {/* Button to open the sidebar. */}
<div className="flex justify-end"> <button
<button onClick={() => setIsSidebarOpen(true)}
onClick={() => setIsSidebarOpen(false)} className={
className="py-2 text-gray-500 hover:text-gray-800" "fixed z-20 p-2 text-gray-500 hover:text-gray-800 left-0"
aria-label="Close sidebar" }
> aria-label={"Open sidebar"}
<ChevronDoubleLeftIcon className="h-5 w-5" /> >
</button> {!isSidebarOpen && (
</div> <ChevronDoubleRightIcon className="h-5 w-5" />
<div className="flex flex-col space-y-8"> )}
{/* user + logout button */} </button>
<div className="flex items-center p-4 space-x-2 border border-gray-200 rounded-md ">
<UserProfile name={name} email={email} />
</div>
{/* navigation menu */}
<div className="flex flex-col space-y-2">
<h4 className="text-xs font-semibold text-gray-500">
Pages
</h4>
<nav className="flex flex-col">
{admin && (
<SidebarItem
icon={<LockClosedIcon />}
text="Admin"
active={true}
redirect="/admin"
/>
)}
<SidebarItem {/* The sidebar itself. */}
icon={<HomeIcon />} <div
text="Home" className={
active={true} "fixed left-0 w-64 h-full border border-gray-200 bg-gray-50 px-4 " +
redirect="/home" (isSidebarOpen
/> ? "translate-x-0" // Open
<SidebarItem : "-translate-x-full opacity-25") + // Closed
icon={<BookmarkIcon />} " transition duration-300 ease-out" // More animation properties
text="Resources" }
active={true} >
redirect="/resource" {/* Button to close sidebar */}
/> <div className="flex justify-end">
<SidebarItem <button
icon={<ClipboardIcon />} onClick={() => setIsSidebarOpen(false)}
text="Services" className="py-2 text-gray-500 hover:text-gray-800"
active={true} aria-label="Close sidebar"
redirect="/service" >
/> <ChevronDoubleLeftIcon className="h-5 w-5" />
<SidebarItem </button>
icon={<BookOpenIcon />} </div>
text="Training Manuals"
active={true} <div className="flex flex-col space-y-8">
redirect="/training-manuals" {/* user + logout button */}
/> <div className="flex items-center p-4 space-x-2 border border-gray-200 rounded-md ">
</nav> <UserProfile name={name} email={email} />
</div>
{/* navigation menu */}
<div className="flex flex-col space-y-2">
<h4 className="text-xs font-semibold text-gray-500">
Pages
</h4>
<nav className="flex flex-col">
{admin && (
<SidebarItem
icon={<LockClosedIcon />}
text="Admin"
active={true}
redirect="/admin"
/>
)}
<SidebarItem
icon={<HomeIcon />}
text="Home"
active={true}
redirect="/home"
/>
<SidebarItem
icon={<BookmarkIcon />}
text="Resources"
active={true}
redirect="/resource"
/>
<SidebarItem
icon={<ClipboardIcon />}
text="Services"
active={true}
redirect="/service"
/>
<SidebarItem
icon={<BookOpenIcon />}
text="Training Manuals"
active={true}
redirect="/training-manuals"
/>
</nav>
</div>
</div> </div>
</div> </div>
</div> </>
); );
}; };