summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHaishan <[email protected]>2019-04-21 00:05:44 +0800
committerHaishan <[email protected]>2019-04-21 21:58:33 +0800
commit882b168082ddbcbe7991a71a09944f1a60084fc3 (patch)
treed12345be635943537042a929aed8376ae4480324 /src/components
parenteda2501b1d68c6f82a4a824ffe12caf5be7b33f2 (diff)
squash: feat(config): add options to select traffic chart style
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Config.js42
-rw-r--r--src/components/Selection.js61
-rw-r--r--src/components/Selection.module.css17
-rw-r--r--src/components/TrafficChart.js194
-rw-r--r--src/components/TrafficChartSample.js54
5 files changed, 193 insertions, 175 deletions
diff --git a/src/components/Config.js b/src/components/Config.js
index 01bc4bd..f937692 100644
--- a/src/components/Config.js
+++ b/src/components/Config.js
@@ -3,15 +3,24 @@ import PropTypes from 'prop-types';
import { useStoreState, useActions } from 'm/store';
import { getConfigs, fetchConfigs, updateConfigs } from 'd/configs';
-import { clearStorage } from 'd/app';
+import {
+ clearStorage,
+ selectChartStyleIndex,
+ getSelectedChartStyleIndex
+} from 'd/app';
import ContentHeader from 'c/ContentHeader';
import Switch from 'c/Switch';
import ToggleSwitch from 'c/ToggleSwitch';
import Input from 'c/Input';
import Button from 'c/Button';
+import Selection from 'c/Selection';
+import TrafficChartSample from 'c/TrafficChartSample';
+
import s0 from 'c/Config.module.css';
+const propsList = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }];
+
const optionsRule = [
{
label: 'Global',
@@ -51,11 +60,14 @@ const optionsLogLevel = [
];
const actions = {
+ selectChartStyleIndex,
fetchConfigs,
updateConfigs
};
-const mapStateToProps = s => ({ configs: getConfigs(s) });
+const mapStateToProps = s => ({
+ configs: getConfigs(s)
+});
export default function ConfigContainer() {
const { fetchConfigs } = useActions(actions);
@@ -66,8 +78,13 @@ export default function ConfigContainer() {
return <Config configs={configs} />;
}
+const mapStateToProps2 = s => ({
+ selectedChartStyleIndex: getSelectedChartStyleIndex(s)
+});
+
function Config({ configs }) {
- const { updateConfigs } = useActions(actions);
+ const { updateConfigs, selectChartStyleIndex } = useActions(actions);
+ const { selectedChartStyleIndex } = useStoreState(mapStateToProps2);
// configState to track component internal state
// prevConfigs to track external props.configs
const [configState, _setConfigState] = useState(configs);
@@ -129,6 +146,10 @@ function Config({ configs }) {
}
}
+ function handleChartStyleIndexOnChange(idx) {
+ selectChartStyleIndex(idx);
+ }
+
return (
<div>
<ContentHeader title="Config" />
@@ -198,8 +219,19 @@ function Config({ configs }) {
</div>
<div className={s0.section}>
- <div className={s0.label}>Actions</div>
- <Button label="Log out" onClick={clearStorage} />
+ <div>
+ <div className={s0.label}>Chart Style</div>
+ <Selection
+ OptionComponent={TrafficChartSample}
+ optionPropsList={propsList}
+ selectedIndex={selectedChartStyleIndex}
+ onChange={handleChartStyleIndexOnChange}
+ />
+ </div>
+ <div>
+ <div className={s0.label}>Action</div>
+ <Button label="Log out" onClick={clearStorage} />
+ </div>
</div>
</div>
);
diff --git a/src/components/Selection.js b/src/components/Selection.js
new file mode 100644
index 0000000..12816f9
--- /dev/null
+++ b/src/components/Selection.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import { func, array, number } from 'prop-types';
+import cx from 'classnames';
+
+import s from './Selection.module.css';
+
+export default function Selection({
+ OptionComponent,
+ optionPropsList,
+ selectedIndex,
+ onChange
+}) {
+ return (
+ // TODO a11y
+ // tabIndex="0"
+ <div className={s.root}>
+ {optionPropsList.map((props, idx) => {
+ const className = cx(s.item, { [s.itemActive]: idx === selectedIndex });
+ return (
+ <div
+ key={idx}
+ className={className}
+ onClick={ev => {
+ ev.preventDefault();
+ if (idx !== selectedIndex) {
+ onChange(idx);
+ }
+ }}
+ >
+ <OptionComponent {...props} />
+ </div>
+ );
+ })}
+ </div>
+ );
+}
+
+Selection.propTypes = {
+ OptionComponent: func,
+ optionPropsList: array,
+ selectedIndex: number,
+ onChange: func
+};
+
+// for test
+export function Option({ title }) {
+ // eslint-disable-next-line no-undef
+ if (__DEV__) {
+ return (
+ <div
+ style={{
+ width: 100,
+ height: 60,
+ backgroundColor: '#eee'
+ }}
+ >
+ {title}
+ </div>
+ );
+ }
+}
diff --git a/src/components/Selection.module.css b/src/components/Selection.module.css
new file mode 100644
index 0000000..a562058
--- /dev/null
+++ b/src/components/Selection.module.css
@@ -0,0 +1,17 @@
+.root {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.item {
+ flex-grow: 0;
+ flex-wrap: 0;
+ margin-right: 10px;
+ margin-bottom: 10px;
+ cursor: pointer;
+ border: 2px solid transparent;
+}
+
+.itemActive {
+ border-color: #387cec;
+}
diff --git a/src/components/TrafficChart.js b/src/components/TrafficChart.js
index 84773c8..cd9e853 100644
--- a/src/components/TrafficChart.js
+++ b/src/components/TrafficChart.js
@@ -1,193 +1,47 @@
-import React, { useEffect } from 'react';
-import prettyBytes from 'm/pretty-bytes';
+import React, { useMemo } from 'react';
import { fetchData } from '../api/traffic';
-import { unstable_createResource as createResource } from '@hsjs/react-cache';
+import useLineChart from '../hooks/useLineChart';
import { useStoreState } from 'm/store';
-import { getClashAPIConfig, getTheme } from 'd/app';
-
-// const delay = ms => new Promise(r => setTimeout(r, ms));
-const chartJSResource = createResource(() => {
- return import(/* webpackChunkName: "chartjs" */
- /* webpackPrefetch: true */
- /* webpackPreload: true */
- 'chart.js/dist/Chart.min.js').then(c => c.default);
-});
-
-const colorCombo = [
- {
- down: {
- backgroundColor: 'rgba(176, 209, 132, 0.8)',
- borderColor: 'rgb(176, 209, 132)'
- },
- up: {
- backgroundColor: 'rgba(181, 220, 231, 0.8)',
- borderColor: 'rgb(181, 220, 231)'
- }
- },
- {
- up: {
- backgroundColor: 'rgb(98, 190, 100)',
- borderColor: 'rgb(78,146,79)'
- },
- down: {
- backgroundColor: 'rgb(160, 230, 66)',
- borderColor: 'rgb(110, 156, 44)'
- }
- },
- {
- up: {
- backgroundColor: 'rgba(94, 175, 223, 0.3)',
- borderColor: 'rgb(94, 175, 223)'
- },
- down: {
- backgroundColor: 'rgba(139, 227, 195, 0.3)',
- borderColor: 'rgb(139, 227, 195)'
- }
- },
- {
- up: {
- backgroundColor: 'rgba(242, 174, 62, 0.3)',
- borderColor: 'rgb(242, 174, 62)'
- },
- down: {
- backgroundColor: 'rgba(69, 154, 248, 0.3)',
- borderColor: 'rgb(69, 154, 248)'
- }
- }
-];
-
-const commonDataSetProps = {
- borderWidth: 1,
- lineTension: 0,
- pointRadius: 0
-};
-
-function getColorComboIndexByTheme(theme) {
- return theme === 'dark' ? 0 : 2;
-}
-
-function getUploadProps(theme = 'dark') {
- const i = getColorComboIndexByTheme(theme);
- return {
- ...commonDataSetProps,
- ...colorCombo[i].up,
- label: 'Up'
- };
-}
-
-function getDownloadProps(theme = 'dark') {
- const i = getColorComboIndexByTheme(theme);
- return {
- ...commonDataSetProps,
- ...colorCombo[i].down,
- label: 'Down'
- };
-}
-
-const options = {
- responsive: true,
- maintainAspectRatio: true,
- title: {
- display: false
- },
- legend: {
- display: true,
- position: 'top',
- labels: {
- fontColor: '#ccc',
- boxWidth: 20
- }
- },
- tooltips: {
- // it's hard to follow the tooltip while the data is streaming
- // so disable it for now
- enabled: false,
- mode: 'index',
- intersect: false,
- animationDuration: 100
- // callbacks: {
- // label(tooltipItem, data) {
- // console.log(tooltipItem);
- // const { datasetIndex, yLabel } = tooltipItem;
- // const l = data.datasets[tooltipItem.datasetIndex].label;
- // console.log(yLabel);
- // const b = prettyBytes(parseInt(yLabel, 10));
- // return l + b;
- // }
- // }
- },
- hover: {
- mode: 'nearest',
- intersect: true
- },
- scales: {
- xAxes: [
- {
- display: false,
- gridLines: {
- display: false
- }
- }
- ],
- yAxes: [
- {
- display: true,
- gridLines: {
- display: true,
- color: '#555',
- borderDash: [3, 6],
- drawBorder: false
- },
- ticks: {
- callback(value) {
- return prettyBytes(value) + '/s ';
- }
- }
- }
- ]
- }
-};
+import { getClashAPIConfig, getSelectedChartStyleIndex } from 'd/app';
+import { chartJSResource, commonDataSetProps, chartStyles } from 'm/chart';
const chartWrapperStyle = {
// make chartjs chart responsive
position: 'relative',
- width: '90%'
+ maxWidth: 1000
};
+const mapStateToProps = s => ({
+ selectedChartStyleIndex: getSelectedChartStyleIndex(s)
+});
+
export default function TrafficChart() {
const Chart = chartJSResource.read();
const { hostname, port, secret } = useStoreState(getClashAPIConfig);
- const theme = useStoreState(getTheme);
-
- useEffect(() => {
- const ctx = document.getElementById('trafficChart').getContext('2d');
- const traffic = fetchData({ hostname, port, secret });
- const upProps = getUploadProps(theme);
- const downProps = getDownloadProps(theme);
- const data = {
+ const { selectedChartStyleIndex } = useStoreState(mapStateToProps);
+ const traffic = fetchData({ hostname, port, secret });
+ const data = useMemo(
+ () => ({
labels: traffic.labels,
datasets: [
{
- ...upProps,
+ ...commonDataSetProps,
+ ...chartStyles[selectedChartStyleIndex].up,
+ label: 'Up',
data: traffic.up
},
{
- ...downProps,
+ ...commonDataSetProps,
+ ...chartStyles[selectedChartStyleIndex].down,
+ label: 'Down',
data: traffic.down
}
]
- };
- const c = new Chart(ctx, {
- type: 'line',
- data,
- options
- });
- const unsubscribe = traffic.subscribe(() => c.update());
- return () => {
- unsubscribe();
- c.destroy();
- };
- }, [hostname, port, secret, theme]);
+ }),
+ [traffic, selectedChartStyleIndex]
+ );
+
+ useLineChart(Chart, 'trafficChart', data, traffic);
return (
<div style={chartWrapperStyle}>
diff --git a/src/components/TrafficChartSample.js b/src/components/TrafficChartSample.js
new file mode 100644
index 0000000..772f6d2
--- /dev/null
+++ b/src/components/TrafficChartSample.js
@@ -0,0 +1,54 @@
+import React, { useMemo } from 'react';
+import useLineChart from '../hooks/useLineChart';
+import { chartJSResource, commonDataSetProps, chartStyles } from 'm/chart';
+
+const extraChartOptions = {
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{ display: false }],
+ yAxes: [{ display: false }]
+ }
+};
+
+const data1 = [23e3, 35e3, 46e3, 33e3, 90e3, 68e3, 23e3, 45e3];
+const data2 = [184e3, 183e3, 196e3, 182e3, 190e3, 186e3, 182e3, 189e3];
+const labels = data1;
+
+export default function TrafficChart({ id }) {
+ const Chart = chartJSResource.read();
+
+ const data = useMemo(
+ () => ({
+ labels,
+ datasets: [
+ {
+ ...commonDataSetProps,
+ ...chartStyles[id].up,
+ data: data1
+ },
+ {
+ ...commonDataSetProps,
+ ...chartStyles[id].down,
+ data: data2
+ }
+ ]
+ }),
+ []
+ );
+
+ const eleId = 'chart-' + id;
+ useLineChart(Chart, eleId, data, null, extraChartOptions);
+
+ return (
+ <div
+ style={{
+ width: 150,
+ padding: 5
+ }}
+ >
+ <canvas id={eleId} />
+ </div>
+ );
+}