feat: enhance Sidebar component with responsive menu and swipe functionality; update UserList component for full-width display
This commit is contained in:
parent
bb751c1476
commit
2f304684f3
@ -24,13 +24,15 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
<div className="flex">
|
||||
<Sidebar items={sidebarItems} />
|
||||
<main className="p-7 w-full">
|
||||
<h1 className="text-2xl font-semibold">
|
||||
{
|
||||
sidebarItems
|
||||
.flat()
|
||||
.find((item) => item.href === pathName)?.title
|
||||
}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<h1 className="text-2xl font-semibold">
|
||||
{
|
||||
sidebarItems
|
||||
.flat()
|
||||
.find((item) => item.href === pathName)?.title
|
||||
}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<Divider className="mt-2 mb-5 bg-foreground-300" />
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
import { Divider, Link } from "@nextui-org/react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { BsChevronDoubleRight } from "react-icons/bs";
|
||||
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher";
|
||||
import { SidebarItem } from "./item";
|
||||
|
||||
@ -16,13 +18,102 @@ export const Sidebar = ({
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const pathName = usePathname();
|
||||
const sidebarRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// State to manage menu openness
|
||||
const [open, setOpen] = useState(false);
|
||||
const [startX, setStartX] = useState(0);
|
||||
const [isSwiping, setIsSwiping] = useState(false);
|
||||
|
||||
// Effect to manage responsiveness
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
if (window.innerWidth >= 1024) {
|
||||
setOpen(true); // Menu always open on larger screens
|
||||
} else {
|
||||
setOpen(false); // Menu closed by default on smaller screens
|
||||
}
|
||||
};
|
||||
|
||||
handleResize(); // Set initial state based on current window size
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
// Effect to close the menu when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (sidebarRef.current && !sidebarRef.current.contains(event.target as Node)) {
|
||||
setOpen(false); // Close the menu if clicked outside
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
|
||||
// Cleanup the event listener when component unmounts
|
||||
return () => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle swipe start (touchstart event)
|
||||
const handleTouchStart = (e: React.TouchEvent) => {
|
||||
const touchStart = e.touches[0].clientX;
|
||||
setStartX(touchStart);
|
||||
setIsSwiping(true);
|
||||
};
|
||||
|
||||
// Handle swipe move (touchmove event)
|
||||
const handleTouchMove = (e: React.TouchEvent) => {
|
||||
if (!isSwiping) return;
|
||||
const touchMove = e.touches[0].clientX;
|
||||
const distance = touchMove - startX;
|
||||
|
||||
if (distance > 50 && !open) {
|
||||
setOpen(true); // Open the menu if swiping right
|
||||
} else if (distance < -50 && open) {
|
||||
setOpen(false); // Close the menu if swiping left
|
||||
}
|
||||
};
|
||||
|
||||
// Handle swipe end (touchend event)
|
||||
const handleTouchEnd = () => {
|
||||
setIsSwiping(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className="h-screen">
|
||||
<div className="flex flex-col w-72 gap-5 h-full px-3 py-4 overflow-y-auto bg-foreground-100">
|
||||
<aside
|
||||
ref={sidebarRef}
|
||||
className={`
|
||||
${open ? "translate-x-0" : "-translate-x-full"}
|
||||
fixed top-0 left-0 z-40 w-64 h-screen transition-transform
|
||||
lg:translate-x-0 lg:static
|
||||
`}
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
>
|
||||
<div className="flex flex-col h-full px-3 py-4 overflow-y-auto bg-foreground-100 scrollbar-hide">
|
||||
{/* Toggle Button - Hidden on large screens */}
|
||||
<button
|
||||
className={`
|
||||
lg:hidden flex items-center justify-center w-12 h-12
|
||||
fixed left-64 transform bg-foreground-200 rounded-full shadow-lg
|
||||
transition-transform duration-500 z-50
|
||||
-translate-x-1/2 top-1/2 -translate-y-1/2
|
||||
${open ? "rotate-180" : "rotate-0"}
|
||||
`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
<span className="text-xl font-bold">
|
||||
<BsChevronDoubleRight className="text-gray-900 dark:text-white" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div className="flex flex-col gap-2 p-2">
|
||||
<Link
|
||||
className="flex text-2xl font-semibold text-gray-900 dark:text-white rounded-lg cursor-pointer"
|
||||
className="text-2xl font-semibold text-gray-900 dark:text-white rounded-lg cursor-pointer"
|
||||
onPress={() => router.push("/")}
|
||||
>
|
||||
Toogether
|
||||
@ -31,7 +122,6 @@ export const Sidebar = ({
|
||||
Manage classes, rooms, and users
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{items.map((group, index) => (
|
||||
<div key={index}>
|
||||
<section className="flex flex-col">
|
||||
@ -39,11 +129,13 @@ export const Sidebar = ({
|
||||
{group.map((item, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={`${
|
||||
pathName === item.href
|
||||
? "bg-foreground-300"
|
||||
: "hover:bg-foreground-200"
|
||||
} rounded-md cursor-pointer`}
|
||||
className={`
|
||||
${
|
||||
pathName === item.href
|
||||
? "bg-foreground-300"
|
||||
: "hover:bg-foreground-200"
|
||||
} rounded-md cursor-pointer
|
||||
`}
|
||||
>
|
||||
<SidebarItem
|
||||
href={item.href}
|
||||
@ -54,13 +146,11 @@ export const Sidebar = ({
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{index < items.length - 1 && (
|
||||
<Divider className="bg-foreground-300 mt-4" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<section className="mt-auto flex justify-between">
|
||||
<ThemeSwitcher />
|
||||
</section>
|
||||
|
@ -24,7 +24,7 @@ export const UserList = () => {
|
||||
|
||||
if (!users) {
|
||||
return (
|
||||
<Card className="w-[300px] space-y-5 p-4" radius="lg">
|
||||
<Card className="w-full space-y-5 p-4" radius="lg">
|
||||
<Skeleton className="rounded-lg">
|
||||
<div className="h-10 rounded-lg bg-default-300" />
|
||||
</Skeleton>
|
||||
@ -44,7 +44,7 @@ export const UserList = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Table aria-label="List of users" className="w-[300px]">
|
||||
<Table aria-label="List of users" className="w-full">
|
||||
<TableHeader>
|
||||
<TableColumn>USERNAME</TableColumn>
|
||||
</TableHeader>
|
||||
|
Loading…
Reference in New Issue
Block a user