Files
AggloSign/backend/public/js/app.js
T
2026-10-02 13:06:14 +02:00

112 lines
5.1 KiB
JavaScript

/*
* Comportements communs du back-office (aucune dépendance).
* Chaque bloc s'active par un attribut data-* : sans JavaScript, les formulaires fonctionnent quand même.
*/
(() => {
'use strict';
// --- Navigation en bandeau (petits écrans) : l'entrée active reste visible ---------------------
const current = document.querySelector('.nav .nav-link.active');
if (current && current.parentElement.scrollWidth > current.parentElement.clientWidth) {
current.parentElement.scrollLeft = current.offsetLeft - 16;
}
// --- Notifications : fermeture manuelle, et automatique après quelques secondes ----------------
const dismiss = (toast) => {
toast.classList.add('is-leaving');
setTimeout(() => toast.remove(), 220);
};
document.querySelectorAll('[data-toast]').forEach((toast) => {
let timer = setTimeout(() => dismiss(toast), 9000);
toast.addEventListener('mouseenter', () => clearTimeout(timer));
toast.addEventListener('mouseleave', () => { timer = setTimeout(() => dismiss(toast), 4000); });
toast.querySelector('[data-dismiss]')?.addEventListener('click', () => { clearTimeout(timer); dismiss(toast); });
});
// --- Confirmation : <form data-confirm="Question ?" data-confirm-label="Supprimer" data-confirm-danger>
const dialog = document.getElementById('confirm-dialog');
if (dialog && typeof dialog.showModal === 'function') {
const message = dialog.querySelector('[data-message]');
const ok = dialog.querySelector('[data-ok]');
let pending = null;
document.addEventListener('submit', (e) => {
const form = e.target;
if (!(form instanceof HTMLFormElement) || !form.dataset.confirm || form.dataset.confirmed) return;
e.preventDefault();
e.stopImmediatePropagation();
pending = form;
message.textContent = form.dataset.confirm;
ok.textContent = form.dataset.confirmLabel || 'Confirmer';
ok.classList.toggle('danger', 'confirmDanger' in form.dataset);
ok.classList.toggle('solid', 'confirmDanger' in form.dataset);
dialog.returnValue = '';
dialog.showModal();
}, true);
dialog.addEventListener('close', () => {
const form = pending;
pending = null;
if (!form || dialog.returnValue !== 'ok') return;
form.dataset.confirmed = '1';
form.requestSubmit ? form.requestSubmit() : form.submit();
});
}
// --- Bouton occupé : <form data-busy="Texte pendant l'envoi…"> ----------------------------------
document.addEventListener('submit', (e) => {
const form = e.target;
if (!(form instanceof HTMLFormElement) || !form.dataset.busy || e.defaultPrevented) return;
const button = form.querySelector('button[type=submit], button:not([type])');
if (!button) return;
button.classList.add('is-busy');
button.setAttribute('aria-disabled', 'true');
const label = button.querySelector('[data-label]');
if (label) label.textContent = form.dataset.busy;
});
// --- Copie dans le presse-papiers : <button data-copy="texte"> ---------------------------------
document.addEventListener('click', async (e) => {
const button = e.target.closest('[data-copy]');
if (!button) return;
const label = button.querySelector('[data-label]') || button;
const initial = label.textContent;
try {
await navigator.clipboard.writeText(button.dataset.copy);
label.textContent = 'Copié';
} catch {
label.textContent = 'Copie impossible';
}
setTimeout(() => { label.textContent = initial; }, 1600);
});
// --- Zone de dépôt : <label class="dropzone" data-dropzone><input type="file"> … ---------------
document.querySelectorAll('[data-dropzone]').forEach((zone) => {
const input = zone.querySelector('input[type=file]');
const name = zone.querySelector('[data-name]');
const prompt = zone.querySelector('[data-prompt]');
if (!input) return;
const show = () => {
const file = input.files[0];
if (name) { name.textContent = file ? file.name : ''; name.hidden = !file; }
if (prompt) prompt.hidden = Boolean(file);
};
input.addEventListener('change', show);
['dragenter', 'dragover'].forEach((type) => zone.addEventListener(type, (e) => {
e.preventDefault();
zone.classList.add('is-over');
}));
['dragleave', 'drop'].forEach((type) => zone.addEventListener(type, () => zone.classList.remove('is-over')));
zone.addEventListener('drop', (e) => {
e.preventDefault();
if (input.disabled || !e.dataTransfer.files.length) return;
const transfer = new DataTransfer();
transfer.items.add(e.dataTransfer.files[0]);
input.files = transfer.files;
input.dispatchEvent(new Event('change', { bubbles: true }));
});
});
})();