Upgrade chart.js
This commit is contained in:
parent
3458ef250d
commit
e8f927bfd3
24 changed files with 164 additions and 239 deletions
|
@ -37,6 +37,7 @@ rules:
|
|||
# disable this temporarily since we have a lot of JS files
|
||||
# and typescript-eslint runs against JS files too
|
||||
'@typescript-eslint/explicit-module-boundary-types': off
|
||||
'@typescript-eslint/ban-ts-ignore': 'off'
|
||||
react-hooks/rules-of-hooks: error
|
||||
react-hooks/exhaustive-deps:
|
||||
- warn
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
"@hsjs/react-cache": "0.0.0-alpha.aa94237",
|
||||
"@reach/tooltip": "0.17.0",
|
||||
"@reach/visually-hidden": "0.17.0",
|
||||
"chart.js": "2.9.4",
|
||||
"chart.js": "3.7.1",
|
||||
"clsx": "^1.1.0",
|
||||
"core-js": "3.22.4",
|
||||
"date-fns": "2.28.0",
|
||||
|
@ -79,11 +79,10 @@
|
|||
"@types/react": "18.0.9",
|
||||
"@types/react-dom": "18.0.3",
|
||||
"@types/react-modal": "3.13.1",
|
||||
"@types/react-tabs": "5.0.5",
|
||||
"@types/react-window": "1.8.5",
|
||||
"@typescript-eslint/eslint-plugin": "5.22.0",
|
||||
"@typescript-eslint/parser": "5.22.0",
|
||||
"@vitejs/plugin-react-refresh": "1.3.6",
|
||||
"@vitejs/plugin-react": "1.3.2",
|
||||
"autoprefixer": "10.4.7",
|
||||
"cssnano": "5.1.7",
|
||||
"eslint": "8.15.0",
|
||||
|
|
|
@ -13,13 +13,12 @@ specifiers:
|
|||
'@types/react': 18.0.9
|
||||
'@types/react-dom': 18.0.3
|
||||
'@types/react-modal': 3.13.1
|
||||
'@types/react-tabs': 5.0.5
|
||||
'@types/react-window': 1.8.5
|
||||
'@typescript-eslint/eslint-plugin': 5.22.0
|
||||
'@typescript-eslint/parser': 5.22.0
|
||||
'@vitejs/plugin-react-refresh': 1.3.6
|
||||
'@vitejs/plugin-react': 1.3.2
|
||||
autoprefixer: 10.4.7
|
||||
chart.js: 2.9.4
|
||||
chart.js: 3.7.1
|
||||
clsx: ^1.1.0
|
||||
core-js: 3.22.4
|
||||
cssnano: 5.1.7
|
||||
|
@ -87,7 +86,7 @@ dependencies:
|
|||
'@hsjs/react-cache': 0.0.0-alpha.aa94237_react@18.1.0
|
||||
'@reach/tooltip': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
||||
'@reach/visually-hidden': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
||||
chart.js: 2.9.4
|
||||
chart.js: 3.7.1
|
||||
clsx: 1.1.1
|
||||
core-js: 3.22.4
|
||||
date-fns: 2.28.0
|
||||
|
@ -133,11 +132,10 @@ devDependencies:
|
|||
'@types/react': 18.0.9
|
||||
'@types/react-dom': 18.0.3
|
||||
'@types/react-modal': 3.13.1
|
||||
'@types/react-tabs': 5.0.5_react@18.1.0
|
||||
'@types/react-window': 1.8.5
|
||||
'@typescript-eslint/eslint-plugin': 5.22.0_tal4xlmvnofklupd3hwjtzfb4q
|
||||
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
|
||||
'@vitejs/plugin-react-refresh': 1.3.6
|
||||
'@vitejs/plugin-react': 1.3.2
|
||||
autoprefixer: 10.4.7_postcss@8.4.13
|
||||
cssnano: 5.1.7_postcss@8.4.13
|
||||
eslint: 8.15.0
|
||||
|
@ -1849,15 +1847,6 @@ packages:
|
|||
'@types/react': 18.0.9
|
||||
dev: true
|
||||
|
||||
/@types/react-tabs/5.0.5_react@18.1.0:
|
||||
resolution: {integrity: sha512-3CZTmjR7nNrZnYbnxp/DtK5e82mhM22dN47aYObmYLcp9fC1XjIEF0mLzGKFl1fR6/R8X7DyGh9hO6lON6LVkQ==}
|
||||
deprecated: This is a stub types definition. react-tabs provides its own type definitions, so you do not need this installed.
|
||||
dependencies:
|
||||
react-tabs: 5.1.0_react@18.1.0
|
||||
transitivePeerDependencies:
|
||||
- react
|
||||
dev: true
|
||||
|
||||
/@types/react-window/1.8.5:
|
||||
resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==}
|
||||
dependencies:
|
||||
|
@ -2025,16 +2014,18 @@ packages:
|
|||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-react-refresh/1.3.6:
|
||||
resolution: {integrity: sha512-iNR/UqhUOmFFxiezt0em9CgmiJBdWR+5jGxB2FihaoJfqGt76kiwaKoVOJVU5NYcDWMdN06LbyN2VIGIoYdsEA==}
|
||||
/@vitejs/plugin-react/1.3.2:
|
||||
resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
deprecated: This package has been deprecated in favor of @vitejs/plugin-react
|
||||
dependencies:
|
||||
'@babel/core': 7.17.10
|
||||
'@babel/plugin-transform-react-jsx': 7.17.3_@babel+core@7.17.10
|
||||
'@babel/plugin-transform-react-jsx-development': 7.16.7_@babel+core@7.17.10
|
||||
'@babel/plugin-transform-react-jsx-self': 7.16.7_@babel+core@7.17.10
|
||||
'@babel/plugin-transform-react-jsx-source': 7.16.7_@babel+core@7.17.10
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
react-refresh: 0.10.0
|
||||
react-refresh: 0.13.0
|
||||
resolve: 1.22.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
@ -2384,24 +2375,8 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/chart.js/2.9.4:
|
||||
resolution: {integrity: sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==}
|
||||
dependencies:
|
||||
chartjs-color: 2.4.1
|
||||
moment: 2.29.3
|
||||
dev: false
|
||||
|
||||
/chartjs-color-string/0.6.0:
|
||||
resolution: {integrity: sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: false
|
||||
|
||||
/chartjs-color/2.4.1:
|
||||
resolution: {integrity: sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==}
|
||||
dependencies:
|
||||
chartjs-color-string: 0.6.0
|
||||
color-convert: 1.9.3
|
||||
/chart.js/3.7.1:
|
||||
resolution: {integrity: sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==}
|
||||
dev: false
|
||||
|
||||
/chokidar/3.5.3:
|
||||
|
@ -2422,11 +2397,13 @@ packages:
|
|||
/clsx/1.1.1:
|
||||
resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/color-convert/1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
dev: true
|
||||
|
||||
/color-convert/2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
|
@ -2437,9 +2414,11 @@ packages:
|
|||
|
||||
/color-name/1.1.3:
|
||||
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
|
||||
dev: true
|
||||
|
||||
/color-name/1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: true
|
||||
|
||||
/colord/2.9.2:
|
||||
resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==}
|
||||
|
@ -4131,10 +4110,6 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/moment/2.29.3:
|
||||
resolution: {integrity: sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==}
|
||||
dev: false
|
||||
|
||||
/ms/2.0.0:
|
||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
||||
dev: true
|
||||
|
@ -4837,8 +4812,8 @@ packages:
|
|||
react-dom: 18.1.0_react@18.1.0
|
||||
dev: false
|
||||
|
||||
/react-refresh/0.10.0:
|
||||
resolution: {integrity: sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==}
|
||||
/react-refresh/0.13.0:
|
||||
resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
|
@ -4890,6 +4865,7 @@ packages:
|
|||
clsx: 1.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 18.1.0
|
||||
dev: false
|
||||
|
||||
/react-tiny-fab/4.0.4_react@18.1.0:
|
||||
resolution: {integrity: sha512-PxT6gEnIQR2vFfeIaa1Oq4PRX+cIEDbEfbS6PyevWCQngrKfqjMKPEcZOaaURaUclB9u3RilgjkaBUQFVlbWcg==}
|
||||
|
|
|
@ -29,6 +29,7 @@ export type ConnectionItem = {
|
|||
chains: string[];
|
||||
// e.g. 'Match', 'DomainKeyword'
|
||||
rule: string;
|
||||
rulePayload?: string;
|
||||
};
|
||||
type ConnectionsData = {
|
||||
downloadTotal: number;
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
import { ClashAPIConfig } from '$src/types';
|
||||
|
||||
import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper';
|
||||
|
||||
const endpoint = '/traffic';
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
|
||||
const Size = 150;
|
||||
|
||||
const traffic = {
|
||||
labels: Array(Size),
|
||||
// labels: [],
|
||||
labels: Array(Size).fill(0),
|
||||
up: Array(Size),
|
||||
down: Array(Size),
|
||||
|
||||
size: Size,
|
||||
subscribers: [],
|
||||
appendData(o) {
|
||||
appendData(o: { up: number; down: number }) {
|
||||
this.up.shift();
|
||||
this.down.shift();
|
||||
this.labels.shift();
|
||||
|
||||
const l = Date.now();
|
||||
this.up.push(o.up);
|
||||
this.down.push(o.down);
|
||||
const t = new Date();
|
||||
const l = '' + t.getMinutes() + t.getSeconds();
|
||||
this.labels.push(l);
|
||||
if (this.up.length > this.size) this.up.shift();
|
||||
if (this.down.length > this.size) this.down.shift();
|
||||
if (this.labels.length > this.size) this.labels.shift();
|
||||
|
||||
this.subscribers.forEach((f) => f(o));
|
||||
},
|
||||
|
||||
subscribe(listener) {
|
||||
subscribe(listener: (x:any) => void) {
|
||||
this.subscribers.push(listener);
|
||||
return () => {
|
||||
const idx = this.subscribers.indexOf(listener);
|
||||
|
@ -37,11 +39,11 @@ const traffic = {
|
|||
let fetched = false;
|
||||
let decoded = '';
|
||||
|
||||
function parseAndAppend(x) {
|
||||
function parseAndAppend(x: string) {
|
||||
traffic.appendData(JSON.parse(x));
|
||||
}
|
||||
|
||||
function pump(reader) {
|
||||
function pump(reader: ReadableStreamDefaultReader) {
|
||||
return reader.read().then(({ done, value }) => {
|
||||
const str = textDecoder.decode(value, { stream: !done });
|
||||
decoded += str;
|
||||
|
@ -73,8 +75,8 @@ function pump(reader) {
|
|||
// other value CLOSED
|
||||
// similar to ws readyState but not the same
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
|
||||
let wsState;
|
||||
function fetchData(apiConfig) {
|
||||
let wsState: number;
|
||||
function fetchData(apiConfig: ClashAPIConfig) {
|
||||
if (fetched || wsState === 1) return traffic;
|
||||
wsState = 1;
|
||||
const url = buildWebSocketURL(apiConfig, endpoint);
|
||||
|
@ -92,7 +94,7 @@ function fetchData(apiConfig) {
|
|||
return traffic;
|
||||
}
|
||||
|
||||
function fetchDataWithFetch(apiConfig) {
|
||||
function fetchDataWithFetch(apiConfig: ClashAPIConfig) {
|
||||
if (fetched) return traffic;
|
||||
fetched = true;
|
||||
const { url, init } = getURLAndInit(apiConfig);
|
||||
|
|
10
src/app.tsx
10
src/app.tsx
|
@ -1,22 +1,22 @@
|
|||
import 'modern-normalize/modern-normalize.css';
|
||||
import './misc/i18n';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Root from './components/Root';
|
||||
import * as swRegistration from './swRegistration';
|
||||
|
||||
const rootEl = document.getElementById('app');
|
||||
const root = createRoot(rootEl);
|
||||
|
||||
Modal.setAppElement(rootEl);
|
||||
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Root />
|
||||
</React.StrictMode>,
|
||||
rootEl
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
swRegistration.register();
|
||||
|
|
|
@ -88,7 +88,7 @@ function formatConnectionDataItem(
|
|||
download,
|
||||
start: now - new Date(start).valueOf(),
|
||||
chains: chains.reverse().join(' / '),
|
||||
rule: (rulePayload == null | rulePayload === '') ? rule : (`${rule}(${rulePayload})`),
|
||||
rule: !rulePayload ? rule : `${rule}(${rulePayload})`,
|
||||
...metadata,
|
||||
host: `${host2}:${destinationPort}`,
|
||||
type: `${type}(${network})`,
|
||||
|
|
|
@ -3,9 +3,9 @@ import '@fontsource/roboto-mono/latin-400.css';
|
|||
import '@fontsource/open-sans/latin-400.css';
|
||||
import '@fontsource/open-sans/latin-700.css';
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import * as React from 'react';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
import { PartialRouteObject } from 'react-router';
|
||||
import { RouteObject } from 'react-router';
|
||||
import { HashRouter as Router, useRoutes } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { About } from 'src/components/about/About';
|
||||
|
@ -24,6 +24,8 @@ import SideBar from './SideBar';
|
|||
import StateProvider from './StateProvider';
|
||||
import StyleGuide from './StyleGuide';
|
||||
|
||||
const { lazy, Suspense } = React;
|
||||
|
||||
const Connections = lazy(() => import('./Connections'));
|
||||
const Config = lazy(() => import('./Config'));
|
||||
const Logs = lazy(() => import('./Logs'));
|
||||
|
@ -38,10 +40,8 @@ const routes = [
|
|||
{ path: '/proxies', element: <Proxies /> },
|
||||
{ path: '/rules', element: <Rules /> },
|
||||
{ path: '/about', element: <About /> },
|
||||
process.env.NODE_ENV === 'development'
|
||||
? { path: '/style', element: <StyleGuide /> }
|
||||
: false,
|
||||
].filter(Boolean) as PartialRouteObject[];
|
||||
process.env.NODE_ENV === 'development' ? { path: '/style', element: <StyleGuide /> } : false,
|
||||
].filter(Boolean) as RouteObject[];
|
||||
|
||||
function RouteInnerApp() {
|
||||
return useRoutes(routes);
|
||||
|
|
|
@ -20,7 +20,13 @@ const { memo } = React;
|
|||
|
||||
const paddingBottom = 30;
|
||||
|
||||
function itemKey(index: number, { rules, provider }) {
|
||||
type ItemData = {
|
||||
rules: any[];
|
||||
provider: any;
|
||||
apiConfig: ClashAPIConfig;
|
||||
};
|
||||
|
||||
function itemKey(index: number, { rules, provider }: ItemData) {
|
||||
const providerQty = provider.names.length;
|
||||
|
||||
if (index < providerQty) {
|
||||
|
@ -88,10 +94,8 @@ function Rules({ apiConfig }: RulesProps) {
|
|||
<ContentHeader title={t('Rules')} />
|
||||
<TextFilter placeholder="Filter" textAtom={ruleFilterText} />
|
||||
</div>
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
|
||||
<div ref={refRulesContainer} style={{ paddingBottom }}>
|
||||
<VariableSizeList
|
||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
||||
height={containerHeight - paddingBottom}
|
||||
width="100%"
|
||||
itemCount={rules.length + provider.names.length}
|
||||
|
|
|
@ -6,16 +6,8 @@ import React from 'react';
|
|||
// this is just workaround
|
||||
immer.setAutoFreeze(false);
|
||||
|
||||
const {
|
||||
createContext,
|
||||
memo,
|
||||
useMemo,
|
||||
useRef,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useContext,
|
||||
useState,
|
||||
} = React;
|
||||
const { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState } =
|
||||
React;
|
||||
|
||||
export { immer };
|
||||
|
||||
|
@ -46,7 +38,7 @@ export default function Provider({ initialState, actions = {}, children }) {
|
|||
}
|
||||
}, [getState]);
|
||||
const dispatch = useCallback(
|
||||
(actionId, fn) => {
|
||||
(actionId: string | ((a: any, b: any) => any), fn: (s: any) => void) => {
|
||||
if (typeof actionId === 'function') return actionId(dispatch, getState);
|
||||
|
||||
const stateNext = produce(getState(), fn);
|
||||
|
@ -61,26 +53,21 @@ export default function Provider({ initialState, actions = {}, children }) {
|
|||
},
|
||||
[getState]
|
||||
);
|
||||
const boundActions = useMemo(() => bindActions(actions, dispatch), [
|
||||
actions,
|
||||
dispatch,
|
||||
]);
|
||||
const boundActions = useMemo(() => bindActions(actions, dispatch), [actions, dispatch]);
|
||||
|
||||
return (
|
||||
<StateContext.Provider value={state}>
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<ActionsContext.Provider value={boundActions}>
|
||||
{children}
|
||||
</ActionsContext.Provider>
|
||||
<ActionsContext.Provider value={boundActions}>{children}</ActionsContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
</StateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function connect(mapStateToProps) {
|
||||
return (Component) => {
|
||||
export function connect(mapStateToProps: any) {
|
||||
return (Component: any) => {
|
||||
const MemoComponent = memo(Component);
|
||||
function Connected(props) {
|
||||
function Connected(props: any) {
|
||||
const state = useContext(StateContext);
|
||||
const dispatch = useContext(DispatchContext);
|
||||
const mapped = mapStateToProps(state, props);
|
||||
|
@ -92,14 +79,13 @@ export function connect(mapStateToProps) {
|
|||
}
|
||||
|
||||
// steal from https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts
|
||||
function bindAction(action, dispatch) {
|
||||
return function (...args) {
|
||||
// @ts-expect-error ts-migrate(2683) FIXME: 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message
|
||||
function bindAction(action: any, dispatch: any) {
|
||||
return function (...args: any[]) {
|
||||
return dispatch(action.apply(this, args));
|
||||
};
|
||||
}
|
||||
|
||||
function bindActions(actions, dispatch) {
|
||||
function bindActions(actions: any, dispatch: any) {
|
||||
const boundActions = {};
|
||||
for (const key in actions) {
|
||||
const action = actions[key];
|
||||
|
|
|
@ -21,9 +21,7 @@ const optionsRule = [
|
|||
{ label: 'Direct', value: 'Direct' },
|
||||
];
|
||||
|
||||
const Pane = ({ children, style }) => (
|
||||
<div style={{ ...paneStyle, ...style }}>{children}</div>
|
||||
);
|
||||
const Pane = ({ children, style }) => <div style={{ ...paneStyle, ...style }}>{children}</div>;
|
||||
|
||||
function useToggle(initialState = false) {
|
||||
const [onoff, setonoff] = React.useState(initialState);
|
||||
|
@ -52,12 +50,7 @@ class StyleGuide extends PureComponent {
|
|||
</Pane>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
<ToggleSwitch
|
||||
name="test"
|
||||
options={optionsRule}
|
||||
value="Rule"
|
||||
onChange={noop}
|
||||
/>
|
||||
<ToggleSwitch name="test" options={optionsRule} value="Rule" onChange={noop} />
|
||||
</Pane>
|
||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||
<Pane>
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React from 'react';
|
||||
import S from 'react-switch';
|
||||
import * as React from 'react';
|
||||
import ReactSwitch from 'react-switch';
|
||||
|
||||
import { State } from '$src/store/types';
|
||||
|
||||
import { getTheme } from '../store/app';
|
||||
import { connect } from './StateProvider';
|
||||
|
||||
// workaround https://github.com/vitejs/vite/issues/2139#issuecomment-802981228
|
||||
const Switch = S.default ? S.default : S;
|
||||
// @ts-ignore
|
||||
const Switch = ReactSwitch.default ? ReactSwitch.default : ReactSwitch;
|
||||
|
||||
function SwitchThemed({ checked = false, onChange, theme, name }) {
|
||||
const offColor = theme === 'dark' ? '#393939' : '#e9e9e9';
|
||||
|
@ -29,6 +32,4 @@ function SwitchThemed({ checked = false, onChange, theme, name }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect((s) => ({
|
||||
theme: getTheme(s),
|
||||
}))(SwitchThemed);
|
||||
export default connect((s: State) => ({ theme: getTheme(s) }))(SwitchThemed);
|
||||
|
|
|
@ -16,8 +16,7 @@ function ToggleSwitch({ options, value, name, onChange }: Props) {
|
|||
);
|
||||
|
||||
const getPortionPercentage = useCallback(
|
||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
||||
(idx) => {
|
||||
(idx: number) => {
|
||||
const w = Math.floor(100 / options.length);
|
||||
if (idx === options.length - 1) {
|
||||
return 100 - options.length * w + w;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { State } from '$src/store/types';
|
||||
|
||||
import { fetchData } from '../api/traffic';
|
||||
import useLineChart from '../hooks/useLineChart';
|
||||
import {
|
||||
|
@ -19,7 +21,7 @@ const chartWrapperStyle = {
|
|||
maxWidth: 1000,
|
||||
};
|
||||
|
||||
const mapState = (s) => ({
|
||||
const mapState = (s: State) => ({
|
||||
apiConfig: getClashAPIConfig(s),
|
||||
selectedChartStyleIndex: getSelectedChartStyleIndex(s),
|
||||
});
|
||||
|
@ -27,7 +29,7 @@ const mapState = (s) => ({
|
|||
export default connect(mapState)(TrafficChart);
|
||||
|
||||
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
||||
const Chart = chartJSResource.read();
|
||||
const ChartMod = chartJSResource.read();
|
||||
const traffic = fetchData(apiConfig);
|
||||
const { t } = useTranslation();
|
||||
const data = useMemo(
|
||||
|
@ -48,10 +50,10 @@ function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
|||
},
|
||||
],
|
||||
}),
|
||||
[traffic, selectedChartStyleIndex, t]
|
||||
[ traffic, selectedChartStyleIndex, t]
|
||||
);
|
||||
|
||||
useLineChart(Chart, 'trafficChart', data, traffic);
|
||||
useLineChart(ChartMod.Chart, 'trafficChart', data, traffic);
|
||||
|
||||
return (
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ position: string; maxWidth: number; }' is ... Remove this comment to see the full error message
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import useLineChart from '../hooks/useLineChart';
|
||||
import {
|
||||
chartJSResource,
|
||||
chartStyles,
|
||||
commonDataSetProps,
|
||||
} from '../misc/chart';
|
||||
import { chartJSResource, chartStyles, commonDataSetProps } from '../misc/chart';
|
||||
|
||||
const extraChartOptions = {
|
||||
legend: {
|
||||
display: false,
|
||||
const { useMemo } = React;
|
||||
|
||||
const extraChartOptions: import('chart.js').ChartOptions<'line'> = {
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{ display: false }],
|
||||
yAxes: [{ display: false }],
|
||||
x: { display: false, type: 'category' },
|
||||
y: { display: false, type: 'linear' },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -22,7 +20,7 @@ const data2 = [184e3, 183e3, 196e3, 182e3, 190e3, 186e3, 182e3, 189e3];
|
|||
const labels = data1;
|
||||
|
||||
export default function TrafficChart({ id }) {
|
||||
const Chart = chartJSResource.read();
|
||||
const ChartMod = chartJSResource.read();
|
||||
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
|
@ -44,7 +42,7 @@ export default function TrafficChart({ id }) {
|
|||
);
|
||||
|
||||
const eleId = 'chart-' + id;
|
||||
useLineChart(Chart, eleId, data, null, extraChartOptions);
|
||||
useLineChart(ChartMod.Chart, eleId, data, null, extraChartOptions);
|
||||
|
||||
return (
|
||||
<div style={{ width: 100, padding: 5 }}>
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
import type { ChartConfiguration } from 'chart.js';
|
||||
import React from 'react';
|
||||
import { commonChartOptions } from 'src/misc/chart';
|
||||
|
||||
const { useEffect } = React;
|
||||
const options = commonChartOptions;
|
||||
|
||||
export default function useLineChart(
|
||||
Chart,
|
||||
elementId,
|
||||
data,
|
||||
subscription,
|
||||
chart: typeof import('chart.js').Chart,
|
||||
elementId: string,
|
||||
data: ChartConfiguration['data'],
|
||||
subscription: any,
|
||||
extraChartOptions = {}
|
||||
) {
|
||||
useEffect(() => {
|
||||
const ctx = document.getElementById(elementId).getContext('2d');
|
||||
const c = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data,
|
||||
options: { ...options, ...extraChartOptions },
|
||||
});
|
||||
const unsubscribe =
|
||||
subscription && subscription.subscribe(() => c.update());
|
||||
const ctx = (document.getElementById(elementId) as HTMLCanvasElement).getContext('2d');
|
||||
const options = { ...commonChartOptions, ...extraChartOptions };
|
||||
const c = new chart(ctx, { type: 'line', data, options });
|
||||
const unsubscribe = subscription && subscription.subscribe(() => c.update());
|
||||
return () => {
|
||||
unsubscribe && unsubscribe();
|
||||
c.destroy();
|
||||
};
|
||||
}, [Chart, elementId, data, subscription, extraChartOptions]);
|
||||
}, [chart, elementId, data, subscription, extraChartOptions]);
|
||||
}
|
||||
|
|
23
src/misc/chart-lib.ts
Normal file
23
src/misc/chart-lib.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
CategoryScale,
|
||||
Chart,
|
||||
Filler,
|
||||
Legend,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
} from 'chart.js';
|
||||
|
||||
// see https://www.chartjs.org/docs/latest/getting-started/integration.html#bundlers-webpack-rollup-etc
|
||||
Chart.register(
|
||||
LineElement,
|
||||
PointElement,
|
||||
LineController,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
Filler,
|
||||
Legend
|
||||
);
|
||||
|
||||
export { Chart };
|
|
@ -1,71 +1,36 @@
|
|||
import { unstable_createResource as createResource } from '@hsjs/react-cache';
|
||||
|
||||
import prettyBytes from './pretty-bytes';
|
||||
|
||||
export const chartJSResource = createResource(() => {
|
||||
return import(
|
||||
/* webpackChunkName: "chartjs" */
|
||||
/* webpackPrefetch: true */
|
||||
/* webpackPreload: true */
|
||||
'chart.js/dist/Chart.min.js'
|
||||
).then((c) => c.default);
|
||||
return import('$src/misc/chart-lib');
|
||||
});
|
||||
|
||||
export const commonDataSetProps = {
|
||||
borderWidth: 1,
|
||||
lineTension: 0,
|
||||
pointRadius: 0,
|
||||
};
|
||||
export const commonDataSetProps = { borderWidth: 1, pointRadius: 0, tension: 0.2, fill: true };
|
||||
|
||||
export const commonChartOptions = {
|
||||
export const commonChartOptions: import('chart.js').ChartOptions<'line'> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
title: {
|
||||
display: false,
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
labels: {
|
||||
fontColor: '#ccc',
|
||||
boxWidth: 20,
|
||||
},
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
animationDuration: 100,
|
||||
},
|
||||
hover: {
|
||||
mode: 'nearest',
|
||||
intersect: true,
|
||||
plugins: {
|
||||
legend: { labels: { boxWidth: 20 } }
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
display: false,
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
x: { display: false, type: 'category' },
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
grid: {
|
||||
display: true,
|
||||
gridLines: {
|
||||
display: true,
|
||||
color: '#555',
|
||||
borderDash: [3, 6],
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
callback(value) {
|
||||
return prettyBytes(value) + '/s ';
|
||||
},
|
||||
color: '#555',
|
||||
drawTicks: false,
|
||||
borderDash: [3, 6],
|
||||
drawBorder: false,
|
||||
},
|
||||
ticks: {
|
||||
callback(value: number) {
|
||||
return prettyBytes(value) + '/s ';
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
// const ProxySortingOptions =
|
|
@ -1,10 +0,0 @@
|
|||
const dsn = 'https://7068a15928ae45cf884dd8398fe8649c@sentry.io/1359284';
|
||||
let Sentry;
|
||||
export async function getSentry() {
|
||||
if (Sentry) return Sentry;
|
||||
const s = await import('@sentry/browser');
|
||||
s.init({ dsn });
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
Sentry = s;
|
||||
return Sentry;
|
||||
}
|
|
@ -22,7 +22,6 @@ export const getLogStreamingPaused = (s: State) => s.app.logStreamingPaused;
|
|||
|
||||
const saveStateDebounced = debounce(saveState, 600);
|
||||
|
||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
||||
function findClashAPIConfigIndex(getState: GetStateFn, { baseURL, secret }) {
|
||||
const arr = getClashAPIConfigs(getState());
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
|
|
|
@ -108,10 +108,8 @@ export type State = {
|
|||
export type GetStateFn = () => State;
|
||||
export interface DispatchFn {
|
||||
(msg: string, change: (s: State) => void): void;
|
||||
(
|
||||
action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>
|
||||
): ReturnType<typeof action>;
|
||||
(action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<
|
||||
(action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>): ReturnType<
|
||||
typeof action
|
||||
>;
|
||||
(action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<typeof action>;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$src": ["src"],
|
||||
"$src/*": ["src/*"]
|
||||
},
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||
"skipLibCheck": true,
|
||||
|
@ -14,20 +18,7 @@
|
|||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react",
|
||||
"downlevelIteration": true,
|
||||
"importHelpers": true,
|
||||
"sourceMap": true,
|
||||
"checkJs": false,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"strictNullChecks": false,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"types": ["jest"]
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["./src"]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineConfig } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import reactRefresh from '@vitejs/plugin-react-refresh';
|
||||
import react from '@vitejs/plugin-react'
|
||||
import * as path from 'path';
|
||||
import * as pkg from './package.json';
|
||||
|
||||
|
@ -12,15 +12,22 @@ export default defineConfig(({ mode }) => ({
|
|||
'process.env.PUBLIC_URL': JSON.stringify('./'),
|
||||
},
|
||||
base: './',
|
||||
resolve: {
|
||||
alias: {
|
||||
$src: path.resolve(__dirname, './src'),
|
||||
src: path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
publicDir: 'assets',
|
||||
build: {
|
||||
// sourcemap: true,
|
||||
// the default value is 'dist'
|
||||
// which make more sense
|
||||
// but change this may break other people's tools
|
||||
outDir: 'public',
|
||||
},
|
||||
plugins: [
|
||||
reactRefresh(),
|
||||
react(),
|
||||
VitePWA({
|
||||
srcDir: 'src',
|
||||
outDir: 'public',
|
||||
|
@ -29,9 +36,4 @@ export default defineConfig(({ mode }) => ({
|
|||
base: './',
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
src: path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
Loading…
Reference in a new issue