mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-08-10 07:11:18 +02:00
pdf-viewer: various visual and usability changes
- fix page nav buttons, smaller default pages, violet bg
- Build toolbar after renderAllPages so it queries existing canvases;
query canvases fresh in the scroll handler so page tracking works
after re-render (zoom in/out).
- Default data-pdf-scale 0.85 (JS fallback 0.8) so pages are a bit
smaller by default.
- Add dark transparent violet background (accent-secondary 22%) to
.pdf-expanded, behind the pages.
- expand to fill column top-to-bottom via CSS flex
Use a flex chain (.tfe-files flex:1, .tfe-file-item:has(.pdf-expanded)
flex:1) so the expanded PDF wrapper fills the right column from under
the header to the bottom of the viewport, instead of viewport-based
clamp math that didn't account for the header height.
- fill right column, center pages, freeze column on expand
The expanded viewer now stays inside the right column instead of
growing the page: the column switches to overflow:hidden while the
PDF panel scrolls internally. Pages are centered horizontally via
flexbox align-items: center.
- fix toolbar not full-width, hide sibling file items
The toolbar was constrained by the parent's align-items:center; fixed
with align-self:stretch. Hiding sibling .tfe-file-item elements when
a PDF is expanded prevents them from rendering above the overlay.
- disable overscroll on expanded view
- use overscroll-behavior-y on container, none on toolbar
- disable body overscroll via .pdf-viewer-open class
- mobile full-viewport, iframe matches pdf height
- mobile overlay above everything via position:fixed
- opaque dark-violet bg on mobile overlay
- tfe: add 'Open in new tab' link above website iframes
- trim column padding when expanded for more vertical space
- 5% zoom increments, subtle padding on expanded column
- prevent horizontal overflow/overscroll
This commit is contained in:
@@ -29,3 +29,8 @@
|
|||||||
- [x] Improve h1, h2, h3 size difference in base.css heading scale
|
- [x] Improve h1, h2, h3 size difference in base.css heading scale
|
||||||
- [x] Rework TFE page desktop layout: two columns 35vw/1fr, left = author/title/meta/synopsis, right = files, media 100% width; columns scroll independently, content fits 100vw; no bottom padding on main; add &scrollbar=0 to PDF iframe URLs
|
- [x] Rework TFE page desktop layout: two columns 35vw/1fr, left = author/title/meta/synopsis, right = files, media 100% width; columns scroll independently, content fits 100vw; no bottom padding on main; add &scrollbar=0 to PDF iframe URLs
|
||||||
- [x] 'Promoteur·ice ULB' remplacé par 'Promoteur·ice université' (changement juste l'étiquette)
|
- [x] 'Promoteur·ice ULB' remplacé par 'Promoteur·ice université' (changement juste l'étiquette)
|
||||||
|
- [x] PDF viewer: fix page navigation buttons (prev/next) — build toolbar after canvases exist & query canvases fresh in scroll handler
|
||||||
|
- [x] PDF viewer: render pages smaller by default (0.85× scale)
|
||||||
|
- [x] PDF viewer: add dark transparent violet background behind pages when expanded
|
||||||
|
- [x] pdf-viewer: expand to fill right column (not whole page), center pages, freeze column on expand
|
||||||
|
- [x] pdf-viewer: fix toolbar width (align-self:stretch), hide sibling file items when expanded
|
||||||
|
|||||||
@@ -57,6 +57,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-m);
|
gap: var(--space-m);
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tfe-file-item {
|
.tfe-file-item {
|
||||||
@@ -70,6 +72,30 @@
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tfe-file-item:has(.pdf-expanded) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tfe-file-item:has(.tfe-file-iframe) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When a PDF is expanded, freeze the right column and hide
|
||||||
|
sibling file items so they can't render above the expanded view.
|
||||||
|
Also trim column padding so the PDF fills more vertical space. */
|
||||||
|
.tfe-right-column:has(.pdf-expanded) {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: var(--space-xs);
|
||||||
|
padding-bottom: var(--space-xs);
|
||||||
|
}
|
||||||
|
.tfe-right-column:has(.pdf-expanded) .tfe-file-item:not(:has(.pdf-expanded)) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.tfe-file-item img {
|
.tfe-file-item img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -95,11 +121,29 @@
|
|||||||
.tfe-file-iframe {
|
.tfe-file-iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: clamp(300px, 80vh, 700px);
|
height: 100%;
|
||||||
|
min-height: 400px;
|
||||||
display: block;
|
display: block;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tfe-website-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3xs);
|
||||||
|
padding: var(--space-2xs) var(--space-xs);
|
||||||
|
font-size: var(--step--1);
|
||||||
|
color: var(--accent-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin-bottom: var(--space-2xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tfe-website-link:hover {
|
||||||
|
background: color-mix(in srgb, var(--accent-primary) 10%, var(--bg-primary));
|
||||||
|
}
|
||||||
|
|
||||||
.tfe-file-item figcaption {
|
.tfe-file-item figcaption {
|
||||||
font-size: var(--step--2);
|
font-size: var(--step--2);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
@@ -312,8 +356,8 @@
|
|||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
outline: none;
|
|
||||||
transition: height 0.35s ease, border-color 0.35s ease;
|
transition: height 0.35s ease, border-color 0.35s ease;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Collapsed state: thumbnail tile ────────── */
|
/* ── Collapsed state: thumbnail tile ────────── */
|
||||||
@@ -395,19 +439,30 @@
|
|||||||
|
|
||||||
/* ── Expanded state: wrapper becomes scroll panel ── */
|
/* ── Expanded state: wrapper becomes scroll panel ── */
|
||||||
|
|
||||||
|
/* Disable root-level overscroll (rubberbanding) while a PDF is open. */
|
||||||
|
body.pdf-viewer-open {
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
|
||||||
.pdf-expanded {
|
.pdf-expanded {
|
||||||
/* Fill viewport minus the column's vertical padding */
|
/* Fill the right column from top to bottom (under the header). */
|
||||||
max-height: calc(100vh - var(--space-xl) * 2);
|
height: 100%;
|
||||||
height: clamp(400px, 80vh, calc(100vh - var(--space-xl) * 2));
|
min-height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overscroll-behavior-y: contain;
|
||||||
border: 2px solid var(--accent-primary);
|
border: 2px solid var(--accent-primary);
|
||||||
display: block;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: color-mix(in srgb, var(--accent-secondary) 22%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Toolbar ────────────────────────────────── */
|
/* ── Toolbar ────────────────────────────────── */
|
||||||
|
|
||||||
.pdf-toolbar {
|
.pdf-toolbar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
align-self: stretch;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -420,6 +475,7 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: var(--step--1);
|
font-size: var(--step--1);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdf-toolbar-btn {
|
.pdf-toolbar-btn {
|
||||||
@@ -505,6 +561,19 @@
|
|||||||
.tfe-title {
|
.tfe-title {
|
||||||
font-size: var(--step-2);
|
font-size: var(--step-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PDF expands to full viewport height on mobile, above everything */
|
||||||
|
.pdf-expanded {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 100;
|
||||||
|
height: auto;
|
||||||
|
min-height: unset;
|
||||||
|
background: color-mix(in srgb, var(--accent-secondary) 92%, #000 8%);
|
||||||
|
}
|
||||||
|
.tfe-file-iframe {
|
||||||
|
height: 100dvh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
async function expand(container, pdfDoc) {
|
async function expand(container, pdfDoc) {
|
||||||
container.classList.remove("pdf-collapsed");
|
container.classList.remove("pdf-collapsed");
|
||||||
container.classList.add("pdf-expanded");
|
container.classList.add("pdf-expanded");
|
||||||
|
document.body.classList.add("pdf-viewer-open");
|
||||||
|
|
||||||
container.addEventListener("contextmenu", (e) => e.preventDefault());
|
container.addEventListener("contextmenu", (e) => e.preventDefault());
|
||||||
container.addEventListener("keydown", (e) => {
|
container.addEventListener("keydown", (e) => {
|
||||||
@@ -132,10 +133,10 @@
|
|||||||
if (oldThumb) oldThumb.remove();
|
if (oldThumb) oldThumb.remove();
|
||||||
|
|
||||||
// Build toolbar
|
// Build toolbar
|
||||||
buildToolbar(container, pdfDoc);
|
// Render pages directly into the wrapper, then build the toolbar
|
||||||
|
// (toolbar queries canvases, so it must run after they exist).
|
||||||
// Render pages directly into the wrapper
|
|
||||||
await renderAllPages(container, pdfDoc);
|
await renderAllPages(container, pdfDoc);
|
||||||
|
buildToolbar(container, pdfDoc);
|
||||||
|
|
||||||
container.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
container.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||||
}
|
}
|
||||||
@@ -143,6 +144,7 @@
|
|||||||
function collapse(container) {
|
function collapse(container) {
|
||||||
container.classList.remove("pdf-expanded");
|
container.classList.remove("pdf-expanded");
|
||||||
container.classList.add("pdf-collapsed");
|
container.classList.add("pdf-collapsed");
|
||||||
|
document.body.classList.remove("pdf-viewer-open");
|
||||||
|
|
||||||
const oldToolbar = container.querySelector(".pdf-toolbar");
|
const oldToolbar = container.querySelector(".pdf-toolbar");
|
||||||
if (oldToolbar) oldToolbar.remove();
|
if (oldToolbar) oldToolbar.remove();
|
||||||
@@ -178,7 +180,7 @@
|
|||||||
const rendering = new Set();
|
const rendering = new Set();
|
||||||
|
|
||||||
const resolveScale = () => {
|
const resolveScale = () => {
|
||||||
const userScale = parseFloat(container.dataset.pdfScale ?? "1");
|
const userScale = parseFloat(container.dataset.pdfScale ?? "0.8");
|
||||||
const containerWidth = container.clientWidth - 16;
|
const containerWidth = container.clientWidth - 16;
|
||||||
if (containerWidth <= 0) return userScale;
|
if (containerWidth <= 0) return userScale;
|
||||||
return (containerWidth / firstViewport.width) * userScale;
|
return (containerWidth / firstViewport.width) * userScale;
|
||||||
@@ -236,7 +238,7 @@
|
|||||||
toolbar.className = "pdf-toolbar";
|
toolbar.className = "pdf-toolbar";
|
||||||
|
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
let currentScale = parseFloat(container.dataset.pdfScale ?? "1");
|
let currentScale = parseFloat(container.dataset.pdfScale ?? "0.8");
|
||||||
const numPages = pdfDoc.numPages;
|
const numPages = pdfDoc.numPages;
|
||||||
|
|
||||||
const updateUI = () => {
|
const updateUI = () => {
|
||||||
@@ -261,14 +263,14 @@
|
|||||||
const pageLabel = mkLabel("");
|
const pageLabel = mkLabel("");
|
||||||
|
|
||||||
const zoomOutBtn = mkBtn("−", "Zoom arrière", () => {
|
const zoomOutBtn = mkBtn("−", "Zoom arrière", () => {
|
||||||
currentScale = Math.max(0.5, currentScale - 0.2);
|
currentScale = Math.max(0.5, currentScale - 0.05);
|
||||||
container.dataset.pdfScale = String(currentScale);
|
container.dataset.pdfScale = String(currentScale);
|
||||||
reRender(container, pdfDoc);
|
reRender(container, pdfDoc);
|
||||||
updateUI();
|
updateUI();
|
||||||
});
|
});
|
||||||
|
|
||||||
const zoomInBtn = mkBtn("+", "Zoom avant", () => {
|
const zoomInBtn = mkBtn("+", "Zoom avant", () => {
|
||||||
currentScale = Math.min(3.0, currentScale + 0.2);
|
currentScale = Math.min(3.0, currentScale + 0.05);
|
||||||
container.dataset.pdfScale = String(currentScale);
|
container.dataset.pdfScale = String(currentScale);
|
||||||
reRender(container, pdfDoc);
|
reRender(container, pdfDoc);
|
||||||
updateUI();
|
updateUI();
|
||||||
@@ -296,9 +298,8 @@
|
|||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
const canvases = container.querySelectorAll(".pdf-canvas");
|
|
||||||
|
|
||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
|
const canvases = container.querySelectorAll(".pdf-canvas");
|
||||||
const mid = container.scrollTop + container.clientHeight * 0.4;
|
const mid = container.scrollTop + container.clientHeight * 0.4;
|
||||||
let best = 1;
|
let best = 1;
|
||||||
let bestDist = Infinity;
|
let bestDist = Infinity;
|
||||||
@@ -352,14 +353,14 @@
|
|||||||
function reRender(container, pdfDoc) {
|
function reRender(container, pdfDoc) {
|
||||||
if (container._onScroll) {
|
if (container._onScroll) {
|
||||||
container.removeEventListener("scroll", container._onScroll);
|
container.removeEventListener("scroll", container._onScroll);
|
||||||
|
container._onScroll = null;
|
||||||
}
|
}
|
||||||
container.querySelectorAll(".pdf-canvas").forEach((c) => {
|
container.querySelectorAll(".pdf-canvas").forEach((c) => {
|
||||||
c.remove();
|
c.remove();
|
||||||
});
|
});
|
||||||
const oldToolbar = container.querySelector(".pdf-toolbar");
|
const oldToolbar = container.querySelector(".pdf-toolbar");
|
||||||
if (oldToolbar) oldToolbar.remove();
|
if (oldToolbar) oldToolbar.remove();
|
||||||
buildToolbar(container, pdfDoc);
|
renderAllPages(container, pdfDoc).then(() => buildToolbar(container, pdfDoc));
|
||||||
renderAllPages(container, pdfDoc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Bootstrap ──────────────────────────────────────────────────
|
// ── Bootstrap ──────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -386,10 +386,16 @@
|
|||||||
?>
|
?>
|
||||||
<div class="pdf-embed-wrapper"
|
<div class="pdf-embed-wrapper"
|
||||||
data-pdf-url="<?= $mediaUrl ?>"
|
data-pdf-url="<?= $mediaUrl ?>"
|
||||||
data-pdf-scale="1"
|
data-pdf-scale="0.85"
|
||||||
data-pdf-label="<?= htmlspecialchars($_pdfLabel) ?>"
|
data-pdf-label="<?= htmlspecialchars($_pdfLabel) ?>"
|
||||||
tabindex="-1"></div>
|
tabindex="-1"></div>
|
||||||
<?php elseif ($isWebsite): ?>
|
<?php elseif ($isWebsite): ?>
|
||||||
|
<a href="<?= $mediaUrl ?>"
|
||||||
|
class="tfe-website-link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">
|
||||||
|
Ouvrir dans un nouvel onglet ↗
|
||||||
|
</a>
|
||||||
<iframe src="<?= $mediaUrl ?>"
|
<iframe src="<?= $mediaUrl ?>"
|
||||||
class="tfe-file-iframe"
|
class="tfe-file-iframe"
|
||||||
title="<?= $fileName ?>"
|
title="<?= $fileName ?>"
|
||||||
|
|||||||
Reference in New Issue
Block a user