your personal website on atproto - mirror blento.app
at funding 72 lines 2.5 kB view raw
1import { COLUMNS } from '$lib'; 2import { CardDefinitionsByType } from '$lib/cards'; 3import { clamp, findValidPosition, fixAllCollisions } from '$lib/helper'; 4import type { Item } from '$lib/types'; 5 6/** 7 * Returns true when mirroring should still happen (i.e. user hasn't edited both layouts). 8 * editedOn: 0/undefined = never, 1 = desktop only, 2 = mobile only, 3 = both 9 */ 10export function shouldMirror(editedOn: number | undefined): boolean { 11 return (editedOn ?? 0) !== 3; 12} 13 14/** Snap a value to the nearest even integer (min 2). */ 15function snapEven(v: number): number { 16 return Math.max(2, Math.round(v / 2) * 2); 17} 18 19/** 20 * Compute the other layout's size for a single item, preserving aspect ratio. 21 * Clamps to the card definition's minW/maxW/minH/maxH if defined. 22 * Mutates the item in-place. 23 */ 24export function mirrorItemSize(item: Item, fromMobile: boolean): void { 25 const def = CardDefinitionsByType[item.cardType]; 26 27 if (fromMobile) { 28 // Mobile → Desktop: halve both dimensions, then clamp to card def constraints 29 // (constraints are in desktop units) 30 item.w = clamp(snapEven(item.mobileW / 2), def?.minW ?? 2, def?.maxW ?? COLUMNS); 31 item.h = clamp(Math.round(item.mobileH / 2), def?.minH ?? 1, def?.maxH ?? Infinity); 32 } else { 33 // Desktop → Mobile: double both dimensions 34 // (don't apply card def constraints — they're in desktop units) 35 item.mobileW = Math.min(item.w * 2, COLUMNS); 36 item.mobileH = Math.max(item.h * 2, 2); 37 } 38} 39 40/** 41 * Mirror the full layout from one view to the other. 42 * Copies sizes proportionally and maps positions, then resolves collisions. 43 * Mutates items in-place. 44 */ 45export function mirrorLayout(items: Item[], fromMobile: boolean): void { 46 // Mirror sizes first 47 for (const item of items) { 48 mirrorItemSize(item, fromMobile); 49 } 50 51 if (fromMobile) { 52 // Mobile → Desktop: reflow items to use the full grid width. 53 // Sort by mobile position so items are placed in reading order. 54 const sorted = items.toSorted((a, b) => a.mobileY - b.mobileY || a.mobileX - b.mobileX); 55 56 // Place each item into the first available spot on the desktop grid 57 const placed: Item[] = []; 58 for (const item of sorted) { 59 item.x = 0; 60 item.y = 0; 61 findValidPosition(item, placed, false); 62 placed.push(item); 63 } 64 } else { 65 // Desktop → Mobile: proportional positions 66 for (const item of items) { 67 item.mobileX = clamp(Math.floor((item.x * 2) / 2) * 2, 0, COLUMNS - item.mobileW); 68 item.mobileY = Math.max(0, Math.round(item.y * 2)); 69 } 70 fixAllCollisions(items, true); 71 } 72}