fixed navbar

This commit is contained in:
Tejas Panchal
2025-07-27 14:42:05 +05:30
parent ef8f075689
commit 7166ff2cb7
3 changed files with 136 additions and 96 deletions

View File

@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faBars, faBars,
faRandom, faRandom,
faMagnifyingGlass,
} 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";
@@ -19,6 +20,7 @@ function Navbar() {
); );
const [isScrolled, setIsScrolled] = useState(false); const [isScrolled, setIsScrolled] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => {
@@ -53,9 +55,8 @@ function Navbar() {
return ( return (
<SearchProvider> <SearchProvider>
<nav <nav
className={`fixed top-0 left-0 w-full z-[1000000] transition-all duration-300 ease-in-out className={`fixed top-0 left-0 w-full z-[1000000] transition-all duration-300 ease-in-out bg-[#0a0a0a]
${isNotHomePage ? "bg-[#18181B]" : "bg-opacity-0"} ${isScrolled ? "bg-opacity-80 backdrop-blur-md shadow-lg" : "bg-opacity-100"}`}
${isScrolled ? "bg-[#18181B]/80 backdrop-blur-md shadow-lg" : ""}`}
> >
<div className="max-w-[1920px] mx-auto px-4 h-16 flex items-center justify-between"> <div className="max-w-[1920px] mx-auto px-4 h-16 flex items-center justify-between">
{/* Left Section */} {/* Left Section */}
@@ -66,7 +67,7 @@ function Navbar() {
className="text-xl text-gray-200 cursor-pointer hover:text-white transition-colors" className="text-xl text-gray-200 cursor-pointer hover:text-white transition-colors"
onClick={handleHamburgerClick} onClick={handleHamburgerClick}
/> />
<Link to="/" className="flex items-center"> <Link to="/home" className="flex items-center">
<img src="/logo.png" alt="JustAnime Logo" className="h-9 w-auto" /> <img src="/logo.png" alt="JustAnime Logo" className="h-9 w-auto" />
</Link> </Link>
</div> </div>
@@ -87,32 +88,45 @@ function Navbar() {
</div> </div>
</div> </div>
{/* Right Section */} {/* Language Toggle - Desktop */}
<div className="flex items-center gap-6"> <div className="hidden md:flex items-center gap-2 bg-[#27272A] rounded-md p-1">
<div className="hidden md:flex items-center gap-2 bg-[#27272A] rounded-md p-1"> {["EN", "JP"].map((lang) => (
{["EN", "JP"].map((lang) => ( <button
<button key={lang}
key={lang} onClick={() => toggleLanguage(lang)}
onClick={() => toggleLanguage(lang)} className={`px-3 py-1 text-sm font-medium rounded ${
className={`px-3 py-1 text-sm font-medium rounded ${ language === lang
language === lang ? "bg-[#3F3F46] text-white"
? "bg-[#3F3F46] text-white" : "text-gray-400 hover:text-white"
: "text-gray-400 hover:text-white" }`}
}`} >
> {lang}
{lang} </button>
</button> ))}
))} </div>
</div>
{/* Mobile Search Icon */}
<div className="md:hidden flex items-center">
<button
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"
title="Search Anime"
>
<FontAwesomeIcon icon={faMagnifyingGlass} className="text-lg" />
</button>
</div> </div>
</div> </div>
{/* Mobile Search */} {/* Mobile Search Dropdown */}
<div className="md:hidden"> {isMobileSearchOpen && (
<MobileSearch /> <div className="md:hidden bg-[#18181B] shadow-lg">
</div> <MobileSearch onClose={() => setIsMobileSearchOpen(false)} />
</div>
)}
{/* Sidebar */}
<Sidebar isOpen={isSidebarOpen} onClose={handleCloseSidebar} />
</nav> </nav>
<Sidebar isOpen={isSidebarOpen} onClose={handleCloseSidebar} />
</SearchProvider> </SearchProvider>
); );
} }

View File

@@ -1,11 +1,12 @@
import Suggestion from '../suggestion/Suggestion'; import Suggestion from '../suggestion/Suggestion';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'; import { faMagnifyingGlass, faRandom } from '@fortawesome/free-solid-svg-icons';
import useSearch from '@/src/hooks/useSearch'; import useSearch from '@/src/hooks/useSearch';
import { useNavigate } from 'react-router-dom'; import { Link, useNavigate, useLocation } from 'react-router-dom';
function MobileSearch() { function MobileSearch({ onClose }) {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const { const {
isSearchVisible, isSearchVisible,
searchValue, searchValue,
@@ -16,60 +17,80 @@ function MobileSearch() {
suggestionRefs, suggestionRefs,
addSuggestionRef, addSuggestionRef,
} = useSearch(); } = useSearch();
const handleSearchClick = () => { const handleSearchClick = () => {
if (searchValue.trim() && window.innerWidth <= 600) { if (searchValue.trim()) {
navigate(`/search?keyword=${encodeURIComponent(searchValue)}`); navigate(`/search?keyword=${encodeURIComponent(searchValue)}`);
onClose?.();
} }
}; };
const handleRandomClick = () => {
if (location.pathname === "/random") {
window.location.reload();
}
onClose?.();
};
return ( return (
<> <div className="w-full p-4 flex flex-col gap-4">
{isSearchVisible && ( <div className="flex items-center gap-2">
<div className="flex w-full mt-2 relative custom-md:hidden px-4"> <div className="relative flex-1">
<div className="relative w-full"> <input
<input type="text"
type="text" className="w-full px-5 py-2 bg-[#2a2a2a]/75 text-white rounded-lg focus:outline-none transition-colors placeholder-white/50"
className="w-full px-5 py-2 bg-[#2a2a2a]/75 text-white border border-white/10 rounded-lg focus:outline-none focus:border-white/30 transition-colors placeholder-white/50" placeholder="Search anime..."
placeholder="Search anime..." value={searchValue}
value={searchValue} onChange={(e) => setSearchValue(e.target.value)}
onChange={(e) => setSearchValue(e.target.value)} onFocus={() => setIsFocused(true)}
onFocus={() => setIsFocused(true)} onBlur={() => {
onBlur={() => { setTimeout(() => {
setTimeout(() => { const isInsideSuggestionBox = suggestionRefs.current.some(
const isInsideSuggestionBox = suggestionRefs.current.some( (ref) => ref && ref.contains(document.activeElement),
(ref) => ref && ref.contains(document.activeElement), );
); if (!isInsideSuggestionBox) {
if (!isInsideSuggestionBox) { setIsFocused(false);
setIsFocused(false);
}
}, 100);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearchClick();
} }
}} }, 100);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearchClick();
}
}}
/>
<button
className="absolute right-4 top-1/2 -translate-y-1/2 text-white/50 hover:text-white transition-colors"
onClick={handleSearchClick}
>
<FontAwesomeIcon
icon={faMagnifyingGlass}
className="text-lg"
/> />
<button </button>
className="absolute right-4 top-1/2 -translate-y-1/2 text-white/50 hover:text-white transition-colors" </div>
onClick={handleSearchClick} <Link
> to={location.pathname === "/random" ? "#" : "/random"}
<FontAwesomeIcon onClick={handleRandomClick}
icon={faMagnifyingGlass} className="p-[10px] aspect-square bg-[#2a2a2a]/75 text-white/50 hover:text-white rounded-lg transition-colors flex items-center justify-center shrink-0"
className="text-lg" title="Random Anime"
/> >
</button> <FontAwesomeIcon icon={faRandom} className="text-lg" />
</div> </Link>
{searchValue.trim() && isFocused && ( </div>
<div {searchValue.trim() && isFocused && (
ref={addSuggestionRef} <div
className="absolute z-[100000] top-full w-full" ref={addSuggestionRef}
> className="absolute z-[100000] left-0 right-0 px-4 mt-[60px]"
<Suggestion keyword={debouncedValue} className="w-full" /> >
</div> <Suggestion
)} keyword={debouncedValue}
className="w-full"
onSuggestionClick={onClose}
/>
</div> </div>
)} )}
</> </div>
); );
} }

View File

@@ -4,7 +4,7 @@ import BouncingLoader from "../ui/bouncingloader/Bouncingloader";
import { FaChevronRight } from "react-icons/fa"; import { FaChevronRight } from "react-icons/fa";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
function Suggestion({ keyword, className }) { function Suggestion({ keyword, className, onSuggestionClick }) {
const [suggestion, setSuggestion] = useState([]); const [suggestion, setSuggestion] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
@@ -31,32 +31,36 @@ function Suggestion({ keyword, className }) {
return ( return (
<div <div
className={`bg-[#2d2b44] ${className} flex ${ className={`bg-zinc-900 ${className} flex ${
loading ? "justify-center py-7" : "justify-start" loading ? "justify-center py-4" : "justify-start"
} ${!suggestion ? "p-3" : "justify-start"} items-center`} } ${!suggestion ? "p-2" : "justify-start"} items-center rounded-lg`}
style={{ boxShadow: "0 20px 20px rgba(0, 0, 0, .3)" }} style={{
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.05)"
}}
> >
{loading ? ( {loading ? (
<BouncingLoader /> <BouncingLoader />
) : error && !suggestion ? ( ) : error && !suggestion ? (
<div>Error loading suggestions</div> <div className="text-gray-400 p-3">Error loading suggestions</div>
) : suggestion && hasFetched ? ( ) : suggestion && hasFetched ? (
<div className="w-full flex flex-col pt-2 overflow-y-auto"> <div className="w-full flex flex-col pt-1 overflow-y-auto">
{suggestion.map((item, index) => ( {suggestion.map((item, index) => (
<Link <Link
to={`/${item.id}`} to={`/${item.id}`}
key={index} key={index}
className="group py-2 flex items-start gap-x-3 hover:bg-[#3c3a5e] cursor-pointer px-[10px]" onClick={onSuggestionClick}
className="group py-2 flex items-start gap-x-3 hover:bg-zinc-800 transition-all duration-200 cursor-pointer px-3"
style={{ style={{
borderBottom: borderBottom:
index === suggestion.length - 1 index === suggestion.length - 1
? "none" ? "none"
: "1px dashed rgba(255, 255, 255, .075)", : "1px solid rgba(255, 255, 255, 0.05)",
}} }}
> >
<img <img
src={`${item.poster}`} src={`${item.poster}`}
className="w-[50px] h-[75px] flex-shrink-0 object-cover" className="w-[45px] h-[65px] flex-shrink-0 object-cover rounded-md shadow-lg"
alt="" alt=""
onError={(e) => { onError={(e) => {
e.target.src = "https://i.postimg.cc/HnHKvHpz/no-avatar.jpg"; e.target.src = "https://i.postimg.cc/HnHKvHpz/no-avatar.jpg";
@@ -64,26 +68,26 @@ function Suggestion({ keyword, className }) {
/> />
<div className="flex flex-col gap-y-[2px]"> <div className="flex flex-col gap-y-[2px]">
{item?.title && ( {item?.title && (
<h1 className="line-clamp-1 leading-5 font-bold text-[15px] group-hover:text-[#ffbade]"> <h1 className="line-clamp-1 leading-5 font-semibold text-[14px] text-gray-100 group-hover:text-white">
{item.title || "N/A"} {item.title || "N/A"}
</h1> </h1>
)} )}
{item?.japanese_title && ( {item?.japanese_title && (
<h1 className="line-clamp-1 leading-5 text-[13px] font-light text-[#aaaaaa]"> <h1 className="line-clamp-1 leading-4 text-[12px] font-normal text-gray-400">
{item.japanese_title || "N/A"} {item.japanese_title || "N/A"}
</h1> </h1>
)} )}
{(item?.releaseDate || item?.showType || item?.duration) && ( {(item?.releaseDate || item?.showType || item?.duration) && (
<div className="flex gap-x-[5px] items-center w-full justify-start mt-[4px]"> <div className="flex gap-x-2 items-center w-full justify-start mt-[2px]">
<p className="leading-5 text-[13px] font-light text-[#aaaaaa]"> <p className="leading-4 text-[12px] font-normal text-gray-400">
{item.releaseDate || "N/A"} {item.releaseDate || "N/A"}
</p> </p>
<span className="dot"></span> <span className="w-1 h-1 rounded-full bg-gray-600"></span>
<p className="leading-5 text-[13px] font-medium group-hover:text-[#ffbade]"> <p className="leading-4 text-[12px] font-medium text-gray-300 group-hover:text-white">
{item.showType || "N/A"} {item.showType || "N/A"}
</p> </p>
<span className="dot"></span> <span className="w-1 h-1 rounded-full bg-gray-600"></span>
<p className="leading-5 text-[13px] font-light text-[#aaaaaa]"> <p className="leading-4 text-[12px] font-normal text-gray-400">
{item.duration || "N/A"} {item.duration || "N/A"}
</p> </p>
</div> </div>
@@ -93,20 +97,21 @@ function Suggestion({ keyword, className }) {
))} ))}
{!loading && hasFetched && ( {!loading && hasFetched && (
<Link <Link
className="w-full flex py-4 justify-center items-center bg-[#ffbade]" className="w-full flex py-2.5 justify-center items-center bg-zinc-800 hover:bg-zinc-700 transition-all duration-200 rounded-b-lg"
to={`/search?keyword=${encodeURIComponent(keyword)}`} to={`/search?keyword=${encodeURIComponent(keyword)}`}
onClick={onSuggestionClick}
> >
<div className="flex w-fit items-center gap-x-2"> <div className="flex w-fit items-center gap-x-2">
<p className="text-[17px] font-light text-black"> <p className="text-[14px] font-medium text-gray-200">
View all results View all results
</p> </p>
<FaChevronRight className="text-black text-[12px] font-black mt-[2px]" /> <FaChevronRight className="text-gray-200 text-[11px] mt-[1px]" />
</div> </div>
</Link> </Link>
)} )}
</div> </div>
) : hasFetched ? ( ) : hasFetched ? (
<p className="text-[17px]">No results found!</p> <p className="text-gray-300 p-3">No results found!</p>
) : null} ) : null}
</div> </div>
); );