Merge branch 'master' of https://git.uesome.de/tanztee/hyprcrd into feature/check-whether-works-as-pwa
This commit is contained in:
commit
14d3cc5434
5 files changed
+54
-2
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hyprcrd",
|
"name": "hyprcrd",
|
||||||
"version": "2.0.13",
|
"version": "2.0.14",
|
||||||
"description": "hyprcrd — self-hosted card manager",
|
"description": "hyprcrd — self-hosted card manager",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -524,6 +524,36 @@ body.rounded-cards .card-done-badge i { position: static; bottom: auto; right: a
|
|||||||
animation: card-remove 0.25s ease-in forwards;
|
animation: card-remove 0.25s ease-in forwards;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
/* Move-in-flight: card ghosted with a spinner overlay while the
|
||||||
|
* PATCH + reloadAll round-trip completes. The pseudo-elements paint
|
||||||
|
* a dim scrim and a centred spinning ring so the origin bucket
|
||||||
|
* clearly shows the card is "leaving". Removed by the next
|
||||||
|
* renderBoard() since the DOM is rebuilt from scratch. */
|
||||||
|
.board-card.card-moving {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.board-card.card-moving::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute; inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
z-index: 3;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.board-card.card-moving::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%; left: 50%;
|
||||||
|
width: 22px; height: 22px;
|
||||||
|
margin: -11px 0 0 -11px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-top-color: var(--t-accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
z-index: 4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
.box-select {
|
.box-select {
|
||||||
position: absolute; border: 1px solid var(--t-accent); background: rgba(56, 189, 248, 0.08);
|
position: absolute; border: 1px solid var(--t-accent); background: rgba(56, 189, 248, 0.08);
|
||||||
pointer-events: none; z-index: 10;
|
pointer-events: none; z-index: 10;
|
||||||
|
|||||||
@@ -50,6 +50,20 @@ export function animateCardRemoval(ids) {
|
|||||||
return new Promise(r => setTimeout(r, 260));
|
return new Promise(r => setTimeout(r, 260));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ghost-and-spinner overlay for cards that are being moved between
|
||||||
|
* buckets. The PATCH + reloadAll round-trip used to leave the original
|
||||||
|
* tile sitting stale in the current bucket for a couple hundred ms;
|
||||||
|
* marking them gives immediate feedback. The class comes off naturally
|
||||||
|
* on the next renderBoard() (the DOM is rebuilt from fresh state). */
|
||||||
|
export function markCardsMoving(ids) {
|
||||||
|
if (!ids || !ids.length) return;
|
||||||
|
for (const id of ids) {
|
||||||
|
const el = board.querySelector(`[data-id="${id}"]`);
|
||||||
|
if (el) el.classList.add('card-moving');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window._markCardsMoving = markCardsMoving;
|
||||||
|
|
||||||
/** Add a pulse class to matching board cards and strip it after the
|
/** Add a pulse class to matching board cards and strip it after the
|
||||||
* animation. `cls` controls the variant: 'card-pulse-new' (3×, for
|
* animation. `cls` controls the variant: 'card-pulse-new' (3×, for
|
||||||
* relay-synced cards) or 'card-pulse-local' (1×, for cards the user
|
* relay-synced cards) or 'card-pulse-local' (1×, for cards the user
|
||||||
|
|||||||
@@ -627,6 +627,7 @@ function renderBar() {
|
|||||||
const targetBid = isSuperTile ? (t.dataset.superShown || bid) : bid;
|
const targetBid = isSuperTile ? (t.dataset.superShown || bid) : bid;
|
||||||
const bucketId = targetBid === 'none' ? null : (['all','current','journal'].includes(targetBid) ? undefined : targetBid);
|
const bucketId = targetBid === 'none' ? null : (['all','current','journal'].includes(targetBid) ? undefined : targetBid);
|
||||||
if (bucketId === undefined) return;
|
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 } })));
|
await Promise.all(ids.map(id => api(`api/cards/${id}`, { method: 'PATCH', body: { bucket_id: bucketId, parent_id: null } })));
|
||||||
window._clearSelection?.();
|
window._clearSelection?.();
|
||||||
await window._reloadAll();
|
await window._reloadAll();
|
||||||
@@ -672,6 +673,7 @@ function renderBar() {
|
|||||||
const selected = window._getSelectedIds?.() || [];
|
const selected = window._getSelectedIds?.() || [];
|
||||||
const ids = selected.includes(String(cardId)) ? selected : [String(cardId)];
|
const ids = selected.includes(String(cardId)) ? selected : [String(cardId)];
|
||||||
const card = state.cards.find(c => String(c.id) === 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) } });
|
const created = await api('api/buckets', { method: 'POST', body: { name: autoBucketNameFromCard(card) } });
|
||||||
if (!created?.id) return;
|
if (!created?.id) return;
|
||||||
await Promise.all(ids.map(id => api(`api/cards/${id}`, { method: 'PATCH', body: { bucket_id: created.id, parent_id: null } })));
|
await Promise.all(ids.map(id => api(`api/cards/${id}`, { method: 'PATCH', body: { bucket_id: created.id, parent_id: null } })));
|
||||||
|
|||||||
@@ -416,7 +416,13 @@ export function showContextMenu(x, y, card) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
grid.querySelectorAll('[data-ctx-color]').forEach(btn => btn.addEventListener('click', async e => { e.stopPropagation(); hideContextMenu(); pushCardEdit('color', card.id, { color: card.color }); await api(`api/cards/${card.id}`, { method: 'PATCH', body: { color: btn.dataset.ctxColor } }); await window._reloadAll(); }));
|
grid.querySelectorAll('[data-ctx-color]').forEach(btn => btn.addEventListener('click', async e => { e.stopPropagation(); hideContextMenu(); pushCardEdit('color', card.id, { color: card.color }); await api(`api/cards/${card.id}`, { method: 'PATCH', body: { color: btn.dataset.ctxColor } }); await window._reloadAll(); }));
|
||||||
buckets.querySelectorAll('[data-ctx-bucket]').forEach(btn => btn.addEventListener('click', async e => { e.stopPropagation(); hideContextMenu(); pushCardEdit('bucket', card.id, { bucket_id: card.bucket_id }); await api(`api/cards/${card.id}`, { method: 'PATCH', body: { bucket_id: btn.dataset.ctxBucket || null } }); await window._reloadAll(); }));
|
buckets.querySelectorAll('[data-ctx-bucket]').forEach(btn => btn.addEventListener('click', async e => {
|
||||||
|
e.stopPropagation(); hideContextMenu();
|
||||||
|
pushCardEdit('bucket', card.id, { bucket_id: card.bucket_id });
|
||||||
|
window._markCardsMoving?.([card.id]);
|
||||||
|
await api(`api/cards/${card.id}`, { method: 'PATCH', body: { bucket_id: btn.dataset.ctxBucket || null } });
|
||||||
|
await window._reloadAll();
|
||||||
|
}));
|
||||||
members.querySelectorAll('[data-ctx-member]').forEach(btn => btn.addEventListener('click', e => { e.stopPropagation(); if (e.target.closest('[data-ctx-kick]')) return; hideContextMenu(); window._openDetail(btn.dataset.ctxMember); }));
|
members.querySelectorAll('[data-ctx-member]').forEach(btn => btn.addEventListener('click', e => { e.stopPropagation(); if (e.target.closest('[data-ctx-kick]')) return; hideContextMenu(); window._openDetail(btn.dataset.ctxMember); }));
|
||||||
members.querySelectorAll('[data-ctx-kick]').forEach(btn => btn.addEventListener('click', async e => { e.stopPropagation(); hideContextMenu(); await api(`api/cards/${btn.dataset.ctxKick}`, { method: 'PATCH', body: { parent_id: null } }); await window._reloadAll(); }));
|
members.querySelectorAll('[data-ctx-kick]').forEach(btn => btn.addEventListener('click', async e => { e.stopPropagation(); hideContextMenu(); await api(`api/cards/${btn.dataset.ctxKick}`, { method: 'PATCH', body: { parent_id: null } }); await window._reloadAll(); }));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user