Skip to content

Commit a29ddab

Browse files
committed
Add sparkline
1 parent 2ac9840 commit a29ddab

File tree

6 files changed

+92
-50
lines changed

6 files changed

+92
-50
lines changed

composer.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,24 @@
1515
}
1616
],
1717
"require": {
18-
"php": "^8.1"
18+
"php": "^8.1",
19+
"illuminate/collections": "^9.43",
20+
"ramsey/uuid": "^4.6",
21+
"spatie/period": "^2.3"
1922
},
2023
"require-dev": {
2124
"friendsofphp/php-cs-fixer": "^3.13",
25+
"larapack/dd": "^1.1",
2226
"phpunit/phpunit": "^9.5"
2327
},
2428
"autoload": {
2529
"psr-4": {
26-
"Brendt\\Sparkline\\": "src"
30+
"Brendt\\SparkLine\\": "src"
2731
}
2832
},
2933
"autoload-dev": {
3034
"psr-4": {
31-
"Brendt\\Sparkline\\Tests\\": "tests"
35+
"Brendt\\SparkLine\\Tests\\": "tests"
3236
}
3337
},
3438
"scripts": {

src/SparkLine.php

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
declare(strict_types=1);
44

5-
namespace App\SparkLine;
5+
namespace Brendt\SparkLine;
66

7+
use DateTimeImmutable;
78
use Illuminate\Support\Collection;
89
use Ramsey\Uuid\Uuid;
910
use Spatie\Period\Period;
@@ -26,23 +27,23 @@ public static function new(Collection $days): self
2627
public function __construct(Collection $days)
2728
{
2829
$this->days = $days
29-
->sortBy(fn (SparkLineDay $day) => $day->day->timestamp)
30-
->mapWithKeys(fn (SparkLineDay $day) => [$day->day->format('Y-m-d') => $day]);
30+
->sortBy(fn(SparkLineDay $day) => $day->day->getTimestamp())
31+
->mapWithKeys(fn(SparkLineDay $day) => [$day->day->format('Y-m-d') => $day]);
3132

3233
$this->maxValue = $this->resolveMaxValueFromDays();
3334
$this->maxItemAmount = $this->resolveMaxItemAmountFromDays();
3435
}
3536

3637
public function getTotal(): int
3738
{
38-
return $this->days->sum(fn (SparkLineDay $day) => $day->count) ?? 0;
39+
return $this->days->sum(fn(SparkLineDay $day) => $day->count) ?? 0;
3940
}
4041

4142
public function getPeriod(): Period
4243
{
4344
return Period::make(
44-
now()->subDays($this->maxItemAmount),
45-
now(),
45+
new DateTimeImmutable("-{$this->maxItemAmount} days"),
46+
new DateTimeImmutable('now'),
4647
);
4748
}
4849

@@ -94,14 +95,22 @@ public function withColors(string ...$colors): self
9495

9596
public function make(): string
9697
{
97-
return view('sparkLine', [
98-
'coordinates' => $this->resolveCoordinates(),
99-
'colors' => $this->resolveColors(),
100-
'width' => $this->width,
101-
'height' => $this->height,
102-
'strokeWidth' => $this->strokeWidth,
103-
'id' => Uuid::uuid4()->toString(),
104-
])->render();
98+
$coordinates = $this->resolveCoordinates();
99+
$colors = $this->resolveColors();
100+
$width = $this->width;
101+
$height = $this->height;
102+
$strokeWidth = $this->strokeWidth;
103+
$id = Uuid::uuid4()->toString();
104+
105+
ob_start();
106+
107+
include __DIR__ . '/sparkLine.view.php';
108+
109+
$svg = ob_get_contents();
110+
111+
ob_end_clean();
112+
113+
return $svg;
105114
}
106115

107116
public function __toString(): string
@@ -129,7 +138,7 @@ private function resolveMaxValueFromDays(): int
129138
}
130139

131140
return $this->days
132-
->sortByDesc(fn (SparkLineDay $day) => $day->count)
141+
->sortByDesc(fn(SparkLineDay $day) => $day->count)
133142
->first()
134143
->count;
135144
}
@@ -144,7 +153,7 @@ private function resolveCoordinates(): string
144153
$step = floor($this->width / $this->maxItemAmount);
145154

