Skip to content

Commit

Permalink
pin/unpin side-nav menu behavior (#1055)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasondicker authored Aug 28, 2024
1 parent 1273bdb commit e55cdaa
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 32 deletions.
90 changes: 60 additions & 30 deletions src/web/src/components/NavBar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@ import Link from "next/link";
import logoPicDark from "public/images/logo-dark.webp";
import logoPicLight from "public/images/logo-light.webp";
import { useMemo, useState } from "react";
import { IoMdClose, IoMdMenu, IoMdSettings } from "react-icons/io";
import { FaArrowLeft, FaArrowRight } from "react-icons/fa";
import { IoMdMenu, IoMdSettings } from "react-icons/io";
import type { TabItem } from "~/api/models/common";
import type { OrganizationInfo } from "~/api/models/user";
import { useDisableBodyScroll } from "~/hooks/useDisableBodyScroll";
import { ROLE_ADMIN } from "~/lib/constants";
import {
RoleView,
activeNavigationRoleViewAtom,
currentOrganisationIdAtom,
userProfileAtom,
} from "~/lib/store";
import { AvatarImage } from "../AvatarImage";
import { Footer } from "../Footer/Footer";
import { SignInButton } from "../SignInButton";
import { SignOutButton } from "../SignOutButton";
import { LanguageSwitcher } from "./LanguageSwitcher";
import { UserMenu } from "./UserMenu";
import { Footer } from "../Footer/Footer";
import { SignOutButton } from "../SignOutButton";
import type { OrganizationInfo } from "~/api/models/user";
import { AvatarImage } from "../AvatarImage";
import { ROLE_ADMIN } from "~/lib/constants";

const navBarLinksUser: TabItem[] = [
{
Expand Down Expand Up @@ -101,17 +102,35 @@ const navBarLinksAdmin: TabItem[] = [
// },
];

export const Navbar: React.FC = () => {
const [isDrawerOpen, setDrawerOpen] = useState(false);
const toggle = () => setDrawerOpen(!isDrawerOpen);
export const Navbar: React.FC<{
isPinned: boolean;
togglePin: (isPinned: boolean) => void;
}> = ({ isPinned, togglePin }) => {
const activeRoleView = useAtomValue(activeNavigationRoleViewAtom);
const currentOrganisationId = useAtomValue(currentOrganisationIdAtom);
const { data: session } = useSession();
const userProfile = useAtomValue(userProfileAtom);
const isAdmin = session?.user?.roles.includes(ROLE_ADMIN);

const [isDrawerOpen, setDrawerOpen] = useState(false);
const [pinned, setPinned] = useState(isPinned);

const onToggle = () => {
if (isDrawerOpen) {
setPinned(false);
togglePin && togglePin(false);
}
setDrawerOpen(!isDrawerOpen);
};

const onTogglePin = () => {
setPinned(!pinned);
if (isDrawerOpen) setDrawerOpen(false);
togglePin && togglePin(!pinned);
};

// 👇 prevent scrolling on the page when the dialogs are open
useDisableBodyScroll(isDrawerOpen);
useDisableBodyScroll(isDrawerOpen && !pinned);

const currentNavbarLinks = useMemo<TabItem[]>(() => {
if (activeRoleView == RoleView.Admin) {
Expand Down Expand Up @@ -253,30 +272,35 @@ export const Navbar: React.FC = () => {
id="nav-drawer"
type="checkbox"
className="drawer-toggle"
checked={isDrawerOpen}
onChange={toggle}
checked={isDrawerOpen || isPinned}
onChange={onToggle}
/>
<div className="drawer-content">
<label
htmlFor="nav-drawer"
className="bg-theme btn !rounded-md border-none px-1 text-white shadow-none duration-0 hover:brightness-95 md:px-3"
className="bg-theme btn drawer w-auto !rounded-md border-none px-1 text-white shadow-none duration-0 hover:brightness-95 md:px-3"
>
{/* BUTTON */}
<IoMdMenu className="h-8 w-8" />
</label>
</div>
<div className="drawer-side">
<label
htmlFor="nav-drawer"
aria-label="close sidebar"
className="drawer-overlay"
></label>
<div className="min-h-screen max-w-[20rem] overflow-y-auto rounded-bl-none rounded-br-lg rounded-tl-none rounded-tr-lg bg-white p-4">
{/* when pinned, need to have the width restrictred to allow the content to be clickable */}
<div className={`drawer-side ${pinned ? "max-w-[20rem]" : ""}`}>
{/* overlay */}
{isDrawerOpen && !isPinned && (
<label
htmlFor="nav-drawer"
aria-label="close sidebar"
className="drawer-overlay"
></label>
)}
<div
className={`min-h-screen max-w-[20rem] overflow-y-auto rounded-bl-none rounded-tl-none bg-white p-4 ${
pinned ? "" : "rounded-br-lg rounded-tr-lg"
}`}
>
<div className="flex h-full flex-col gap-2">
{/* HEADER */}
<div className="flex grow-0 flex-row items-center justify-center">
<div className="grow">
{/* LOGO */}
<Image
src={logoPicDark}
alt="Logo"
Expand All @@ -285,19 +309,28 @@ export const Navbar: React.FC = () => {
height={41}
/>
</div>
{/* CLOSE BUTTON */}
<label
{/* close button removed */}
{/* <label
htmlFor="nav-drawer"
className="drawer-close btn btn-sm !rounded-md border-none text-gray-dark shadow-md hover:bg-gray"
aria-label="close sidebar"
>
<IoMdClose className="h-5 w-5" />
</label>
</label> */}

{/* pin toggle button */}
<button
onClick={onTogglePin}
className="btn btn-sm hidden !rounded-full border-none text-gray-dark shadow-none hover:bg-gray md:block"
aria-label="pin sidebar"
>
{isPinned && <FaArrowLeft className="h-4 w-4" />}
{!isPinned && <FaArrowRight className="h-4 w-4" />}
</button>
</div>

<div className="divider my-2 grow-0 !bg-gray" />

{/* MENU */}
<ul className="menu grow p-0">
{currentNavbarLinks.map((link, index) => (
<li
Expand Down Expand Up @@ -328,7 +361,6 @@ export const Navbar: React.FC = () => {

<div className="divider my-2 grow-0 !bg-gray" />

{/* ORGANISATIONS */}
{(userProfile?.adminsOf?.length ?? 0) > 0 && (
<>
<div
Expand Down Expand Up @@ -361,7 +393,6 @@ export const Navbar: React.FC = () => {
</>
)}

{/* ADMIN */}
{(activeRoleView == RoleView.Admin || isAdmin) && (
<>
<ul className="menu grow p-0">
Expand Down Expand Up @@ -392,7 +423,6 @@ export const Navbar: React.FC = () => {

<div className="divider my-2 grow-0 !bg-gray" />

{/* FOOTER */}
<div className="grow-0">
<Footer />
</div>
Expand Down
12 changes: 10 additions & 2 deletions src/web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ const MyApp = ({
? getTheme(component)
: THEME_PURPLE;

// pinned state for sidebar
const [isPinned, setPinned] = useState(false);

const handlePinToggle = (isPinned: boolean) => {
setPinned(isPinned);
};
return (
<Provider>
<SessionProvider session={pageProps.session}>
Expand All @@ -100,11 +106,13 @@ const MyApp = ({
<HydrationBoundary state={pageProps.dehydratedState}>
<div
id="mainContent"
className={`${nunito.variable} font-nunito`}
className={`${nunito.variable} font-nunito ${
isPinned ? "ml-[20rem]" : ""
}`}
>
<ConfirmationModalContextProvider>
<Global />
<Navbar />
<Navbar isPinned={isPinned} togglePin={handlePinToggle} />
{getLayout(component)}
<ToastContainer
containerId="toastContainer"
Expand Down

0 comments on commit e55cdaa

Please sign in to comment.