PDF UX: auto-reorder on drag, visual delete animation, remove apply button

- Reorder: drag-drop auto-saves after 800ms, no button needed
- Delete: selected pages fade out + shrink on delete click
- Delete button shows Удаление... state
- Reorder hint shows Сохранение.../Порядок сохранён
- Better drop positioning (left/right of target)
This commit is contained in:
treamz 2026-03-22 01:46:48 +03:00
parent 78c181aa6c
commit 93952d0844

View File

@ -769,8 +769,7 @@
<span class="option-label">Перетаскивайте страницы мышкой для нового порядка</span>
<div class="page-grid" id="reorderPageGrid"></div>
<div class="grid-action-bar">
<button class="grid-action-btn primary" id="btnReorderApply" onclick="executeReorderPages()">Применить порядок</button>
<span class="page-grid-hint" id="reorderHint"></span>
<span class="page-grid-hint" id="reorderHint">Перетащите и отпустите — порядок сохранится автоматически</span>
</div>
</div>
</div>
@ -1746,19 +1745,20 @@
const fromPage = e.dataTransfer.getData('text/plain');
const fromEl = grid.querySelector('[data-page="' + fromPage + '"]');
if (fromEl && fromEl !== thumb) {
// Swap positions in DOM
const parent = thumb.parentNode;
const thumbNext = thumb.nextSibling;
if (fromEl.nextSibling === thumb) {
parent.insertBefore(thumb, fromEl);
// Insert dragged element before or after drop target
const rect = thumb.getBoundingClientRect();
const midX = rect.left + rect.width / 2;
if (e.clientX < midX) {
grid.insertBefore(fromEl, thumb);
} else {
parent.insertBefore(fromEl, thumb);
parent.insertBefore(thumb, thumbNext);
grid.insertBefore(fromEl, thumb.nextSibling);
}
// Update visual numbers
// Update visual order numbers
Array.from(grid.children).forEach((el, idx) => {
el.querySelector('.page-thumb-num').textContent = idx + 1;
});
// Auto-save reorder
autoSaveReorder();
}
});
}
@ -1798,12 +1798,25 @@
const fileId = state.selectedFileId;
if (!fileId) { showError('Выберите файл'); return; }
clearError();
// Hide deleted pages visually
const grid = document.getElementById('deletePageGrid');
if (grid) {
grid.querySelectorAll('.page-thumb.selected').forEach(el => {
el.style.transition = 'opacity .3s, transform .3s';
el.style.opacity = '0';
el.style.transform = 'scale(0.8)';
setTimeout(() => el.remove(), 300);
});
}
document.getElementById('btnDeleteSelected').disabled = true;
document.getElementById('btnDeleteSelected').textContent = 'Удаление...';
try {
const res = await fetch('/pdf/delete', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ fileId, pages: pages.join(',') }) });
const data = await res.json();
if (!res.ok) throw new Error(data.error);
showResult(data);
} catch(e) { showError(e.message); }
document.getElementById('btnDeleteSelected').textContent = 'Удалить выбранные (0)';
};
window.executeRotatePages = async function() {
@ -1847,6 +1860,31 @@
}
}
// Auto-save reorder on drop
let _reorderTimeout = null;
function autoSaveReorder() {
clearTimeout(_reorderTimeout);
const hint = document.getElementById('reorderHint');
if (hint) hint.textContent = 'Сохранение...';
_reorderTimeout = setTimeout(async () => {
const grid = document.getElementById('reorderPageGrid');
if (!grid) return;
const order = Array.from(grid.querySelectorAll('.page-thumb')).map(t => parseInt(t.dataset.page));
const fileId = state.selectedFileId;
if (!fileId) return;
try {
const res = await fetch('/pdf/reorder', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ fileId, order }) });
const data = await res.json();
if (!res.ok) throw new Error(data.error);
if (hint) hint.textContent = 'Порядок сохранён';
showResult(data);
} catch(e) {
if (hint) hint.textContent = 'Ошибка: ' + e.message;
}
}, 800);
}
})();
</script>