Files
2026-02-10 12:04:43 +01:00

85 lines
2.9 KiB
JavaScript

/**
* Alpine.data('dropdown') - Header navigation dropdown menus
* Supports click toggle, hover keep-open, and click-outside close.
*/
import Alpine from '@alpinejs/csp';
Alpine.data('dropdown', () => ({
open: false,
_closeTimeout: null,
toggle() {
this.open = !this.open;
},
close() {
this.open = false;
},
delayedClose() {
this._closeTimeout = setTimeout(() => this.close(), 300);
},
cancelClose() {
clearTimeout(this._closeTimeout);
}
}));
// Nested submenu (e.g. Foreign languages)
Alpine.data('submenu', () => ({
open: false,
_closeTimeout: null,
show() {
clearTimeout(this._closeTimeout);
this.open = true;
},
delayedHide() {
this._closeTimeout = setTimeout(() => { this.open = false; }, 200);
},
cancelHide() {
clearTimeout(this._closeTimeout);
}
}));
/**
* Document-level delegation for .dropdown-container and .submenu-container
* elements that don't have x-data attributes yet.
*/
(function() {
var containers = document.querySelectorAll('.dropdown-container');
containers.forEach(function(container) {
if (container.hasAttribute('x-data')) return;
var toggle = container.querySelector('.dropdown-toggle');
var menu = container.querySelector('.dropdown-menu');
if (!toggle || !menu) return;
var closeTimeout;
menu.style.display = 'none';
toggle.addEventListener('click', function(ev) {
ev.preventDefault(); ev.stopPropagation();
var isOpen = menu.style.display === 'block';
containers.forEach(function(c) { var m = c.querySelector('.dropdown-menu'); if (m && c !== container) m.style.display = 'none'; });
menu.style.display = isOpen ? 'none' : 'block';
});
container.addEventListener('mouseenter', function() { clearTimeout(closeTimeout); });
container.addEventListener('mouseleave', function() { closeTimeout = setTimeout(function() { menu.style.display = 'none'; }, 300); });
menu.addEventListener('mouseenter', function() { clearTimeout(closeTimeout); });
});
document.addEventListener('click', function(ev) {
if (!ev.target.closest('.dropdown-container')) containers.forEach(function(c) { var m = c.querySelector('.dropdown-menu'); if (m) m.style.display = 'none'; });
});
// Nested submenus
document.querySelectorAll('.submenu-container').forEach(function(container) {
if (container.hasAttribute('x-data')) return;
var sub = container.querySelector('.submenu');
if (!sub) return;
var t;
container.addEventListener('mouseenter', function() { clearTimeout(t); sub.style.display = 'block'; });
container.addEventListener('mouseleave', function() { t = setTimeout(function() { sub.style.display = 'none'; }, 200); });
sub.addEventListener('mouseenter', function() { clearTimeout(t); });
});
})();