From 296c447ac49e72abc8b0e6e5a4210910129c0c52 Mon Sep 17 00:00:00 2001 From: g Date: Wed, 7 Feb 2024 16:59:54 -0500 Subject: [PATCH] feat(dashboard): collapsible sidebar --- .../components/molecules/nav-bar/nav-bar.tsx | 10 ++- .../app/src/components/sidebar-provider.tsx | 86 +++++++++++++++++++ frontend/app/src/pages/authenticated.tsx | 2 +- frontend/app/src/pages/main/index.tsx | 15 ++-- frontend/app/src/pages/root.tsx | 11 ++- 5 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 frontend/app/src/components/sidebar-provider.tsx diff --git a/frontend/app/src/components/molecules/nav-bar/nav-bar.tsx b/frontend/app/src/components/molecules/nav-bar/nav-bar.tsx index 91620d67a..2853f36ef 100644 --- a/frontend/app/src/components/molecules/nav-bar/nav-bar.tsx +++ b/frontend/app/src/components/molecules/nav-bar/nav-bar.tsx @@ -14,6 +14,8 @@ import { useNavigate } from 'react-router-dom'; import api, { User } from '@/lib/api'; import { useApiError } from '@/lib/hooks'; import { useMutation } from '@tanstack/react-query'; +import hatchet from '@/assets/hatchet_logo.png'; +import { useSidebar } from '@/components/sidebar-provider'; interface MainNavProps { user: User; @@ -22,6 +24,7 @@ interface MainNavProps { export default function MainNav({ user }: MainNavProps) { const navigate = useNavigate(); const { handleApiError } = useApiError({}); + const { toggleSidebarOpen } = useSidebar(); const logoutMutation = useMutation({ mutationKey: ['user:update:logout'], @@ -35,8 +38,11 @@ export default function MainNav({ user }: MainNavProps) { }); return ( -
-
+
+
+
diff --git a/frontend/app/src/components/sidebar-provider.tsx b/frontend/app/src/components/sidebar-provider.tsx new file mode 100644 index 000000000..e853f0133 --- /dev/null +++ b/frontend/app/src/components/sidebar-provider.tsx @@ -0,0 +1,86 @@ +import { + PropsWithChildren, + createContext, + useContext, + useEffect, + useState, +} from 'react'; + +type SidebarState = 'open' | 'closed'; + +type SidebarProviderProps = PropsWithChildren & { + defaultSidebarOpen?: SidebarState; +}; + +type SidebarProviderState = { + sidebarOpen: SidebarState; + setSidebarOpen: (open: SidebarState) => void; + toggleSidebarOpen: () => void; +}; + +const initialState: SidebarProviderState = { + sidebarOpen: 'closed', + setSidebarOpen: () => null, + toggleSidebarOpen: () => null, +}; + +const SidebarProviderContext = + createContext(initialState); + +export function SidebarProvider({ + children, + defaultSidebarOpen = 'closed', + + ...props +}: SidebarProviderProps) { + const [sidebarOpen, setSidebarOpen] = useState( + () => defaultSidebarOpen, + ); + + useEffect(() => { + const handleResize = () => { + if (window.innerWidth >= 768) { + console.log('open'); + setSidebarOpen('open'); + } else { + console.log('closed'); + setSidebarOpen('closed'); + } + }; + + handleResize(); + + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return ( + { + setSidebarOpen(open); + }, + toggleSidebarOpen: () => { + setSidebarOpen((state) => (state === 'open' ? 'closed' : 'open')); + }, + }} + > + {children} + + ); +} + +export const useSidebar = () => { + const context = useContext(SidebarProviderContext); + + if (context === undefined) { + throw new Error('useSidebar must be used within a SidebarProvider'); + } + + return context; +}; diff --git a/frontend/app/src/pages/authenticated.tsx b/frontend/app/src/pages/authenticated.tsx index 43935dbc6..6355ddabc 100644 --- a/frontend/app/src/pages/authenticated.tsx +++ b/frontend/app/src/pages/authenticated.tsx @@ -109,7 +109,7 @@ export default function Authenticated() { return (
-
+
diff --git a/frontend/app/src/pages/main/index.tsx b/frontend/app/src/pages/main/index.tsx index 988c67342..d4878d27e 100644 --- a/frontend/app/src/pages/main/index.tsx +++ b/frontend/app/src/pages/main/index.tsx @@ -9,7 +9,6 @@ import { ServerStackIcon, Squares2X2Icon, } from '@heroicons/react/24/outline'; -import hatchet from '@/assets/hatchet_logo.png'; import invariant from 'tiny-invariant'; import { @@ -32,7 +31,7 @@ import { Popover, PopoverContent, } from '@radix-ui/react-popover'; -import React from 'react'; +import React, { useState } from 'react'; import { MembershipsContextType, UserContextType, @@ -40,6 +39,7 @@ import { } from '@/lib/outlet'; import { useTenantContext } from '@/lib/atoms'; import { Loading, Spinner } from '@/components/ui/loading.tsx'; +import { useSidebar } from '@/components/sidebar-provider'; function Main() { const ctx = useOutletContext(); @@ -61,7 +61,7 @@ function Main() { return (
-
+
@@ -76,12 +76,17 @@ interface SidebarProps extends React.HTMLAttributes { } function Sidebar({ className, memberships, currTenant }: SidebarProps) { + const { sidebarOpen } = useSidebar(); + + if (sidebarOpen === 'closed') { + return null; + } + return ( -
+
- Hatchet

Events

diff --git a/frontend/app/src/pages/root.tsx b/frontend/app/src/pages/root.tsx index 49444bfab..7143088ab 100644 --- a/frontend/app/src/pages/root.tsx +++ b/frontend/app/src/pages/root.tsx @@ -1,3 +1,4 @@ +import { SidebarProvider } from '@/components/sidebar-provider'; import { ThemeProvider } from '@/components/theme-provider'; import { Toaster } from '@/components/ui/toaster'; import { Outlet } from 'react-router-dom'; @@ -5,10 +6,12 @@ import { Outlet } from 'react-router-dom'; function Root() { return ( -
- - -
+ +
+ + +
+
); }