@props([ 'series' => [], // [['label' => 'Cash', 'value' => 12000], …] — max 4 slices 'formatter' => null, 'centerLabel' => null, 'centerValue' => null, ]) @php /* * Share-of-total, capped at four slices. * * A fifth hue would have to be generated, which the palette forbids, so * anything past the top four folds into "Other". Every slice is also * labelled in the legend, so identity never depends on colour. */ $rows = collect($series)->sortByDesc('value')->values(); if ($rows->count() > 4) { $head = $rows->take(3); $otherTotal = $rows->skip(3)->sum('value'); $rows = $head->push(['label' => 'Other', 'value' => $otherTotal])->values(); } $total = (float) $rows->sum('value'); $format = $formatter ?? fn ($value) => number_format((float) $value); $size = 160; $radius = 62; $stroke = 22; $circumference = 2 * M_PI * $radius; $offset = 0; $segments = []; foreach ($rows as $index => $row) { $fraction = $total > 0 ? (float) $row['value'] / $total : 0; $segments[] = [ 'label' => $row['label'], 'value' => (float) $row['value'], 'percent' => round($fraction * 100, 1), 'slot' => ($index % 4) + 1, // A 2px surface gap between adjacent arcs. 'dash' => max(0, $circumference * $fraction - 2).' '.$circumference, 'offset' => -$offset, ]; $offset += $circumference * $fraction; } @endphp
@if ($total <= 0)
No data for this period.
@else
@foreach ($segments as $segment) {{ $segment['label'] }}: {{ $format($segment['value']) }} ({{ $segment['percent'] }}%) @endforeach @if ($centerValue) {{ $centerValue }} {{ $centerLabel }} @endif {{-- Legend: always present, always labelled. --}}
    @foreach ($segments as $segment)
  • {{ $segment['label'] }} {{ $format($segment['value']) }} {{ $segment['percent'] }}%
  • @endforeach
@endif