87 lines
2.7 KiB
JavaScript
87 lines
2.7 KiB
JavaScript
// ── Undo system ──
|
|
|
|
import { api } from './util.js';
|
|
|
|
const MAX_STACK = 30;
|
|
const stack = [];
|
|
|
|
export function pushUndo(label, restoreFn) {
|
|
stack.push({ label, restore: restoreFn, time: Date.now() });
|
|
if (stack.length > MAX_STACK) stack.shift();
|
|
showHint(label);
|
|
}
|
|
|
|
export async function undo() {
|
|
const entry = stack.pop();
|
|
if (!entry) return;
|
|
await entry.restore();
|
|
await window._reloadAll();
|
|
showHint(`undo: ${entry.label}`);
|
|
}
|
|
|
|
export function canUndo() { return stack.length > 0; }
|
|
export function lastLabel() { return stack.length ? stack[stack.length - 1].label : ''; }
|
|
|
|
// Snapshot helpers
|
|
|
|
export function snapshotCard(card) {
|
|
return { ...card };
|
|
}
|
|
|
|
export function snapshotCards(cards) {
|
|
return cards.map(c => ({ ...c }));
|
|
}
|
|
|
|
export function pushCardEdit(label, cardId, prevData) {
|
|
pushUndo(label, async () => {
|
|
await api(`api/cards/${cardId}`, { method: 'PATCH', body: prevData });
|
|
});
|
|
}
|
|
|
|
export function pushCardDelete(label, card) {
|
|
pushUndo(label, async () => {
|
|
const created = await api('api/cards', { method: 'POST', body: {
|
|
title: card.title, body: card.body, color: card.color,
|
|
bucket_id: card.bucket_id, parent_id: card.parent_id, position: card.position,
|
|
}});
|
|
// Restore fields that POST doesn't accept
|
|
if (created?.id && card.due_date) {
|
|
await api(`api/cards/${created.id}`, { method: 'PATCH', body: { due_date: card.due_date } });
|
|
}
|
|
});
|
|
}
|
|
|
|
export function pushReorder(label, cards) {
|
|
const prev = cards.map(c => ({ id: c.id, position: c.position }));
|
|
pushUndo(label, async () => {
|
|
await Promise.all(prev.map(p => api(`api/cards/${p.id}`, { method: 'PATCH', body: { position: p.position } })));
|
|
});
|
|
}
|
|
|
|
export function pushBatch(label, patches) {
|
|
// patches = [{cardId, prevData}, ...]
|
|
pushUndo(label, async () => {
|
|
await Promise.all(patches.map(p => api(`api/cards/${p.cardId}`, { method: 'PATCH', body: p.prevData })));
|
|
});
|
|
}
|
|
|
|
// Toast hint
|
|
let hintTimer = null;
|
|
function showHint(text) {
|
|
let el = document.getElementById('undo-hint');
|
|
if (!el) {
|
|
el = document.createElement('div');
|
|
el.id = 'undo-hint';
|
|
el.className = 'fixed bottom-16 left-1/2 -translate-x-1/2 z-[95] bg-t-surface border border-t-border px-4 py-2 text-xs font-pixel text-t-muted shadow-lg transition-all';
|
|
document.body.appendChild(el);
|
|
}
|
|
el.textContent = text + ' (ctrl+z undo)';
|
|
el.style.opacity = '1';
|
|
el.style.transform = 'translateX(-50%) translateY(0)';
|
|
clearTimeout(hintTimer);
|
|
hintTimer = setTimeout(() => {
|
|
el.style.opacity = '0';
|
|
el.style.transform = 'translateX(-50%) translateY(8px)';
|
|
}, 2500);
|
|
}
|