mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
The htmx:afterSwap handler was calling initAccordions(e.detail.target) but after an outerHTML swap, e.detail.target references the old detached DOM element, not the new live element. This meant accordion click handlers were never attached to the new filter column toggles after applying or removing a filter — breaking all subsequent interaction. Fix: query the live DOM with document.querySelector() instead.
127 lines
4.7 KiB
PHP
127 lines
4.7 KiB
PHP
<main class="search-main" id="main-content">
|
|
<h1 class="sr-only">Répertoire</h1>
|
|
<span id="rep-indicator" class="rep-indicator htmx-indicator" aria-hidden="true"></span>
|
|
<?php include APP_ROOT . '/templates/partials/repertoire-index.php'; ?>
|
|
</main>
|
|
<!-- Student popover -->
|
|
<div id="student-popover" class="student-popover" hidden aria-live="polite"></div>
|
|
|
|
<script src="/assets/js/vendor/htmx.min.js"></script>
|
|
<script>
|
|
(function () {
|
|
var popover = document.getElementById('student-popover');
|
|
var currentAnchor = null;
|
|
|
|
function position(anchor) {
|
|
var r = anchor.getBoundingClientRect();
|
|
var left = r.right + window.scrollX + 12;
|
|
var top = r.top + window.scrollY;
|
|
if (left + 300 > window.innerWidth + window.scrollX) left = r.left + window.scrollX - 312;
|
|
popover.style.left = left + 'px';
|
|
popover.style.top = top + 'px';
|
|
}
|
|
|
|
document.body.addEventListener('mouseenter', function (e) {
|
|
var a = e.target.closest('[data-student-name]');
|
|
if (!a) return;
|
|
currentAnchor = a;
|
|
}, true);
|
|
|
|
document.body.addEventListener('htmx:afterSwap', function (e) {
|
|
if (e.detail.target !== popover) return;
|
|
if (currentAnchor) position(currentAnchor);
|
|
popover.hidden = false;
|
|
});
|
|
|
|
document.body.addEventListener('mouseleave', function (e) {
|
|
if (!e.target.closest('[data-student-name]') && !e.target.closest('#student-popover')) return;
|
|
setTimeout(function () {
|
|
if (!document.querySelector('[data-student-name]:hover') &&
|
|
!document.querySelector('#student-popover:hover')) {
|
|
popover.hidden = true;
|
|
}
|
|
}, 120);
|
|
}, true);
|
|
}());
|
|
</script>
|
|
|
|
<script>
|
|
// Mobile accordion: single-open behavior + HTMX re-init
|
|
(function () {
|
|
var INDEX_SEL = '#repertoire-index';
|
|
var ACCORDION_SEL = '.rep-accordion';
|
|
var TOGGLE_SEL = '.rep-accordion__toggle';
|
|
var PANEL_SEL = '.rep-accordion__panel';
|
|
|
|
function isMobile() {
|
|
return window.matchMedia('(max-width: 1025px)').matches;
|
|
}
|
|
|
|
function initAccordions(root) {
|
|
if (!isMobile()) return;
|
|
var toggles = root.querySelectorAll(TOGGLE_SEL);
|
|
toggles.forEach(function (btn) {
|
|
// Skip students column — always visible, not an accordion
|
|
if (btn.closest('[data-col="students"]')) return;
|
|
if (btn._accordionBound) return;
|
|
btn._accordionBound = true;
|
|
btn.addEventListener('click', function () {
|
|
var section = btn.closest(ACCORDION_SEL);
|
|
var panel = section.querySelector(PANEL_SEL);
|
|
var isOpen = btn.getAttribute('aria-expanded') === 'true';
|
|
|
|
// Close all others (except students)
|
|
root.querySelectorAll(ACCORDION_SEL).forEach(function (s) {
|
|
if (s.dataset.col === 'students') return;
|
|
var p = s.querySelector(PANEL_SEL);
|
|
var t = s.querySelector(TOGGLE_SEL);
|
|
if (s !== section) {
|
|
t.setAttribute('aria-expanded', 'false');
|
|
p.classList.remove('is-open');
|
|
}
|
|
});
|
|
|
|
// Toggle this one
|
|
var nowOpen = !isOpen;
|
|
btn.setAttribute('aria-expanded', nowOpen ? 'true' : 'false');
|
|
if (nowOpen) {
|
|
panel.classList.add('is-open');
|
|
} else {
|
|
panel.classList.remove('is-open');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Initial bind
|
|
initAccordions(document);
|
|
|
|
// Re-bind after HTMX swaps.
|
|
// Must query the live DOM — e.detail.target is the *old* detached element after outerHTML swaps.
|
|
document.body.addEventListener('htmx:afterSwap', function (e) {
|
|
if (e.detail.target && e.detail.target.matches && e.detail.target.matches(INDEX_SEL)) {
|
|
var liveIndex = document.querySelector(INDEX_SEL);
|
|
if (liveIndex) initAccordions(liveIndex);
|
|
}
|
|
});
|
|
|
|
// Re-bind on resize crossing the breakpoint
|
|
var wasMobile = isMobile();
|
|
window.addEventListener('resize', function () {
|
|
var nowMobile = isMobile();
|
|
if (nowMobile !== wasMobile) {
|
|
wasMobile = nowMobile;
|
|
// When switching to desktop, close all panels
|
|
if (!nowMobile) {
|
|
document.querySelectorAll(INDEX_SEL + ' ' + TOGGLE_SEL).forEach(function (btn) {
|
|
btn.setAttribute('aria-expanded', 'false');
|
|
});
|
|
document.querySelectorAll(INDEX_SEL + ' ' + PANEL_SEL).forEach(function (p) {
|
|
p.classList.remove('is-open');
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}());
|
|
</script>
|