未验证 提交 4d3365d9 编写于 作者: P Peter Pan 提交者: GitHub

use content loader to improve user experience (#908)

* chore: change title order of sample pages

* chore: update dependencies

* chore: use content loader to improve user experience

* fix: fix color in dark mode

* fix: remove unused variables
上级 3196e74f
...@@ -38,13 +38,13 @@ ...@@ -38,13 +38,13 @@
"version": "yarn format && git add -A" "version": "yarn format && git add -A"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "4.11.0", "@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.11.0", "@typescript-eslint/parser": "4.12.0",
"eslint": "7.16.0", "eslint": "7.17.0",
"eslint-config-prettier": "7.1.0", "eslint-config-prettier": "7.1.0",
"eslint-plugin-license-header": "0.2.0", "eslint-plugin-license-header": "0.2.0",
"eslint-plugin-prettier": "3.3.0", "eslint-plugin-prettier": "3.3.1",
"eslint-plugin-react": "7.21.5", "eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0", "eslint-plugin-react-hooks": "4.2.0",
"lerna": "3.22.1", "lerna": "3.22.1",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
......
...@@ -35,13 +35,13 @@ ...@@ -35,13 +35,13 @@
], ],
"dependencies": { "dependencies": {
"@visualdl/server": "2.1.4", "@visualdl/server": "2.1.4",
"open": "7.3.0", "open": "7.3.1",
"ora": "5.1.0", "ora": "5.2.0",
"pm2": "4.5.1", "pm2": "4.5.1",
"yargs": "16.2.0" "yargs": "16.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "14.14.16", "@types/node": "14.14.20",
"@types/yargs": "15.0.12", "@types/yargs": "15.0.12",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"ts-node": "9.1.1", "ts-node": "9.1.1",
......
...@@ -30,5 +30,5 @@ module.exports = { ...@@ -30,5 +30,5 @@ module.exports = {
} }
] ]
], ],
plugins: ['styled-components', '@babel/plugin-proposal-class-properties'] plugins: ['styled-components', '@babel/plugin-proposal-class-properties', 'emotion']
}; };
...@@ -49,22 +49,23 @@ ...@@ -49,22 +49,23 @@
"i18next-fetch-backend": "3.0.0", "i18next-fetch-backend": "3.0.0",
"jszip": "3.5.0", "jszip": "3.5.0",
"lodash": "4.17.20", "lodash": "4.17.20",
"mime-types": "2.1.27", "mime-types": "2.1.28",
"moment": "2.29.1", "moment": "2.29.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"numeric": "1.2.6", "numeric": "1.2.6",
"polished": "4.0.5", "polished": "4.0.5",
"query-string": "6.13.7", "query-string": "6.13.8",
"react": "17.0.1", "react": "17.0.1",
"react-content-loader": "5.1.4",
"react-dom": "17.0.1", "react-dom": "17.0.1",
"react-helmet": "6.1.0", "react-helmet": "6.1.0",
"react-i18next": "11.8.4", "react-i18next": "11.8.5",
"react-input-range": "1.3.0", "react-input-range": "1.3.0",
"react-is": "17.0.1", "react-is": "17.0.1",
"react-rangeslider": "2.2.0", "react-rangeslider": "2.2.0",
"react-redux": "7.2.2", "react-redux": "7.2.2",
"react-router-dom": "5.2.0", "react-router-dom": "5.2.0",
"react-spinners": "0.9.0", "react-spinners": "0.10.4",
"react-toastify": "6.2.0", "react-toastify": "6.2.0",
"redux": "4.0.5", "redux": "4.0.5",
"styled-components": "5.2.1", "styled-components": "5.2.1",
...@@ -85,15 +86,15 @@ ...@@ -85,15 +86,15 @@
"@snowpack/plugin-optimize": "0.2.10", "@snowpack/plugin-optimize": "0.2.10",
"@snowpack/plugin-run-script": "2.2.1", "@snowpack/plugin-run-script": "2.2.1",
"@svgr/core": "5.5.0", "@svgr/core": "5.5.0",
"@testing-library/jest-dom": "5.11.6", "@testing-library/jest-dom": "5.11.8",
"@testing-library/react": "11.2.2", "@testing-library/react": "11.2.2",
"@types/d3": "6.2.0", "@types/d3": "6.2.0",
"@types/d3-format": "2.0.0", "@types/d3-format": "2.0.0",
"@types/echarts": "4.9.3", "@types/echarts": "4.9.3",
"@types/file-saver": "2.0.1", "@types/file-saver": "2.0.1",
"@types/jest": "26.0.19", "@types/jest": "26.0.20",
"@types/loadable__component": "5.13.1", "@types/loadable__component": "5.13.1",
"@types/lodash": "4.14.166", "@types/lodash": "4.14.167",
"@types/mime-types": "2.1.0", "@types/mime-types": "2.1.0",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/numeric": "1.2.1", "@types/numeric": "1.2.1",
...@@ -101,8 +102,8 @@ ...@@ -101,8 +102,8 @@
"@types/react-dom": "17.0.0", "@types/react-dom": "17.0.0",
"@types/react-helmet": "6.1.0", "@types/react-helmet": "6.1.0",
"@types/react-rangeslider": "2.2.3", "@types/react-rangeslider": "2.2.3",
"@types/react-redux": "7.1.14", "@types/react-redux": "7.1.15",
"@types/react-router-dom": "5.1.6", "@types/react-router-dom": "5.1.7",
"@types/snowpack-env": "2.3.3", "@types/snowpack-env": "2.3.3",
"@types/styled-components": "5.1.7", "@types/styled-components": "5.1.7",
"@visualdl/mock": "2.1.4", "@visualdl/mock": "2.1.4",
......
...@@ -68,7 +68,7 @@ const CollapseIcon = styled(Icon)<{opened?: boolean}>` ...@@ -68,7 +68,7 @@ const CollapseIcon = styled(Icon)<{opened?: boolean}>`
`; `;
type ChartCollapseProps = { type ChartCollapseProps = {
title: string; title: React.ReactNode;
opened?: boolean; opened?: boolean;
total?: number; total?: number;
}; };
......
...@@ -14,11 +14,11 @@ ...@@ -14,11 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
import {ChartCollapseTitle as ChartCollapseTitleLoader, Chart as ChartLoader} from '~/components/Loader/ChartPage';
import React, {FunctionComponent, PropsWithChildren, useCallback, useEffect, useMemo, useState} from 'react'; import React, {FunctionComponent, PropsWithChildren, useCallback, useEffect, useMemo, useState} from 'react';
import {Trans, useTranslation} from 'react-i18next'; import {Trans, useTranslation} from 'react-i18next';
import {WithStyled, headerHeight, link, primaryColor, rem, transitionProps} from '~/utils/style'; import {WithStyled, headerHeight, link, rem, transitionProps} from '~/utils/style';
import BarLoader from 'react-spinners/BarLoader';
import Chart from '~/components/Chart'; import Chart from '~/components/Chart';
import ChartCollapse from '~/components/ChartCollapse'; import ChartCollapse from '~/components/ChartCollapse';
import Pagination from '~/components/Pagination'; import Pagination from '~/components/Pagination';
...@@ -57,14 +57,6 @@ const Search = styled.div` ...@@ -57,14 +57,6 @@ const Search = styled.div`
margin-bottom: ${rem(16)}; margin-bottom: ${rem(16)};
`; `;
const Loading = styled.div`
display: flex;
justify-content: center;
align-items: center;
min-height: ${rem(200)};
padding: ${rem(40)} 0;
`;
const Empty = styled.div<{height?: string}>` const Empty = styled.div<{height?: string}>`
width: 100%; width: 100%;
text-align: center; text-align: center;
...@@ -94,7 +86,7 @@ export interface WithChart<T extends Item> { ...@@ -94,7 +86,7 @@ export interface WithChart<T extends Item> {
type ChartPageProps<T extends Item> = { type ChartPageProps<T extends Item> = {
items?: T[]; items?: T[];
running?: boolean; running?: boolean;
loading?: boolean; loading?: boolean | React.ReactNode;
chartSize?: { chartSize?: {
width?: string; width?: string;
height?: string; height?: string;
...@@ -154,45 +146,90 @@ const ChartPage = <T extends Item>({ ...@@ -154,45 +146,90 @@ const ChartPage = <T extends Item>({
const total = useMemo(() => Math.ceil(matchedTags.length / pageSize), [matchedTags]); const total = useMemo(() => Math.ceil(matchedTags.length / pageSize), [matchedTags]);
const withCharts = useCallback( const withCharts = useCallback(
(charts: T[], search?: boolean) => (charts: T[], search?: boolean) => (
loading ? ( <Wrapper>
<Loading> {loading ? (
<BarLoader color={primaryColor} width="20%" height="4px" /> Array.from({length: 2}).map((_, index) => (
</Loading> <Chart
) : ( cid={Symbol()}
<Wrapper> key={index}
{charts.length ? ( width={chartSize?.width ?? rem(430)}
charts.map((item, j) => { height={chartSize?.height ?? rem(337)}
const cid = Symbol(item.label); >
return ( {loading === true ? <ChartLoader /> : loading}
<Chart </Chart>
cid={cid} ))
key={item.id || item.label} ) : charts.length ? (
width={chartSize?.width ?? rem(430)} charts.map((item, j) => {
height={chartSize?.height ?? rem(337)} const cid = Symbol(item.label);
> return (
{withChart?.({...item, cid}, j)} <Chart
</Chart> cid={cid}
); key={item.id || item.label}
}) width={chartSize?.width ?? rem(430)}
) : ( height={chartSize?.height ?? rem(337)}
<Empty height={rem(500)}> >
{search ? ( {withChart?.({...item, cid}, j)}
<Trans i18nKey="common:search-empty"> </Chart>
Nothing found. Please try again with another word. );
<br /> })
Or you can <a onClick={() => setInputValue('')}>see all charts</a>. ) : (
</Trans> <Empty height={rem(500)}>
) : ( {search ? (
t('common:empty') <Trans i18nKey="common:search-empty">
)} Nothing found. Please try again with another word.
</Empty> <br />
)} Or you can <a onClick={() => setInputValue('')}>see all charts</a>.
</Wrapper> </Trans>
), ) : (
[withChart, loading, chartSize, t] t('common:empty')
)}
</Empty>
)}
</Wrapper>
),
[loading, t, chartSize?.width, chartSize?.height, withChart]
); );
const content = useMemo(() => {
if (loading) {
return Array.from({length: 3}).map((_, index) => (
<ChartCollapse key={index} title={<ChartCollapseTitleLoader />} opened={!index}>
{withCharts([])}
</ChartCollapse>
));
}
if (searchValue) {
return (
<ChartCollapse title={t('common:search-result')} total={matchedTags.length}>
{withCharts(pageMatchedTags, true)}
{pageMatchedTags.length ? <StyledPagination page={page} total={total} onChange={setPage} /> : null}
</ChartCollapse>
);
}
if (groupedItems.length) {
return groupedItems.map((groupedItem, i) => (
<ChartCollapse
title={groupedItem[0]}
key={groupedItem[0]}
total={groupedItem[1].length}
opened={i === 0}
>
{withCharts(groupedItem[1])}
</ChartCollapse>
));
}
return (
<Empty height={`calc(100vh - ${headerHeight} - ${rem(96)})`}>
<Trans i18nKey="common:unselected-empty">
Nothing selected.
<br />
Please select display data from right side.
</Trans>
</Empty>
);
}, [groupedItems, loading, matchedTags.length, page, pageMatchedTags, searchValue, t, total, withCharts]);
return ( return (
<div className={className}> <div className={className}>
<Search> <Search>
...@@ -203,31 +240,7 @@ const ChartPage = <T extends Item>({ ...@@ -203,31 +240,7 @@ const ChartPage = <T extends Item>({
onChange={(value: string) => setInputValue(value)} onChange={(value: string) => setInputValue(value)}
/> />
</Search> </Search>
{searchValue ? ( {content}
<ChartCollapse title={t('common:search-result')} total={matchedTags.length}>
{withCharts(pageMatchedTags, true)}
{pageMatchedTags.length ? <StyledPagination page={page} total={total} onChange={setPage} /> : null}
</ChartCollapse>
) : groupedItems.length ? (
groupedItems.map((groupedItem, i) => (
<ChartCollapse
title={groupedItem[0]}
key={groupedItem[0]}
total={groupedItem[1].length}
opened={i === 0}
>
{withCharts(groupedItem[1])}
</ChartCollapse>
))
) : (
<Empty height={`calc(100vh - ${headerHeight} - ${rem(96)})`}>
<Trans i18nKey="common:unselected-empty">
Nothing selected.
<br />
Please select display data from right side.
</Trans>
</Empty>
)}
</div> </div>
); );
}; };
......
...@@ -22,6 +22,7 @@ import {AsideSection} from '~/components/Aside'; ...@@ -22,6 +22,7 @@ import {AsideSection} from '~/components/Aside';
import Field from '~/components/Field'; import Field from '~/components/Field';
import RunAside from '~/components/RunAside'; import RunAside from '~/components/RunAside';
import StepSlider from '~/components/CurvesPage/StepSlider'; import StepSlider from '~/components/CurvesPage/StepSlider';
import StepSliderLoader from '~/components/Loader/curves/StepSlider';
import TimeModeSelect from '~/components/TimeModeSelect'; import TimeModeSelect from '~/components/TimeModeSelect';
import {TimeType} from '~/resource/curves'; import {TimeType} from '~/resource/curves';
import {cycleFetcher} from '~/utils/fetch'; import {cycleFetcher} from '~/utils/fetch';
...@@ -139,13 +140,14 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading, ...@@ -139,13 +140,14 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading,
onToggleRunning(running); onToggleRunning(running);
}, [onToggleRunning, running]); }, [onToggleRunning, running]);
return runs.length ? ( return (
<RunAside <RunAside
runs={runs} runs={runs}
selectedRuns={selectedRuns} selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns} onChangeRuns={onChangeRuns}
running={running} running={running}
onToggleRunning={setRunning} onToggleRunning={setRunning}
loading={loading}
> >
<AsideSection> <AsideSection>
<Field label={t('curves:time-display-type')}> <Field label={t('curves:time-display-type')}>
...@@ -153,14 +155,24 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading, ...@@ -153,14 +155,24 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading,
</Field> </Field>
</AsideSection> </AsideSection>
<StepSliderWrapper> <StepSliderWrapper>
{curveRun.map(run => ( {loading ? (
<AsideSection key={run.label}> <AsideSection>
<StepSlider run={run} type={timeType} onChange={index => onChangeIndexes(run.label, index)} /> <StepSliderLoader />
</AsideSection> </AsideSection>
))} ) : (
curveRun.map(run => (
<AsideSection key={run.label}>
<StepSlider
run={run}
type={timeType}
onChange={index => onChangeIndexes(run.label, index)}
/>
</AsideSection>
))
)}
</StepSliderWrapper> </StepSliderWrapper>
</RunAside> </RunAside>
) : null; );
}; };
export default CurveAside; export default CurveAside;
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, {FunctionComponent} from 'react';
import ContentLoader from './ContentLoader';
export const Chart: FunctionComponent = () => {
return (
<ContentLoader viewBox="0 0 428 335">
<rect x="20" y="20" rx="3" ry="3" width="200" height="16" />
<rect x="20" y="56" rx="3" ry="3" width="390" height="231" />
<rect x="20" y="301" rx="3" ry="3" width="16" height="16" />
<rect x="52" y="301" rx="3" ry="3" width="16" height="16" />
</ContentLoader>
);
};
export const SampleChart: FunctionComponent<{height: number}> = ({height}) => {
return (
<ContentLoader viewBox={`0 0 428 ${height}`}>
<rect x="20" y="20" rx="3" ry="3" width="200" height="18" />
<rect x="333" y="26.5" rx="2.5" ry="2.5" width="17" height="5" />
<rect x="358" y="22" rx="3" ry="3" width="50" height="14" />
<rect x="20" y="61" rx="3" ry="3" width="50" height="12" />
<rect x="298" y="61" rx="3" ry="3" width="110" height="12" />
<rect x="20" y="84" rx="2" ry="2" width="388" height="4" />
<circle cx="20" cy="86" r="6" />
<rect x="20" y="116" rx="3" ry="3" width="388" height={`${height - 170}`} />
<rect x="20" y={`${height - 34}`} rx="3" ry="3" width="16" height="16" />
<rect x="20" y={`${height - 34}`} rx="3" ry="3" width="16" height="16" />
<rect x="52" y={`${height - 34}`} rx="3" ry="3" width="16" height="16" />
<rect x="358" y={`${height - 32}`} rx="3" ry="3" width="50" height="12" />
</ContentLoader>
);
};
export const ChartCollapseTitle: FunctionComponent = () => {
return (
<ContentLoader viewBox="0 0 200 18" width={200} height={18}>
<rect x="0" y="0" rx="3" ry="3" width="200" height="18" />
</ContentLoader>
);
};
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, {FunctionComponent} from 'react';
import ContentLoader from 'react-content-loader';
import type {IContentLoaderProps} from 'react-content-loader';
import {themes} from '~/utils/theme';
import useTheme from '~/hooks/useTheme';
const RunList: FunctionComponent<IContentLoaderProps> = ({children, ...props}) => {
const theme = useTheme();
return (
<ContentLoader
backgroundColor={themes[theme].loaderBackgroundColor}
foregroundColor={themes[theme].loaderForegroundColor}
{...props}
>
{children}
</ContentLoader>
);
};
export default RunList;
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, {FunctionComponent} from 'react';
import ContentLoader from './ContentLoader';
const RunList: FunctionComponent<{count?: number}> = ({count}) => {
return (
<ContentLoader viewBox={`0 0 220 ${(count ?? 2) * 36}`}>
{Array.from({length: count ?? 2}).map((_, i) => (
<>
<rect x="0" y={`${11 * (i + 1) + 25 * i + 4.5}`} width="16" height="16" />
<circle cx="32" cy={`${11 * (i + 1) + 25 * i + 12.5}`} r="6" />
<rect x="46" y={`${11 * (i + 1) + 25 * i + 5.5}`} rx="3" ry="3" width="100" height="14" />
</>
))}
</ContentLoader>
);
};
export default RunList;
/**
* Copyright 2020 Baidu Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, {FunctionComponent} from 'react';
import ContentLoader from '../ContentLoader';
const StepSlider: FunctionComponent = () => {
return (
<ContentLoader viewBox="0 0 220 67">
<circle cx="6" cy="9.5" r="6" />
<rect x="20" y="2.5" width="50" height="14" />
<rect x="20" y="29.5" width="60" height="12" />
<rect x="0" y="55" rx="2" ry="2" width="220" height="4" />
<circle cx="220" cy="57" r="6" />
</ContentLoader>
);
};
export default StepSlider;
...@@ -21,6 +21,7 @@ import {ellipsis, em, rem, size} from '~/utils/style'; ...@@ -21,6 +21,7 @@ import {ellipsis, em, rem, size} from '~/utils/style';
import Checkbox from '~/components/Checkbox'; import Checkbox from '~/components/Checkbox';
import Field from '~/components/Field'; import Field from '~/components/Field';
import type {Run} from '~/types'; import type {Run} from '~/types';
import RunListLoader from '~/components/Loader/RunList';
import RunningToggle from '~/components/RunningToggle'; import RunningToggle from '~/components/RunningToggle';
import SearchInput from '~/components/SearchInput'; import SearchInput from '~/components/SearchInput';
import styled from 'styled-components'; import styled from 'styled-components';
...@@ -89,6 +90,7 @@ export type RunAsideProps = { ...@@ -89,6 +90,7 @@ export type RunAsideProps = {
onChangeRuns?: (runs: Run[]) => unknown; onChangeRuns?: (runs: Run[]) => unknown;
running?: boolean; running?: boolean;
onToggleRunning?: (running: boolean) => unknown; onToggleRunning?: (running: boolean) => unknown;
loading?: boolean;
}; };
const RunAside: FunctionComponent<RunAsideProps> = ({ const RunAside: FunctionComponent<RunAsideProps> = ({
...@@ -97,6 +99,7 @@ const RunAside: FunctionComponent<RunAsideProps> = ({ ...@@ -97,6 +99,7 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
onChangeRuns, onChangeRuns,
running, running,
onToggleRunning, onToggleRunning,
loading,
children children
}) => { }) => {
const {t} = useTranslation('common'); const {t} = useTranslation('common');
...@@ -150,20 +153,24 @@ const RunAside: FunctionComponent<RunAsideProps> = ({ ...@@ -150,20 +153,24 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
{t('common:select-all')} {t('common:select-all')}
</Checkbox> </Checkbox>
<div className="run-list"> <div className="run-list">
{filteredRuns.map((run, index) => ( {loading ? (
<div key={index}> <RunListLoader />
<Checkbox ) : (
value={selectedRuns?.map(r => r.label)?.includes(run.label)} filteredRuns.map((run, index) => (
title={run.label} <div key={index}>
onChange={value => setSelectedRuns(run, value)} <Checkbox
> value={selectedRuns?.map(r => r.label)?.includes(run.label)}
<span className="run-item"> title={run.label}
<i style={{backgroundColor: run.colors[0]}}></i> onChange={value => setSelectedRuns(run, value)}
{run.label} >
</span> <span className="run-item">
</Checkbox> <i style={{backgroundColor: run.colors[0]}}></i>
</div> {run.label}
))} </span>
</Checkbox>
</div>
))
)}
</div> </div>
</Field> </Field>
</AsideSection> </AsideSection>
......
...@@ -50,7 +50,6 @@ const PRCurve: FunctionComponent = () => { ...@@ -50,7 +50,6 @@ const PRCurve: FunctionComponent = () => {
onToggleRunning={setRunning} onToggleRunning={setRunning}
/> />
} }
loading={loading}
> >
{!loading && !tags.length ? ( {!loading && !tags.length ? (
<Error /> <Error />
......
...@@ -50,7 +50,6 @@ const ROCCurve: FunctionComponent = () => { ...@@ -50,7 +50,6 @@ const ROCCurve: FunctionComponent = () => {
onToggleRunning={setRunning} onToggleRunning={setRunning}
/> />
} }
loading={loading}
> >
{!loading && !tags.length ? ( {!loading && !tags.length ? (
<Error /> <Error />
......
...@@ -41,29 +41,29 @@ const Histogram: FunctionComponent = () => { ...@@ -41,29 +41,29 @@ const Histogram: FunctionComponent = () => {
const [mode, setMode] = useState<Modes>(Modes.Offset); const [mode, setMode] = useState<Modes>(Modes.Offset);
const aside = useMemo( const aside = useMemo(
() => () => (
runs.length ? ( <RunAside
<RunAside runs={runs}
runs={runs} selectedRuns={selectedRuns}
selectedRuns={selectedRuns} onChangeRuns={onChangeRuns}
onChangeRuns={onChangeRuns} running={running}
running={running} onToggleRunning={setRunning}
onToggleRunning={setRunning} loading={loading}
> >
<AsideSection> <AsideSection>
<Field label={t('histogram:mode')}> <Field label={t('histogram:mode')}>
<RadioGroup value={mode} onChange={setMode}> <RadioGroup value={mode} onChange={setMode}>
{modes.map(value => ( {modes.map(value => (
<RadioButton key={value} value={value}> <RadioButton key={value} value={value}>
{t(`histogram:mode-value.${value}`)} {t(`histogram:mode-value.${value}`)}
</RadioButton> </RadioButton>
))} ))}
</RadioGroup> </RadioGroup>
</Field> </Field>
</AsideSection> </AsideSection>
</RunAside> </RunAside>
) : null, ),
[t, mode, onChangeRuns, running, runs, selectedRuns] [loading, mode, onChangeRuns, running, runs, selectedRuns, t]
); );
const withChart = useCallback<WithChart<TagWithSingleRun>>( const withChart = useCallback<WithChart<TagWithSingleRun>>(
...@@ -74,7 +74,7 @@ const Histogram: FunctionComponent = () => { ...@@ -74,7 +74,7 @@ const Histogram: FunctionComponent = () => {
return ( return (
<> <>
<Title>{t('common:histogram')}</Title> <Title>{t('common:histogram')}</Title>
<Content aside={aside} loading={loading}> <Content aside={aside}>
{!loading && !runs.length ? ( {!loading && !runs.length ? (
<Error /> <Error />
) : ( ) : (
......
...@@ -23,6 +23,7 @@ import AudioChart from '~/components/SamplePage/AudioChart'; ...@@ -23,6 +23,7 @@ import AudioChart from '~/components/SamplePage/AudioChart';
import Content from '~/components/Content'; import Content from '~/components/Content';
import Error from '~/components/Error'; import Error from '~/components/Error';
import RunAside from '~/components/RunAside'; import RunAside from '~/components/RunAside';
import {SampleChart as SampleChartLoader} from '~/components/Loader/ChartPage';
import Title from '~/components/Title'; import Title from '~/components/Title';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import useTagFilter from '~/hooks/useTagFilter'; import useTagFilter from '~/hooks/useTagFilter';
...@@ -51,17 +52,17 @@ const AudioSample: FunctionComponent = () => { ...@@ -51,17 +52,17 @@ const AudioSample: FunctionComponent = () => {
const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loading} = useTagFilter('audio', running); const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loading} = useTagFilter('audio', running);
const aside = useMemo( const aside = useMemo(
() => () => (
runs.length ? ( <RunAside
<RunAside runs={runs}
runs={runs} selectedRuns={selectedRuns}
selectedRuns={selectedRuns} onChangeRuns={onChangeRuns}
onChangeRuns={onChangeRuns} running={running}
running={running} onToggleRunning={setRunning}
onToggleRunning={setRunning} loading={loading}
></RunAside> ></RunAside>
) : null, ),
[onChangeRuns, running, runs, selectedRuns] [loading, onChangeRuns, running, runs, selectedRuns]
); );
const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>( const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
...@@ -72,9 +73,9 @@ const AudioSample: FunctionComponent = () => { ...@@ -72,9 +73,9 @@ const AudioSample: FunctionComponent = () => {
return ( return (
<> <>
<Title> <Title>
{t('common:sample')} - {t('common:audio')} {t('common:audio')} - {t('common:sample')}
</Title> </Title>
<Content aside={aside} loading={loading}> <Content aside={aside}>
{!loading && !runs.length ? ( {!loading && !runs.length ? (
<Error /> <Error />
) : ( ) : (
...@@ -82,7 +83,7 @@ const AudioSample: FunctionComponent = () => { ...@@ -82,7 +83,7 @@ const AudioSample: FunctionComponent = () => {
items={tagsWithSingleRun} items={tagsWithSingleRun}
chartSize={chartSize} chartSize={chartSize}
withChart={withChart} withChart={withChart}
loading={loading} loading={loading && <SampleChartLoader height={242} />}
/> />
)} )}
</Content> </Content>
......
...@@ -26,6 +26,7 @@ import Error from '~/components/Error'; ...@@ -26,6 +26,7 @@ import Error from '~/components/Error';
import Field from '~/components/Field'; import Field from '~/components/Field';
import ImageChart from '~/components/SamplePage/ImageChart'; import ImageChart from '~/components/SamplePage/ImageChart';
import RunAside from '~/components/RunAside'; import RunAside from '~/components/RunAside';
import {SampleChart as SampleChartLoader} from '~/components/Loader/ChartPage';
import Slider from '~/components/Slider'; import Slider from '~/components/Slider';
import Title from '~/components/Title'; import Title from '~/components/Title';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
...@@ -48,33 +49,33 @@ const ImageSample: FunctionComponent = () => { ...@@ -48,33 +49,33 @@ const ImageSample: FunctionComponent = () => {
const [contrast, setContrast] = useState(1); const [contrast, setContrast] = useState(1);
const aside = useMemo( const aside = useMemo(
() => () => (
runs.length ? ( <RunAside
<RunAside runs={runs}
runs={runs} selectedRuns={selectedRuns}
selectedRuns={selectedRuns} onChangeRuns={onChangeRuns}
onChangeRuns={onChangeRuns} running={running}
running={running} onToggleRunning={setRunning}
onToggleRunning={setRunning} loading={loading}
> >
<AsideSection> <AsideSection>
<Checkbox value={showActualSize} onChange={setShowActualSize}> <Checkbox value={showActualSize} onChange={setShowActualSize}>
{t('sample:show-actual-size')} {t('sample:show-actual-size')}
</Checkbox> </Checkbox>
</AsideSection> </AsideSection>
<AsideSection> <AsideSection>
<Field label={t('sample:brightness')}> <Field label={t('sample:brightness')}>
<Slider min={0} max={2} step={0.01} value={brightness} onChange={setBrightness} /> <Slider min={0} max={2} step={0.01} value={brightness} onChange={setBrightness} />
</Field> </Field>
</AsideSection> </AsideSection>
<AsideSection> <AsideSection>
<Field label={t('sample:contrast')}> <Field label={t('sample:contrast')}>
<Slider min={0} max={2} step={0.01} value={contrast} onChange={setContrast} /> <Slider min={0} max={2} step={0.01} value={contrast} onChange={setContrast} />
</Field> </Field>
</AsideSection> </AsideSection>
</RunAside> </RunAside>
) : null, ),
[t, brightness, contrast, onChangeRuns, running, runs, selectedRuns, showActualSize] [brightness, contrast, loading, onChangeRuns, running, runs, selectedRuns, showActualSize, t]
); );
const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>( const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
...@@ -94,9 +95,9 @@ const ImageSample: FunctionComponent = () => { ...@@ -94,9 +95,9 @@ const ImageSample: FunctionComponent = () => {
return ( return (
<> <>
<Title> <Title>
{t('common:sample')} - {t('common:image')} {t('common:image')} - {t('common:sample')}
</Title> </Title>
<Content aside={aside} loading={loading}> <Content aside={aside}>
{!loading && !runs.length ? ( {!loading && !runs.length ? (
<Error /> <Error />
) : ( ) : (
...@@ -104,7 +105,7 @@ const ImageSample: FunctionComponent = () => { ...@@ -104,7 +105,7 @@ const ImageSample: FunctionComponent = () => {
items={tagsWithSingleRun} items={tagsWithSingleRun}
chartSize={chartSize} chartSize={chartSize}
withChart={withChart} withChart={withChart}
loading={loading} loading={loading && <SampleChartLoader height={404} />}
/> />
)} )}
</Content> </Content>
......
...@@ -78,65 +78,66 @@ const Scalar: FunctionComponent = () => { ...@@ -78,65 +78,66 @@ const Scalar: FunctionComponent = () => {
const [showMostValue, setShowMostValue] = useState(false); const [showMostValue, setShowMostValue] = useState(false);
const aside = useMemo( const aside = useMemo(
() => () => (
runs.length ? ( <RunAside
<RunAside runs={runs}
runs={runs} selectedRuns={selectedRuns}
selectedRuns={selectedRuns} onChangeRuns={onChangeRuns}
onChangeRuns={onChangeRuns} running={running}
running={running} onToggleRunning={setRunning}
onToggleRunning={setRunning} loading={loading}
> >
<AsideSection> <AsideSection>
<Field> <Field>
<Checkbox value={ignoreOutliers} onChange={setIgnoreOutliers}> <Checkbox value={ignoreOutliers} onChange={setIgnoreOutliers}>
{t('scalar:ignore-outliers')} {t('scalar:ignore-outliers')}
</Checkbox> </Checkbox>
</Field> </Field>
<Field> <Field>
<Checkbox value={showMostValue} onChange={setShowMostValue}> <Checkbox value={showMostValue} onChange={setShowMostValue}>
{t('scalar:show-most-value')} {t('scalar:show-most-value')}
</Checkbox> </Checkbox>
</Field> </Field>
<TooltipSortingDiv> <TooltipSortingDiv>
<span>{t('scalar:tooltip-sorting')}</span> <span>{t('scalar:tooltip-sorting')}</span>
<Select <Select
list={toolTipSortingValues.map(value => ({ list={toolTipSortingValues.map(value => ({
label: t(`scalar:tooltip-sorting-value.${value}`), label: t(`scalar:tooltip-sorting-value.${value}`),
value value
}))} }))}
value={tooltipSorting} value={tooltipSorting}
onChange={setTooltipSorting} onChange={setTooltipSorting}
/> />
</TooltipSortingDiv> </TooltipSortingDiv>
</AsideSection> </AsideSection>
<AsideSection> <AsideSection>
<Field label={t('scalar:smoothing')}> <Field label={t('scalar:smoothing')}>
<Slider min={0} max={0.99} step={0.01} value={smoothing} onChangeComplete={setSmoothing} /> <Slider min={0} max={0.99} step={0.01} value={smoothing} onChangeComplete={setSmoothing} />
</Field> </Field>
<Field> <Field>
<Checkbox value={smoothedDataOnly} onChange={setSmoothedDataOnly}> <Checkbox value={smoothedDataOnly} onChange={setSmoothedDataOnly}>
{t('scalar:smoothed-data-only')} {t('scalar:smoothed-data-only')}
</Checkbox> </Checkbox>
</Field> </Field>
</AsideSection> </AsideSection>
<AsideSection> <AsideSection>
<Field label={t('scalar:x-axis')}> <Field label={t('scalar:x-axis')}>
<TimeModeSelect value={xAxis} onChange={setXAxis} /> <TimeModeSelect value={xAxis} onChange={setXAxis} />
</Field> </Field>
</AsideSection> </AsideSection>
</RunAside> </RunAside>
) : null, ),
[ [
t,
ignoreOutliers, ignoreOutliers,
showMostValue, loading,
smoothedDataOnly,
onChangeRuns, onChangeRuns,
running, running,
runs, runs,
selectedRuns, selectedRuns,
showMostValue,
smoothedDataOnly,
smoothing, smoothing,
t,
tooltipSorting, tooltipSorting,
xAxis xAxis
] ]
...@@ -163,7 +164,7 @@ const Scalar: FunctionComponent = () => { ...@@ -163,7 +164,7 @@ const Scalar: FunctionComponent = () => {
return ( return (
<> <>
<Title>{t('common:scalar')}</Title> <Title>{t('common:scalar')}</Title>
<Content aside={aside} loading={loading}> <Content aside={aside}>
{!loading && !runs.length ? ( {!loading && !runs.length ? (
<Error /> <Error />
) : ( ) : (
......
...@@ -29,8 +29,8 @@ const initState: ThemeState = { ...@@ -29,8 +29,8 @@ const initState: ThemeState = {
selected: theme selected: theme
}; };
window.document.body.classList.remove('light', 'dark', 'auto'); window.document.documentElement.classList.remove('light', 'dark', 'auto');
window.document.body.classList.add(initState.selected); window.document.documentElement.classList.add(initState.selected);
function themeReducer(state = initState, action: ThemeActionTypes): ThemeState { function themeReducer(state = initState, action: ThemeActionTypes): ThemeState {
switch (action.type) { switch (action.type) {
...@@ -41,8 +41,8 @@ function themeReducer(state = initState, action: ThemeActionTypes): ThemeState { ...@@ -41,8 +41,8 @@ function themeReducer(state = initState, action: ThemeActionTypes): ThemeState {
}; };
case ActionTypes.SELECT_THEME: case ActionTypes.SELECT_THEME:
window.localStorage.setItem(STORAGE_KEY, action.theme); window.localStorage.setItem(STORAGE_KEY, action.theme);
window.document.body.classList.remove('light', 'dark', 'auto'); window.document.documentElement.classList.remove('light', 'dark', 'auto');
window.document.body.classList.add(action.theme); window.document.documentElement.classList.add(action.theme);
return { return {
...state, ...state,
theme: action.theme === 'auto' ? autoTheme : action.theme, theme: action.theme === 'auto' ? autoTheme : action.theme,
......
...@@ -59,6 +59,9 @@ export const themes = { ...@@ -59,6 +59,9 @@ export const themes = {
borderFocusedColor: darken(0.15, '#ddd'), borderFocusedColor: darken(0.15, '#ddd'),
borderActiveColor: darken(0.3, '#ddd'), borderActiveColor: darken(0.3, '#ddd'),
loaderBackgroundColor: '#f5f6f7',
loaderForegroundColor: '#eee',
navbarTextColor: '#fff', navbarTextColor: '#fff',
navbarBackgroundColor: '#1527c2', navbarBackgroundColor: '#1527c2',
navbarHoverBackgroundColor: lighten(0.05, '#1527c2'), navbarHoverBackgroundColor: lighten(0.05, '#1527c2'),
...@@ -100,6 +103,9 @@ export const themes = { ...@@ -100,6 +103,9 @@ export const themes = {
borderFocusedColor: lighten(0.15, '#3f3f42'), borderFocusedColor: lighten(0.15, '#3f3f42'),
borderActiveColor: lighten(0.3, '#3f3f42'), borderActiveColor: lighten(0.3, '#3f3f42'),
loaderBackgroundColor: '#333',
loaderForegroundColor: '#666',
navbarTextColor: '#fff', navbarTextColor: '#fff',
navbarBackgroundColor: '#262629', navbarBackgroundColor: '#262629',
navbarHoverBackgroundColor: lighten(0.05, '#262629'), navbarHoverBackgroundColor: lighten(0.05, '#262629'),
...@@ -156,16 +162,16 @@ export const variables = css` ...@@ -156,16 +162,16 @@ export const variables = css`
${generateThemeVariables(themes[THEME || 'light'])} ${generateThemeVariables(themes[THEME || 'light'])}
body.auto { &.auto {
${generateThemeVariables(themes.light)} ${generateThemeVariables(themes.light)}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
${generateThemeVariables(themes.dark)} ${generateThemeVariables(themes.dark)}
} }
} }
body.light { &.light {
${generateThemeVariables(themes.light)} ${generateThemeVariables(themes.light)}
} }
body.dark { &.dark {
${generateThemeVariables(themes.dark)} ${generateThemeVariables(themes.dark)}
} }
} }
......
...@@ -34,12 +34,12 @@ ...@@ -34,12 +34,12 @@
"devDependencies": { "devDependencies": {
"@types/express": "4.17.9", "@types/express": "4.17.9",
"@types/mkdirp": "1.0.1", "@types/mkdirp": "1.0.1",
"@types/node": "14.14.16", "@types/node": "14.14.20",
"@types/node-fetch": "2.5.7", "@types/node-fetch": "2.5.7",
"@types/rimraf": "3.0.0", "@types/rimraf": "3.0.0",
"cpy-cli": "3.1.1", "cpy-cli": "3.1.1",
"get-port": "5.1.1", "get-port": "5.1.1",
"mime-types": "2.1.27", "mime-types": "2.1.28",
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
......
...@@ -37,12 +37,12 @@ ...@@ -37,12 +37,12 @@
"express": "4.17.1", "express": "4.17.1",
"faker": "5.1.0", "faker": "5.1.0",
"isomorphic-unfetch": "3.1.0", "isomorphic-unfetch": "3.1.0",
"mime-types": "2.1.27" "mime-types": "2.1.28"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "4.17.9", "@types/express": "4.17.9",
"@types/faker": "5.1.5", "@types/faker": "5.1.5",
"@types/node": "14.14.16", "@types/node": "14.14.20",
"cpy-cli": "3.1.1", "cpy-cli": "3.1.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"ts-node": "9.1.1", "ts-node": "9.1.1",
......
...@@ -40,19 +40,19 @@ ...@@ -40,19 +40,19 @@
"pako": "1.0.11" "pako": "1.0.11"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "10.1.0", "autoprefixer": "10.2.0",
"copy-webpack-plugin": "7.0.0", "copy-webpack-plugin": "7.0.0",
"css-loader": "5.0.1", "css-loader": "5.0.1",
"html-webpack-plugin": "4.5.0", "html-webpack-plugin": "4.5.1",
"mini-css-extract-plugin": "1.3.3", "mini-css-extract-plugin": "1.3.3",
"postcss": "8.2.1", "postcss": "8.2.2",
"postcss-loader": "4.1.0", "postcss-loader": "4.1.0",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"sass": "1.30.0", "sass": "1.32.2",
"sass-loader": "10.1.0", "sass-loader": "10.1.0",
"terser": "5.5.1", "terser": "5.5.1",
"webpack": "5.11.0", "webpack": "5.11.1",
"webpack-cli": "4.3.0" "webpack-cli": "4.3.1"
}, },
"engines": { "engines": {
"node": ">=12", "node": ">=12",
......
...@@ -47,10 +47,10 @@ ...@@ -47,10 +47,10 @@
"devDependencies": { "devDependencies": {
"@types/enhanced-resolve": "3.0.6", "@types/enhanced-resolve": "3.0.6",
"@types/express": "4.17.9", "@types/express": "4.17.9",
"@types/node": "14.14.16", "@types/node": "14.14.20",
"@visualdl/mock": "2.1.4", "@visualdl/mock": "2.1.4",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"nodemon": "2.0.6", "nodemon": "2.0.7",
"ts-node": "9.1.1", "ts-node": "9.1.1",
"typescript": "4.0.5" "typescript": "4.0.5"
}, },
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册