merge: fix/40-reader-mobile-bar-overflow dans develop (barre lecteur compactée et wrappée sur mobile, plus de rognage à droite — correctif #40)

This commit is contained in:
Git Agent
2026-08-26 13:12:10 +02:00
3 changed files with 91 additions and 9 deletions

View File

@ -1,4 +1,4 @@
import { ArrowLeft, ArrowRight } from "lucide-react";
import { ArrowLeft, ArrowRight, Maximize2, Minimize2 } from "lucide-react";
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
import { ErrorRibbon } from "../components/ui";
import { navigate } from "../router";
@ -189,9 +189,9 @@ export function ReaderShell({ title, backHref, error, onRetry, controls, zoomCon
setHeaderVisible(true);
}}
>
<button className="ghost-button" onClick={() => navigate(backHref)}>
<button className="ghost-button reader-back-button" onClick={() => navigate(backHref)} aria-label="Retour">
<ArrowLeft size={17} />
Retour
<span>Retour</span>
</button>
<strong className="reader-title">{title}</strong>
<span className="reader-position">{controls.positionLabel}</span>
@ -204,12 +204,12 @@ export function ReaderShell({ title, backHref, error, onRetry, controls, zoomCon
</select>
</label>
)}
<button className="ghost-button" onClick={controls.onPrevious} disabled={!controls.canPrevious}>
<button className="ghost-button reader-nav-button reader-previous-button" onClick={controls.onPrevious} disabled={!controls.canPrevious} aria-label="Page precedente">
<ArrowLeft size={16} />
Precedent
<span>Precedent</span>
</button>
<button className="ghost-button" onClick={controls.onNext} disabled={!controls.canNext}>
Suivant
<button className="ghost-button reader-nav-button reader-next-button" onClick={controls.onNext} disabled={!controls.canNext} aria-label="Page suivante">
<span>Suivant</span>
<ArrowRight size={16} />
</button>
{zoomControls && (
@ -226,8 +226,9 @@ export function ReaderShell({ title, backHref, error, onRetry, controls, zoomCon
</button>
</div>
)}
<button className="ghost-button reader-fullscreen-toggle" onClick={() => void toggleFullscreen()} aria-pressed={fullscreenActive}>
{fullscreenActive ? "Quitter le plein écran" : "Plein écran"}
<button className="ghost-button reader-fullscreen-toggle" onClick={() => void toggleFullscreen()} aria-pressed={fullscreenActive} aria-label={fullscreenActive ? "Quitter le plein écran" : "Plein écran"}>
{fullscreenActive ? <Minimize2 size={16} /> : <Maximize2 size={16} />}
<span>{fullscreenActive ? "Quitter le plein écran" : "Plein écran"}</span>
</button>
</header>
{error && (

View File

@ -222,6 +222,15 @@ describe("reader runtime helpers", () => {
expect(verticalContent).toContain("align-self: start");
});
it("lets the mobile reader toolbar wrap instead of overflowing horizontally", () => {
const styles = readFileSync(new URL("../styles/app.css", import.meta.url), "utf8");
expect(styles).toContain(".reader-topbar {\n gap: 8px;\n flex-wrap: wrap;");
expect(styles).toContain("overflow: hidden");
expect(styles).toContain(".reader-back-button span,\n .reader-nav-button span,\n .reader-fullscreen-toggle span,\n .reader-mode-control > span");
expect(styles).toContain(".reader-mode-control select {\n width: 100%;");
});
it("detects fullscreen support and active fullscreen element", () => {
const fullscreenElement = { requestFullscreen: () => Promise.resolve() } as unknown as HTMLElement;
const regularElement = {} as HTMLElement;

View File

@ -1282,6 +1282,78 @@ main.app-main-reader .reader-page {
.reader-topbar {
gap: 8px;
flex-wrap: wrap;
align-content: start;
overflow: hidden;
}
.reader-back-button,
.reader-nav-button,
.reader-fullscreen-toggle {
width: 34px;
min-width: 34px;
padding: 0;
}
.reader-back-button {
order: 1;
}
.reader-title {
order: 2;
flex: 1 1 120px;
font-size: 0.9rem;
}
.reader-position {
order: 3;
min-width: 78px;
font-size: 0.78rem;
}
.reader-fullscreen-toggle {
order: 4;
margin-left: 0;
}
.reader-mode-control {
order: 5;
flex: 1 1 132px;
min-width: 0;
}
.reader-mode-control select {
width: 100%;
min-width: 0;
max-width: 100%;
font-size: 0.78rem;
}
.reader-previous-button {
order: 6;
}
.reader-next-button {
order: 7;
}
.reader-zoom-controls {
order: 8;
flex: 0 1 auto;
min-width: 0;
gap: 4px;
}
.reader-zoom-label {
min-width: 46px;
font-size: 0.78rem;
}
.reader-back-button span,
.reader-nav-button span,
.reader-fullscreen-toggle span,
.reader-mode-control > span {
display: none;
}
.reader-zoom-reset {