File size: 2,692 Bytes
917b070
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@using TaskTrackingSystem.WebApp.Components.Shared.Charts

<div class="@WrapperClass">
    @if (!Items.Any() || Items.Sum(item => item.Value) <= 0)
    {
        <div class="flex h-56 items-center justify-center text-sm text-slate-400">@EmptyText</div>
    }
    else
    {
        <div class="flex justify-center">
            <svg viewBox="0 0 190 190" class="h-56 w-56 -rotate-90">
                <circle cx="95" cy="95" r="70" fill="none" stroke="#e2e8f0" stroke-width="24" />
                @foreach (var slice in Slices)
                {
                    <circle cx="95" cy="95" r="70" fill="none" stroke="@slice.Color" stroke-width="24" stroke-dasharray="@slice.DashArray" stroke-dashoffset="@slice.DashOffset" stroke-linecap="round" />
                }
            </svg>
        </div>

        @if (ShowLegend)
        {
            <div class="mt-3 space-y-3">
                @foreach (var item in Items)
                {
                    <div class="flex items-center justify-between rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3">
                        <div class="flex items-center gap-3">
                            <span class="h-3 w-3 rounded-full" style="background-color: @item.Color"></span>
                            <span class="text-sm text-slate-600">@item.Label</span>
                        </div>
                        <span class="text-sm font-semibold text-slate-900">@item.Value.ToString(ValueFormat)</span>
                    </div>
                }
            </div>
        }
    }
</div>

@code {
    [Parameter] public IReadOnlyList<ChartDataPoint> Items { get; set; } = Array.Empty<ChartDataPoint>();
    [Parameter] public bool ShowLegend { get; set; } = true;
    [Parameter] public string EmptyText { get; set; } = "No data available";
    [Parameter] public string ValueFormat { get; set; } = "0";
    [Parameter] public string WrapperClass { get; set; } = string.Empty;

    private IReadOnlyList<Slice> Slices => BuildSlices();

    private IReadOnlyList<Slice> BuildSlices()
    {
        const double radius = 70;
        var circumference = 2 * Math.PI * radius;
        var total = Items.Sum(item => Math.Max(0, item.Value));
        var offset = 0.0;
        var slices = new List<Slice>();

        foreach (var item in Items)
        {
            var value = Math.Max(0, item.Value);
            var arc = total > 0 ? circumference * value / total : 0;
            slices.Add(new Slice(item.Color, $"{arc:0.##} {(circumference - arc):0.##}", -offset));
            offset += arc;
        }

        return slices;
    }

    private sealed record Slice(string Color, string DashArray, double DashOffset);
}