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">
|
<div className="flex">
|
||||||
<Sidebar items={sidebarItems} />
|
<Sidebar items={sidebarItems} />
|
||||||
<main className="p-7 w-full">
|
<main className="p-7 w-full">
|
||||||
<h1 className="text-2xl font-semibold">
|
<div className="flex items-center gap-2">
|
||||||
{
|
<h1 className="text-2xl font-semibold">
|
||||||
sidebarItems
|
{
|
||||||
.flat()
|
sidebarItems
|
||||||
.find((item) => item.href === pathName)?.title
|
.flat()
|
||||||
}
|
.find((item) => item.href === pathName)?.title
|
||||||
</h1>
|
}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Divider className="mt-2 mb-5 bg-foreground-300" />
|
<Divider className="mt-2 mb-5 bg-foreground-300" />
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import { Divider, Link } from "@nextui-org/react";
|
import { Divider, Link } from "@nextui-org/react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { BsChevronDoubleRight } from "react-icons/bs";
|
||||||
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher";
|
import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher";
|
||||||
import { SidebarItem } from "./item";
|
import { SidebarItem } from "./item";
|
||||||
|
|
||||||
@ -16,13 +18,102 @@ export const Sidebar = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathName = usePathname();
|
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 (
|
return (
|
||||||
<aside className="h-screen">
|
<aside
|
||||||
<div className="flex flex-col w-72 gap-5 h-full px-3 py-4 overflow-y-auto bg-foreground-100">
|
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">
|
<div className="flex flex-col gap-2 p-2">
|
||||||
<Link
|
<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("/")}
|
onPress={() => router.push("/")}
|
||||||
>
|
>
|
||||||
Toogether
|
Toogether
|
||||||
@ -31,7 +122,6 @@ export const Sidebar = ({
|
|||||||
Manage classes, rooms, and users
|
Manage classes, rooms, and users
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{items.map((group, index) => (
|
{items.map((group, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<section className="flex flex-col">
|
<section className="flex flex-col">
|
||||||
@ -39,11 +129,13 @@ export const Sidebar = ({
|
|||||||
{group.map((item, index) => (
|
{group.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
className={`${
|
className={`
|
||||||
pathName === item.href
|
${
|
||||||
? "bg-foreground-300"
|
pathName === item.href
|
||||||
: "hover:bg-foreground-200"
|
? "bg-foreground-300"
|
||||||
} rounded-md cursor-pointer`}
|
: "hover:bg-foreground-200"
|
||||||
|
} rounded-md cursor-pointer
|
||||||
|
`}
|
||||||
>
|
>
|
||||||
<SidebarItem
|
<SidebarItem
|
||||||
href={item.href}
|
href={item.href}
|
||||||
@ -54,13 +146,11 @@ export const Sidebar = ({
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{index < items.length - 1 && (
|
{index < items.length - 1 && (
|
||||||
<Divider className="bg-foreground-300 mt-4" />
|
<Divider className="bg-foreground-300 mt-4" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<section className="mt-auto flex justify-between">
|
<section className="mt-auto flex justify-between">
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
</section>
|
</section>
|
||||||
|
@ -24,7 +24,7 @@ export const UserList = () => {
|
|||||||
|
|
||||||
if (!users) {
|
if (!users) {
|
||||||
return (
|
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">
|
<Skeleton className="rounded-lg">
|
||||||
<div className="h-10 rounded-lg bg-default-300" />
|
<div className="h-10 rounded-lg bg-default-300" />
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
@ -44,7 +44,7 @@ export const UserList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table aria-label="List of users" className="w-[300px]">
|
<Table aria-label="List of users" className="w-full">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableColumn>USERNAME</TableColumn>
|
<TableColumn>USERNAME</TableColumn>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
Loading…
Reference in New Issue
Block a user