your personal website on atproto - mirror blento.app

like this

jycouet 510a5ac3 04eb63a7

+61 -31
+15 -12
src/lib/cards/GitHubContributorsCard/GitHubContributorsCard.svelte
··· 13 13 let owner: string = $derived(item.cardData.owner ?? ''); 14 14 let repo: string = $derived(item.cardData.repo ?? ''); 15 15 let repoKey: string = $derived(owner && repo ? `${owner}/${repo}` : ''); 16 - let layout: 'hex' | 'grid' = $derived(item.cardData.layout ?? 'hex'); 16 + let layout: 'grid' | 'cinema' = $derived(item.cardData.layout ?? 'grid'); 17 + let shape: 'square' | 'circle' = $derived(item.cardData.shape ?? 'square'); 17 18 18 19 let serverContributors: GitHubContributor[] = $derived.by(() => { 19 20 if (!repoKey) return []; ··· 61 62 const MIN_SIZE = 16; 62 63 const MAX_SIZE = 120; 63 64 64 - function hexCapacity(size: number, availW: number, availH: number): number { 65 + function cinemaCapacity(size: number, availW: number, availH: number): number { 65 66 const colsWide = Math.floor((availW + GAP) / (size + GAP)); 66 67 if (colsWide < 1) return 0; 67 68 const colsNarrow = Math.max(1, colsWide - 1); ··· 85 86 86 87 let lo = MIN_SIZE; 87 88 let hi = MAX_SIZE; 88 - const capacityFn = layout === 'hex' ? hexCapacity : gridCapacity; 89 + const capacityFn = layout === 'cinema' ? cinemaCapacity : gridCapacity; 89 90 90 91 while (lo <= hi) { 91 92 const mid = Math.floor((lo + hi) / 2); 92 - const availW = containerWidth - (layout === 'hex' ? mid / 2 : 0); 93 - const availH = containerHeight - (layout === 'hex' ? mid / 2 : 0); 93 + const availW = containerWidth - (layout === 'cinema' ? mid / 2 : 0); 94 + const availH = containerHeight - (layout === 'cinema' ? mid / 2 : 0); 94 95 if (availW <= 0 || availH <= 0) { 95 96 hi = mid - 1; 96 97 continue; ··· 105 106 return Math.max(MIN_SIZE, hi); 106 107 }); 107 108 108 - let padding = $derived(layout === 'hex' ? computedSize / 4 : 0); 109 + let padding = $derived(layout === 'cinema' ? computedSize / 4 : 0); 109 110 110 111 let rows = $derived.by(() => { 111 - const availW = containerWidth - (layout === 'hex' ? computedSize / 4 : 0); 112 + const availW = containerWidth - (layout === 'cinema' ? computedSize / 4 : 0); 112 113 if (availW <= 0) return [] as GitHubContributor[][]; 113 114 114 115 const colsWide = Math.floor((availW + GAP) / (computedSize + GAP)); 115 - const colsNarrow = layout === 'hex' ? Math.max(1, colsWide - 1) : colsWide; 116 + const colsNarrow = layout === 'cinema' ? Math.max(1, colsWide - 1) : colsWide; 116 117 117 118 // Calculate row sizes from bottom up, then reverse for incomplete row at top 118 119 const rowSizes: number[] = []; 119 120 let remaining = namedContributors.length; 120 121 let rowNum = 0; 121 122 while (remaining > 0) { 122 - const cols = layout === 'hex' && rowNum % 2 === 0 ? colsNarrow : colsWide; 123 + const cols = layout === 'cinema' && rowNum % 2 === 0 ? colsNarrow : colsWide; 123 124 rowSizes.push(Math.min(cols, remaining)); 124 125 remaining -= cols; 125 126 rowNum++; ··· 139 140 let textSize = $derived( 140 141 computedSize < 24 ? 'text-[10px]' : computedSize < 40 ? 'text-xs' : 'text-sm' 141 142 ); 143 + 144 + let shapeClass = $derived(shape === 'circle' ? 'rounded-full' : 'rounded-lg'); 142 145 </script> 143 146 144 147 <div ··· 162 165 href="https://github.com/{contributor.username}" 163 166 target="_blank" 164 167 rel="noopener noreferrer" 165 - class="accent:ring-accent-500 block rounded-full ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900" 168 + class="accent:ring-accent-500 block {shapeClass} ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900" 166 169 > 167 170 {#if contributor.avatarUrl} 168 171 <img 169 172 src={contributor.avatarUrl} 170 173 alt={contributor.username} 171 - class="rounded-full object-cover" 174 + class="{shapeClass} object-cover" 172 175 style="width: {computedSize}px; height: {computedSize}px;" 173 176 /> 174 177 {:else} 175 178 <div 176 - class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center rounded-full" 179 + class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center {shapeClass}" 177 180 style="width: {computedSize}px; height: {computedSize}px;" 178 181 > 179 182 <span
+46 -19
src/lib/cards/GitHubContributorsCard/GitHubContributorsCardSettings.svelte
··· 5 5 let { item = $bindable() }: SettingsComponentProps = $props(); 6 6 7 7 const layoutOptions = [ 8 - { value: 'hex', label: 'Hexagon' }, 9 - { value: 'grid', label: 'Grid' } 8 + { value: 'grid', label: 'Grid' }, 9 + { value: 'cinema', label: 'Cinema' } 10 + ]; 11 + 12 + const shapeOptions = [ 13 + { value: 'square', label: 'Square' }, 14 + { value: 'circle', label: 'Circle' } 10 15 ]; 11 16 12 - let layout = $derived(item.cardData.layout ?? 'hex'); 17 + let layout = $derived(item.cardData.layout ?? 'grid'); 18 + let shape = $derived(item.cardData.shape ?? 'square'); 13 19 </script> 14 20 15 - <div class="flex flex-col gap-2"> 16 - <Label>Layout</Label> 17 - <div class="flex gap-2"> 18 - {#each layoutOptions as opt (opt.value)} 19 - <button 20 - class={[ 21 - 'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors', 22 - layout === opt.value 23 - ? 'bg-accent-500 border-accent-500 text-white' 24 - : 'bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 text-base-900 dark:text-base-100 hover:border-accent-400' 25 - ]} 26 - onclick={() => (item.cardData.layout = opt.value)} 27 - > 28 - {opt.label} 29 - </button> 30 - {/each} 21 + <div class="flex flex-col gap-4"> 22 + <div class="flex flex-col gap-2"> 23 + <Label>Layout</Label> 24 + <div class="flex gap-2"> 25 + {#each layoutOptions as opt (opt.value)} 26 + <button 27 + class={[ 28 + 'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors', 29 + layout === opt.value 30 + ? 'bg-accent-500 border-accent-500 text-white' 31 + : 'bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 text-base-900 dark:text-base-100 hover:border-accent-400' 32 + ]} 33 + onclick={() => (item.cardData.layout = opt.value)} 34 + > 35 + {opt.label} 36 + </button> 37 + {/each} 38 + </div> 39 + </div> 40 + 41 + <div class="flex flex-col gap-2"> 42 + <Label>Shape</Label> 43 + <div class="flex gap-2"> 44 + {#each shapeOptions as opt (opt.value)} 45 + <button 46 + class={[ 47 + 'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors', 48 + shape === opt.value 49 + ? 'bg-accent-500 border-accent-500 text-white' 50 + : 'bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 text-base-900 dark:text-base-100 hover:border-accent-400' 51 + ]} 52 + onclick={() => (item.cardData.shape = opt.value)} 53 + > 54 + {opt.label} 55 + </button> 56 + {/each} 57 + </div> 31 58 </div> 32 59 </div>