tangled
alpha
login
or
join now
flo-bit.dev
/
blento
21
fork
atom
your personal website on atproto - mirror
blento.app
21
fork
atom
overview
issues
pulls
pipelines
like this
jycouet
1 month ago
510a5ac3
04eb63a7
+61
-31
2 changed files
expand all
collapse all
unified
split
src
lib
cards
GitHubContributorsCard
GitHubContributorsCard.svelte
GitHubContributorsCardSettings.svelte
+15
-12
src/lib/cards/GitHubContributorsCard/GitHubContributorsCard.svelte
···
13
let owner: string = $derived(item.cardData.owner ?? '');
14
let repo: string = $derived(item.cardData.repo ?? '');
15
let repoKey: string = $derived(owner && repo ? `${owner}/${repo}` : '');
16
-
let layout: 'hex' | 'grid' = $derived(item.cardData.layout ?? 'hex');
0
17
18
let serverContributors: GitHubContributor[] = $derived.by(() => {
19
if (!repoKey) return [];
···
61
const MIN_SIZE = 16;
62
const MAX_SIZE = 120;
63
64
-
function hexCapacity(size: number, availW: number, availH: number): number {
65
const colsWide = Math.floor((availW + GAP) / (size + GAP));
66
if (colsWide < 1) return 0;
67
const colsNarrow = Math.max(1, colsWide - 1);
···
85
86
let lo = MIN_SIZE;
87
let hi = MAX_SIZE;
88
-
const capacityFn = layout === 'hex' ? hexCapacity : gridCapacity;
89
90
while (lo <= hi) {
91
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);
94
if (availW <= 0 || availH <= 0) {
95
hi = mid - 1;
96
continue;
···
105
return Math.max(MIN_SIZE, hi);
106
});
107
108
-
let padding = $derived(layout === 'hex' ? computedSize / 4 : 0);
109
110
let rows = $derived.by(() => {
111
-
const availW = containerWidth - (layout === 'hex' ? computedSize / 4 : 0);
112
if (availW <= 0) return [] as GitHubContributor[][];
113
114
const colsWide = Math.floor((availW + GAP) / (computedSize + GAP));
115
-
const colsNarrow = layout === 'hex' ? Math.max(1, colsWide - 1) : colsWide;
116
117
// Calculate row sizes from bottom up, then reverse for incomplete row at top
118
const rowSizes: number[] = [];
119
let remaining = namedContributors.length;
120
let rowNum = 0;
121
while (remaining > 0) {
122
-
const cols = layout === 'hex' && rowNum % 2 === 0 ? colsNarrow : colsWide;
123
rowSizes.push(Math.min(cols, remaining));
124
remaining -= cols;
125
rowNum++;
···
139
let textSize = $derived(
140
computedSize < 24 ? 'text-[10px]' : computedSize < 40 ? 'text-xs' : 'text-sm'
141
);
0
0
142
</script>
143
144
<div
···
162
href="https://github.com/{contributor.username}"
163
target="_blank"
164
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"
166
>
167
{#if contributor.avatarUrl}
168
<img
169
src={contributor.avatarUrl}
170
alt={contributor.username}
171
-
class="rounded-full object-cover"
172
style="width: {computedSize}px; height: {computedSize}px;"
173
/>
174
{:else}
175
<div
176
-
class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center rounded-full"
177
style="width: {computedSize}px; height: {computedSize}px;"
178
>
179
<span
···
13
let owner: string = $derived(item.cardData.owner ?? '');
14
let repo: string = $derived(item.cardData.repo ?? '');
15
let repoKey: string = $derived(owner && repo ? `${owner}/${repo}` : '');
16
+
let layout: 'grid' | 'cinema' = $derived(item.cardData.layout ?? 'grid');
17
+
let shape: 'square' | 'circle' = $derived(item.cardData.shape ?? 'square');
18
19
let serverContributors: GitHubContributor[] = $derived.by(() => {
20
if (!repoKey) return [];
···
62
const MIN_SIZE = 16;
63
const MAX_SIZE = 120;
64
65
+
function cinemaCapacity(size: number, availW: number, availH: number): number {
66
const colsWide = Math.floor((availW + GAP) / (size + GAP));
67
if (colsWide < 1) return 0;
68
const colsNarrow = Math.max(1, colsWide - 1);
···
86
87
let lo = MIN_SIZE;
88
let hi = MAX_SIZE;
89
+
const capacityFn = layout === 'cinema' ? cinemaCapacity : gridCapacity;
90
91
while (lo <= hi) {
92
const mid = Math.floor((lo + hi) / 2);
93
+
const availW = containerWidth - (layout === 'cinema' ? mid / 2 : 0);
94
+
const availH = containerHeight - (layout === 'cinema' ? mid / 2 : 0);
95
if (availW <= 0 || availH <= 0) {
96
hi = mid - 1;
97
continue;
···
106
return Math.max(MIN_SIZE, hi);
107
});
108
109
+
let padding = $derived(layout === 'cinema' ? computedSize / 4 : 0);
110
111
let rows = $derived.by(() => {
112
+
const availW = containerWidth - (layout === 'cinema' ? computedSize / 4 : 0);
113
if (availW <= 0) return [] as GitHubContributor[][];
114
115
const colsWide = Math.floor((availW + GAP) / (computedSize + GAP));
116
+
const colsNarrow = layout === 'cinema' ? Math.max(1, colsWide - 1) : colsWide;
117
118
// Calculate row sizes from bottom up, then reverse for incomplete row at top
119
const rowSizes: number[] = [];
120
let remaining = namedContributors.length;
121
let rowNum = 0;
122
while (remaining > 0) {
123
+
const cols = layout === 'cinema' && rowNum % 2 === 0 ? colsNarrow : colsWide;
124
rowSizes.push(Math.min(cols, remaining));
125
remaining -= cols;
126
rowNum++;
···
140
let textSize = $derived(
141
computedSize < 24 ? 'text-[10px]' : computedSize < 40 ? 'text-xs' : 'text-sm'
142
);
143
+
144
+
let shapeClass = $derived(shape === 'circle' ? 'rounded-full' : 'rounded-lg');
145
</script>
146
147
<div
···
165
href="https://github.com/{contributor.username}"
166
target="_blank"
167
rel="noopener noreferrer"
168
+
class="accent:ring-accent-500 block {shapeClass} ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900"
169
>
170
{#if contributor.avatarUrl}
171
<img
172
src={contributor.avatarUrl}
173
alt={contributor.username}
174
+
class="{shapeClass} object-cover"
175
style="width: {computedSize}px; height: {computedSize}px;"
176
/>
177
{:else}
178
<div
179
+
class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center {shapeClass}"
180
style="width: {computedSize}px; height: {computedSize}px;"
181
>
182
<span
+46
-19
src/lib/cards/GitHubContributorsCard/GitHubContributorsCardSettings.svelte
···
5
let { item = $bindable() }: SettingsComponentProps = $props();
6
7
const layoutOptions = [
8
-
{ value: 'hex', label: 'Hexagon' },
9
-
{ value: 'grid', label: 'Grid' }
0
0
0
0
0
10
];
11
12
-
let layout = $derived(item.cardData.layout ?? 'hex');
0
13
</script>
14
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}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
31
</div>
32
</div>
···
5
let { item = $bindable() }: SettingsComponentProps = $props();
6
7
const layoutOptions = [
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' }
15
];
16
17
+
let layout = $derived(item.cardData.layout ?? 'grid');
18
+
let shape = $derived(item.cardData.shape ?? 'square');
19
</script>
20
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>
58
</div>
59
</div>