refactor(chart): lazy load Chart.js with suspense
- chore: add ico favicon - chore: lint - chore: add react-hooks lint rules
This commit is contained in:
parent
d247e37890
commit
7f75345c03
12 changed files with 81 additions and 67 deletions
|
@ -10,9 +10,9 @@ parser: babel-eslint
|
|||
plugins:
|
||||
- import
|
||||
- react
|
||||
- react-hooks
|
||||
- jest
|
||||
|
||||
|
||||
extends:
|
||||
- eslint:recommended
|
||||
- plugin:import/errors
|
||||
|
@ -31,3 +31,4 @@ rules:
|
|||
react/jsx-uses-react: "error"
|
||||
react/jsx-uses-vars: "error"
|
||||
react/react-in-jsx-scope: "error"
|
||||
react-hooks/rules-of-hooks: error
|
||||
|
|
BIN
assets/yacd.ico
Normal file
BIN
assets/yacd.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -40,6 +40,7 @@
|
|||
"modern-normalize": "^0.5.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.7.0-alpha.0",
|
||||
"react-cache": "^2.0.0-alpha.0",
|
||||
"react-dom": "^16.7.0-alpha.0",
|
||||
"react-modal": "^3.6.1",
|
||||
"react-redux": "^6.0.0-alpha.2a2f108",
|
||||
|
@ -71,6 +72,7 @@
|
|||
"eslint-plugin-import": "2.14.0",
|
||||
"eslint-plugin-jest": "^21.26.1",
|
||||
"eslint-plugin-react": "7.11.1",
|
||||
"eslint-plugin-react-hooks": "^0.0.0",
|
||||
"file-loader": "^2.0.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^1.0.1",
|
||||
|
|
|
@ -26,6 +26,7 @@ let even = false;
|
|||
const store = {
|
||||
logs: [],
|
||||
size: Size,
|
||||
fetched: false,
|
||||
subscribers: [],
|
||||
appendData(o) {
|
||||
const now = new Date();
|
||||
|
@ -68,15 +69,19 @@ function pump(reader) {
|
|||
});
|
||||
}
|
||||
|
||||
let fetched = false;
|
||||
function fetchLogs() {
|
||||
if (fetched) return store;
|
||||
if (store.fetched) return store;
|
||||
store.fetched = true;
|
||||
const { url, init } = getURLAndInit();
|
||||
fetch(url, init).then(response => {
|
||||
fetched = true;
|
||||
const reader = response.body.getReader();
|
||||
pump(reader);
|
||||
});
|
||||
fetch(url, init)
|
||||
.then(response => {
|
||||
const reader = response.body.getReader();
|
||||
pump(reader);
|
||||
})
|
||||
.catch(err => {
|
||||
store.fetched = false;
|
||||
console.log('Error', err);
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
import ContentHeader from 'c/ContentHeader';
|
||||
import TrafficChart from 'c/TrafficChart';
|
||||
import TrafficNow from 'c/TrafficNow';
|
||||
import Loading from 'c/Loading';
|
||||
import s0 from 'c/Home.module.scss';
|
||||
|
||||
export default function Home() {
|
||||
|
@ -14,7 +15,9 @@ export default function Home() {
|
|||
<TrafficNow />
|
||||
</div>
|
||||
<div className={s0.chart}>
|
||||
<TrafficChart />
|
||||
<Suspense fallback={<Loading height="200px" />} maxDuration={10}>
|
||||
<TrafficChart />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import style from './Loading.module.scss';
|
||||
import s0 from './Loading.module.scss';
|
||||
|
||||
const Loading = () => {
|
||||
const Loading = ({ height }) => {
|
||||
const style = height ? { height } : {};
|
||||
return (
|
||||
<div className={style.loading}>
|
||||
<div className={style.left + ' ' + style.circle} />
|
||||
<div className={style.right + ' ' + style.circle} />
|
||||
<div className={s0.loading} style={style}>
|
||||
<div className={s0.pulse} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading.propTypes = {
|
||||
height: PropTypes.string
|
||||
};
|
||||
|
||||
export default Loading;
|
||||
|
|
|
@ -1,50 +1,31 @@
|
|||
$color1: #2a477a;
|
||||
$color2: #dddddd;
|
||||
// $color1: #2a477a;
|
||||
$color1: #dddddd;
|
||||
|
||||
@keyframes moveRight {
|
||||
0% {
|
||||
transform: translate(-50px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveLeft {
|
||||
0% {
|
||||
transform: translate(50px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-10px);
|
||||
}
|
||||
}
|
||||
$size: 40px;
|
||||
|
||||
.loading {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin: 0 auto;
|
||||
height: 30vh;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.left {
|
||||
.pulse {
|
||||
width: $size;
|
||||
height: $size;
|
||||
margin: 10px;
|
||||
background-color: $color1;
|
||||
left: 50%;
|
||||
animation: moveRight 1s ease-in-out 0s infinite alternate;
|
||||
border-radius: 100%;
|
||||
animation: pulseScaleOut 1s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.right {
|
||||
background-color: $color2;
|
||||
right: 50%;
|
||||
animation: moveLeft 1s ease-in-out 0s infinite alternate;
|
||||
@keyframes pulseScaleOut {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ export default function Logs() {
|
|||
const x = fetchLogs();
|
||||
setLogs(x.logs);
|
||||
return x.subscribe(() => setLogs(x.logs));
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import Chart from 'chart.js/dist/Chart.min.js';
|
||||
import prettyBytes from 'm/pretty-bytes';
|
||||
|
||||
import { fetchData } from '../api/traffic';
|
||||
import { unstable_createResource as createResource } from 'react-cache';
|
||||
|
||||
// const delay = ms => new Promise(r => setTimeout(r, ms));
|
||||
const chartJSResource = createResource(() => {
|
||||
return import('chart.js/dist/Chart.min.js').then(c => c.default);
|
||||
});
|
||||
|
||||
const colorCombo = {
|
||||
0: {
|
||||
|
@ -108,13 +112,15 @@ const options = {
|
|||
};
|
||||
|
||||
const chartWrapperStyle = {
|
||||
// make chartjs chart responsive
|
||||
position: 'relative',
|
||||
width: '90%'
|
||||
};
|
||||
|
||||
export default function TrafficChart() {
|
||||
const Chart = chartJSResource.read();
|
||||
useEffect(() => {
|
||||
const ctx = document.getElementById('myChart').getContext('2d');
|
||||
const ctx = document.getElementById('trafficChart').getContext('2d');
|
||||
const traffic = fetchData();
|
||||
const data = {
|
||||
labels: traffic.labels,
|
||||
|
@ -139,7 +145,7 @@ export default function TrafficChart() {
|
|||
|
||||
return (
|
||||
<div style={chartWrapperStyle}>
|
||||
<canvas id="myChart" />
|
||||
<canvas id="trafficChart" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="shortcut icon" href="yacd.ico">
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="yacd-64.png">
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="yacd-128.png">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name=viewport content="width=device-width, initial-scale=1">
|
||||
<meta name="application-name" content="yacd">
|
||||
<meta name="description" content="Yet Another Clash Dashboard">
|
||||
<meta name="theme-color" content="#202020">
|
||||
<link id="favicon" rel="icon" type="image/png" sizes="64x64" href="yacd-64.png">
|
||||
<link id="favicon" rel="icon" type="image/png" sizes="128x128" href="yacd-128.png">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="prefetch" href="https://cdn.jsdelivr.net/npm/@hsjs/fonts@0.0.1/robotomono/v5/L0x5DF4xlVMF-BfR8bXMIjhLq3-cXbKD.woff2">
|
||||
<meta property="og:image" content="https://user-images.githubusercontent.com/1166872/47304841-536f3d80-d65a-11e8-8908-1917127dafc5.png">
|
||||
|
|
|
@ -106,11 +106,11 @@ module.exports = {
|
|||
// test: /[\\/]node_modules[\\/](core-js)[\\/]/,
|
||||
// chunks: 'all'
|
||||
// },
|
||||
chartjs: {
|
||||
test: /[\\/]node_modules[\\/]chart\.js[\\/]/,
|
||||
// name: 'chartjs',
|
||||
chunks: 'all'
|
||||
},
|
||||
// chartjs: {
|
||||
// test: /[\\/]node_modules[\\/]chart\.js[\\/]/,
|
||||
// // name: 'chartjs',
|
||||
// chunks: 'all'
|
||||
// },
|
||||
react: {
|
||||
test: /[\\/]node_modules[\\/](react-dom|react|redux|react-router|react-router-dom|schedule|react-redux|react-modal)[\\/]/,
|
||||
chunks: 'all'
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -2919,6 +2919,11 @@ eslint-plugin-jest@^21.26.1:
|
|||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-21.26.2.tgz#5b24413970e83e2c5b87c5c047a08a4881783605"
|
||||
integrity sha512-SCTBC6q182D4qQlQAN81D351jdte/YwTMo4f+l19Gvh1VemaNZP7ak3MLLvw6xkL9dO2FxVjCLk5DCdl1KfdLw==
|
||||
|
||||
eslint-plugin-react-hooks@^0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-0.0.0.tgz#9988f14082a159931c3dfa9ba699130457da927a"
|
||||
integrity sha512-SXyU7C3E8AJbXKMdb10P/zHazcxzfuWR5OFwAVZKXVU7P/H56NLszVG6WdQBo9Pt80FfnPXtUGGbWhs3/98N4w==
|
||||
|
||||
eslint-plugin-react@7.11.1:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c"
|
||||
|
@ -6603,6 +6608,11 @@ rc@^1.2.7:
|
|||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
react-cache@^2.0.0-alpha.0:
|
||||
version "2.0.0-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/react-cache/-/react-cache-2.0.0-alpha.0.tgz#d02d16a4565fd9f12478a2a41e980ba4fcbab4d5"
|
||||
integrity sha512-o7nA1dIbi6wOoIoQPQBL58CgIQ4tCA3itIR3WlE2VHMIFkIXbvsMyolg+Q9wDVUlCvU6b2n40RTTv8vOmjXCOQ==
|
||||
|
||||
react-dom@^16.7.0-alpha.0:
|
||||
version "16.7.0-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.0.tgz#8379158d4c76d63c989f325f45dfa5762582584f"
|
||||
|
|
Loading…
Reference in a new issue