Add auto-hide nav bars on scroll and chapter selector bottom sheet

Nav bars hide when scrolling down and reappear on scroll up.
Replace static page count with a chapter picker that opens as a
bottom sheet drawer for quick chapter navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
yiekheng 2026-04-11 18:38:54 +08:00
parent 6257f6c79d
commit e7dc39738c
2 changed files with 91 additions and 4 deletions

View File

@ -53,6 +53,11 @@ export default async function ChapterReaderPage({ params }: Props) {
? manga.chapters[chapterIndex + 1].number
: null;
const allChapters = manga.chapters.map((c) => ({
number: c.number,
title: c.title,
}));
return (
<PageReader
pages={currentChapter.pages}
@ -62,6 +67,7 @@ export default async function ChapterReaderPage({ params }: Props) {
chapterTitle={currentChapter.title}
prevChapter={prevChapter}
nextChapter={nextChapter}
chapters={allChapters}
/>
);
}

View File

@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useState, useEffect, useRef } from "react";
import Link from "next/link";
type PageData = {
@ -8,6 +8,11 @@ type PageData = {
imageUrl: string;
};
type ChapterInfo = {
number: number;
title: string;
};
type PageReaderProps = {
pages: PageData[];
mangaSlug: string;
@ -16,6 +21,7 @@ type PageReaderProps = {
chapterTitle: string;
prevChapter: number | null;
nextChapter: number | null;
chapters: ChapterInfo[];
};
export function PageReader({
@ -26,8 +32,26 @@ export function PageReader({
chapterTitle,
prevChapter,
nextChapter,
chapters,
}: PageReaderProps) {
const [showUI, setShowUI] = useState(true);
const [showDrawer, setShowDrawer] = useState(false);
const lastScrollY = useRef(0);
useEffect(() => {
const handleScroll = () => {
const currentY = window.scrollY;
if (currentY > lastScrollY.current && currentY > 50) {
setShowUI(false);
} else if (currentY < lastScrollY.current) {
setShowUI(true);
}
lastScrollY.current = currentY;
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div className="min-h-screen bg-black relative">
@ -106,9 +130,23 @@ export function PageReader({
) : (
<div />
)}
<span className="text-white/50 text-xs">
{pages.length} pages
</span>
<button
onClick={() => setShowDrawer(true)}
className="flex items-center gap-1 text-white/80 hover:text-white text-sm transition-colors"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
className="w-5 h-5"
>
<line x1="3" y1="6" x2="21" y2="6" />
<line x1="3" y1="12" x2="21" y2="12" />
<line x1="3" y1="18" x2="21" y2="18" />
</svg>
Ch. {chapterNumber}
</button>
{nextChapter ? (
<Link
href={`/manga/${mangaSlug}/${nextChapter}`}
@ -135,6 +173,49 @@ export function PageReader({
)}
</div>
</div>
{/* Chapter drawer overlay */}
{showDrawer && (
<div
className="fixed inset-0 z-[60]"
onClick={() => setShowDrawer(false)}
>
{/* Backdrop */}
<div className="absolute inset-0 bg-black/60" />
{/* Bottom sheet */}
<div
className="absolute bottom-0 left-0 right-0 max-h-[60vh] bg-zinc-900 rounded-t-2xl overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
{/* Handle + header */}
<div className="sticky top-0 bg-zinc-900 z-10">
<div className="flex justify-center pt-2 pb-1">
<div className="w-10 h-1 rounded-full bg-white/20" />
</div>
<div className="px-4 pb-2 border-b border-white/10">
<span className="text-white text-sm font-medium">Chapters</span>
</div>
</div>
{/* Chapter list */}
<div className="overflow-y-auto max-h-[calc(60vh-3rem)] pb-safe">
{chapters.map((ch) => (
<Link
key={ch.number}
href={`/manga/${mangaSlug}/${ch.number}`}
className={`block px-4 py-3 text-sm transition-colors ${
ch.number === chapterNumber
? "bg-white/10 text-white font-medium"
: "text-white/70 hover:bg-white/5 hover:text-white"
}`}
onClick={() => setShowDrawer(false)}
>
Ch. {ch.number} {ch.title}
</Link>
))}
</div>
</div>
</div>
)}
</div>
);
}