Files
homepage/src/lib/components/fitness/RingGraph.svelte
T
Alexander 9d8d1ec41f
CI / update (push) Successful in 3m28s
refactor: extract RingGraph, StatsRingGraph, MacroBreakdown components
Consolidate 6 duplicated instances of the SVG arc ring pattern into a
composable component hierarchy: RingGraph (base ring), StatsRingGraph
(ring with target markers), and MacroBreakdown (3 rings + kcal + detail
table). Removes ~400 lines of duplicated SVG/CSS from FoodSearch,
nutrition page, meals page, stats page, food detail page, and recipe
NutritionSummary.
2026-04-08 22:21:24 +02:00

96 lines
2.0 KiB
Svelte

<script module>
export const RADIUS = 28;
export const ARC_DEGREES = 300;
export const ARC_LENGTH = (ARC_DEGREES / 360) * 2 * Math.PI * RADIUS;
export const ARC_ROTATE = 120;
/** @param {number} percent */
export function strokeOffset(percent) {
return ARC_LENGTH - (Math.min(percent, 100) / 100) * ARC_LENGTH;
}
</script>
<script>
/**
* @type {{
* percent: number,
* color: string,
* label?: string,
* sublabel?: string,
* extra?: import('svelte').Snippet,
* }}
*/
let {
percent = 0,
color = 'currentColor',
label = '',
sublabel = '',
extra = undefined,
} = $props();
</script>
<div class="ring-wrap">
<svg class="ring-svg" viewBox="0 0 70 70">
<circle class="ring-bg" cx="35" cy="35" r={RADIUS}
stroke-dasharray="{ARC_LENGTH} {2 * Math.PI * RADIUS}"
transform="rotate({ARC_ROTATE} 35 35)" />
<circle class="ring-fill" cx="35" cy="35" r={RADIUS}
stroke-dasharray="{ARC_LENGTH} {2 * Math.PI * RADIUS}"
stroke-dashoffset={strokeOffset(percent)}
transform="rotate({ARC_ROTATE} 35 35)"
stroke={color} />
{@render extra?.()}
<text class="ring-text" x="35" y="35">{percent}%</text>
</svg>
{#if label}
<span class="ring-label">{label}</span>
{/if}
{#if sublabel}
<span class="ring-sublabel">{sublabel}</span>
{/if}
</div>
<style>
.ring-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.1rem;
flex: 1;
}
.ring-svg {
width: 72px;
height: 72px;
overflow: visible;
}
.ring-bg {
fill: none;
stroke: var(--color-border);
stroke-width: 5;
stroke-linecap: round;
}
.ring-fill {
fill: none;
stroke-width: 5;
stroke-linecap: round;
transition: stroke-dashoffset 0.4s ease;
}
.ring-text {
font-size: 14px;
font-weight: 700;
fill: currentColor;
text-anchor: middle;
dominant-baseline: central;
}
.ring-label {
font-size: 0.78rem;
font-weight: 600;
color: var(--color-text-primary);
text-align: center;
}
.ring-sublabel {
font-size: 0.72rem;
color: var(--color-text-tertiary);
}
</style>