mirror of
https://github.com/JustAnimeCore/JustAnime.git
synced 2026-04-17 22:01:45 +00:00
sidebar DONE
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
|||||||
faBars,
|
faBars,
|
||||||
faRandom,
|
faRandom,
|
||||||
faMagnifyingGlass,
|
faMagnifyingGlass,
|
||||||
|
faXmark,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { useLanguage } from "@/src/context/LanguageContext";
|
import { useLanguage } from "@/src/context/LanguageContext";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
@@ -109,10 +110,14 @@ function Navbar() {
|
|||||||
<div className="md:hidden flex items-center">
|
<div className="md:hidden flex items-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsMobileSearchOpen(!isMobileSearchOpen)}
|
onClick={() => setIsMobileSearchOpen(!isMobileSearchOpen)}
|
||||||
className="p-[10px] aspect-square bg-[#2a2a2a]/75 text-white/50 hover:text-white rounded-lg transition-colors flex items-center justify-center"
|
className="p-[10px] aspect-square bg-[#2a2a2a]/75 text-white/50 hover:text-white rounded-lg transition-colors flex items-center justify-center w-[38px] h-[38px]"
|
||||||
title="Search Anime"
|
title={isMobileSearchOpen ? "Close Search" : "Search Anime"}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faMagnifyingGlass} className="text-lg" />
|
<FontAwesomeIcon
|
||||||
|
icon={isMobileSearchOpen ? faXmark : faMagnifyingGlass}
|
||||||
|
className="w-[18px] h-[18px] transition-transform duration-200"
|
||||||
|
style={{ transform: isMobileSearchOpen ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
33
src/components/sidebar/Sidebar.css
Normal file
33
src/components/sidebar/Sidebar.css
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
.sidebar {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.sidebar-open {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 5px; /* Prevent layout shift when scrollbar disappears */
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrapper to maintain scroll position when sidebar is opened */
|
||||||
|
.sidebar-scroll-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
@@ -1,22 +1,42 @@
|
|||||||
import { FaChevronLeft } from "react-icons/fa";
|
import { FaChevronLeft } from "react-icons/fa";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faFilm, faRandom } from "@fortawesome/free-solid-svg-icons";
|
import { faFilm, faRandom, faHome, faClock, faFire, faTv, faPlay, faCirclePlay, faFilePen, faPaperPlane } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { useLanguage } from "@/src/context/LanguageContext";
|
import { useLanguage } from "@/src/context/LanguageContext";
|
||||||
import { useEffect } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import {
|
import "./Sidebar.css";
|
||||||
cleanupScrollbar,
|
|
||||||
toggleScrollbar,
|
const MENU_ITEMS = [
|
||||||
} from "@/src/helper/toggleScrollbar";
|
{ name: "Home", path: "/home", icon: faHome },
|
||||||
|
{ name: "Subbed Anime", path: "/subbed-anime", icon: faFilePen },
|
||||||
|
{ name: "Dubbed Anime", path: "/dubbed-anime", icon: faPlay },
|
||||||
|
{ name: "Most Popular", path: "/most-popular", icon: faFire },
|
||||||
|
{ name: "Movies", path: "/movie", icon: faFilm },
|
||||||
|
{ name: "TV Series", path: "/tv", icon: faTv },
|
||||||
|
{ name: "OVAs", path: "/ova", icon: faCirclePlay },
|
||||||
|
{ name: "ONAs", path: "/ona", icon: faPlay },
|
||||||
|
{ name: "Specials", path: "/special", icon: faClock },
|
||||||
|
{ name: "Join Telegram", path: "https://t.me/zenime_discussion", icon: faPaperPlane },
|
||||||
|
];
|
||||||
|
|
||||||
const Sidebar = ({ isOpen, onClose }) => {
|
const Sidebar = ({ isOpen, onClose }) => {
|
||||||
const { language, toggleLanguage } = useLanguage();
|
const { language, toggleLanguage } = useLanguage();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const scrollPosition = useRef(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
toggleScrollbar(isOpen);
|
if (isOpen) {
|
||||||
|
scrollPosition.current = window.pageYOffset;
|
||||||
|
document.body.classList.add('sidebar-open');
|
||||||
|
document.body.style.top = `-${scrollPosition.current}px`;
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('sidebar-open');
|
||||||
|
document.body.style.top = '';
|
||||||
|
window.scrollTo(0, scrollPosition.current);
|
||||||
|
}
|
||||||
return () => {
|
return () => {
|
||||||
cleanupScrollbar();
|
document.body.classList.remove('sidebar-open');
|
||||||
|
document.body.style.top = '';
|
||||||
};
|
};
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
@@ -28,110 +48,83 @@ const Sidebar = ({ isOpen, onClose }) => {
|
|||||||
<>
|
<>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div
|
<div
|
||||||
className={`fixed top-0 left-0 bottom-0 right-0 w-screen h-screen transform transition-all duration-400 ease-in-out ${
|
className="fixed inset-0 transform transition-all duration-400 ease-in-out backdrop-blur-lg bg-black/50"
|
||||||
isOpen ? "backdrop-blur-lg" : "backdrop-blur-none"
|
|
||||||
}`}
|
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
style={{ zIndex: 1000000, background: "rgba(32, 31, 49, .8)" }}
|
style={{ zIndex: 1000000 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`fixed h-full top-0 left-0 z-50 flex transition-transform duration-300 ease-in-out ${
|
className={`fixed h-[100dvh] w-[280px] max-[575px]:w-[260px] top-0 left-0 transform transition-transform duration-300 ease-in-out ${
|
||||||
isOpen ? "translate-x-0" : "-translate-x-full"
|
isOpen ? "translate-x-0" : "-translate-x-full"
|
||||||
}`}
|
}`}
|
||||||
style={{ zIndex: 1000200 }}
|
style={{ zIndex: 1000200 }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="bg-white/10 w-[260px] py-8 h-full flex flex-col items-start max-[575px]:w-56 overflow-y-auto sidebar"
|
className="bg-[#0a0a0a] w-full h-full flex flex-col items-start overflow-y-auto sidebar"
|
||||||
style={{
|
|
||||||
zIndex: 300,
|
|
||||||
borderRight: "1px solid rgba(0, 0, 0, .1)",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="px-4 w-full">
|
{/* Header */}
|
||||||
|
<div className="w-full p-6 border-b border-white/5">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="w-full text-white flex items-baseline h-fit gap-x-1 z-[100] px-3 py-2 bg-[#4f4d6e] rounded-3xl"
|
className="w-full flex items-center justify-center gap-2 py-2.5 px-4 bg-white/10 hover:bg-white/15 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
<FaChevronLeft className="text-sm font-bold" />
|
<FaChevronLeft className="text-sm" />
|
||||||
<p>Close menu</p>
|
<span className="text-sm font-medium">Close Menu</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-7 w-full py-3 justify-center px-auto mt-8 bg-black/10 max-[575px]:gap-x-4 lg:hidden">
|
|
||||||
{[
|
{/* Quick Actions */}
|
||||||
{ icon: faRandom, label: "Random" },
|
<div className="w-full p-4 border-b border-white/5 lg:hidden">
|
||||||
{ icon: faFilm, label: "Movie" },
|
<div className="grid grid-cols-3 gap-2">
|
||||||
].map((item, index) => (
|
|
||||||
<Link
|
<Link
|
||||||
to={`/${item.label}`}
|
to="/random"
|
||||||
key={index}
|
className="flex flex-col items-center gap-2 p-3 rounded-lg bg-white/5 hover:bg-white/10 transition-colors"
|
||||||
className="flex flex-col gap-y-1 items-center"
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon icon={faRandom} className="text-lg" />
|
||||||
icon={item.icon}
|
<span className="text-xs font-medium">Random</span>
|
||||||
className="text-[#ffbade] text-xl font-bold max-[575px]:text-[15px]"
|
|
||||||
/>
|
|
||||||
<p className="text-[15px] max-[575px]:text-[13px]">
|
|
||||||
{item.label}
|
|
||||||
</p>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
<Link
|
||||||
<div className="flex flex-col gap-y-1 items-center w-auto justify-center">
|
to="/movie"
|
||||||
<div className="flex">
|
className="flex flex-col items-center gap-2 p-3 rounded-lg bg-white/5 hover:bg-white/10 transition-colors"
|
||||||
{["EN", "JP"].map((lang, index) => (
|
>
|
||||||
<button
|
<FontAwesomeIcon icon={faFilm} className="text-lg" />
|
||||||
key={lang}
|
<span className="text-xs font-medium">Movie</span>
|
||||||
onClick={() => toggleLanguage(lang)}
|
</Link>
|
||||||
className={`px-1 py-[1px] text-xs font-bold ${
|
<div className="flex flex-col items-center gap-2 p-3 rounded-lg bg-white/5">
|
||||||
index === 0 ? "rounded-l-[3px]" : "rounded-r-[3px]"
|
<div className="flex bg-white/10 rounded">
|
||||||
} ${
|
{["EN", "JP"].map((lang, index) => (
|
||||||
language === lang
|
<button
|
||||||
? "bg-[#ffbade] text-black"
|
key={lang}
|
||||||
: "bg-gray-600 text-white"
|
onClick={() => toggleLanguage(lang)}
|
||||||
} max-[575px]:text-[9px] max-[575px]:py-0`}
|
className={`px-2 py-1 text-xs font-medium transition-colors ${
|
||||||
>
|
language === lang
|
||||||
{lang}
|
? "bg-white/15 text-white"
|
||||||
</button>
|
: "text-white/60 hover:text-white"
|
||||||
))}
|
}`}
|
||||||
</div>
|
>
|
||||||
<div className="w-full">
|
{lang}
|
||||||
<p className="whitespace-nowrap text-[15px] max-[575px]:text-[13px]">
|
</button>
|
||||||
Anime name
|
))}
|
||||||
</p>
|
</div>
|
||||||
|
<span className="text-xs font-medium text-white/60">Language</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="text-white mt-8 w-full">
|
|
||||||
{[
|
{/* Menu Items */}
|
||||||
{ name: "Home", path: "/home" },
|
<div className="w-full p-2">
|
||||||
{ name: "Subbed Anime", path: "/subbed-anime" },
|
{MENU_ITEMS.map((item, index) => (
|
||||||
{ name: "Dubbed Anime", path: "/dubbed-anime" },
|
<Link
|
||||||
{ name: "Most Popular", path: "/most-popular" },
|
|
||||||
{ name: "Movies", path: "/movie" },
|
|
||||||
{ name: "TV Series", path: "/tv" },
|
|
||||||
{ name: "OVAs", path: "/ova" },
|
|
||||||
{ name: "ONAs", path: "/ona" },
|
|
||||||
{ name: "Specials", path: "/special" },
|
|
||||||
{
|
|
||||||
name: "Join Telegram",
|
|
||||||
path: "https://t.me/zenime_discussion",
|
|
||||||
},
|
|
||||||
].map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={index}
|
key={index}
|
||||||
className="py-4 w-full font-semibold"
|
to={item.path}
|
||||||
style={{ borderBottom: "1px solid rgba(255, 255, 255, .08)" }}
|
className="flex items-center gap-3 px-4 py-3 rounded-lg text-white/60 hover:text-white hover:bg-white/5 transition-colors"
|
||||||
>
|
>
|
||||||
<Link
|
<FontAwesomeIcon icon={item.icon} className="text-lg w-5" />
|
||||||
to={item.path}
|
<span className="font-medium">{item.name}</span>
|
||||||
className="px-4 hover:text-[#ffbade] hover:cursor-pointer w-fit line-clamp-1"
|
</Link>
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user