Fix card-enter animation re-trigger and drag hover suppression
Remove card-enter class after entrance animation completes to prevent re-triggering when card-highlight is removed. Change fill-mode from both to backwards so stale transforms don't block hover effects. Suppress hover globally during drag via body.cs-drag-active class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,12 @@ export class CardSection {
|
||||
* @returns {{added: Set<string>, replaced: Set<string>, removed: Set<string>}}
|
||||
*/
|
||||
reconcile(items) {
|
||||
// Skip DOM mutations while a drag is in progress — would destroy drag state
|
||||
if (this._dragState) {
|
||||
this._pendingReconcile = items;
|
||||
return { added: new Set(), replaced: new Set(), removed: new Set() };
|
||||
}
|
||||
|
||||
const content = document.querySelector(`[data-cs-content="${this.sectionKey}"]`);
|
||||
if (!content) return { added: new Set(), replaced: new Set(), removed: new Set() };
|
||||
|
||||
@@ -240,6 +246,7 @@ export class CardSection {
|
||||
if (card) {
|
||||
card.style.animationDelay = `${delay}ms`;
|
||||
card.classList.add('card-enter');
|
||||
card.addEventListener('animationend', () => card.classList.remove('card-enter'), { once: true });
|
||||
delay += 30;
|
||||
}
|
||||
}
|
||||
@@ -347,6 +354,7 @@ export class CardSection {
|
||||
cards.forEach((card, i) => {
|
||||
card.style.animationDelay = `${i * 30}ms`;
|
||||
card.classList.add('card-enter');
|
||||
card.addEventListener('animationend', () => card.classList.remove('card-enter'), { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -502,19 +510,16 @@ export class CardSection {
|
||||
ds.clone.style.left = (e.clientX - ds.offsetX) + 'px';
|
||||
ds.clone.style.top = (e.clientY - ds.offsetY) + 'px';
|
||||
|
||||
// Find drop target
|
||||
const target = this._getDropTarget(e.clientX, e.clientY, ds.content);
|
||||
if (target && target !== ds.placeholder) {
|
||||
const rect = target.getBoundingClientRect();
|
||||
const midX = rect.left + rect.width / 2;
|
||||
const midY = rect.top + rect.height / 2;
|
||||
// For grid: use combined horizontal+vertical check
|
||||
const insertBefore = (e.clientY < midY) || (e.clientY < midY + rect.height * 0.3 && e.clientX < midX);
|
||||
if (insertBefore) {
|
||||
ds.content.insertBefore(ds.placeholder, target);
|
||||
} else {
|
||||
ds.content.insertBefore(ds.placeholder, target.nextSibling);
|
||||
}
|
||||
// Only move placeholder when cursor enters a card's rect
|
||||
const { card: target, before } = this._getDropTarget(e.clientX, e.clientY, ds.content);
|
||||
if (!target) return; // cursor is in a gap — keep placeholder where it is
|
||||
if (target === ds.lastTarget && before === ds.lastBefore) return; // same position
|
||||
ds.lastTarget = target;
|
||||
ds.lastBefore = before;
|
||||
if (before) {
|
||||
ds.content.insertBefore(ds.placeholder, target);
|
||||
} else {
|
||||
ds.content.insertBefore(ds.placeholder, target.nextSibling);
|
||||
}
|
||||
|
||||
// Auto-scroll near viewport edges
|
||||
@@ -548,6 +553,7 @@ export class CardSection {
|
||||
// Hide original
|
||||
ds.card.style.display = 'none';
|
||||
ds.content.classList.add('cs-dragging');
|
||||
document.body.classList.add('cs-drag-active');
|
||||
}
|
||||
|
||||
_onDragEnd() {
|
||||
@@ -564,21 +570,30 @@ export class CardSection {
|
||||
ds.placeholder.remove();
|
||||
ds.clone.remove();
|
||||
ds.content.classList.remove('cs-dragging');
|
||||
document.body.classList.remove('cs-drag-active');
|
||||
|
||||
// Save new order from DOM
|
||||
const keys = this._readDomOrder(ds.content);
|
||||
this._saveOrder(keys);
|
||||
|
||||
// Flush any reconcile that was deferred during drag
|
||||
if (this._pendingReconcile) {
|
||||
const items = this._pendingReconcile;
|
||||
this._pendingReconcile = null;
|
||||
this.reconcile(items);
|
||||
}
|
||||
}
|
||||
|
||||
_getDropTarget(x, y, content) {
|
||||
// Temporarily show all cards for hit testing
|
||||
const els = document.elementsFromPoint(x, y);
|
||||
for (const el of els) {
|
||||
if (el === content) break;
|
||||
const card = el.closest(`[${this.keyAttr}]`);
|
||||
if (card && card.style.display !== 'none' && content.contains(card)) return card;
|
||||
const cards = content.querySelectorAll(`[${this.keyAttr}]`);
|
||||
for (const card of cards) {
|
||||
if (card.style.display === 'none') continue;
|
||||
const r = card.getBoundingClientRect();
|
||||
if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {
|
||||
return { card, before: x < r.left + r.width / 2 };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return { card: null, before: false };
|
||||
}
|
||||
|
||||
_readDomOrder(content) {
|
||||
|
||||
Reference in New Issue
Block a user