Files
xamxam/app/templates/partials/form/language-search.php
Pontoporeia b56d073210 refactor: extract inline JS into app/ modules, remove dead overtype-webcomponent
- Remove overtype-webcomponent.min.js (zero references)
- Extract copyLogContent + fallbackCopy + HTMX tab-updater → app/admin-logs.js
  (removes duplicate from both system.php and parametres.php)
- Extract copyUrl → app/clipboard.js (shared by acces.php)
- Extract tag/language pill-search logic → app/pill-search.js
  Generalized with data-pill-search attributes, auto-inits via
  DOMContentLoaded + htmx:afterSwap
- Extract access-request form handler → app/access-request.js
  (was inline in templates/public/tfe.php)

Files created: admin-logs.js, clipboard.js, pill-search.js, access-request.js
Files modified: 9 templates/controllers to drop inline scripts and
  reference external JS files
2026-05-19 00:08:06 +02:00

103 lines
5.7 KiB
PHP

<?php
/**
* Language search partial — interactive "Autre(s) langue(s)" input with HTMX-powered suggestions.
*
* Replaces the old plain text input with an interactive component:
* - Type to search among existing languages via HTMX
* - If the language doesn't exist, a "Créer" option appears
* - Selected languages are shown as pills with a round delete button (bin icon)
* - All language names are lowercased and deduplicated
*
* Variables consumed:
* string $name — base input name (hidden inputs will be name[]); default 'language_autre'
* string $label — visible label text
* string $placeholder — placeholder text for the search input
* string $hint — optional hint shown above the input
* string $hxPost — HTMX POST endpoint for language search
* array $selectedLanguages — array of ['id' => int|null, 'name' => string] for pre-filled languages
* string|null $id — override the id attribute prefix
* int $maxLanguages — maximum number of languages (default 10)
* bool $required — whether at least one "other language" is required (default false)
*/
$name = $name ?? 'language_autre';
$label = $label ?? 'Autre(s) langue(s)';
$placeholder = $placeholder ?? 'Rechercher une langue…';
$hint = $hint ?? null;
$hxPost = $hxPost ?? '/admin/language-search-fragment.php';
$selectedLanguages = $selectedLanguages ?? [];
$id = $id ?? $name;
$maxLanguages = $maxLanguages ?? 10;
$required = $required ?? false;
$langCount = count($selectedLanguages);
?>
<div id="<?= htmlspecialchars($id) ?>-search-container" data-pill-search data-pill-name="<?= htmlspecialchars($name) ?>" data-pill-max="<?= (int)$maxLanguages ?>" data-pill-min="0" data-pill-required="0" data-pill-role="lang">
<span class="admin-row-label"><?= htmlspecialchars($label) ?><span id="language-autre-required"><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span></span>
<div class="tag-search-wrapper">
<?php if ($hint): ?>
<small class="tag-search-hint"><?= htmlspecialchars($hint) ?></small>
<?php endif; ?>
<!-- Active language pills -->
<div class="tag-search-pills" id="<?= htmlspecialchars($id) ?>-pills">
<?php foreach ($selectedLanguages as $lang): ?>
<span class="tag-pill">
<input type="hidden" name="<?= htmlspecialchars($name) ?>[]" value="<?= htmlspecialchars($lang['name']) ?>">
<span class="tag-pill-name"><?= htmlspecialchars($lang['name']) ?></span>
<button type="button" class="tag-pill-remove" title="Retirer «&nbsp;<?= htmlspecialchars($lang['name']) ?>&nbsp;»" aria-label="Retirer <?= htmlspecialchars($lang['name']) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168V104a8,8,0,0,1,16,0v64a8,8,0,0,1-16,0Zm48,0V104a8,8,0,0,1,16,0v64a8,8,0,0,1-16,0Z"></path></svg>
</button>
</span>
<?php endforeach; ?>
</div>
<!-- Counter visible only when languages exist or max reached -->
<div class="tag-search-counter" id="<?= htmlspecialchars($id) ?>-counter"<?= $langCount === 0 ? ' style="display:none"' : '' ?>>
<span class="tag-search-count" id="<?= htmlspecialchars($id) ?>-count"><?= $langCount ?>/<?= (int)$maxLanguages ?></span>
<?php if ($langCount >= $maxLanguages): ?>
<span class="tag-search-max-msg">Maximum de langues atteint</span>
<?php endif; ?>
</div>
<!-- Search input (hidden when max languages reached) -->
<div class="tag-search-input-wrap"<?= $langCount >= $maxLanguages ? ' style="display:none"' : '' ?>>
<input type="text"
name="language_search_q"
id="<?= htmlspecialchars($id) ?>-search"
class="tag-search-input"
placeholder="<?= htmlspecialchars($placeholder) ?>"
autocomplete="off"
hx-post="<?= htmlspecialchars($hxPost) ?>"
hx-trigger="input changed delay:200ms, focus"
hx-target="#<?= htmlspecialchars($id) ?>-suggestions"
hx-swap="innerHTML"
hx-include="#<?= htmlspecialchars($id) ?>-pills">
</div>
<!-- Suggestions dropdown (positioned absolutely over content) -->
<div class="tag-search-suggestions" id="<?= htmlspecialchars($id) ?>-suggestions" role="listbox"></div>
</div>
</div>
<script>
// Language-specific: toggle checkbox-list asterisk based on pills presence
(function () {
var container = document.getElementById(<?= json_encode($id . '-search-container') ?>);
if (!container) return;
var pills = container.querySelector('.tag-search-pills');
if (!pills) return;
function check() {
var asteriskEl = document.getElementById('languages-required-asterisk');
if (!asteriskEl) return;
var n = pills.querySelectorAll('.tag-pill').length;
var checkboxes = document.querySelectorAll('#languages-fieldset input[type="checkbox"]:checked');
asteriskEl.innerHTML = (n === 0 && checkboxes.length === 0) ? ' <span class="asterisk">*</span>' : '';
}
var observer = new MutationObserver(check);
observer.observe(pills, { childList: true });
check();
})();
</script>
<?php
unset($name, $label, $placeholder, $hint, $hxPost, $selectedLanguages, $id, $maxLanguages, $langCount, $required);