import { useEffect, useRef, useState } from 'react'; import { Animated, Easing, Text, View } from 'react-native'; import Svg, { Circle, Path } from 'react-native-svg'; import { useColors } from '../../lib/theme'; const SIZE = 88; const STROKE = 11; const R = (SIZE - STROKE) / 2; /** Geräte-Kategorie-Farben — auch im Gesamt-Ring (Verteilung) verwendet. */ export const MOBILE_COLOR = '#22c55e'; export const DESKTOP_COLOR = '#2563eb'; export type SlotSegment = { value: number; color: string }; function polar(cx: number, cy: number, r: number, deg: number) { const rad = (deg * Math.PI) / 180; return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) }; } function arcPath(cx: number, cy: number, r: number, startDeg: number, endDeg: number) { const start = polar(cx, cy, r, startDeg); const end = polar(cx, cy, r, endDeg); const large = endDeg - startDeg > 180 ? 1 : 0; return `M ${start.x} ${start.y} A ${r} ${r} 0 ${large} 1 ${end.x} ${end.y}`; } /** * Voller Progress-Ring mit Mehr-Segment-Support (react-native-svg, keine Lib). * - Einzel-Kategorie: ein Segment (Mobil/Computer). * - Gesamt: zwei Segmente (Mobil-/Computer-Anteil farblich getrennt). * Animiert über sweep der Bögen. */ export function DeviceSlotDonut({ segments, total, label, atLimit, }: { segments: SlotSegment[]; total: number; label: string; atLimit: boolean; }) { const colors = useColors(); const cx = SIZE / 2; const cy = SIZE / 2; const r = R; const safeTotal = Math.max(1, total); const used = segments.reduce((s, x) => s + x.value, 0); const anim = useRef(new Animated.Value(0)).current; const [progress, setProgress] = useState(0); useEffect(() => { anim.setValue(0); const l = anim.addListener(({ value }) => setProgress(value)); Animated.timing(anim, { toValue: 1, duration: 1400, easing: Easing.out(Easing.cubic), useNativeDriver: false, }).start(); return () => anim.removeListener(l); }, [used, total, anim]); // Konsekutive Bögen, Start oben (-90°). let cum = -90; const arcs = segments.map((seg) => { const span = 360 * (seg.value / safeTotal); const start = cum; const end = cum + span; cum = end; return { start, end, color: seg.color }; }); return ( {arcs.map((a, i) => { const animEnd = a.start + (a.end - a.start) * progress; if (animEnd <= a.start + 0.5) return null; // Voll-Kreis vermeiden (start==end wäre degeneriert). const drawEnd = Math.min(animEnd, a.start + 359.99); return ( ); })} {used} /{total} {label} ); }