tangled
alpha
login
or
join now
flo-bit.dev
/
blento
your personal website on atproto - mirror
blento.app
20
fork
atom
overview
issues
pulls
pipelines
commit
Florian
1 week ago
f0db04ac
bcc90181
+79
-40
2 changed files
expand all
collapse all
unified
split
src
lib
cards
GitHubContributorsCard
GitHubContributorsCard.svelte
index.ts
+64
-40
src/lib/cards/GitHubContributorsCard/GitHubContributorsCard.svelte
···
44
);
45
if (response.ok) {
46
const data = await response.json();
47
-
clientContributors = [
48
-
...data,
49
-
...data.map((v) => {
50
-
return { ...v, username: v.username + '1' };
51
-
})
52
-
];
53
}
54
} catch (error) {
55
console.error('Failed to fetch GitHub contributors:', error);
···
65
const MIN_SIZE = 16;
66
const MAX_SIZE = 80;
67
0
0
0
0
0
0
0
0
0
0
0
0
68
let computedSize = $derived.by(() => {
69
if (!containerWidth || !containerHeight || totalItems === 0) return 40;
70
···
73
74
while (lo <= hi) {
75
const mid = Math.floor((lo + hi) / 2);
76
-
// Reserve ~1 avatar of padding on each side
77
-
const availW = containerWidth - mid * 2;
78
-
const availH = containerHeight - mid * 2;
79
if (availW <= 0 || availH <= 0) {
80
hi = mid - 1;
81
continue;
82
}
83
-
const cols = Math.floor((availW + GAP) / (mid + GAP));
84
-
const rows = Math.floor((availH + GAP) / (mid + GAP));
85
-
if (cols > 0 && rows > 0 && cols * rows >= totalItems) {
86
lo = mid + 1;
87
} else {
88
hi = mid - 1;
···
94
95
let padding = $derived(computedSize / 2);
96
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
97
let textSize = $derived(
98
computedSize < 24 ? 'text-[10px]' : computedSize < 40 ? 'text-xs' : 'text-sm'
99
);
···
112
{/if}
113
{:else if totalItems > 0}
114
<div style="padding: {padding}px;">
115
-
<div class="flex flex-wrap items-center justify-center" style="gap: {GAP}px;">
116
-
{#each namedContributors as contributor (contributor.username)}
117
-
<div class="relative">
118
-
<a
119
-
href="https://github.com/{contributor.username}"
120
-
target="_blank"
121
-
rel="noopener noreferrer"
122
-
class="accent:ring-accent-500 relative block rounded-full ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900"
123
-
>
124
-
{#if contributor.avatarUrl}
125
-
<img
126
-
src={contributor.avatarUrl}
127
-
alt={contributor.username}
128
-
class="rounded-full object-cover"
129
-
style="width: {computedSize}px; height: {computedSize}px;"
130
-
/>
131
-
{:else}
132
-
<div
133
-
class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center rounded-full"
134
-
style="width: {computedSize}px; height: {computedSize}px;"
135
-
>
136
-
<span
137
-
class="text-base-500 dark:text-base-400 accent:text-accent-100 {textSize} font-medium"
138
>
139
-
{contributor.username.charAt(0).toUpperCase()}
140
-
</span>
141
-
</div>
142
-
{/if}
143
-
</a>
0
0
0
0
144
</div>
145
{/each}
146
</div>
···
44
);
45
if (response.ok) {
46
const data = await response.json();
47
+
clientContributors = data;
0
0
0
0
0
48
}
49
} catch (error) {
50
console.error('Failed to fetch GitHub contributors:', error);
···
60
const MIN_SIZE = 16;
61
const MAX_SIZE = 80;
62
63
+
function hexCapacity(size: number, availW: number, availH: number): number {
64
+
const colsWide = Math.floor((availW + GAP) / (size + GAP));
65
+
if (colsWide < 1) return 0;
66
+
const colsNarrow = Math.max(1, colsWide - 1);
67
+
const maxRows = Math.floor((availH + GAP) / (size + GAP));
68
+
let capacity = 0;
69
+
for (let r = 0; r < maxRows; r++) {
70
+
capacity += r % 2 === 0 ? colsWide : colsNarrow;
71
+
}
72
+
return capacity;
73
+
}
74
+
75
let computedSize = $derived.by(() => {
76
if (!containerWidth || !containerHeight || totalItems === 0) return 40;
77
···
80
81
while (lo <= hi) {
82
const mid = Math.floor((lo + hi) / 2);
83
+
const availW = containerWidth - mid;
84
+
const availH = containerHeight - mid;
0
85
if (availW <= 0 || availH <= 0) {
86
hi = mid - 1;
87
continue;
88
}
89
+
if (hexCapacity(mid, availW, availH) >= totalItems) {
0
0
90
lo = mid + 1;
91
} else {
92
hi = mid - 1;
···
98
99
let padding = $derived(computedSize / 2);
100
101
+
let rows = $derived.by(() => {
102
+
const availW = containerWidth - computedSize;
103
+
if (availW <= 0) return [] as GitHubContributor[][];
104
+
const colsWide = Math.floor((availW + GAP) / (computedSize + GAP));
105
+
const colsNarrow = Math.max(1, colsWide - 1);
106
+
107
+
const result: GitHubContributor[][] = [];
108
+
let idx = 0;
109
+
let rowNum = 0;
110
+
while (idx < namedContributors.length) {
111
+
const cols = rowNum % 2 === 0 ? colsWide : colsNarrow;
112
+
result.push(namedContributors.slice(idx, idx + cols));
113
+
idx += cols;
114
+
rowNum++;
115
+
}
116
+
return result;
117
+
});
118
+
119
let textSize = $derived(
120
computedSize < 24 ? 'text-[10px]' : computedSize < 40 ? 'text-xs' : 'text-sm'
121
);
···
134
{/if}
135
{:else if totalItems > 0}
136
<div style="padding: {padding}px;">
137
+
<div class="flex flex-col items-center" style="gap: {GAP}px;">
138
+
{#each rows as row, rowIdx (rowIdx)}
139
+
<div class="flex justify-center" style="gap: {GAP}px;">
140
+
{#each row as contributor (contributor.username)}
141
+
<a
142
+
href="https://github.com/{contributor.username}"
143
+
target="_blank"
144
+
rel="noopener noreferrer"
145
+
class="accent:ring-accent-500 block rounded-full ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900"
146
+
>
147
+
{#if contributor.avatarUrl}
148
+
<img
149
+
src={contributor.avatarUrl}
150
+
alt={contributor.username}
151
+
class="rounded-full object-cover"
152
+
style="width: {computedSize}px; height: {computedSize}px;"
153
+
/>
154
+
{:else}
155
+
<div
156
+
class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center rounded-full"
157
+
style="width: {computedSize}px; height: {computedSize}px;"
0
0
158
>
159
+
<span
160
+
class="text-base-500 dark:text-base-400 accent:text-accent-100 {textSize} font-medium"
161
+
>
162
+
{contributor.username.charAt(0).toUpperCase()}
163
+
</span>
164
+
</div>
165
+
{/if}
166
+
</a>
167
+
{/each}
168
</div>
169
{/each}
170
</div>
+15
src/lib/cards/GitHubContributorsCard/index.ts
···
43
}
44
return contributorsData;
45
},
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
46
allowSetColor: true,
47
defaultColor: 'base',
48
minW: 2,
···
43
}
44
return contributorsData;
45
},
46
+
onUrlHandler: (url, item) => {
47
+
const match = url.match(/github\.com\/([^/]+)\/([^/]+)/);
48
+
if (!match) return null;
49
+
50
+
item.cardData.owner = match[1];
51
+
item.cardData.repo = match[2];
52
+
53
+
item.w = 4;
54
+
item.h = 2;
55
+
item.mobileW = 8;
56
+
item.mobileH = 4;
57
+
58
+
return item;
59
+
},
60
+
urlHandlerPriority: 1,
61
allowSetColor: true,
62
defaultColor: 'base',
63
minW: 2,