159 lines
3.6 KiB
Svelte
159 lines
3.6 KiB
Svelte
<script lang="ts">
|
|
import type { BtopData, ContributionDay } from '#lib/btop/types';
|
|
import BtopBox from './BtopBox.svelte';
|
|
|
|
let { data }: { data: BtopData | null } = $props();
|
|
|
|
const levelOpacity = [1, 0.35, 0.6, 0.8, 1];
|
|
|
|
const calendar = $derived(data?.contributions ?? null);
|
|
|
|
function dayBackground({ githubCount, giteaCount }: ContributionDay): string | undefined {
|
|
if (githubCount > 0 && giteaCount > 0) return 'linear-gradient(135deg, var(--green) 50%, var(--orange) 50%)';
|
|
if (giteaCount > 0) return 'var(--orange)';
|
|
if (githubCount > 0) return 'var(--green)';
|
|
return undefined;
|
|
}
|
|
|
|
function dayTitle(day: ContributionDay): string {
|
|
return `${day.count} contributions on ${day.date} (GitHub ${day.githubCount}, Gitea ${day.giteaCount})`;
|
|
}
|
|
</script>
|
|
|
|
<BtopBox title="net · commits" number={3} color="blue">
|
|
{#if calendar && data}
|
|
<div class="stats">
|
|
<span><b class="value">{calendar.total.toLocaleString('en-US')}</b> contributions in the last year</span>
|
|
<span class="muted">streak <b class="value">{calendar.currentStreak}d</b></span>
|
|
{#if calendar.bestDay}
|
|
<span class="muted">peak <b class="value">{calendar.bestDay.count}</b> on {calendar.bestDay.date}</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="heatmap" aria-label="Contribution graph">
|
|
{#each calendar.weeks as week, weekIndex (weekIndex)}
|
|
<span class="week">
|
|
{#each week as day (day.date)}
|
|
<span
|
|
class="day"
|
|
class:empty={day.count === 0}
|
|
style:background={dayBackground(day)}
|
|
style:opacity={levelOpacity[day.level]}
|
|
title={dayTitle(day)}
|
|
></span>
|
|
{/each}
|
|
</span>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="legend">
|
|
<a class="source" href={data.github.profileUrl} target="_blank" rel="noopener noreferrer">
|
|
<span class="swatch github"></span>GitHub <b>{calendar.githubTotal.toLocaleString('en-US')}</b>
|
|
</a>
|
|
{#if calendar.giteaProfileUrl}
|
|
<a class="source" href={calendar.giteaProfileUrl} target="_blank" rel="noopener noreferrer">
|
|
<span class="swatch gitea"></span>Gitea <b>{calendar.giteaTotal.toLocaleString('en-US')}</b>
|
|
</a>
|
|
{/if}
|
|
<span class="scale muted">
|
|
less
|
|
{#each levelOpacity as opacity, level (level)}
|
|
<span class="swatch" class:empty={level === 0} style:opacity></span>
|
|
{/each}
|
|
more
|
|
</span>
|
|
</div>
|
|
{:else}
|
|
<p class="muted loading">connecting to github...</p>
|
|
{/if}
|
|
</BtopBox>
|
|
|
|
<style>
|
|
.stats {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.2rem 1rem;
|
|
margin-bottom: 0.6rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.value {
|
|
color: var(--blue);
|
|
}
|
|
|
|
.heatmap {
|
|
display: grid;
|
|
grid-auto-columns: minmax(0, 1fr);
|
|
grid-auto-flow: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.week {
|
|
display: grid;
|
|
grid-template-rows: repeat(7, auto);
|
|
gap: 2px;
|
|
}
|
|
|
|
.day {
|
|
display: block;
|
|
aspect-ratio: 1;
|
|
min-width: 4px;
|
|
border-radius: 1px;
|
|
}
|
|
|
|
.empty {
|
|
background: color-mix(in srgb, var(--foreground) 10%, transparent);
|
|
}
|
|
|
|
.legend {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 0.3rem 0.9rem;
|
|
margin-top: 0.5rem;
|
|
font-size: 0.72rem;
|
|
}
|
|
|
|
.source {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.source:hover {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.source b {
|
|
color: var(--blue);
|
|
}
|
|
|
|
.scale {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.swatch {
|
|
display: inline-block;
|
|
width: 9px;
|
|
height: 9px;
|
|
border-radius: 1px;
|
|
background: var(--green);
|
|
}
|
|
|
|
.swatch.gitea {
|
|
background: var(--orange);
|
|
}
|
|
|
|
.swatch.empty {
|
|
background: color-mix(in srgb, var(--foreground) 10%, transparent);
|
|
}
|
|
|
|
.loading {
|
|
margin: 0;
|
|
font-size: 0.8rem;
|
|
}
|
|
</style>
|