your personal website on atproto - mirror blento.app

commit

+145 -144
-140
src/lib/helper.ts
··· 3 3 import { CardDefinitionsByType } from './cards'; 4 4 import { deleteRecord, getCDNImageBlobUrl, putRecord, uploadBlob } from '$lib/atproto'; 5 5 import * as TID from '@atcute/tid'; 6 - import { overlaps } from './layout'; 7 - 8 6 export function clamp(value: number, min: number, max: number): number { 9 7 return Math.min(Math.max(value, min), max); 10 8 } ··· 28 26 'bg-pink-500', 29 27 'bg-rose-500' 30 28 ]; 31 - 32 29 33 30 export function sortItems(a: Item, b: Item) { 34 31 return a.y * COLUMNS + a.x - b.y * COLUMNS - b.x; ··· 50 47 a.color === b.color && 51 48 a.page === b.page 52 49 ); 53 - } 54 - 55 - export function setPositionOfNewItem( 56 - newItem: Item, 57 - items: Item[], 58 - viewportCenter?: { gridY: number; isMobile: boolean } 59 - ) { 60 - if (viewportCenter) { 61 - const { gridY, isMobile } = viewportCenter; 62 - 63 - if (isMobile) { 64 - // Place at viewport center Y 65 - newItem.mobileY = Math.max(0, Math.round(gridY - newItem.mobileH / 2)); 66 - newItem.mobileY = Math.floor(newItem.mobileY / 2) * 2; 67 - 68 - // Try to find a free X at this Y 69 - let found = false; 70 - for ( 71 - newItem.mobileX = 0; 72 - newItem.mobileX <= COLUMNS - newItem.mobileW; 73 - newItem.mobileX += 2 74 - ) { 75 - if (!items.some((item) => overlaps(newItem, item, true))) { 76 - found = true; 77 - break; 78 - } 79 - } 80 - if (!found) { 81 - newItem.mobileX = 0; 82 - } 83 - 84 - // Desktop: derive from mobile 85 - newItem.y = Math.max(0, Math.round(newItem.mobileY / 2)); 86 - found = false; 87 - for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x += 2) { 88 - if (!items.some((item) => overlaps(newItem, item, false))) { 89 - found = true; 90 - break; 91 - } 92 - } 93 - if (!found) { 94 - newItem.x = 0; 95 - } 96 - } else { 97 - // Place at viewport center Y 98 - newItem.y = Math.max(0, Math.round(gridY - newItem.h / 2)); 99 - 100 - // Try to find a free X at this Y 101 - let found = false; 102 - for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x += 2) { 103 - if (!items.some((item) => overlaps(newItem, item, false))) { 104 - found = true; 105 - break; 106 - } 107 - } 108 - if (!found) { 109 - newItem.x = 0; 110 - } 111 - 112 - // Mobile: derive from desktop 113 - newItem.mobileY = Math.max(0, Math.round(newItem.y * 2)); 114 - found = false; 115 - for ( 116 - newItem.mobileX = 0; 117 - newItem.mobileX <= COLUMNS - newItem.mobileW; 118 - newItem.mobileX += 2 119 - ) { 120 - if (!items.some((item) => overlaps(newItem, item, true))) { 121 - found = true; 122 - break; 123 - } 124 - } 125 - if (!found) { 126 - newItem.mobileX = 0; 127 - } 128 - } 129 - return; 130 - } 131 - 132 - let foundPosition = false; 133 - while (!foundPosition) { 134 - for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) { 135 - const collision = items.find((item) => overlaps(newItem, item, false)); 136 - if (!collision) { 137 - foundPosition = true; 138 - break; 139 - } 140 - } 141 - if (!foundPosition) newItem.y += 1; 142 - } 143 - 144 - let foundMobilePosition = false; 145 - while (!foundMobilePosition) { 146 - for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX += 1) { 147 - const collision = items.find((item) => overlaps(newItem, item, true)); 148 - 149 - if (!collision) { 150 - foundMobilePosition = true; 151 - break; 152 - } 153 - } 154 - if (!foundMobilePosition) newItem.mobileY! += 1; 155 - } 156 - } 157 - 158 - /** 159 - * Find a valid position for a new item in a single mode (desktop or mobile). 160 - * This modifies the item's position properties in-place. 161 - */ 162 - export function findValidPosition(newItem: Item, items: Item[], mobile: boolean) { 163 - if (mobile) { 164 - let foundPosition = false; 165 - newItem.mobileY = 0; 166 - while (!foundPosition) { 167 - for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX++) { 168 - const collision = items.find((item) => overlaps(newItem, item, true)); 169 - if (!collision) { 170 - foundPosition = true; 171 - break; 172 - } 173 - } 174 - if (!foundPosition) newItem.mobileY! += 1; 175 - } 176 - } else { 177 - let foundPosition = false; 178 - newItem.y = 0; 179 - while (!foundPosition) { 180 - for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) { 181 - const collision = items.find((item) => overlaps(newItem, item, false)); 182 - if (!collision) { 183 - foundPosition = true; 184 - break; 185 - } 186 - } 187 - if (!foundPosition) newItem.y += 1; 188 - } 189 - } 190 50 } 191 51 192 52 export async function refreshData(data: { updatedAt?: number; handle: string }) {
+142
src/lib/layout.ts
··· 108 108 const compacted = verticalCompactor.compact(layout, COLUMNS) as LayoutItem[]; 109 109 applyLayout(items, compacted, mobile); 110 110 } 111 + 112 + export function setPositionOfNewItem( 113 + newItem: Item, 114 + items: Item[], 115 + viewportCenter?: { gridY: number; isMobile: boolean } 116 + ) { 117 + const desktopLayout = toLayout(items, false); 118 + const mobileLayout = toLayout(items, true); 119 + 120 + function hasCollision(mobile: boolean): boolean { 121 + const layout = mobile ? mobileLayout : desktopLayout; 122 + return getFirstCollision(layout, toLayoutItem(newItem, mobile)) !== undefined; 123 + } 124 + 125 + if (viewportCenter) { 126 + const { gridY, isMobile } = viewportCenter; 127 + 128 + if (isMobile) { 129 + // Place at viewport center Y 130 + newItem.mobileY = Math.max(0, Math.round(gridY - newItem.mobileH / 2)); 131 + newItem.mobileY = Math.floor(newItem.mobileY / 2) * 2; 132 + 133 + // Try to find a free X at this Y 134 + let found = false; 135 + for ( 136 + newItem.mobileX = 0; 137 + newItem.mobileX <= COLUMNS - newItem.mobileW; 138 + newItem.mobileX += 2 139 + ) { 140 + if (!hasCollision(true)) { 141 + found = true; 142 + break; 143 + } 144 + } 145 + if (!found) { 146 + newItem.mobileX = 0; 147 + } 148 + 149 + // Desktop: derive from mobile 150 + newItem.y = Math.max(0, Math.round(newItem.mobileY / 2)); 151 + found = false; 152 + for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x += 2) { 153 + if (!hasCollision(false)) { 154 + found = true; 155 + break; 156 + } 157 + } 158 + if (!found) { 159 + newItem.x = 0; 160 + } 161 + } else { 162 + // Place at viewport center Y 163 + newItem.y = Math.max(0, Math.round(gridY - newItem.h / 2)); 164 + 165 + // Try to find a free X at this Y 166 + let found = false; 167 + for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x += 2) { 168 + if (!hasCollision(false)) { 169 + found = true; 170 + break; 171 + } 172 + } 173 + if (!found) { 174 + newItem.x = 0; 175 + } 176 + 177 + // Mobile: derive from desktop 178 + newItem.mobileY = Math.max(0, Math.round(newItem.y * 2)); 179 + found = false; 180 + for ( 181 + newItem.mobileX = 0; 182 + newItem.mobileX <= COLUMNS - newItem.mobileW; 183 + newItem.mobileX += 2 184 + ) { 185 + if (!hasCollision(true)) { 186 + found = true; 187 + break; 188 + } 189 + } 190 + if (!found) { 191 + newItem.mobileX = 0; 192 + } 193 + } 194 + return; 195 + } 196 + 197 + let foundPosition = false; 198 + while (!foundPosition) { 199 + for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) { 200 + if (!hasCollision(false)) { 201 + foundPosition = true; 202 + break; 203 + } 204 + } 205 + if (!foundPosition) newItem.y += 1; 206 + } 207 + 208 + let foundMobilePosition = false; 209 + while (!foundMobilePosition) { 210 + for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX += 1) { 211 + if (!hasCollision(true)) { 212 + foundMobilePosition = true; 213 + break; 214 + } 215 + } 216 + if (!foundMobilePosition) newItem.mobileY! += 1; 217 + } 218 + } 219 + 220 + /** 221 + * Find a valid position for a new item in a single mode (desktop or mobile). 222 + * This modifies the item's position properties in-place. 223 + */ 224 + export function findValidPosition(newItem: Item, items: Item[], mobile: boolean) { 225 + const layout = toLayout(items, mobile); 226 + 227 + if (mobile) { 228 + let foundPosition = false; 229 + newItem.mobileY = 0; 230 + while (!foundPosition) { 231 + for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX++) { 232 + if (!getFirstCollision(layout, toLayoutItem(newItem, true))) { 233 + foundPosition = true; 234 + break; 235 + } 236 + } 237 + if (!foundPosition) newItem.mobileY! += 1; 238 + } 239 + } else { 240 + let foundPosition = false; 241 + newItem.y = 0; 242 + while (!foundPosition) { 243 + for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) { 244 + if (!getFirstCollision(layout, toLayoutItem(newItem, false))) { 245 + foundPosition = true; 246 + break; 247 + } 248 + } 249 + if (!foundPosition) newItem.y += 1; 250 + } 251 + } 252 + }
+1 -2
src/lib/website/EditableWebsite.svelte
··· 11 11 isTyping, 12 12 savePage, 13 13 scrollToItem, 14 - setPositionOfNewItem, 15 14 validateLink, 16 15 getImage 17 16 } from '../helper'; ··· 39 38 import CardCommand from '$lib/components/card-command/CardCommand.svelte'; 40 39 import { shouldMirror, mirrorLayout } from './layout-mirror'; 41 40 import { SvelteMap } from 'svelte/reactivity'; 42 - import { fixCollisions, compactItems, fixAllCollisions } from '$lib/layout'; 41 + import { fixCollisions, compactItems, fixAllCollisions, setPositionOfNewItem } from '$lib/layout'; 43 42 44 43 let { 45 44 data
+2 -2
src/lib/website/layout-mirror.ts
··· 1 1 import { COLUMNS } from '$lib'; 2 2 import { CardDefinitionsByType } from '$lib/cards'; 3 - import { clamp, findValidPosition } from '$lib/helper'; 4 - import { fixAllCollisions } from '$lib/layout'; 3 + import { clamp } from '$lib/helper'; 4 + import { fixAllCollisions, findValidPosition } from '$lib/layout'; 5 5 import type { Item } from '$lib/types'; 6 6 7 7 /**