mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
feat: require 3 mots-clés in partage, language asterisk toggle, admin auto-save checkboxes
- tag-search: add minTags/required params, counter shows red if < 3, accent if ≥ 3 - form.php: pass minTags=3 for partage mode keywords - checkbox-list: support labelHtml for raw HTML label with targetable asterisk span - language-autre-fragment: OOB swap updates #languages-required-asterisk when autre pills change - language-search: client-side update #languages-required-asterisk on pill add/remove - contenus.php: replace 3 form+submit-button fieldsets with HTMX auto-save checkboxes - settings.php: detect HX-Request header, return OOB CSRF token updates, skip redirect
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
*
|
||||
* Variables consumed:
|
||||
* string $name — input name attribute (will be posted as array: name[])
|
||||
* string $label — group label text
|
||||
* string $label — group label text (htmlspecialchars'd unless $labelHtml is set)
|
||||
* string $labelHtml — raw HTML label override (bypasses htmlspecialchars, optional)
|
||||
* array $options — each element must have 'id' and 'name' keys
|
||||
* array $checked — array of 'id' values that are currently checked
|
||||
* bool $required — whether at least one checkbox must be checked; default false
|
||||
@@ -27,7 +28,7 @@ $hxSwap = $hxSwap ?? 'outerHTML';
|
||||
$hxInclude = $hxInclude ?? 'this';
|
||||
?>
|
||||
<div>
|
||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span>
|
||||
<span class="admin-row-label"><?= isset($labelHtml) ? $labelHtml : (htmlspecialchars($label) . ($required ? ' <span class="asterisk">*</span>' : '')) ?></span>
|
||||
<fieldset class="admin-checkbox-group"
|
||||
<?= $required ? ' required aria-required="true"' : '' ?>
|
||||
<?php if ($hxPost !== ''): ?>
|
||||
|
||||
@@ -193,12 +193,14 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
|
||||
$label = "Langue(s) du TFE :";
|
||||
$options = $languages;
|
||||
$checked = $formData["languages"] ?? [];
|
||||
$required = !$adminMode;
|
||||
$_hasLangAutre = !empty($formData['language_autre']) && is_array($formData['language_autre']) && count(array_filter($formData['language_autre'], fn($l) => is_string($l) && trim($l) !== '')) > 0;
|
||||
$required = !$adminMode && !$_hasLangAutre;
|
||||
$hxPost = $mode === 'partage' ? "/partage/language-autre-fragment" : "/admin/language-autre-fragment.php";
|
||||
$hxTarget = "#language-autre-required";
|
||||
$hxTarget = "#languages-required-asterisk";
|
||||
$hxSwap = "outerHTML";
|
||||
$labelHtml = htmlspecialchars($label) . '<span id="languages-required-asterisk">' . ($required ? ' <span class="asterisk">*</span>' : '') . '</span>';
|
||||
include APP_ROOT . "/templates/partials/form/checkbox-list.php";
|
||||
unset($hxSwap);
|
||||
unset($hxSwap, $_hasLangAutre, $labelHtml);
|
||||
?>
|
||||
<?php
|
||||
$_langAutreRequired = empty($formData["languages"]);
|
||||
@@ -274,9 +276,11 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
|
||||
$placeholder = "Rechercher un mot-clé…";
|
||||
$hint = "Tapez pour rechercher ou créer des mots-clés.";
|
||||
$selectedTags = $_selectedTags;
|
||||
$required = !$adminMode;
|
||||
$minTags = ($mode === 'partage') ? 3 : 0;
|
||||
$hxPost = ($mode === 'partage') ? "/partage/tag-search-fragment" : "/admin/tag-search-fragment.php";
|
||||
include APP_ROOT . "/templates/partials/form/tag-search.php";
|
||||
unset($_tagsRaw, $_selectedTags, $_t, $name, $label, $placeholder, $hint, $selectedTags, $hxPost);
|
||||
unset($_tagsRaw, $_selectedTags, $_t, $name, $label, $placeholder, $hint, $selectedTags, $hxPost, $minTags, $required);
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
|
||||
@@ -100,6 +100,14 @@ $langCount = count($selectedLanguages);
|
||||
if (countEl) countEl.textContent = n + '/' + maxLanguages;
|
||||
if (counter) counter.style.display = (n > 0) ? '' : 'none';
|
||||
|
||||
// Toggle the checkbox-list asterisk: if any "autre" language pill
|
||||
// is present, the checkbox list is no longer required.
|
||||
const asteriskEl = document.getElementById('languages-required-asterisk');
|
||||
if (asteriskEl) {
|
||||
const checkboxes = document.querySelectorAll('#languages-fieldset input[type="checkbox"]:checked');
|
||||
asteriskEl.innerHTML = (n === 0 && checkboxes.length === 0) ? ' <span class="asterisk">*</span>' : '';
|
||||
}
|
||||
|
||||
// Show/hide search input based on max
|
||||
const wrap = container.querySelector('.tag-search-input-wrap');
|
||||
const maxMsg = container.querySelector('.tag-search-max-msg');
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* array $selectedTags — array of ['id' => int|null, 'name' => string] for pre-filled tags
|
||||
* string|null $id — override the id attribute prefix
|
||||
* int $maxTags — maximum number of tags (default 10)
|
||||
* int $minTags — minimum required tags (default 0, no requirement)
|
||||
* bool $required — whether minTags enforcement applies (default false)
|
||||
*/
|
||||
|
||||
$name = $name ?? 'tag';
|
||||
@@ -27,10 +29,13 @@ $hxPost = $hxPost ?? '/admin/tag-search-fragment.php';
|
||||
$selectedTags = $selectedTags ?? [];
|
||||
$id = $id ?? $name;
|
||||
$maxTags = $maxTags ?? 10;
|
||||
$minTags = $minTags ?? 0;
|
||||
$required = $required ?? false;
|
||||
$tagCount = count($selectedTags);
|
||||
$belowMin = $required && $tagCount < $minTags;
|
||||
?>
|
||||
<div id="<?= htmlspecialchars($id) ?>-search-container">
|
||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?></span>
|
||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span>
|
||||
<div class="tag-search-wrapper">
|
||||
<?php if ($hint): ?>
|
||||
<small class="tag-search-hint"><?= htmlspecialchars($hint) ?></small>
|
||||
@@ -49,9 +54,9 @@ $tagCount = count($selectedTags);
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Counter visible only when tags exist or max reached -->
|
||||
<div class="tag-search-counter" id="<?= htmlspecialchars($id) ?>-counter"<?= $tagCount === 0 ? ' style="display:none"' : '' ?>>
|
||||
<span class="tag-search-count" id="<?= htmlspecialchars($id) ?>-count"><?= $tagCount ?>/<?= (int)$maxTags ?></span>
|
||||
<!-- Counter visible only when tags exist or max reached, or min requirement active -->
|
||||
<div class="tag-search-counter" id="<?= htmlspecialchars($id) ?>-counter"<?= ($tagCount === 0 && !$required) ? ' style="display:none"' : '' ?>>
|
||||
<span class="tag-search-count" id="<?= htmlspecialchars($id) ?>-count"<?= $belowMin ? ' style="color:var(--text-danger)"' : ($tagCount >= $minTags && $required ? ' style="color:var(--accent)"' : '') ?>><?= $tagCount ?>/<?= (int)$maxTags ?><?= $required ? ' (min ' . (int)$minTags . ')' : '' ?></span>
|
||||
<?php if ($tagCount >= $maxTags): ?>
|
||||
<span class="tag-search-max-msg">Maximum de mots-clés atteint</span>
|
||||
<?php endif; ?>
|
||||
@@ -90,13 +95,23 @@ $tagCount = count($selectedTags);
|
||||
const countEl = document.getElementById(<?= json_encode($id . '-count') ?>);
|
||||
const counter = document.getElementById(<?= json_encode($id . '-counter') ?>);
|
||||
const maxTags = <?= (int)$maxTags ?>;
|
||||
const minTags = <?= (int)$minTags ?>;
|
||||
const required = <?= json_encode($required) ?>;
|
||||
const inputName = <?= json_encode($name) ?>;
|
||||
let selectedIdx = -1;
|
||||
|
||||
function updateCount() {
|
||||
const n = pills.querySelectorAll('.tag-pill').length;
|
||||
if (countEl) countEl.textContent = n + '/' + maxTags;
|
||||
if (counter) counter.style.display = (n > 0) ? '' : 'none';
|
||||
const suffix = required ? ' (min ' + minTags + ')' : '';
|
||||
if (countEl) countEl.textContent = n + '/' + maxTags + suffix;
|
||||
if (counter) counter.style.display = (n > 0 || required) ? '' : 'none';
|
||||
if (countEl && required) {
|
||||
if (n < minTags) {
|
||||
countEl.style.color = 'var(--text-danger)';
|
||||
} else {
|
||||
countEl.style.color = 'var(--accent)';
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide search input based on max
|
||||
const wrap = container.querySelector('.tag-search-input-wrap');
|
||||
|
||||
Reference in New Issue
Block a user