v2.0.14 — ghost + spinner on card move

Moving a card to another bucket took a visible round-trip (PATCH +
reloadAll) during which the card sat unchanged in the source bucket,
leaving the user wondering if the drop registered. Now every
bucket-move path marks the affected cards `.card-moving` as soon as
the action fires: 50% opacity, dark scrim and a centred accent-ring
spinner. The next renderBoard() rebuilds the DOM so the class comes
off automatically — no explicit cleanup needed.

Call sites:
- drag-drop onto a bucket tile (single + multi-select)
- drag onto an existing bucket to nest it (auto-super-bucket create)
- context-menu → Bucket → <target>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-19 14:44:03 +02:00
1 parent 8cbec8d601
commit 4baeee159e
6 files changed
+55 -3

No files matched your search

+2
View File
@@ -627,6 +627,7 @@ function renderBar() {
const targetBid = isSuperTile ? (t.dataset.superShown || bid) : bid;
const bucketId = targetBid === 'none' ? null : (['all','current','journal'].includes(targetBid) ? undefined : targetBid);
if (bucketId === undefined) return;
window._markCardsMoving?.(ids);
await Promise.all(ids.map(id => api(`api/cards/${id}`, { method: 'PATCH', body: { bucket_id: bucketId, parent_id: null } })));
window._clearSelection?.();
await window._reloadAll();
@@ -672,6 +673,7 @@ function renderBar() {
const selected = window._getSelectedIds?.() || [];
const ids = selected.includes(String(cardId)) ? selected : [String(cardId)];
const card = state.cards.find(c => String(c.id) === String(cardId));
window._markCardsMoving?.(ids);
const created = await api('api/buckets', { method: 'POST', body: { name: autoBucketNameFromCard(card) } });
if (!created?.id) return;
await Promise.all(ids.map(id => api(`api/cards/${id}`, { method: 'PATCH', body: { bucket_id: created.id, parent_id: null } })));