146155
return collect(range(0, $this->maxItemAmount))
147-
->map(fn (int $days) => now()->subDays($days)->format('Y-m-d'))
156+
->map(fn(int $days) => (new DateTimeImmutable("-{$days} days"))->format('Y-m-d'))
148157
->reverse()
149158
->mapWithKeys(function (string $key) {
150159
/** @var SparkLineDay|null $day */
@@ -157,7 +166,7 @@ private function resolveCoordinates(): string
157166
];
158167
})
159168
->values()
160-
->map(fn (int $count, int $index) => $index * $step . ',' . $count)
169+
->map(fn(int $count, int $index) => $index * $step . ',' . $count)
161170
->implode(' ');
162171
}
163172
}

src/SparkLineDay.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22

33
declare(strict_types=1);
44

5-
namespace App\SparkLine;
5+
namespace Brendt\SparkLine;
66

7-
use Carbon\Carbon;
7+
use DateTimeImmutable;
88

99
final class SparkLineDay
1010
{
1111
public function __construct(
1212
public readonly int $count,
13-
public readonly Carbon $day,
13+
public readonly DateTimeImmutable $day,
1414
) {
1515
}
1616

1717
public function rebase(int $base, int $max): self
1818
{
1919
return new self(
20-
count: $this->count * ($base / $max),
20+
count: (int) floor($this->count * ($base / $max)),
2121
day: $this->day,
2222
);
2323
}

src/sparkLine.blade.php

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/sparkLine.view.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<svg width="<?= $width ?>" height="<?= $height ?>">
2+
<defs>
3+
<linearGradient id="gradient-<?= $id ?>" x1="0" x2="0" y1="1" y2="0">
4+
<?php
5+
foreach ($colors as $percentage => $color) {
6+
echo <<<HTML
7+
<stop offset="{$percentage}%" stop-color="{$color}"></stop>
8+
HTML;
9+
10+
echo PHP_EOL;
11+
}
12+
?>
13+
</linearGradient>
14+
<mask id="sparkline-<?= $id ?>" x="0" y="0" width="<?= $width ?>" height="<?= $height - 2 ?>">
15+
<polyline
16+
transform="translate(0, <?= $height - 2 ?>) scale(1,-1)"
17+
points="<?= $coordinates ?>"
18+
fill="transparent"
19+
stroke="<?= $colors[0] ?>"
20+
stroke-width="<?= $strokeWidth ?>"
21+
>
22+
</polyline>
23+
</mask>
24+
</defs>
25+
26+
<g transform="translate(0, 0)">
27+
<rect x="0" y="0" width="<?= $width ?>" height="<?= $height ?>" style="stroke: none; fill: url(#gradient-<?= $id ?>); mask: url(#sparkline-<?= $id ?>)"></rect>
28+
</g>
29+
</svg>

tests/SparkLineTest.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,35 @@
22

33
declare(strict_types=1);
44

5-
namespace Brendt\Sparkline\Tests;
5+
namespace Brendt\SparkLine\Tests;
66

7-
final class SparkLineTest
7+
use Brendt\SparkLine\SparkLine;
8+
use Brendt\SparkLine\SparkLineDay;
9+
use DateTimeImmutable;
10+
use Illuminate\Support\Collection;
11+
use PHPUnit\Framework\TestCase;
12+
13+
final class SparkLineTest extends TestCase
814
{
15+
private function days(): Collection
16+
{
17+
return collect([
18+
new SparkLineDay(
19+
count: 1,
20+
day: new DateTimeImmutable('-2 days')
21+
),
22+
new SparkLineDay(
23+
count: 2,
24+
day: new DateTimeImmutable('-1 day')
25+
),
26+
]);
27+
}
28+
929
/** @test */
10-
public function it_generates_a_sparkline()
30+
public function it_creates_a_sparkline(): void
1131
{
32+
$sparkLine = SparkLine::new($this->days())->make();
33+
34+
$this->assertStringContainsString('<svg', $sparkLine);
1235
}
1336
}

0 commit comments

Comments
 (0)