Refactor apropos/charte/licence pages: shared layout, TOC anchors, and UI polish

Unify the three public pages (à propos, charte, licence) onto a single
grid layout (.page-content) with sticky TOC sidebar, replacing the old
separate  /  /  markup.

- Merge about.php, charte.php, licence.php templates into shared
  .page-content / .content-section structure
- Add CommonMark HeadingPermalinkExtension for stable heading anchors
- Use SlugNormalizer for TOC links so they match rendered heading IDs
- Standardize link styling across content blocks: bold black, accent on
  hover (consistent with global link style)
- Fix code block wrapping: use pre-wrap instead of pre, constrain grid
  columns with min-width:0, auto scrollbar
- Fix apropos page grid placement: force content-section into column 2
  so contacts and credits stay in the content area, not the sidebar

Also includes accumulated WIP changes:
- Header gradient: hardcoded purple-to-green (replaces CSS variables)
- Search placeholder font
- Duration field: replace minutes/sec/heures with h:m:s time inputs
- TFE file optional for formats 1,4,6 with client-side JS toggle
- Licence form: em-dash to hyphen, details/summary classes
- Pill search: block Enter key form submission when no results
- Draft autosave: remove CSRF rotation (broke concurrent FilePond uploads)
- Language pill: clear hints for excluded main languages
- Search results: gradient placeholder cards for items without covers
- TFE display: format durée values as XhYm instead of decimal
This commit is contained in:
Pontoporeia
2026-06-15 16:35:17 +02:00
parent 928e074d24
commit 19bf9f101a
27 changed files with 636 additions and 342 deletions

View File

@@ -418,37 +418,71 @@ if ($filesMode === 'add'): ?>
<?php endif; ?>
<!-- ═══════════════════ Durée ═══════════════════ -->
<fieldset>
<?php
$_durRaw = $durationValue ?? ($formData['duration_value'] ?? null);
$_durUnit = $durationUnit ?? ($formData['duration_unit'] ?? 'pages');
$_durFloat = $_durRaw !== null && $_durRaw !== '' ? (float)$_durRaw : null;
// Pre-split stored hours into h/m/s for the time-input fields
$_durH = '';
$_durM = '';
$_durS = '';
if ($_durFloat !== null && $_durUnit === 'durée') {
$_durH = (int)floor($_durFloat);
$_remaining = round(($_durFloat - $_durH) * 3600);
$_durM = (int)floor($_remaining / 60);
$_durS = $_remaining % 60;
$_durH = (string)$_durH;
$_durM = (string)$_durM;
$_durS = (string)$_durS;
}
?>
<fieldset id="duration-fieldset">
<legend>Durée</legend>
<div class="admin-form-group admin-form-group--inline">
<div class="admin-form-group">
<div>
<label for="duration_unit">Unité :</label>
<select id="duration_unit" name="duration_unit">
<?php
$_currentUnit = $durationUnit ?? ($formData['duration_unit'] ?? 'pages');
$_units = [
'pages' => 'pages',
'minutes' => 'minutes',
'sec' => 'secondes',
'heures' => 'heures',
'mo' => 'Mo',
'durée' => 'durée (h:m:s)',
];
foreach ($_units as $_val => $_label): ?>
<option value="<?= $_val ?>" <?= $_currentUnit === $_val ? 'selected' : '' ?>><?= htmlspecialchars($_label) ?></option>
<?php endforeach; unset($_units, $_currentUnit, $_val, $_label); ?>
<option value="<?= $_val ?>" <?= $_durUnit === $_val ? 'selected' : '' ?>><?= htmlspecialchars($_label) ?></option>
<?php endforeach; unset($_units, $_val, $_label); ?>
</select>
</div>
<div>
<label for="duration_value">Valeur :</label>
<input type="number" id="duration_value" name="duration_value"
value="<?= htmlspecialchars((string)($durationValue ?? ($formData['duration_value'] ?? ''))) ?>"
step="0.1" min="0" placeholder="0"
<!-- Integer input for pages / Mo -->
<div id="duration-value-integer"<?= $_durUnit === 'durée' ? ' style="display:none"' : '' ?>>
<label for="duration_value_int">Valeur :</label>
<input type="number" id="duration_value_int"
value="<?= htmlspecialchars($_durUnit !== 'durée' ? (string)($_durFloat ?? '') : '') ?>"
step="1" min="0" placeholder="0"
style="width: 8ch;">
</div>
<!-- Time inputs for durée -->
<div id="duration-value-time" class="duration-time-inputs"<?= $_durUnit !== 'durée' ? ' style="display:none"' : '' ?>>
<label>Durée :</label>
<span class="duration-time-fields">
<input type="number" id="duration_h" value="<?= htmlspecialchars($_durH) ?>"
step="1" min="0" placeholder="0" style="width: 5ch;" aria-label="Heures">
<span>h</span>
<input type="number" id="duration_m" value="<?= htmlspecialchars($_durM) ?>"
step="1" min="0" max="59" placeholder="0" style="width: 5ch;" aria-label="Minutes">
<span>m</span>
<input type="number" id="duration_s" value="<?= htmlspecialchars($_durS) ?>"
step="1" min="0" max="59" placeholder="0" style="width: 5ch;" aria-label="Secondes">
<span>s</span>
</span>
</div>
</div>
<small>Optionnel. Exemples : 88 pages, 32 minutes, 1.5 heures, 120 Mo.</small>
</div>
<!-- Hidden field: always submitted, populated by JS on unit change / submit -->
<input type="hidden" id="duration_value" name="duration_value"
value="<?= htmlspecialchars((string)($_durFloat ?? '')) ?>">
<small>Optionnel. Exemples : 88 pages, 120 Mo, 1h30.</small>
</fieldset>
<?php unset($_durRaw, $_durUnit, $_durFloat, $_durH, $_durM, $_durS, $_remaining); ?>
<!-- ═══════════════════ Degrés d'ouverture et licences ═══════════════════ -->
<?php
@@ -607,3 +641,47 @@ if ($filesMode === 'add'): ?>
</div>
</form>
<script>
(function() {
var unit = document.getElementById('duration_unit');
var hidden = document.getElementById('duration_value');
var intWrap = document.getElementById('duration-value-integer');
var intInput = document.getElementById('duration_value_int');
var timeWrap = document.getElementById('duration-value-time');
var hInput = document.getElementById('duration_h');
var mInput = document.getElementById('duration_m');
var sInput = document.getElementById('duration_s');
if (!unit || !hidden) return;
function updateHidden() {
if (unit.value === 'durée') {
var h = parseInt(hInput.value, 10) || 0;
var m = parseInt(mInput.value, 10) || 0;
var s = parseInt(sInput.value, 10) || 0;
var total = h + m / 60 + s / 3600;
hidden.value = total > 0 ? total.toFixed(6) : '';
} else {
hidden.value = intInput.value;
}
}
function toggleFields() {
if (unit.value === 'durée') {
intWrap.style.display = 'none';
timeWrap.style.display = '';
} else {
timeWrap.style.display = 'none';
intWrap.style.display = '';
}
updateHidden();
}
unit.addEventListener('change', toggleFields);
if (intInput) intInput.addEventListener('input', updateHidden);
if (hInput) hInput.addEventListener('input', updateHidden);
if (mInput) mInput.addEventListener('input', updateHidden);
if (sInput) sInput.addEventListener('input', updateHidden);
toggleFields();
})();
</script>