a926e1825d
- Admin articles : filtres auteur/catégorie/statut par GET, compteur de résultats
- Admin articles : suppression massive avec checkboxes, confirmation JS, contrôle d'ownership
- Profil auteur : bio tronquée à 3 lignes avec bouton 'plus', limite à 6 articles affichés
- Profil auteur : bouton CTA pill 'Mes liens' vers /liens/{slug}
- Page liens : boutons pill colorés (palette auto par index), fond gris clair, grand avatar
30 lines
1.4 KiB
JavaScript
30 lines
1.4 KiB
JavaScript
(function() {
|
|
const list = document.getElementById('links-sortable');
|
|
if (!list) return;
|
|
let dragged = null;
|
|
list.querySelectorAll('li').forEach(li => {
|
|
li.setAttribute('draggable', true);
|
|
li.addEventListener('dragstart', () => { dragged = li; li.style.opacity = '.4'; });
|
|
li.addEventListener('dragend', () => { dragged = null; li.style.opacity = ''; saveOrder(); });
|
|
li.addEventListener('dragover', e => { e.preventDefault(); const after = getDragAfter(list, e.clientY); after ? list.insertBefore(dragged, after) : list.appendChild(dragged); });
|
|
});
|
|
function getDragAfter(container, y) {
|
|
return [...container.querySelectorAll('li:not([style*="opacity"])')].reduce((closest, el) => {
|
|
const box = el.getBoundingClientRect();
|
|
const offset = y - box.top - box.height / 2;
|
|
return offset < 0 && offset > closest.offset ? { offset, element: el } : closest;
|
|
}, { offset: Number.NEGATIVE_INFINITY }).element;
|
|
}
|
|
function saveOrder() {
|
|
const form = document.getElementById('reorder-form');
|
|
if (!form) return;
|
|
form.querySelectorAll('input').forEach(i => i.remove());
|
|
list.querySelectorAll('li[data-id]').forEach(li => {
|
|
const inp = document.createElement('input');
|
|
inp.type = 'hidden'; inp.name = 'order[]'; inp.value = li.dataset.id;
|
|
form.appendChild(inp);
|
|
});
|
|
form.submit();
|
|
}
|
|
})();
|