feat: Add physics model design document, implement multi-card snapping and layout system improvements, and refine pointer event handling.

This commit is contained in:
2025-11-26 17:30:46 +01:00
parent 155d8c5c4a
commit 835dcc4faa
4 changed files with 69 additions and 4 deletions
+38 -2
View File
@@ -179,6 +179,27 @@ export class LayoutCard implements LayoutCardState {
});
}
snapTo(x: number, y: number) {
this.update({ x, y });
if (!this.ref)
return;
this.ref.classList.add('desk-item--swoop');
this.ref.style.transform = `translate3d(${this.x}px, ${this.y}px, 0) rotate(${this.rotation}deg)`;
const cleanup = () => {
if (this.ref) {
this.ref.classList.remove('desk-item--swoop');
}
};
this.ref.addEventListener('transitionend', cleanup, { once: true });
// Safety timeout in case transitionend doesn't fire (e.g. element removed)
setTimeout(cleanup, 350);
}
toSnapshot(): LayoutCardState {
return {
id: this.id,
@@ -252,14 +273,29 @@ export class LayoutStore {
for (const card of this.items.values()) {
const dx = card.x - x;
const dy = card.y - y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < radius + card.outerRadius) {
// Check if center is within inner radius of top card
// Use squared distance to avoid sqrt
const distSq = dx * dx + dy * dy;
if (distSq < radius * radius) {
result.push(card);
}
}
return result;
}
getStackBelow(topCard: LayoutCard): string[] {
const candidates = this.getCardsInCircle(topCard.x, topCard.y, topCard.innerRadius);
return candidates
.filter(other => {
if (other.id === topCard.id) return false;
if (other.z >= topCard.z) return false;
return true;
})
.map(c => c.id);
}
getSnapshot() {
return Array.from(this.items.values()).map(card => card.toSnapshot());
}