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:
parent
6257f6c79d
commit
e7dc39738c
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user