Spaces:
Runtime error
Runtime error
File size: 3,815 Bytes
3dd5c9a | 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | @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" role="img" aria-label="Task distribution pie chart">
@foreach (var slice in Slices)
{
if (slice.IsFullCircle)
{
<circle cx="95" cy="95" r="72" fill="@slice.Color" stroke="var(--surface)" stroke-width="2" />
}
else
{
<path d="@slice.Path" fill="@slice.Color" stroke="var(--surface)" stroke-width="2" stroke-linejoin="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">@AppLocalization.Number(item.Value, 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; } = AppLocalization.Text("report.noChartData", "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 center = 95;
const double radius = 72;
var total = Items.Sum(item => Math.Max(0, item.Value));
var startAngle = -90.0;
var slices = new List<Slice>();
foreach (var item in Items)
{
var value = Math.Max(0, item.Value);
if (value <= 0 || total <= 0)
{
continue;
}
var sweep = 360 * value / total;
var endAngle = startAngle + sweep;
if (sweep >= 359.999)
{
slices.Add(new Slice(item.Color, string.Empty, true));
startAngle = endAngle;
continue;
}
var start = PointOnCircle(center, center, radius, startAngle);
var end = PointOnCircle(center, center, radius, endAngle);
var largeArc = sweep > 180 ? 1 : 0;
var path = $"M {center:0.##} {center:0.##} L {start.X:0.##} {start.Y:0.##} A {radius:0.##} {radius:0.##} 0 {largeArc} 1 {end.X:0.##} {end.Y:0.##} Z";
slices.Add(new Slice(item.Color, path, false));
startAngle = endAngle;
}
return slices;
}
private static (double X, double Y) PointOnCircle(double centerX, double centerY, double radius, double angleDegrees)
{
var radians = angleDegrees * Math.PI / 180;
return (centerX + radius * Math.Cos(radians), centerY + radius * Math.Sin(radians));
}
private sealed record Slice(string Color, string Path, bool IsFullCircle);
}
|