Creating custom donut chart with animations

This commit is contained in:
Eric Herbrandson
2019-04-23 21:58:19 -05:00
parent db079e78d0
commit 4fb8d09a01
7 changed files with 55 additions and 50 deletions

View File

@@ -2945,11 +2945,6 @@
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
"chartist": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/chartist/-/chartist-0.10.1.tgz",
"integrity": "sha1-PdUT1THfymt453f+BQDZx+ZAaTE="
},
"check-types": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz",
@@ -15830,14 +15825,6 @@
}
}
},
"react-chartist": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/react-chartist/-/react-chartist-0.13.3.tgz",
"integrity": "sha512-lHA2JKy6S81/4KNhr1kXXXVyfeSOPVaxLX9Ohf31ntOj7XNGu5392qIvss+zfUWUuj5XCO1fy7w2fUCCUXkjBA==",
"requires": {
"prop-types": "^15.5.8"
}
},
"react-dev-utils": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-7.0.5.tgz",

View File

@@ -3,13 +3,11 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"chartist": "^0.10.1",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"node-sass": "^4.11.0",
"page": "^1.11.4",
"react": "^16.8.2",
"react-chartist": "^0.13.3",
"react-dom": "^16.8.2",
"react-modal": "^3.8.1",
"react-scripts": "2.1.5",

View File

@@ -1,14 +1,6 @@
import _ from 'lodash';
import React from 'react';
import ChartistGraph from 'react-chartist';
const options = {
donut: true,
donutWidth: 23,
donutSolid: true,
startAngle: 0,
showLabel: false,
};
import Donut from './donut';
export default function Chart(props) {
const {used = 0, usedSuffix, available = 0, availableSuffix, pending = 0, decimals = 1} = props;
@@ -16,12 +8,11 @@ export default function Chart(props) {
const fixedUsed = _.round(used, decimals);
const fixedPending = _.round(pending, decimals);
const fixedAvailable = _.round(available, decimals);
const fixedRemaining = fixedAvailable - fixedUsed - fixedPending;
const data = getData(fixedUsed, fixedPending, fixedRemaining);
const {percent, percent2} = getData(fixedUsed, fixedPending, fixedAvailable);
return (
<div className='charts_donut'>
<ChartistGraph data={{series: data}} options={options} type='Pie' />
<Donut percent={percent} percent2={percent2} />
<span className='chart_donutLabel'>
<div>
{Number.isFinite(fixedUsed) && (
@@ -45,12 +36,16 @@ export default function Chart(props) {
);
}
function getData(used, pending, remaining) {
function getData(used, pending, available) {
// If there's a negative amount remaining, show an all red chart
if (remaining < 0) return [0, 1, 0];
const remaining = available - used - pending;
if (remaining < 0) return {percent: 0, percent2: 1};
// If there's no data, show an all grey chart
if (!used && !pending && !remaining) return [0, 0, 1];
if (!available) return {percent: 0, percent2: 0};
return [used, pending, remaining];
const percent = used / available;
const percent2 = pending / available;
return {percent, percent2};
}

View File

@@ -1,19 +1,11 @@
import React from 'react';
import ChartistGraph from 'react-chartist';
import Donut from './donut';
import LoadingEllipsis from './loadingEllipsis';
const options = {
donut: true,
donutWidth: 25,
donutSolid: true,
startAngle: 0,
showLabel: false,
};
export default function Chart() {
return (
<div className='charts_donut'>
<ChartistGraph data={{series: [0, 0, 1]}} options={options} type='Pie' />
<Donut />
<LoadingEllipsis />
</div>
);

View File

@@ -86,12 +86,6 @@ $ct-series-colors: (
color: $color-light;
}
.ct-chart {
grid-column: 1;
grid-row: 1;
pointer-events: none;
}
@media only screen and (max-width: $media-small) {
.chart_donutLabel {
font-size: $font-size;
@@ -140,4 +134,35 @@ $ct-series-colors: (
display: block;
text-align: center;
}
}
}
.donut {
width: 150px;
height: 150px;
grid-column: 1;
grid-row: 1;
pointer-events: none;
}
.donut_layer {
fill: transparent;
stroke-width: 6;
}
.donut_background {
@extend .donut_layer;
stroke: $color-light;
}
.donut_layer1 {
@extend .donut_layer;
stroke: $color-accent;
}
.donut_layer2 {
@extend .donut_layer;
stroke: $color-error;
}

View File

@@ -12,8 +12,6 @@
@import './elements/textarea.scss';
@import './elements/textbox.scss';
@import 'chartist/dist/scss/chartist.scss';
html, body, div, input, select {
box-sizing: border-box;
}

View File

@@ -8,6 +8,7 @@ const toString = require('stream-to-string');
const {Issuer} = require('openid-client');
const NODE_ENV = process.env.NODE_ENV;
const DEBUG_VERBOSE = !!process.env.DEBUG_VERBOSE;
const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID;
const OIDC_SECRET = process.env.OIDC_SECRET;
const OIDC_URL = process.env.OIDC_URL;
@@ -34,6 +35,10 @@ const proxySettings = {
onError,
};
if (DEBUG_VERBOSE) {
proxySettings.onProxyRes = onProxyRes;
}
const app = express();
app.disable('x-powered-by'); // for security reasons, best not to tell attackers too much about our backend
app.use(logging);
@@ -76,6 +81,11 @@ function onError(err, req, res) {
console.log('Error in proxied request', err, req.method, req.url);
}
function onProxyRes(proxyRes, req, res) {
console.log('VERBOSE REQUEST', req.method, req.protocol, req.hostname, req.url, req.headers);
console.log('VERBOSE RESPONSE', proxyRes.statusCode, proxyRes.headers);
}
function handleErrors(err, req, res, next) {
console.error('An error occured during the request', err, req.method, req.url);