La page a une largeur/un ratio fixes côté moteur (960px, A4 paysage) — sans mise à l'échelle, elle débordait verticalement de la fenêtre d'aperçu (plus petite qu'un canevas d'édition en plein écran) et défilait/rognait au lieu de tenir entière. Un script calcule maintenant le facteur d'échelle qui la fait toujours tenir en entier (transform: scale, jamais un agrandissement au-delà de 1), recalculé au redimensionnement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
159 lines
5.0 KiB
HTML
159 lines
5.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Aperçu — {{ theme.name }}</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='document/document-editor.css') }}">
|
||
{% if theme.font_url %}
|
||
<link rel="stylesheet" href="{{ theme.font_url }}">
|
||
{% endif %}
|
||
<link rel="stylesheet" href="{{ url_for('static', filename=theme.css_path) }}">
|
||
<style>
|
||
html, body {
|
||
margin: 0;
|
||
padding: 0;
|
||
height: 100%;
|
||
background: #efece4;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.previewPages {
|
||
flex: 1;
|
||
min-height: 0;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 16px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* La page réelle a une largeur/un ratio FIXES côté moteur
|
||
(--doc-page-width/aspect-ratio, voir static/document/
|
||
document-editor.css) — sans mise à l'échelle, elle déborde
|
||
verticalement de cette fenêtre d'aperçu plus petite qu'un canevas
|
||
d'édition en plein écran, et défile/rogne au lieu de tenir
|
||
entière (retour utilisateur du 24/09/2026). `forgeDocFitPreviewPage`
|
||
calcule un facteur d'échelle qui la fait TOUJOURS tenir
|
||
entièrement, sans barre de défilement. */
|
||
.previewPages .docPage {
|
||
flex-shrink: 0;
|
||
transform-origin: center center;
|
||
}
|
||
|
||
.previewPages .docPage[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.previewNav {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 14px;
|
||
padding: 10px;
|
||
background: #1a1b20;
|
||
color: #fff;
|
||
font: 600 13px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
}
|
||
|
||
.previewNav button {
|
||
border: none;
|
||
background: #2a2c33;
|
||
color: #fff;
|
||
border-radius: 6px;
|
||
padding: 6px 12px;
|
||
cursor: pointer;
|
||
font: inherit;
|
||
}
|
||
|
||
.previewNav button:disabled {
|
||
opacity: 0.4;
|
||
cursor: default;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="previewPages">
|
||
{% for rendered_page in rendered_pages %}
|
||
<div class="docPage" data-page-index="{{ loop.index0 }}" {% if not loop.first %}hidden{% endif %}>
|
||
<div class="docPageContent">
|
||
{{ rendered_page | safe }}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% if rendered_pages|length > 1 %}
|
||
<div class="previewNav">
|
||
<button type="button" id="previewPrevBtn">‹ Précédent</button>
|
||
<span id="previewPageLabel"></span>
|
||
<button type="button" id="previewNextBtn">Suivant ›</button>
|
||
</div>
|
||
{% endif %}
|
||
<script>
|
||
(function previewPageNav() {
|
||
var pages = Array.prototype.slice.call(document.querySelectorAll('.previewPages .docPage'));
|
||
if (pages.length <= 1) return;
|
||
var current = 0;
|
||
var prevBtn = document.getElementById('previewPrevBtn');
|
||
var nextBtn = document.getElementById('previewNextBtn');
|
||
var label = document.getElementById('previewPageLabel');
|
||
|
||
function render() {
|
||
pages.forEach(function pageAt(page, index) {
|
||
page.hidden = index !== current;
|
||
});
|
||
label.textContent = 'Page ' + (current + 1) + ' / ' + pages.length;
|
||
prevBtn.disabled = current === 0;
|
||
nextBtn.disabled = current === pages.length - 1;
|
||
}
|
||
|
||
prevBtn.addEventListener('click', function goPrev() {
|
||
if (current > 0) { current -= 1; render(); }
|
||
});
|
||
nextBtn.addEventListener('click', function goNext() {
|
||
if (current < pages.length - 1) { current += 1; render(); }
|
||
});
|
||
render();
|
||
}());
|
||
|
||
(function fitPreviewPage() {
|
||
// Toutes les pages partagent EXACTEMENT la même largeur/le même ratio
|
||
// fixes côté moteur (--doc-page-width/aspect-ratio) quel que soit leur
|
||
// contenu (voir static/document/document-editor.css) : mesurer une
|
||
// seule page suffit à connaître la taille "naturelle" de toutes.
|
||
var container = document.querySelector('.previewPages');
|
||
var pages = Array.prototype.slice.call(document.querySelectorAll('.previewPages .docPage'));
|
||
if (!container || !pages.length) return;
|
||
var reference = pages[0];
|
||
|
||
function applyFit() {
|
||
var wasHidden = reference.hidden;
|
||
var previousTransform = reference.style.transform;
|
||
reference.hidden = false;
|
||
reference.style.transform = 'none';
|
||
var naturalWidth = reference.offsetWidth;
|
||
var naturalHeight = reference.offsetHeight;
|
||
reference.hidden = wasHidden;
|
||
reference.style.transform = previousTransform;
|
||
if (!naturalWidth || !naturalHeight) return;
|
||
var availableWidth = container.clientWidth - 32;
|
||
var availableHeight = container.clientHeight - 32;
|
||
// Jamais agrandie au-delà de sa taille réelle (max 1) — un simple
|
||
// rétrécissement pour tenir, jamais un zoom artificiel.
|
||
var scale = Math.min(availableWidth / naturalWidth, availableHeight / naturalHeight, 1);
|
||
pages.forEach(function scalePage(page) {
|
||
page.style.transform = 'scale(' + scale + ')';
|
||
});
|
||
}
|
||
|
||
applyFit();
|
||
window.addEventListener('resize', applyFit);
|
||
}());
|
||
</script>
|
||
</body>
|
||
</html>
|