tangled
alpha
login
or
join now
flo-bit.dev
/
blento
22
fork
atom
your personal website on atproto - mirror
blento.app
22
fork
atom
overview
issues
1
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
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
16
-
let layout: 'hex' | 'grid' = $derived(item.cardData.layout ?? 'hex');
16
16
+
let layout: 'grid' | 'cinema' = $derived(item.cardData.layout ?? 'grid');
17
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
64
-
function hexCapacity(size: number, availW: number, availH: number): number {
65
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
88
-
const capacityFn = layout === 'hex' ? hexCapacity : gridCapacity;
89
89
+
const capacityFn = layout === 'cinema' ? cinemaCapacity : gridCapacity;
89
90
90
91
while (lo <= hi) {
91
92
const mid = Math.floor((lo + hi) / 2);
92
92
-
const availW = containerWidth - (layout === 'hex' ? mid / 2 : 0);
93
93
-
const availH = containerHeight - (layout === 'hex' ? mid / 2 : 0);
93
93
+
const availW = containerWidth - (layout === 'cinema' ? mid / 2 : 0);
94
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
108
-
let padding = $derived(layout === 'hex' ? computedSize / 4 : 0);
109
109
+
let padding = $derived(layout === 'cinema' ? computedSize / 4 : 0);
109
110
110
111
let rows = $derived.by(() => {
111
111
-
const availW = containerWidth - (layout === 'hex' ? computedSize / 4 : 0);
112
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
115
-
const colsNarrow = layout === 'hex' ? Math.max(1, colsWide - 1) : colsWide;
116
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
122
-
const cols = layout === 'hex' && rowNum % 2 === 0 ? colsNarrow : colsWide;
123
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
143
+
144
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
165
-
class="accent:ring-accent-500 block rounded-full ring-2 ring-white transition-transform hover:scale-110 dark:ring-neutral-900"
168
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
171
-
class="rounded-full object-cover"
174
174
+
class="{shapeClass} object-cover"
172
175
style="width: {computedSize}px; height: {computedSize}px;"
173
176
/>
174
177
{:else}
175
178
<div
176
176
-
class="bg-base-200 dark:bg-base-700 accent:bg-accent-400 flex items-center justify-center rounded-full"
179
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
8
-
{ value: 'hex', label: 'Hexagon' },
9
9
-
{ value: 'grid', label: 'Grid' }
8
8
+
{ value: 'grid', label: 'Grid' },
9
9
+
{ value: 'cinema', label: 'Cinema' }
10
10
+
];
11
11
+
12
12
+
const shapeOptions = [
13
13
+
{ value: 'square', label: 'Square' },
14
14
+
{ value: 'circle', label: 'Circle' }
10
15
];
11
16
12
12
-
let layout = $derived(item.cardData.layout ?? 'hex');
17
17
+
let layout = $derived(item.cardData.layout ?? 'grid');
18
18
+
let shape = $derived(item.cardData.shape ?? 'square');
13
19
</script>
14
20
15
15
-
<div class="flex flex-col gap-2">
16
16
-
<Label>Layout</Label>
17
17
-
<div class="flex gap-2">
18
18
-
{#each layoutOptions as opt (opt.value)}
19
19
-
<button
20
20
-
class={[
21
21
-
'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors',
22
22
-
layout === opt.value
23
23
-
? 'bg-accent-500 border-accent-500 text-white'
24
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
25
-
]}
26
26
-
onclick={() => (item.cardData.layout = opt.value)}
27
27
-
>
28
28
-
{opt.label}
29
29
-
</button>
30
30
-
{/each}
21
21
+
<div class="flex flex-col gap-4">
22
22
+
<div class="flex flex-col gap-2">
23
23
+
<Label>Layout</Label>
24
24
+
<div class="flex gap-2">
25
25
+
{#each layoutOptions as opt (opt.value)}
26
26
+
<button
27
27
+
class={[
28
28
+
'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors',
29
29
+
layout === opt.value
30
30
+
? 'bg-accent-500 border-accent-500 text-white'
31
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
32
+
]}
33
33
+
onclick={() => (item.cardData.layout = opt.value)}
34
34
+
>
35
35
+
{opt.label}
36
36
+
</button>
37
37
+
{/each}
38
38
+
</div>
39
39
+
</div>
40
40
+
41
41
+
<div class="flex flex-col gap-2">
42
42
+
<Label>Shape</Label>
43
43
+
<div class="flex gap-2">
44
44
+
{#each shapeOptions as opt (opt.value)}
45
45
+
<button
46
46
+
class={[
47
47
+
'flex-1 rounded-xl border px-3 py-2 text-sm transition-colors',
48
48
+
shape === opt.value
49
49
+
? 'bg-accent-500 border-accent-500 text-white'
50
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
51
+
]}
52
52
+
onclick={() => (item.cardData.shape = opt.value)}
53
53
+
>
54
54
+
{opt.label}
55
55
+
</button>
56
56
+
{/each}
57
57
+
</div>
31
58
</div>
32
59
</div>