Replace chart-js with backend rendering
Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
This commit is contained in:
parent
0b4782fcab
commit
1277f8f96f
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Renders a bargraph
|
||||
*
|
||||
* @param string $dom_id
|
||||
* @param string $key key name of the x-axis
|
||||
* @param array $row_names key names for the data rows
|
||||
* @param array $colors colors for the data rows
|
||||
* @param array $data the data
|
||||
* @return string
|
||||
*/
|
||||
function bargraph($dom_id, $key, $row_names, $colors, $data)
|
||||
{
|
||||
$labels = [];
|
||||
foreach ($data as $dataset) {
|
||||
$labels[] = $dataset[$key];
|
||||
}
|
||||
|
||||
$datasets = [];
|
||||
foreach ($row_names as $row_key => $name) {
|
||||
$values = [];
|
||||
foreach ($data as $dataset) {
|
||||
$values[] = $dataset[$row_key];
|
||||
}
|
||||
$datasets[] = [
|
||||
'label' => $name,
|
||||
'backgroundColor' => $colors[$row_key],
|
||||
'data' => $values
|
||||
];
|
||||
}
|
||||
|
||||
return '<canvas id="' . $dom_id . '" style="width: 100%; height: 300px;"></canvas>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
var ctx = $(\'#' . $dom_id . '\').get(0).getContext(\'2d\');
|
||||
var chart = new Chart(ctx, ' . json_encode([
|
||||
'type' => 'bar',
|
||||
'data' => [
|
||||
'labels' => $labels,
|
||||
'datasets' => $datasets
|
||||
]
|
||||
]) . ');
|
||||
});
|
||||
</script>';
|
||||
}
|
|
@ -55,7 +55,6 @@ $includeFiles = [
|
|||
__DIR__ . '/../includes/controller/user_driver_licenses_controller.php',
|
||||
__DIR__ . '/../includes/controller/user_worklog_controller.php',
|
||||
|
||||
__DIR__ . '/../includes/helper/graph_helper.php',
|
||||
__DIR__ . '/../includes/helper/legacy_helper.php',
|
||||
__DIR__ . '/../includes/helper/message_helper.php',
|
||||
__DIR__ . '/../includes/helper/email_helper.php',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Engelsystem\Helpers\BarChart;
|
||||
use Engelsystem\Models\User\User;
|
||||
|
||||
/**
|
||||
|
@ -193,8 +194,8 @@ function admin_arrive()
|
|||
], $users_matched),
|
||||
div('row', [
|
||||
div('col-md-4', [
|
||||
heading(__('Planned arrival statistics'), 2),
|
||||
bargraph('planned_arrives', 'day', [
|
||||
heading(__('Planned arrival statistics'), 3),
|
||||
BarChart::render([
|
||||
'count' => __('arrived'),
|
||||
'sum' => __('arrived sum')
|
||||
], [
|
||||
|
@ -208,8 +209,8 @@ function admin_arrive()
|
|||
], $planned_arrival_at_day)
|
||||
]),
|
||||
div('col-md-4', [
|
||||
heading(__('Arrival statistics'), 2),
|
||||
bargraph('arrives', 'day', [
|
||||
heading(__('Arrival statistics'), 3),
|
||||
BarChart::render([
|
||||
'count' => __('arrived'),
|
||||
'sum' => __('arrived sum')
|
||||
], [
|
||||
|
@ -223,8 +224,8 @@ function admin_arrive()
|
|||
], $arrival_at_day)
|
||||
]),
|
||||
div('col-md-4', [
|
||||
heading(__('Planned departure statistics'), 2),
|
||||
bargraph('planned_departures', 'day', [
|
||||
heading(__('Planned departure statistics'), 3),
|
||||
BarChart::render([
|
||||
'count' => __('arrived'),
|
||||
'sum' => __('arrived sum')
|
||||
], [
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.2",
|
||||
"bootstrap": "^5.1.0",
|
||||
"chart.js": "^2.9.3",
|
||||
"core-js": "^3.6.5",
|
||||
"jquery": "^3.5.1",
|
||||
"jquery-ui": "^1.13.0",
|
||||
|
|
|
@ -4,7 +4,6 @@ require('jquery-ui');
|
|||
window.bootstrap = require('bootstrap');
|
||||
window.moment = require('moment');
|
||||
require('moment/locale/de');
|
||||
require('chart.js');
|
||||
require('./forms');
|
||||
require('./sticky-headers');
|
||||
require('./moment-countdown');
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
.barchart {
|
||||
--barchart-padding-left: 50px;
|
||||
--barchart-bar-margin: 2%;
|
||||
--barchart-group-margin: 2%;
|
||||
--barchart-label-font-size: 14px;
|
||||
|
||||
height: 300px;
|
||||
margin-bottom: map-get($spacers, 3);
|
||||
margin-top: map-get($spacers, 4);
|
||||
position: relative;
|
||||
// enable printing backgrounds
|
||||
print-color-adjust: exact;
|
||||
|
||||
&-20 {
|
||||
// >= 20 bars
|
||||
--barchart-group-margin: .75%;
|
||||
}
|
||||
|
||||
&-40 {
|
||||
// >= 40 bars
|
||||
--barchart-bar-margin: 1px;
|
||||
--barchart-bar-margin: .5px;
|
||||
--barchart-group-margin: .5%;
|
||||
}
|
||||
|
||||
&-50 {
|
||||
// >= 50 bars
|
||||
--barchart-bar-margin: .5px;
|
||||
--barchart-group-margin: 0.2%;
|
||||
|
||||
.barchart-group:nth-child(2n) .barchart-xlabel {
|
||||
// hide every second label
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-graph-container {
|
||||
align-items: flex-end;
|
||||
display: flex;
|
||||
left: var(--barchart-padding-left);
|
||||
bottom: 75px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-group {
|
||||
align-items: flex-end;
|
||||
border-right: 1px solid $gray-400;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
padding-left: var(--barchart-group-margin);
|
||||
padding-right: var(--barchart-group-margin);
|
||||
position: relative;
|
||||
|
||||
&:first-of-type {
|
||||
border-left: 1px solid $gray-400;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-right: 1px solid $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
&-bar {
|
||||
flex-grow: 1;
|
||||
margin-left: var(--barchart-bar-margin);
|
||||
margin-right: var(--barchart-bar-margin);
|
||||
}
|
||||
|
||||
&-ygraph {
|
||||
background-color: $gray-400;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&-xlabel {
|
||||
bottom: 0;
|
||||
font-size: var(--barchart-label-font-size);
|
||||
left: 50%;
|
||||
line-height: 1;
|
||||
position: absolute;
|
||||
transform-origin: top right;
|
||||
transform: translateY(100%) translateX(-100%) rotate(-45deg);
|
||||
}
|
||||
|
||||
&-ylabel {
|
||||
font-size: var(--barchart-label-font-size);
|
||||
left: calc(-1 * var(--barchart-padding-left));
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
transform: translateY(50%);
|
||||
width: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.barchart-legend {
|
||||
margin-bottom: map-get($spacers, 5);
|
||||
padding-left: 50px;
|
||||
// enable printing backgrounds
|
||||
print-color-adjust: exact;
|
||||
|
||||
&-item {
|
||||
align-items: center;
|
||||
border-left-style: solid;
|
||||
border-left-width: 50px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
height: 20px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 8px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@ $form-label-font-weight: $font-weight-bold;
|
|||
@import "~select2/src/scss/core";
|
||||
@import "~select2-bootstrap-5-theme/src/include-all";
|
||||
@import "error";
|
||||
@import "barchart";
|
||||
|
||||
$navbar-height: 3.125rem;
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<div class="barchart {{ barChartClass }}">
|
||||
<div class="barchart-graph-container">
|
||||
{% for yLabel in yLabels %}
|
||||
<label class="barchart-ylabel" style="bottom: {{ yLabel.bottom }};">
|
||||
{{ yLabel.label }}
|
||||
</label>
|
||||
<hr class="barchart-ygraph" style="bottom: {{ yLabel.bottom }};" />
|
||||
{% endfor %}
|
||||
{% for group in groups %}
|
||||
<div class="barchart-group">
|
||||
{% for bar in group.bars %}
|
||||
<div
|
||||
class="barchart-bar"
|
||||
title="bar.title"
|
||||
style="height: {{ bar.height }}; background-color: {{ bar.bg }};"
|
||||
></div>
|
||||
{% endfor %}
|
||||
<div class="barchart-xlabel">{{ group.label }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="barchart-legend">
|
||||
{% for key, color in colors %}
|
||||
<div
|
||||
class="barchart-legend-item"
|
||||
style="border-left-color: {{ color }};"
|
||||
>
|
||||
{{ rowLabels[key] }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -374,6 +374,9 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Bar Chart</h3>
|
||||
{{ bar_chart | raw }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Engelsystem\Controllers;
|
||||
|
||||
use Engelsystem\Config\Config;
|
||||
use Engelsystem\Helpers\BarChart;
|
||||
use Engelsystem\Http\Response;
|
||||
use Engelsystem\Models\User\PersonalData;
|
||||
use Engelsystem\Models\User\State;
|
||||
|
@ -58,6 +59,7 @@ class DesignController extends BaseController
|
|||
'demo_user' => $demoUser,
|
||||
'demo_user_2' => $demoUser2,
|
||||
'themes' => $themes,
|
||||
'bar_chart' => BarChart::render(...BarChart::generateChartDemoData(23)),
|
||||
];
|
||||
|
||||
return $this->response->withView(
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Engelsystem\Helpers;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class BarChart
|
||||
{
|
||||
/**
|
||||
* Renders a bar chart using the "components/barchart" view.
|
||||
*
|
||||
* @param array<string> $rowLabels Map row key => row label
|
||||
* @param array<string, string> $colors Map row key => color
|
||||
* @param array<mixed, array> $data The chart data group key => [ row name => value ]
|
||||
*/
|
||||
public static function render(
|
||||
array $rowLabels,
|
||||
array $colors,
|
||||
array $data
|
||||
): string {
|
||||
$groupLabels = [];
|
||||
$max = 0;
|
||||
|
||||
foreach ($data as $groupKey => $groupData) {
|
||||
$date = DateTimeImmutable::createFromFormat('Y-m-d', $groupKey);
|
||||
$groupLabels[$groupKey] = $groupKey;
|
||||
|
||||
if ($date) {
|
||||
$groupLabels[$groupKey] = $date->format(__('Y-m-d'));
|
||||
}
|
||||
|
||||
foreach ($rowLabels as $rowKey => $rowName) {
|
||||
$max = max($max, $groupData[$rowKey]);
|
||||
}
|
||||
}
|
||||
|
||||
$roundedMax = (int) ceil($max / 5) * 5;
|
||||
$step = $roundedMax / 5;
|
||||
|
||||
return view('components/barchart', [
|
||||
'groups' => self::calculateChartGroups(
|
||||
$rowLabels,
|
||||
$colors,
|
||||
$data,
|
||||
$roundedMax,
|
||||
$groupLabels
|
||||
),
|
||||
'colors' => $colors,
|
||||
'rowLabels' => $rowLabels,
|
||||
'barChartClass' => self::calculateBarChartClass($rowLabels, $data),
|
||||
'yLabels' => self::calculateYLabels($roundedMax),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function calculateChartGroups(
|
||||
array $rowLabels,
|
||||
array $colors,
|
||||
array $data,
|
||||
int $max,
|
||||
array $groupLabels
|
||||
): array {
|
||||
$chartGroups = [];
|
||||
|
||||
foreach ($data as $groupKey => $groupData) {
|
||||
$group = [
|
||||
'label' => $groupLabels[$groupKey],
|
||||
'bars' => [],
|
||||
];
|
||||
|
||||
foreach ($rowLabels as $rowKey => $rowName) {
|
||||
$value = $groupData[$rowKey];
|
||||
$group['bars'][] = [
|
||||
'value' => $value,
|
||||
'title' => $group['label'] . "\n" . $rowName . ': ' . $value,
|
||||
'height' => ($value / $max * 100) . '%',
|
||||
'bg' => $colors[$rowKey],
|
||||
];
|
||||
}
|
||||
|
||||
$chartGroups[] = $group;
|
||||
}
|
||||
|
||||
return $chartGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $max Max Y value
|
||||
* @return array<array{label: string, bottom: string}>
|
||||
*/
|
||||
private static function calculateYLabels(int $max): array
|
||||
{
|
||||
$step = $max / 5;
|
||||
$yLabels = [];
|
||||
|
||||
for ($y = 0; $y <= $max; $y += $step) {
|
||||
$yLabels[] = [
|
||||
'label' => $y,
|
||||
'bottom' => ($y / $max * 100) . '%',
|
||||
];
|
||||
}
|
||||
|
||||
return $yLabels;
|
||||
}
|
||||
|
||||
private static function calculateBarChartClass(array $rowLabels, array $data): string
|
||||
{
|
||||
$bars = count($data) * count($rowLabels);
|
||||
|
||||
if ($bars >= 50) {
|
||||
return 'barchart-50';
|
||||
}
|
||||
|
||||
if ($bars >= 40) {
|
||||
return 'barchart-40';
|
||||
}
|
||||
|
||||
if ($bars >= 20) {
|
||||
return 'barchart-20';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates bar chart demo data.
|
||||
*
|
||||
* @param int $days Number of days to generate data for
|
||||
* @return array ready to be passed to BarChart::render
|
||||
*/
|
||||
public static function generateChartDemoData(int $days): array
|
||||
{
|
||||
$step = floor(10000 / $days + 1);
|
||||
$now = CarbonImmutable::now();
|
||||
$twoWeeksAgo = $now->subDays($days);
|
||||
$current = $twoWeeksAgo;
|
||||
|
||||
$demoData = [];
|
||||
$count = 1;
|
||||
|
||||
while ($current->isBefore($now)) {
|
||||
$current_key = $current->format('Y-m-d');
|
||||
$demoData[$current_key] = [
|
||||
'day' => $current_key,
|
||||
'count' => $step,
|
||||
'sum' => $step * $count,
|
||||
];
|
||||
$current = $current->addDay(1);
|
||||
$count++;
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'count' => __('arrived'),
|
||||
'sum' => __('arrived sum'),
|
||||
],
|
||||
[
|
||||
|
||||
'count' => '#090',
|
||||
'sum' => '#888'
|
||||
],
|
||||
$demoData,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Engelsystem\Test\Unit\Controllers;
|
||||
|
||||
use Engelsystem\Application;
|
||||
use Engelsystem\Config\Config;
|
||||
use Engelsystem\Controllers\DesignController;
|
||||
use Engelsystem\Http\Response;
|
||||
|
@ -10,6 +11,14 @@ use PHPUnit\Framework\MockObject\MockObject;
|
|||
|
||||
class DesignControllerTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->mockRenderer();
|
||||
$this->mockTranslator();
|
||||
Application::setInstance($this->app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\DesignController::__construct
|
||||
* @covers \Engelsystem\Controllers\DesignController::index
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Engelsystem\Test\Unit\Helpers;
|
||||
|
||||
use Engelsystem\Application;
|
||||
use Engelsystem\Helpers\BarChart;
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use Engelsystem\Test\Unit\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class BarChartTest extends TestCase
|
||||
{
|
||||
private const ROW_LABELS = [
|
||||
'a' => 'a label',
|
||||
'b' => 'b label',
|
||||
];
|
||||
|
||||
private const COLORS = [
|
||||
'a' => '#000',
|
||||
'b' => '#fff',
|
||||
];
|
||||
|
||||
private const DATA = [
|
||||
'2022-07-11' => [
|
||||
'day' => '2022-07-11',
|
||||
'a' => 1,
|
||||
'b' => 2,
|
||||
],
|
||||
];
|
||||
|
||||
/** @var Renderer&MockObject */
|
||||
private $rendererMock;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->rendererMock = $this->mockRenderer(false);
|
||||
$this->mockTranslator();
|
||||
Application::setInstance($this->app);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Application::setInstance(null);
|
||||
}
|
||||
|
||||
public function testRender(): void
|
||||
{
|
||||
$this->rendererMock->expects(self::once())
|
||||
->method('render')
|
||||
->with('components/barchart', [
|
||||
'groups' => [
|
||||
[
|
||||
'bars' => [
|
||||
[
|
||||
'value' => 1,
|
||||
'title' => "2022-07-11\na label: 1",
|
||||
'height' => '20%',
|
||||
'bg' => '#000',
|
||||
],
|
||||
[
|
||||
'value' => 2,
|
||||
'title' => "2022-07-11\nb label: 2",
|
||||
'height' => '40%',
|
||||
'bg' => '#fff',
|
||||
],
|
||||
],
|
||||
'label' => '2022-07-11',
|
||||
],
|
||||
],
|
||||
'colors' => self::COLORS,
|
||||
'rowLabels' => self::ROW_LABELS,
|
||||
'barChartClass' => '',
|
||||
'yLabels' => [
|
||||
[
|
||||
'label' => '0',
|
||||
'bottom' => '0%',
|
||||
],
|
||||
[
|
||||
'label' => '1',
|
||||
'bottom' => '20%',
|
||||
],
|
||||
[
|
||||
'label' => '2',
|
||||
'bottom' => '40%',
|
||||
],
|
||||
[
|
||||
'label' => '3',
|
||||
'bottom' => '60%',
|
||||
],
|
||||
[
|
||||
'label' => '4',
|
||||
'bottom' => '80%',
|
||||
],
|
||||
[
|
||||
'label' => '5',
|
||||
'bottom' => '100%',
|
||||
],
|
||||
],
|
||||
])
|
||||
->willReturn('test bar chart');
|
||||
self::assertSame(
|
||||
'test bar chart',
|
||||
BarChart::render(self::ROW_LABELS, self::COLORS, self::DATA)
|
||||
);
|
||||
}
|
||||
|
||||
public function provideBarChartClassTestData(): array
|
||||
{
|
||||
return [
|
||||
[10, 'barchart-20'], // number to be passed to BarChart::generateChartDemoData, expected class
|
||||
[20, 'barchart-40'],
|
||||
[25, 'barchart-50'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideBarChartClassTestData
|
||||
*/
|
||||
public function testRenderBarChartClass(int $testDataCount, string $expectedClass): void
|
||||
{
|
||||
$this->rendererMock->expects(self::once())
|
||||
->method('render')
|
||||
->willReturnCallback(
|
||||
function (string $template, array $data = []) use ($expectedClass) {
|
||||
self::assertSame($expectedClass, $data['barChartClass']);
|
||||
return '';
|
||||
}
|
||||
);
|
||||
BarChart::render(...BarChart::generateChartDemoData($testDataCount));
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Engelsystem\Test\Unit;
|
||||
|
||||
use Engelsystem\Application;
|
||||
use Engelsystem\Renderer\Renderer;
|
||||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
@ -56,4 +57,35 @@ abstract class TestCase extends PHPUnitTestCase
|
|||
$faker->addProvider(new FakerProvider($faker));
|
||||
$this->app->instance(Generator::class, $faker);
|
||||
}
|
||||
|
||||
protected function mockTranslator(): void
|
||||
{
|
||||
$translator = $this->getMockBuilder(Translator::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['translate'])
|
||||
->getMock();
|
||||
$translator->method('translate')
|
||||
->willReturnCallback(fn(string $key, array $replace = []) => $key);
|
||||
$this->app->instance('translator', $translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $mockImplementation Whether to mock the Renderer methods
|
||||
* @return Renderer&MockObject
|
||||
*/
|
||||
protected function mockRenderer(bool $mockImplementation = true): Renderer
|
||||
{
|
||||
$renderer = $this->getMockBuilder(Renderer::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['render'])
|
||||
->getMock();
|
||||
|
||||
if ($mockImplementation) {
|
||||
$renderer->method('render')
|
||||
->willReturnCallback(fn(string $template, array $data = []) => $template . json_encode($data));
|
||||
}
|
||||
|
||||
$this->app->instance('renderer', $renderer);
|
||||
return $renderer;
|
||||
}
|
||||
}
|
||||
|
|
32
yarn.lock
32
yarn.lock
|
@ -1304,29 +1304,6 @@ chalk@^2.0.0:
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chart.js@^2.9.3:
|
||||
version "2.9.4"
|
||||
resolved "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz"
|
||||
integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
|
||||
dependencies:
|
||||
chartjs-color "^2.1.0"
|
||||
moment "^2.10.2"
|
||||
|
||||
chartjs-color-string@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz"
|
||||
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@^2.1.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz"
|
||||
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
|
||||
dependencies:
|
||||
chartjs-color-string "^0.6.0"
|
||||
color-convert "^1.9.3"
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0":
|
||||
version "3.5.3"
|
||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
|
||||
|
@ -1356,7 +1333,7 @@ clone-deep@^4.0.1:
|
|||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
color-convert@^1.9.0, color-convert@^1.9.3:
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
|
@ -1368,11 +1345,6 @@ color-name@1.1.3:
|
|||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
color-name@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colord@^2.9.1:
|
||||
version "2.9.2"
|
||||
resolved "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz"
|
||||
|
@ -2106,7 +2078,7 @@ moment-timezone@^0.5.31:
|
|||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0", moment@^2.10.2, moment@^2.29.2:
|
||||
"moment@>= 2.9.0", moment@^2.29.2:
|
||||
version "2.29.2"
|
||||
resolved "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz"
|
||||
integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==
|
||||
|
|
Loading…
Reference in New Issue