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

Configure Feed

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

change size stuff

Florian 593f3bc1 e066220d

+88 -109
+88 -109
src/lib/cards/BaseCard/BaseEditingCard.svelte
··· 3 3 import type { HTMLAttributes } from 'svelte/elements'; 4 4 import BaseCard from './BaseCard.svelte'; 5 5 import type { Item } from '$lib/types'; 6 - import { Button, Input, Popover } from '@foxui/core'; 7 - import { getCanEdit } from '$lib/helper'; 6 + import { Button, Popover } from '@foxui/core'; 7 + import { getCanEdit, getIsMobile } from '$lib/helper'; 8 8 import { ColorSelect } from '@foxui/colors'; 9 9 import { CardDefinitionsByType, getColor } from '..'; 10 10 import { COLUMNS } from '$lib'; 11 - import { dev } from '$app/environment'; 12 11 13 12 let colorsChoices = [ 14 13 { class: 'text-base-500', label: 'base' }, ··· 53 52 let selectedColor = $derived(colorsChoices.find((c) => getColor(item) === c.label)); 54 53 55 54 let canEdit = getCanEdit(); 55 + let isMobile = getIsMobile(); 56 56 57 57 let colorPopoverOpen = $state(false); 58 58 ··· 64 64 const maxW = $derived(cardDef.maxW ?? COLUMNS); 65 65 const maxH = $derived(cardDef.maxH ?? COLUMNS); 66 66 67 - function canSetSize(w: number, h: number) { 68 - if (!cardDef) return false; 67 + // Resize handle state 68 + let isResizing = $state(false); 69 + let resizeStartX = $state(0); 70 + let resizeStartY = $state(0); 71 + let resizeStartW = $state(0); 72 + let resizeStartH = $state(0); 73 + 74 + function handleResizeStart(e: PointerEvent) { 75 + e.preventDefault(); 76 + e.stopPropagation(); 77 + isResizing = true; 78 + resizeStartX = e.clientX; 79 + resizeStartY = e.clientY; 80 + // For mobile view, sizes are doubled so we need to account for that 81 + resizeStartW = isMobile() ? (item.mobileW ?? item.w) / 2 : item.w; 82 + resizeStartH = isMobile() ? (item.mobileH ?? item.h) / 2 : item.h; 83 + 84 + document.addEventListener('pointermove', handleResizeMove); 85 + document.addEventListener('pointerup', handleResizeEnd); 86 + } 87 + 88 + function handleResizeMove(e: PointerEvent) { 89 + if (!isResizing || !ref) return; 90 + 91 + // Get the container width to calculate cell size 92 + const container = ref.closest('.\\@container\\/grid') as HTMLElement; 93 + if (!container) return; 94 + 95 + const containerRect = container.getBoundingClientRect(); 96 + const cellSize = containerRect.width / COLUMNS; 97 + 98 + // Calculate delta in grid units (each visual unit is 2 grid units) 99 + const deltaX = e.clientX - resizeStartX; 100 + const deltaY = e.clientY - resizeStartY; 101 + 102 + // Convert pixel delta to grid units (2 grid units = 1 visual cell) 103 + const gridDeltaW = Math.round(deltaX / cellSize); 104 + const gridDeltaH = Math.round(deltaY / cellSize); 105 + 106 + // Calculate new size (snap to even numbers since visual units are 2 grid units) 107 + let newW = resizeStartW + gridDeltaW; 108 + let newH = resizeStartH + gridDeltaH; 109 + 110 + // Snap to increments of 2 111 + newW = Math.round(newW / 2) * 2; 112 + newH = newH; 113 + 114 + // Clamp to min/max 115 + newW = Math.max(minW, Math.min(maxW, newW)); 116 + newH = Math.max(minH, Math.min(maxH, newH)); 117 + 118 + // Only call onsetsize if size changed 119 + const currentW = isMobile() ? (item.mobileW ?? item.w) / 2 : item.w; 120 + const currentH = isMobile() ? (item.mobileH ?? item.h) / 2 : item.h; 69 121 70 - return w >= minW && w <= maxW && h >= minH && h <= maxH; 122 + if (newW !== currentW || newH !== currentH) { 123 + onsetsize?.(newW, newH); 124 + } 71 125 } 72 126 73 - let customSizePopoverOpen = $state(false); 127 + function handleResizeEnd() { 128 + isResizing = false; 129 + document.removeEventListener('pointermove', handleResizeMove); 130 + document.removeEventListener('pointerup', handleResizeEnd); 131 + } 74 132 </script> 75 133 76 134 <BaseCard {item} {...rest} isEditing={true} bind:ref> ··· 107 165 <div 108 166 class={[ 109 167 'absolute -bottom-7 z-50 w-full items-center justify-center text-xs group-focus-within:inline-flex group-hover:inline-flex', 110 - colorPopoverOpen || customSizePopoverOpen ? 'inline-flex' : 'hidden' 168 + colorPopoverOpen ? 'inline-flex' : 'hidden' 111 169 ]} 112 170 > 113 171 <div ··· 152 210 /> 153 211 </Popover> 154 212 155 - {#if canSetSize(2, 2)} 156 - <button 157 - onclick={() => { 158 - onsetsize?.(2, 2); 159 - }} 160 - class="hover:bg-accent-500/10 cursor-pointer rounded-xl p-2" 161 - > 162 - <div class="border-base-900 dark:border-base-50 size-3 rounded-sm border-2"></div> 163 - 164 - <span class="sr-only">set size to 1x1</span> 165 - </button> 166 - {/if} 167 - 168 - {#if canSetSize(4, 2)} 169 - <button 170 - onclick={() => { 171 - onsetsize?.(4, 2); 172 - }} 173 - class="hover:bg-accent-500/10 cursor-pointer rounded-xl p-2" 174 - > 175 - <div class="border-base-900 dark:border-base-50 h-3 w-5 rounded-sm border-2"></div> 176 - <span class="sr-only">set size to 2x1</span> 177 - </button> 178 - {/if} 179 - {#if canSetSize(2, 4)} 180 - <button 181 - onclick={() => { 182 - onsetsize?.(2, 4); 183 - }} 184 - class="hover:bg-accent-500/10 cursor-pointer rounded-xl p-2" 185 - > 186 - <div class="border-base-900 dark:border-base-50 h-5 w-3 rounded-sm border-2"></div> 187 - 188 - <span class="sr-only">set size to 1x2</span> 189 - </button> 190 - {/if} 191 - {#if canSetSize(4, 4)} 192 - <button 193 - onclick={() => { 194 - onsetsize?.(4, 4); 195 - }} 196 - class="hover:bg-accent-500/10 cursor-pointer rounded-xl p-2" 197 - > 198 - <div class="border-base-900 dark:border-base-50 h-5 w-5 rounded-sm border-2"></div> 199 - 200 - <span class="sr-only">set size to 2x2</span> 201 - </button> 202 - {/if} 203 - 204 - {#if dev} 205 - <Popover bind:open={customSizePopoverOpen}> 206 - {#snippet child({ props })} 207 - <button {...props} class="hover:bg-accent-500/10 cursor-pointer rounded-xl p-2"> 208 - <div 209 - class="border-base-900 dark:border-base-50 h-5 w-5 rounded-sm border-2" 210 - ></div> 211 - 212 - <span class="sr-only">set size to 2x2</span> 213 - </button> 214 - {/snippet} 215 - 216 - <div class="flex flex-col gap-4"> 217 - <div> 218 - <Button 219 - onclick={() => { 220 - if (canSetSize(item.w - 1, item.h)) { 221 - onsetsize?.(item.w - 1, item.h); 222 - } 223 - }}>-</Button 224 - > 225 - 226 - <Button 227 - onclick={() => { 228 - if (canSetSize(item.w + 1, item.h)) { 229 - onsetsize?.(item.w + 1, item.h); 230 - } 231 - }}>+</Button 232 - > 233 - </div> 234 - 235 - <div> 236 - <Button 237 - onclick={() => { 238 - if (canSetSize(item.w, item.h - 1)) { 239 - onsetsize?.(item.w, item.h - 1); 240 - } 241 - }}>-</Button 242 - > 243 - 244 - <Button 245 - onclick={() => { 246 - if (canSetSize(item.w, item.h + 1)) { 247 - onsetsize?.(item.w, item.h + 1); 248 - } 249 - }}>+</Button 250 - > 251 - </div> 252 - </div> 253 - </Popover> 254 - {/if} 255 - 256 213 {#if onshowsettings} 257 214 <button 258 215 onclick={() => { ··· 284 241 </button> 285 242 {/if} 286 243 </div> 244 + </div> 245 + 246 + <!-- Resize handle at bottom right corner --> 247 + <!-- svelte-ignore a11y_no_static_element_interactions --> 248 + <div 249 + class={[ 250 + 'absolute -bottom-2 -right-2 z-50 cursor-se-resize rounded-full p-1 group-focus-within:flex group-hover:flex', 251 + isResizing ? 'flex' : 'hidden' 252 + ]} 253 + onpointerdown={handleResizeStart} 254 + > 255 + <div class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-600 flex size-5 items-center justify-center rounded-full border shadow-md"> 256 + <svg 257 + xmlns="http://www.w3.org/2000/svg" 258 + viewBox="0 0 16 16" 259 + fill="currentColor" 260 + class="text-base-500 size-3" 261 + > 262 + <path d="M5.5 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm4 4a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" /> 263 + </svg> 264 + </div> 265 + <span class="sr-only">Resize card</span> 287 266 </div> 288 267 {/if} 289 268 {/snippet}