未验证 提交 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 @@
"version": "yarn format && git add -A"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.11.0",
"@typescript-eslint/parser": "4.11.0",
"eslint": "7.16.0",
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"eslint": "7.17.0",
"eslint-config-prettier": "7.1.0",
"eslint-plugin-license-header": "0.2.0",
"eslint-plugin-prettier": "3.3.0",
"eslint-plugin-react": "7.21.5",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"lerna": "3.22.1",
"lint-staged": "10.5.3",
......
......@@ -35,13 +35,13 @@
],
"dependencies": {
"@visualdl/server": "2.1.4",
"open": "7.3.0",
"ora": "5.1.0",
"open": "7.3.1",
"ora": "5.2.0",
"pm2": "4.5.1",
"yargs": "16.2.0"
},
"devDependencies": {
"@types/node": "14.14.16",
"@types/node": "14.14.20",
"@types/yargs": "15.0.12",
"cross-env": "7.0.3",
"ts-node": "9.1.1",
......
......@@ -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 @@
"i18next-fetch-backend": "3.0.0",
"jszip": "3.5.0",
"lodash": "4.17.20",
"mime-types": "2.1.27",
"mime-types": "2.1.28",
"moment": "2.29.1",
"nprogress": "0.2.0",
"numeric": "1.2.6",
"polished": "4.0.5",
"query-string": "6.13.7",
"query-string": "6.13.8",
"react": "17.0.1",
"react-content-loader": "5.1.4",
"react-dom": "17.0.1",
"react-helmet": "6.1.0",
"react-i18next": "11.8.4",
"react-i18next": "11.8.5",
"react-input-range": "1.3.0",
"react-is": "17.0.1",
"react-rangeslider": "2.2.0",
"react-redux": "7.2.2",
"react-router-dom": "5.2.0",
"react-spinners": "0.9.0",
"react-spinners": "0.10.4",
"react-toastify": "6.2.0",
"redux": "4.0.5",
"styled-components": "5.2.1",
......@@ -85,15 +86,15 @@
"@snowpack/plugin-optimize": "0.2.10",
"@snowpack/plugin-run-script": "2.2.1",
"@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",
"@types/d3": "6.2.0",
"@types/d3-format": "2.0.0",
"@types/echarts": "4.9.3",
"@types/file-saver": "2.0.1",
"@types/jest": "26.0.19",
"@types/jest": "26.0.20",
"@types/loadable__component": "5.13.1",
"@types/lodash": "4.14.166",
"@types/lodash": "4.14.167",
"@types/mime-types": "2.1.0",
"@types/nprogress": "0.2.0",
"@types/numeric": "1.2.1",
......@@ -101,8 +102,8 @@
"@types/react-dom": "17.0.0",
"@types/react-helmet": "6.1.0",
"@types/react-rangeslider": "2.2.3",
"@types/react-redux": "7.1.14",
"@types/react-router-dom": "5.1.6",
"@types/react-redux": "7.1.15",
"@types/react-router-dom": "5.1.7",
"@types/snowpack-env": "2.3.3",
"@types/styled-components": "5.1.7",
"@visualdl/mock": "2.1.4",
......
......@@ -68,7 +68,7 @@ const CollapseIcon = styled(Icon)<{opened?: boolean}>`
`;
type ChartCollapseProps = {
title: string;
title: React.ReactNode;
opened?: boolean;
total?: number;
};
......
......@@ -14,11 +14,11 @@
* 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 {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 ChartCollapse from '~/components/ChartCollapse';
import Pagination from '~/components/Pagination';
......@@ -57,14 +57,6 @@ const Search = styled.div`
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}>`
width: 100%;
text-align: center;
......@@ -94,7 +86,7 @@ export interface WithChart<T extends Item> {
type ChartPageProps<T extends Item> = {
items?: T[];
running?: boolean;
loading?: boolean;
loading?: boolean | React.ReactNode;
chartSize?: {
width?: string;
height?: string;
......@@ -154,45 +146,90 @@ const ChartPage = <T extends Item>({
const total = useMemo(() => Math.ceil(matchedTags.length / pageSize), [matchedTags]);
const withCharts = useCallback(
(charts: T[], search?: boolean) =>
loading ? (
<Loading>
<BarLoader color={primaryColor} width="20%" height="4px" />
</Loading>
) : (
<Wrapper>
{charts.length ? (
charts.map((item, j) => {
const cid = Symbol(item.label);
return (
<Chart
cid={cid}
key={item.id || item.label}
width={chartSize?.width ?? rem(430)}
height={chartSize?.height ?? rem(337)}
>
{withChart?.({...item, cid}, j)}
</Chart>
);
})
) : (
<Empty height={rem(500)}>
{search ? (
<Trans i18nKey="common:search-empty">
Nothing found. Please try again with another word.
<br />
Or you can <a onClick={() => setInputValue('')}>see all charts</a>.
</Trans>
) : (
t('common:empty')
)}
</Empty>
)}
</Wrapper>
),
[withChart, loading, chartSize, t]
(charts: T[], search?: boolean) => (
<Wrapper>
{loading ? (
Array.from({length: 2}).map((_, index) => (
<Chart
cid={Symbol()}
key={index}
width={chartSize?.width ?? rem(430)}
height={chartSize?.height ?? rem(337)}
>
{loading === true ? <ChartLoader /> : loading}
</Chart>
))
) : charts.length ? (
charts.map((item, j) => {
const cid = Symbol(item.label);
return (
<Chart
cid={cid}
key={item.id || item.label}
width={chartSize?.width ?? rem(430)}
height={chartSize?.height ?? rem(337)}
>
{withChart?.({...item, cid}, j)}
</Chart>
);
})
) : (
<Empty height={rem(500)}>
{search ? (
<Trans i18nKey="common:search-empty">
Nothing found. Please try again with another word.
<br />
Or you can <a onClick={() => setInputValue('')}>see all charts</a>.
</Trans>
) : (
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 (
<div className={className}>
<Search>
......@@ -203,31 +240,7 @@ const ChartPage = <T extends Item>({
onChange={(value: string) => setInputValue(value)}
/>
</Search>
{searchValue ? (
<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>
)}
{content}
</div>
);
};
......
......@@ -22,6 +22,7 @@ import {AsideSection} from '~/components/Aside';
import Field from '~/components/Field';
import RunAside from '~/components/RunAside';
import StepSlider from '~/components/CurvesPage/StepSlider';
import StepSliderLoader from '~/components/Loader/curves/StepSlider';
import TimeModeSelect from '~/components/TimeModeSelect';
import {TimeType} from '~/resource/curves';
import {cycleFetcher} from '~/utils/fetch';
......@@ -139,13 +140,14 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading,
onToggleRunning(running);
}, [onToggleRunning, running]);
return runs.length ? (
return (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
loading={loading}
>
<AsideSection>
<Field label={t('curves:time-display-type')}>
......@@ -153,14 +155,24 @@ const CurveAside: FunctionComponent<CurveAsideProps> = ({type, onChangeLoading,
</Field>
</AsideSection>
<StepSliderWrapper>
{curveRun.map(run => (
<AsideSection key={run.label}>
<StepSlider run={run} type={timeType} onChange={index => onChangeIndexes(run.label, index)} />
{loading ? (
<AsideSection>
<StepSliderLoader />
</AsideSection>
))}
) : (
curveRun.map(run => (
<AsideSection key={run.label}>
<StepSlider
run={run}
type={timeType}
onChange={index => onChangeIndexes(run.label, index)}
/>
</AsideSection>
))
)}
</StepSliderWrapper>
</RunAside>
) : null;
);
};
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';
import Checkbox from '~/components/Checkbox';
import Field from '~/components/Field';
import type {Run} from '~/types';
import RunListLoader from '~/components/Loader/RunList';
import RunningToggle from '~/components/RunningToggle';
import SearchInput from '~/components/SearchInput';
import styled from 'styled-components';
......@@ -89,6 +90,7 @@ export type RunAsideProps = {
onChangeRuns?: (runs: Run[]) => unknown;
running?: boolean;
onToggleRunning?: (running: boolean) => unknown;
loading?: boolean;
};
const RunAside: FunctionComponent<RunAsideProps> = ({
......@@ -97,6 +99,7 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
onChangeRuns,
running,
onToggleRunning,
loading,
children
}) => {
const {t} = useTranslation('common');
......@@ -150,20 +153,24 @@ const RunAside: FunctionComponent<RunAsideProps> = ({
{t('common:select-all')}
</Checkbox>
<div className="run-list">
{filteredRuns.map((run, index) => (
<div key={index}>
<Checkbox
value={selectedRuns?.map(r => r.label)?.includes(run.label)}
title={run.label}
onChange={value => setSelectedRuns(run, value)}
>
<span className="run-item">
<i style={{backgroundColor: run.colors[0]}}></i>
{run.label}
</span>
</Checkbox>
</div>
))}
{loading ? (
<RunListLoader />
) : (
filteredRuns.map((run, index) => (
<div key={index}>
<Checkbox
value={selectedRuns?.map(r => r.label)?.includes(run.label)}
title={run.label}
onChange={value => setSelectedRuns(run, value)}
>
<span className="run-item">
<i style={{backgroundColor: run.colors[0]}}></i>
{run.label}
</span>
</Checkbox>
</div>
))
)}
</div>
</Field>
</AsideSection>
......
......@@ -50,7 +50,6 @@ const PRCurve: FunctionComponent = () => {
onToggleRunning={setRunning}
/>
}
loading={loading}
>
{!loading && !tags.length ? (
<Error />
......
......@@ -50,7 +50,6 @@ const ROCCurve: FunctionComponent = () => {
onToggleRunning={setRunning}
/>
}
loading={loading}
>
{!loading && !tags.length ? (
<Error />
......
......@@ -41,29 +41,29 @@ const Histogram: FunctionComponent = () => {
const [mode, setMode] = useState<Modes>(Modes.Offset);
const aside = useMemo(
() =>
runs.length ? (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
>
<AsideSection>
<Field label={t('histogram:mode')}>
<RadioGroup value={mode} onChange={setMode}>
{modes.map(value => (
<RadioButton key={value} value={value}>
{t(`histogram:mode-value.${value}`)}
</RadioButton>
))}
</RadioGroup>
</Field>
</AsideSection>
</RunAside>
) : null,
[t, mode, onChangeRuns, running, runs, selectedRuns]
() => (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
loading={loading}
>
<AsideSection>
<Field label={t('histogram:mode')}>
<RadioGroup value={mode} onChange={setMode}>
{modes.map(value => (
<RadioButton key={value} value={value}>
{t(`histogram:mode-value.${value}`)}
</RadioButton>
))}
</RadioGroup>
</Field>
</AsideSection>
</RunAside>
),
[loading, mode, onChangeRuns, running, runs, selectedRuns, t]
);
const withChart = useCallback<WithChart<TagWithSingleRun>>(
......@@ -74,7 +74,7 @@ const Histogram: FunctionComponent = () => {
return (
<>
<Title>{t('common:histogram')}</Title>
<Content aside={aside} loading={loading}>
<Content aside={aside}>
{!loading && !runs.length ? (
<Error />
) : (
......
......@@ -23,6 +23,7 @@ import AudioChart from '~/components/SamplePage/AudioChart';
import Content from '~/components/Content';
import Error from '~/components/Error';
import RunAside from '~/components/RunAside';
import {SampleChart as SampleChartLoader} from '~/components/Loader/ChartPage';
import Title from '~/components/Title';
import {rem} from '~/utils/style';
import useTagFilter from '~/hooks/useTagFilter';
......@@ -51,17 +52,17 @@ const AudioSample: FunctionComponent = () => {
const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loading} = useTagFilter('audio', running);
const aside = useMemo(
() =>
runs.length ? (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
></RunAside>
) : null,
[onChangeRuns, running, runs, selectedRuns]
() => (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
loading={loading}
></RunAside>
),
[loading, onChangeRuns, running, runs, selectedRuns]
);
const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
......@@ -72,9 +73,9 @@ const AudioSample: FunctionComponent = () => {
return (
<>
<Title>
{t('common:sample')} - {t('common:audio')}
{t('common:audio')} - {t('common:sample')}
</Title>
<Content aside={aside} loading={loading}>
<Content aside={aside}>
{!loading && !runs.length ? (
<Error />
) : (
......@@ -82,7 +83,7 @@ const AudioSample: FunctionComponent = () => {
items={tagsWithSingleRun}
chartSize={chartSize}
withChart={withChart}
loading={loading}
loading={loading && <SampleChartLoader height={242} />}
/>
)}
</Content>
......
......@@ -26,6 +26,7 @@ import Error from '~/components/Error';
import Field from '~/components/Field';
import ImageChart from '~/components/SamplePage/ImageChart';
import RunAside from '~/components/RunAside';
import {SampleChart as SampleChartLoader} from '~/components/Loader/ChartPage';
import Slider from '~/components/Slider';
import Title from '~/components/Title';
import {rem} from '~/utils/style';
......@@ -48,33 +49,33 @@ const ImageSample: FunctionComponent = () => {
const [contrast, setContrast] = useState(1);
const aside = useMemo(
() =>
runs.length ? (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
>
<AsideSection>
<Checkbox value={showActualSize} onChange={setShowActualSize}>
{t('sample:show-actual-size')}
</Checkbox>
</AsideSection>
<AsideSection>
<Field label={t('sample:brightness')}>
<Slider min={0} max={2} step={0.01} value={brightness} onChange={setBrightness} />
</Field>
</AsideSection>
<AsideSection>
<Field label={t('sample:contrast')}>
<Slider min={0} max={2} step={0.01} value={contrast} onChange={setContrast} />
</Field>
</AsideSection>
</RunAside>
) : null,
[t, brightness, contrast, onChangeRuns, running, runs, selectedRuns, showActualSize]
() => (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
loading={loading}
>
<AsideSection>
<Checkbox value={showActualSize} onChange={setShowActualSize}>
{t('sample:show-actual-size')}
</Checkbox>
</AsideSection>
<AsideSection>
<Field label={t('sample:brightness')}>
<Slider min={0} max={2} step={0.01} value={brightness} onChange={setBrightness} />
</Field>
</AsideSection>
<AsideSection>
<Field label={t('sample:contrast')}>
<Slider min={0} max={2} step={0.01} value={contrast} onChange={setContrast} />
</Field>
</AsideSection>
</RunAside>
),
[brightness, contrast, loading, onChangeRuns, running, runs, selectedRuns, showActualSize, t]
);
const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
......@@ -94,9 +95,9 @@ const ImageSample: FunctionComponent = () => {
return (
<>
<Title>
{t('common:sample')} - {t('common:image')}
{t('common:image')} - {t('common:sample')}
</Title>
<Content aside={aside} loading={loading}>
<Content aside={aside}>
{!loading && !runs.length ? (
<Error />
) : (
......@@ -104,7 +105,7 @@ const ImageSample: FunctionComponent = () => {
items={tagsWithSingleRun}
chartSize={chartSize}
withChart={withChart}
loading={loading}
loading={loading && <SampleChartLoader height={404} />}
/>
)}
</Content>
......
......@@ -78,65 +78,66 @@ const Scalar: FunctionComponent = () => {
const [showMostValue, setShowMostValue] = useState(false);
const aside = useMemo(
() =>
runs.length ? (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
>
<AsideSection>
<Field>
<Checkbox value={ignoreOutliers} onChange={setIgnoreOutliers}>
{t('scalar:ignore-outliers')}
</Checkbox>
</Field>
<Field>
<Checkbox value={showMostValue} onChange={setShowMostValue}>
{t('scalar:show-most-value')}
</Checkbox>
</Field>
<TooltipSortingDiv>
<span>{t('scalar:tooltip-sorting')}</span>
<Select
list={toolTipSortingValues.map(value => ({
label: t(`scalar:tooltip-sorting-value.${value}`),
value
}))}
value={tooltipSorting}
onChange={setTooltipSorting}
/>
</TooltipSortingDiv>
</AsideSection>
<AsideSection>
<Field label={t('scalar:smoothing')}>
<Slider min={0} max={0.99} step={0.01} value={smoothing} onChangeComplete={setSmoothing} />
</Field>
<Field>
<Checkbox value={smoothedDataOnly} onChange={setSmoothedDataOnly}>
{t('scalar:smoothed-data-only')}
</Checkbox>
</Field>
</AsideSection>
<AsideSection>
<Field label={t('scalar:x-axis')}>
<TimeModeSelect value={xAxis} onChange={setXAxis} />
</Field>
</AsideSection>
</RunAside>
) : null,
() => (
<RunAside
runs={runs}
selectedRuns={selectedRuns}
onChangeRuns={onChangeRuns}
running={running}
onToggleRunning={setRunning}
loading={loading}
>
<AsideSection>
<Field>
<Checkbox value={ignoreOutliers} onChange={setIgnoreOutliers}>
{t('scalar:ignore-outliers')}
</Checkbox>
</Field>
<Field>
<Checkbox value={showMostValue} onChange={setShowMostValue}>
{t('scalar:show-most-value')}
</Checkbox>
</Field>
<TooltipSortingDiv>
<span>{t('scalar:tooltip-sorting')}</span>
<Select
list={toolTipSortingValues.map(value => ({
label: t(`scalar:tooltip-sorting-value.${value}`),
value
}))}
value={tooltipSorting}
onChange={setTooltipSorting}
/>
</TooltipSortingDiv>
</AsideSection>
<AsideSection>
<Field label={t('scalar:smoothing')}>
<Slider min={0} max={0.99} step={0.01} value={smoothing} onChangeComplete={setSmoothing} />
</Field>
<Field>
<Checkbox value={smoothedDataOnly} onChange={setSmoothedDataOnly}>
{t('scalar:smoothed-data-only')}
</Checkbox>
</Field>
</AsideSection>
<AsideSection>
<Field label={t('scalar:x-axis')}>
<TimeModeSelect value={xAxis} onChange={setXAxis} />
</Field>
</AsideSection>
</RunAside>
),
[
t,
ignoreOutliers,
showMostValue,
smoothedDataOnly,
loading,
onChangeRuns,
running,
runs,
selectedRuns,
showMostValue,
smoothedDataOnly,
smoothing,
t,
tooltipSorting,
xAxis
]
......@@ -163,7 +164,7 @@ const Scalar: FunctionComponent = () => {
return (
<>
<Title>{t('common:scalar')}</Title>
<Content aside={aside} loading={loading}>
<Content aside={aside}>
{!loading && !runs.length ? (
<Error />
) : (
......
......@@ -29,8 +29,8 @@ const initState: ThemeState = {
selected: theme
};
window.document.body.classList.remove('light', 'dark', 'auto');
window.document.body.classList.add(initState.selected);
window.document.documentElement.classList.remove('light', 'dark', 'auto');
window.document.documentElement.classList.add(initState.selected);
function themeReducer(state = initState, action: ThemeActionTypes): ThemeState {
switch (action.type) {
......@@ -41,8 +41,8 @@ function themeReducer(state = initState, action: ThemeActionTypes): ThemeState {
};
case ActionTypes.SELECT_THEME:
window.localStorage.setItem(STORAGE_KEY, action.theme);
window.document.body.classList.remove('light', 'dark', 'auto');
window.document.body.classList.add(action.theme);
window.document.documentElement.classList.remove('light', 'dark', 'auto');
window.document.documentElement.classList.add(action.theme);
return {
...state,
theme: action.theme === 'auto' ? autoTheme : action.theme,
......
......@@ -59,6 +59,9 @@ export const themes = {
borderFocusedColor: darken(0.15, '#ddd'),
borderActiveColor: darken(0.3, '#ddd'),
loaderBackgroundColor: '#f5f6f7',
loaderForegroundColor: '#eee',
navbarTextColor: '#fff',
navbarBackgroundColor: '#1527c2',
navbarHoverBackgroundColor: lighten(0.05, '#1527c2'),
......@@ -100,6 +103,9 @@ export const themes = {
borderFocusedColor: lighten(0.15, '#3f3f42'),
borderActiveColor: lighten(0.3, '#3f3f42'),
loaderBackgroundColor: '#333',
loaderForegroundColor: '#666',
navbarTextColor: '#fff',
navbarBackgroundColor: '#262629',
navbarHoverBackgroundColor: lighten(0.05, '#262629'),
......@@ -156,16 +162,16 @@ export const variables = css`
${generateThemeVariables(themes[THEME || 'light'])}
body.auto {
&.auto {
${generateThemeVariables(themes.light)}
@media (prefers-color-scheme: dark) {
${generateThemeVariables(themes.dark)}
}
}
body.light {
&.light {
${generateThemeVariables(themes.light)}
}
body.dark {
&.dark {
${generateThemeVariables(themes.dark)}
}
}
......
......@@ -34,12 +34,12 @@
"devDependencies": {
"@types/express": "4.17.9",
"@types/mkdirp": "1.0.1",
"@types/node": "14.14.16",
"@types/node": "14.14.20",
"@types/node-fetch": "2.5.7",
"@types/rimraf": "3.0.0",
"cpy-cli": "3.1.1",
"get-port": "5.1.1",
"mime-types": "2.1.27",
"mime-types": "2.1.28",
"mkdirp": "1.0.4",
"node-fetch": "2.6.1",
"rimraf": "3.0.2",
......
......@@ -37,12 +37,12 @@
"express": "4.17.1",
"faker": "5.1.0",
"isomorphic-unfetch": "3.1.0",
"mime-types": "2.1.27"
"mime-types": "2.1.28"
},
"devDependencies": {
"@types/express": "4.17.9",
"@types/faker": "5.1.5",
"@types/node": "14.14.16",
"@types/node": "14.14.20",
"cpy-cli": "3.1.1",
"rimraf": "3.0.2",
"ts-node": "9.1.1",
......
......@@ -40,19 +40,19 @@
"pako": "1.0.11"
},
"devDependencies": {
"autoprefixer": "10.1.0",
"autoprefixer": "10.2.0",
"copy-webpack-plugin": "7.0.0",
"css-loader": "5.0.1",
"html-webpack-plugin": "4.5.0",
"html-webpack-plugin": "4.5.1",
"mini-css-extract-plugin": "1.3.3",
"postcss": "8.2.1",
"postcss": "8.2.2",
"postcss-loader": "4.1.0",
"rimraf": "3.0.2",
"sass": "1.30.0",
"sass": "1.32.2",
"sass-loader": "10.1.0",
"terser": "5.5.1",
"webpack": "5.11.0",
"webpack-cli": "4.3.0"
"webpack": "5.11.1",
"webpack-cli": "4.3.1"
},
"engines": {
"node": ">=12",
......
......@@ -47,10 +47,10 @@
"devDependencies": {
"@types/enhanced-resolve": "3.0.6",
"@types/express": "4.17.9",
"@types/node": "14.14.16",
"@types/node": "14.14.20",
"@visualdl/mock": "2.1.4",
"cross-env": "7.0.3",
"nodemon": "2.0.6",
"nodemon": "2.0.7",
"ts-node": "9.1.1",
"typescript": "4.0.5"
},
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册