feat: add thumbnail display toggle functionality in BookReader component
This commit is contained in:
@@ -18,6 +18,7 @@ import { useTranslate } from "@/hooks/useTranslate";
|
|||||||
export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps) {
|
export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps) {
|
||||||
const [isDoublePage, setIsDoublePage] = useState(false);
|
const [isDoublePage, setIsDoublePage] = useState(false);
|
||||||
const [showControls, setShowControls] = useState(false);
|
const [showControls, setShowControls] = useState(false);
|
||||||
|
const [showThumbnails, setShowThumbnails] = useState(false);
|
||||||
const readerRef = useRef<HTMLDivElement>(null);
|
const readerRef = useRef<HTMLDivElement>(null);
|
||||||
const isLandscape = useOrientation();
|
const isLandscape = useOrientation();
|
||||||
const { direction, toggleDirection, isRTL } = useReadingDirection();
|
const { direction, toggleDirection, isRTL } = useReadingDirection();
|
||||||
@@ -126,6 +127,8 @@ export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps)
|
|||||||
onToggleFullscreen={() => toggleFullscreen(readerRef.current)}
|
onToggleFullscreen={() => toggleFullscreen(readerRef.current)}
|
||||||
direction={direction}
|
direction={direction}
|
||||||
onToggleDirection={toggleDirection}
|
onToggleDirection={toggleDirection}
|
||||||
|
showThumbnails={showThumbnails}
|
||||||
|
onToggleThumbnails={() => setShowThumbnails(!showThumbnails)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ReaderContent
|
<ReaderContent
|
||||||
@@ -148,6 +151,7 @@ export function BookReader({ book, pages, onClose, nextBook }: BookReaderProps)
|
|||||||
pages={pages}
|
pages={pages}
|
||||||
onPageChange={navigateToPage}
|
onPageChange={navigateToPage}
|
||||||
showControls={showControls}
|
showControls={showControls}
|
||||||
|
showThumbnails={showThumbnails}
|
||||||
book={book}
|
book={book}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Minimize2,
|
Minimize2,
|
||||||
MoveRight,
|
MoveRight,
|
||||||
MoveLeft,
|
MoveLeft,
|
||||||
|
Images,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { PageInput } from "./PageInput";
|
import { PageInput } from "./PageInput";
|
||||||
@@ -29,6 +30,8 @@ export const ControlButtons = ({
|
|||||||
direction,
|
direction,
|
||||||
onToggleDirection,
|
onToggleDirection,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
|
showThumbnails,
|
||||||
|
onToggleThumbnails,
|
||||||
}: ControlButtonsProps) => {
|
}: ControlButtonsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -95,6 +98,21 @@ export const ControlButtons = ({
|
|||||||
>
|
>
|
||||||
{isFullscreen ? <Minimize2 className="h-6 w-6" /> : <Maximize2 className="h-6 w-6" />}
|
{isFullscreen ? <Minimize2 className="h-6 w-6" /> : <Maximize2 className="h-6 w-6" />}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onToggleThumbnails();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"p-2 rounded-full bg-background/50 hover:bg-background/80 transition-colors",
|
||||||
|
showThumbnails && "ring-2 ring-primary"
|
||||||
|
)}
|
||||||
|
aria-label={t(
|
||||||
|
showThumbnails ? "reader.controls.thumbnails.hide" : "reader.controls.thumbnails.show"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Images className="h-6 w-6" />
|
||||||
|
</button>
|
||||||
<div className="p-2 rounded-full bg-background/50" onClick={(e) => e.stopPropagation()}>
|
<div className="p-2 rounded-full bg-background/50" onClick={(e) => e.stopPropagation()}>
|
||||||
<PageInput
|
<PageInput
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const NavigationBar = ({
|
|||||||
pages,
|
pages,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
showControls,
|
showControls,
|
||||||
|
showThumbnails,
|
||||||
book,
|
book,
|
||||||
}: NavigationBarProps) => {
|
}: NavigationBarProps) => {
|
||||||
const [isTooSmall, setIsTooSmall] = useState(false);
|
const [isTooSmall, setIsTooSmall] = useState(false);
|
||||||
@@ -31,21 +32,21 @@ export const NavigationBar = ({
|
|||||||
return () => window.removeEventListener("resize", checkHeight);
|
return () => window.removeEventListener("resize", checkHeight);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Scroll à l'ouverture des contrôles et au changement de page
|
// Scroll à l'ouverture des vignettes et au changement de page
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (showControls && !isTooSmall) {
|
if (showThumbnails && !isTooSmall) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const thumbnail = document.getElementById(`thumbnail-${currentPage}`);
|
const thumbnail = document.getElementById(`thumbnail-${currentPage}`);
|
||||||
if (thumbnail) {
|
if (thumbnail) {
|
||||||
thumbnail.scrollIntoView({
|
thumbnail.scrollIntoView({
|
||||||
behavior: showControls ? "instant" : "smooth",
|
behavior: showThumbnails ? "instant" : "smooth",
|
||||||
block: "nearest",
|
block: "nearest",
|
||||||
inline: "center",
|
inline: "center",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [showControls, currentPage, isTooSmall]);
|
}, [showThumbnails, currentPage, isTooSmall]);
|
||||||
|
|
||||||
if (isTooSmall) {
|
if (isTooSmall) {
|
||||||
return null;
|
return null;
|
||||||
@@ -55,10 +56,10 @@ export const NavigationBar = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute bottom-0 left-0 right-0 bg-background/50 backdrop-blur-sm border-t border-border/40 transition-all duration-300 ease-in-out z-30",
|
"absolute bottom-0 left-0 right-0 bg-background/50 backdrop-blur-sm border-t border-border/40 transition-all duration-300 ease-in-out z-30",
|
||||||
showControls ? "h-52 opacity-100" : "h-0 opacity-0"
|
showThumbnails ? "h-52 opacity-100" : "h-0 opacity-0"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{showControls && (
|
{showThumbnails && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
id="thumbnails-container"
|
id="thumbnails-container"
|
||||||
@@ -88,9 +89,11 @@ export const NavigationBar = ({
|
|||||||
<div className="w-[calc(50vw-18rem)] flex-shrink-0" />
|
<div className="w-[calc(50vw-18rem)] flex-shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-full px-4 py-2 rounded-full bg-background/50 text-sm">
|
{showControls && (
|
||||||
Page {currentPage} / {pages.length}
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-full px-4 py-2 rounded-full bg-background/50 text-sm">
|
||||||
</div>
|
Page {currentPage} / {pages.length}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export interface NavigationBarProps {
|
|||||||
pages: number[];
|
pages: number[];
|
||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
showControls: boolean;
|
showControls: boolean;
|
||||||
|
showThumbnails: boolean;
|
||||||
book: KomgaBook;
|
book: KomgaBook;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +50,8 @@ export interface ControlButtonsProps {
|
|||||||
onToggleFullscreen: () => void;
|
onToggleFullscreen: () => void;
|
||||||
direction: "ltr" | "rtl";
|
direction: "ltr" | "rtl";
|
||||||
onToggleDirection: () => void;
|
onToggleDirection: () => void;
|
||||||
|
showThumbnails: boolean;
|
||||||
|
onToggleThumbnails: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UsePageNavigationProps {
|
export interface UsePageNavigationProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user