feat(reader): goto page

This commit is contained in:
Julien Froidefond
2025-02-22 16:57:40 +01:00
parent 886ed87f1a
commit 584ce58e5a
4 changed files with 115 additions and 0 deletions

View File

@@ -194,6 +194,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
onToggleControls={() => setShowControls(!showControls)}
onPreviousPage={handlePreviousPage}
onNextPage={handleNextPage}
onPageChange={navigateToPage}
onClose={onClose}
currentPage={currentPage}
totalPages={pages.length}

View File

@@ -11,6 +11,7 @@ import {
MoveLeft,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { PageInput } from "./PageInput";
export const ControlButtons = ({
showControls,
@@ -26,6 +27,7 @@ export const ControlButtons = ({
onToggleFullscreen,
direction,
onToggleDirection,
onPageChange,
}: ControlButtonsProps) => {
return (
<>
@@ -82,6 +84,16 @@ export const ControlButtons = ({
>
{isFullscreen ? <Minimize2 className="h-6 w-6" /> : <Maximize2 className="h-6 w-6" />}
</button>
<div className="p-2 rounded-full bg-background/50" onClick={(e) => e.stopPropagation()}>
<PageInput
currentPage={currentPage}
totalPages={totalPages}
onPageChange={(page) => {
onToggleControls();
onPageChange(page);
}}
/>
</div>
</div>
{/* Bouton fermer */}

View File

@@ -0,0 +1,101 @@
import { useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { ArrowRight } from "lucide-react";
interface PageInputProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
}
export const PageInput = ({ currentPage, totalPages, onPageChange }: PageInputProps) => {
const [isEditing, setIsEditing] = useState(false);
const [inputValue, setInputValue] = useState(currentPage.toString());
const inputRef = useRef<HTMLInputElement>(null);
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
handleGoToPage();
} else if (e.key === "Escape") {
setIsEditing(false);
setInputValue(currentPage.toString());
}
};
const handleGoToPage = () => {
const value = parseInt(inputValue);
if (!isNaN(value) && value >= 1 && value <= totalPages) {
onPageChange(value);
setIsEditing(false);
}
};
const handleClick = () => {
setIsEditing(true);
setInputValue(currentPage.toString());
setTimeout(() => {
inputRef.current?.select();
}, 0);
};
const handleBlur = (e: React.FocusEvent) => {
// Ne pas fermer si on clique sur le bouton "Aller à"
if (e.relatedTarget?.getAttribute("data-action") === "goto") {
return;
}
setIsEditing(false);
setInputValue(currentPage.toString());
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// Ne garder que les chiffres
const value = e.target.value.replace(/[^0-9]/g, "");
if (value === "" || (parseInt(value) >= 1 && parseInt(value) <= totalPages)) {
setInputValue(value);
}
};
return (
<div
className="relative flex items-center gap-1"
role="group"
aria-label="Navigation par numéro de page"
>
{isEditing ? (
<>
<input
ref={inputRef}
type="text"
value={inputValue}
onChange={handleChange}
className={cn(
"w-12 bg-background/50 text-center rounded-md py-1 px-2",
"focus:outline-none focus:ring-2 focus:ring-primary",
"text-sm text-foreground"
)}
onKeyDown={handleKeyDown}
onBlur={handleBlur}
aria-label="Entrez un numéro de page"
/>
<button
onClick={handleGoToPage}
data-action="goto"
className="p-1 rounded-md bg-background/50 hover:bg-background/80 transition-colors"
aria-label="Aller à cette page"
>
<ArrowRight className="h-4 w-4" />
</button>
</>
) : (
<button
onClick={handleClick}
className="text-sm text-foreground/80 hover:text-foreground transition-colors"
tabIndex={0}
aria-label="Cliquez pour naviguer vers une page spécifique"
>
{currentPage}
</button>
)}
</div>
);
};

View File

@@ -38,6 +38,7 @@ export interface ControlButtonsProps {
onToggleControls: () => void;
onPreviousPage: () => void;
onNextPage: () => void;
onPageChange: (page: number) => void;
onClose?: () => void;
currentPage: number;
totalPages: number;