// Windows slide from where they were to where a layout change puts them. // Every window is measured before the change and again on the next frame, // and each one that moved slides, at its new size and never scaled, from // the corner of its old box to the corner of its new one; one that grew is // cut to its old size as it starts and opens out to its right and bottom, // so what shows goes from the old box to the new. A move between two spots // on a canvas is Svelte Flow's, and is left to it. const DURATION = 200; const EASING = 'cubic-bezier(0.23, 1, 0.32, 1)'; const RADIUS = 14; type Seen = { rect: DOMRect; canvas: boolean }; function measure(board: string): Map { const seen = new Map(); for (const host of document.querySelectorAll(`.window-host[data-window^="${CSS.escape(board)}:"]`)) { if (host.closest('.window-parking')) continue; const rect = host.getBoundingClientRect(); if (rect.width && rect.height) seen.set(host, { rect, canvas: !!host.closest('.svelte-flow__node') }); } return seen; } function same(a: DOMRect, b: DOMRect) { return Math.abs(a.left - b.left) < 1 && Math.abs(a.top - b.top) < 1 && Math.abs(a.width - b.width) < 1 && Math.abs(a.height - b.height) < 1; } /** Measures `board`'s windows now, and slides each one that has moved by the next frame. */ export function reflow(board: string) { if (matchMedia('(prefers-reduced-motion: reduce)').matches) return; const before = measure(board); if (!before.size) return; requestAnimationFrame(() => { for (const [host, now] of measure(board)) { const was = before.get(host); if (!was || (was.canvas && now.canvas)) continue; const from = was.rect; const to = now.rect; if (same(from, to)) continue; // A window on a canvas is in its zoom: screen pixels over the window's own. const zoom = to.width / host.offsetWidth || 1; const x = (from.left - to.left) / zoom; const y = (from.top - to.top) / zoom; // How much of its right and bottom is cut off at the start: what it has grown by. const across = Math.max(0, to.width - from.width) / zoom; const down = Math.max(0, to.height - from.height) / zoom; host.animate( [ { transform: `translate(${x}px, ${y}px)`, clipPath: `inset(0px ${across}px ${down}px 0px round ${RADIUS}px)` }, { transform: 'none', clipPath: `inset(0px 0px round ${RADIUS}px)` }, ], { duration: DURATION, easing: EASING }, ); } }); }