️ feat: add search dialog on mobile

This commit is contained in:
Wahyu Kurniawan 2024-03-06 10:31:11 +07:00
parent 5a86abd133
commit 1ddc3b81c7
No known key found for this signature in database
GPG Key ID: 040A1549143A8E33

View File

@ -13,6 +13,7 @@ import { Outlet, useParams } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { cn } from 'utils/classnames';
import { useMediaQuery } from 'usehooks-ts';
import { ProjectSearchBarDialog } from 'components/projects/ProjectSearchBar';
export interface DashboardLayoutProps
extends ComponentPropsWithoutRef<'section'> {}
@ -23,7 +24,9 @@ export const DashboardLayout = ({
}: DashboardLayoutProps) => {
const { orgSlug } = useParams();
const isDesktop = useMediaQuery('(min-width: 1024px)');
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isSearchOpen, setIsSearchOpen] = useState(false);
useEffect(() => {
if (isDesktop) {
@ -32,86 +35,96 @@ export const DashboardLayout = ({
}, [isDesktop]);
return (
<section
{...props}
className={cn(
'flex flex-col lg:flex-row h-screen bg-snowball-50',
className,
)}
>
{/* Header on mobile */}
<div className="flex lg:hidden items-center px-4 py-4 justify-between">
<Logo orgSlug={orgSlug} />
<div className="flex items-center gap-0.5">
<AnimatePresence>
{isSidebarOpen ? (
<motion.div
key="crossIcon"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.2, delay: 0.3 },
}}
exit={{ opacity: 0, transition: { duration: 0 } }}
>
<Button
iconOnly
variant="ghost"
onClick={() => setIsSidebarOpen(false)}
<>
<section
{...props}
className={cn(
'flex flex-col lg:flex-row h-screen bg-snowball-50',
className,
)}
>
{/* Header on mobile */}
<div className="flex lg:hidden items-center px-4 py-4 justify-between">
<Logo orgSlug={orgSlug} />
<div className="flex items-center gap-0.5">
<AnimatePresence>
{isSidebarOpen ? (
<motion.div
key="crossIcon"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.2, delay: 0.3 },
}}
exit={{ opacity: 0, transition: { duration: 0 } }}
>
<CrossIcon size={20} />
</Button>
</motion.div>
) : (
<motion.div
key="menuIcons"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.2, delay: 0.2 },
}}
exit={{ opacity: 0, transition: { duration: 0 } }}
>
<>
<Button iconOnly variant="ghost">
<NotificationBellIcon size={18} />
</Button>
<Button iconOnly variant="ghost">
<SearchIcon size={18} />
</Button>
<Button
iconOnly
variant="ghost"
onClick={() => setIsSidebarOpen(true)}
onClick={() => setIsSidebarOpen(false)}
>
<MenuIcon size={18} />
<CrossIcon size={20} />
</Button>
</>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
<div className="flex h-full w-full overflow-hidden">
<Sidebar mobileOpen={isSidebarOpen} />
<motion.div
className={cn(
'w-full h-full pr-1 pl-1 py-1 md:pl-0 md:pr-3 md:py-3 overflow-y-hidden min-w-[320px]',
{ 'flex-shrink-0': isSidebarOpen || !isDesktop },
)}
initial={{ x: 0 }} // Initial state, no translation
animate={{
x: isSidebarOpen ? '10px' : 0, // Translate X based on sidebar state
}}
transition={{ type: 'spring', stiffness: 260, damping: 20 }}
>
<div className="rounded-3xl bg-base-bg h-full shadow-card overflow-y-auto relative">
<OctokitProvider>
<Outlet />
</OctokitProvider>
</motion.div>
) : (
<motion.div
key="menuIcons"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.2, delay: 0.2 },
}}
exit={{ opacity: 0, transition: { duration: 0 } }}
>
<>
<Button iconOnly variant="ghost">
<NotificationBellIcon size={18} />
</Button>
<Button
iconOnly
variant="ghost"
onClick={() => setIsSearchOpen(true)}
>
<SearchIcon size={18} />
</Button>
<Button
iconOnly
variant="ghost"
onClick={() => setIsSidebarOpen(true)}
>
<MenuIcon size={18} />
</Button>
</>
</motion.div>
)}
</AnimatePresence>
</div>
</motion.div>
</div>
</section>
</div>
<div className="flex h-full w-full overflow-hidden">
<Sidebar mobileOpen={isSidebarOpen} />
<motion.div
className={cn(
'w-full h-full pr-1 pl-1 py-1 md:pl-0 md:pr-3 md:py-3 overflow-y-hidden min-w-[320px]',
{ 'flex-shrink-0': isSidebarOpen || !isDesktop },
)}
initial={{ x: 0 }} // Initial state, no translation
animate={{
x: isSidebarOpen ? '10px' : 0, // Translate X based on sidebar state
}}
transition={{ type: 'spring', stiffness: 260, damping: 20 }}
>
<div className="rounded-3xl bg-base-bg h-full shadow-card overflow-y-auto relative">
<OctokitProvider>
<Outlet />
</OctokitProvider>
</div>
</motion.div>
</div>
</section>
<ProjectSearchBarDialog
open={isSearchOpen}
onClose={() => setIsSearchOpen(false)}
/>
</>
);
};