your personal website on atproto - mirror blento.app
25
fork

Configure Feed

Select the types of activity you want to include in your feed.

at fix/layout-stuff 90 lines 3.1 kB view raw
1import { COLUMNS } from '$lib'; 2import { CardDefinitionsByType } from '$lib/cards'; 3import { clamp } from '$lib/helper'; 4import { findValidPosition } from './algorithms'; 5import type { Item } from '$lib/types'; 6 7export type LayoutMode = 'desktop-leads' | 'mobile-leads' | 'independent'; 8 9/** 10 * Determine whether mirroring should happen and in which direction. 11 * Returns 'desktop' or 'mobile' for the source layout, or false if no mirroring. 12 * 13 * @param editedOn - bitflag: 0=never, 1=desktop, 2=mobile, 3=both 14 * @param layoutMode - explicit override (takes precedence when set) 15 * @param editingMobile - true if the current edit is on mobile 16 */ 17export function shouldMirror( 18 editedOn: number | undefined, 19 layoutMode: LayoutMode | undefined, 20 editingMobile: boolean 21): boolean { 22 if (layoutMode) { 23 if (layoutMode === 'independent') return false; 24 if (layoutMode === 'desktop-leads') return !editingMobile; 25 if (layoutMode === 'mobile-leads') return editingMobile; 26 return false; 27 } 28 // Legacy behavior: mirror as long as both layouts haven't been edited 29 return (editedOn ?? 0) !== 3; 30} 31 32/** Snap a value to the nearest even integer (min 2). */ 33function snapEven(v: number): number { 34 return Math.max(2, Math.round(v / 2) * 2); 35} 36 37/** 38 * Compute the other layout's size for a single item, preserving aspect ratio. 39 * Clamps to the card definition's minW/maxW/minH/maxH if defined. 40 * Mutates the item in-place. 41 */ 42export function mirrorItemSize(item: Item, fromMobile: boolean): void { 43 const def = CardDefinitionsByType[item.cardType]; 44 45 if (fromMobile) { 46 // Mobile → Desktop: halve both dimensions, then clamp to card def constraints 47 // (constraints are in desktop units) 48 item.w = clamp(snapEven(item.mobileW / 2), def?.minW ?? 2, def?.maxW ?? COLUMNS); 49 item.h = clamp(Math.round(item.mobileH / 2), def?.minH ?? 1, def?.maxH ?? Infinity); 50 } else { 51 // Desktop → Mobile: double both dimensions 52 // (don't apply card def constraints — they're in desktop units) 53 item.mobileW = Math.min(item.w * 2, COLUMNS); 54 item.mobileH = Math.max(item.h * 2, 2); 55 } 56} 57 58/** 59 * Mirror the full layout from one view to the other. 60 * Copies sizes proportionally and reflows items in reading order, then resolves collisions. 61 * Mutates items in-place. 62 */ 63export function mirrorLayout(items: Item[], fromMobile: boolean): void { 64 // Mirror sizes first 65 for (const item of items) { 66 mirrorItemSize(item, fromMobile); 67 } 68 69 if (fromMobile) { 70 // Mobile → Desktop: reflow items in mobile reading order 71 const sorted = items.toSorted((a, b) => a.mobileY - b.mobileY || a.mobileX - b.mobileX); 72 const placed: Item[] = []; 73 for (const item of sorted) { 74 item.x = 0; 75 item.y = 0; 76 findValidPosition(item, placed, false); 77 placed.push(item); 78 } 79 } else { 80 // Desktop → Mobile: reflow items in desktop reading order 81 const sorted = items.toSorted((a, b) => a.y - b.y || a.x - b.x); 82 const placed: Item[] = []; 83 for (const item of sorted) { 84 item.mobileX = 0; 85 item.mobileY = 0; 86 findValidPosition(item, placed, true); 87 placed.push(item); 88 } 89 } 90}