From b7726fde0edf3c45136929290b3af27a54dbbca1 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Fri, 11 Sep 2020 12:41:37 +0800 Subject: [PATCH] fix bugs from Kristy and improve interaction (#809) * feat: split page runs in global states * fix: bring loading back when switching to a visited page * fix: scroll tooltips in scalar & pr-curve to prevent content overflow --- .../core/src/components/GlobalState.tsx | 18 ++++++++- .../components/PRCurvePage/PRCurveChart.tsx | 4 +- .../src/components/ScalarPage/ScalarChart.tsx | 4 +- .../core/src/components/TooltipTable.tsx | 4 ++ .../packages/core/src/hooks/useECharts.ts | 9 ++++- .../packages/core/src/hooks/useGlobalState.ts | 40 +++++++++++++++++-- .../packages/core/src/hooks/useTagFilter.ts | 23 ++++++++++- frontend/packages/core/src/pages/graph.tsx | 4 +- 8 files changed, 93 insertions(+), 13 deletions(-) diff --git a/frontend/packages/core/src/components/GlobalState.tsx b/frontend/packages/core/src/components/GlobalState.tsx index 3b4c9c86..b9d637cc 100644 --- a/frontend/packages/core/src/components/GlobalState.tsx +++ b/frontend/packages/core/src/components/GlobalState.tsx @@ -8,8 +8,24 @@ interface GlobalDispatch { (state: GlobalStateType, newState: Partial): GlobalStateType; } +// TODO: use redux const GlobalState: FunctionComponent = ({children}) => { - const [state, dispatch] = useReducer((state, newState) => ({...state, ...newState}), globalState); + const [state, dispatch] = useReducer( + (state, newState) => + Object.entries(newState).reduce( + (m, [key, value]) => { + if (m.hasOwnProperty(key)) { + m[key] = {...m[key], ...value}; + } else { + m[key] = value; + } + return m; + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + {...state} as any + ), + globalState + ); return ( diff --git a/frontend/packages/core/src/components/PRCurvePage/PRCurveChart.tsx b/frontend/packages/core/src/components/PRCurvePage/PRCurveChart.tsx index b7e3ae89..772fef12 100644 --- a/frontend/packages/core/src/components/PRCurvePage/PRCurveChart.tsx +++ b/frontend/packages/core/src/components/PRCurvePage/PRCurveChart.tsx @@ -171,7 +171,9 @@ const PRCurveChart: FunctionComponent = ({cid, runs, tag, run ...chartOptions, tooltip: { ...chartOptions.tooltip, - formatter + formatter, + hideDelay: 300, + enterable: true } }), [formatter] diff --git a/frontend/packages/core/src/components/ScalarPage/ScalarChart.tsx b/frontend/packages/core/src/components/ScalarPage/ScalarChart.tsx index 49cacc22..e7121a28 100644 --- a/frontend/packages/core/src/components/ScalarPage/ScalarChart.tsx +++ b/frontend/packages/core/src/components/ScalarPage/ScalarChart.tsx @@ -204,7 +204,9 @@ const ScalarChart: FunctionComponent = ({ ...chartOptions, tooltip: { ...chartOptions.tooltip, - formatter + formatter, + hideDelay: 300, + enterable: true }, xAxis: { type: xAxisType, diff --git a/frontend/packages/core/src/components/TooltipTable.tsx b/frontend/packages/core/src/components/TooltipTable.tsx index 0d45b740..d5b4c8b0 100644 --- a/frontend/packages/core/src/components/TooltipTable.tsx +++ b/frontend/packages/core/src/components/TooltipTable.tsx @@ -5,6 +5,10 @@ import type {Run} from '~/types'; import styled from 'styled-components'; const Wrapper = styled.div` + max-height: ${rem(160)}; + overflow: hidden auto; + overscroll-behavior: auto contain; + table { border-spacing: none; text-align: left; diff --git a/frontend/packages/core/src/hooks/useECharts.ts b/frontend/packages/core/src/hooks/useECharts.ts index c480285c..0554b700 100644 --- a/frontend/packages/core/src/hooks/useECharts.ts +++ b/frontend/packages/core/src/hooks/useECharts.ts @@ -30,6 +30,8 @@ const useECharts = echartInstance.current?.dispatchAction({type: 'hideTip'}), []); + const createChart = useCallback(() => { (async () => { const {default: echarts} = await import('echarts'); @@ -41,6 +43,8 @@ const useECharts = { if (options.zoom) { echartInstance.current?.dispatchAction({ @@ -57,15 +61,16 @@ const useECharts = { if (echartInstance.current) { onDispose.current?.(echartInstance.current); } echartInstance.current?.dispose(); + ref.current?.removeEventListener('mouseleave', hideTip); setEchart(null); - }, []); + }, [hideTip]); useEffect(() => { createChart(); diff --git a/frontend/packages/core/src/hooks/useGlobalState.ts b/frontend/packages/core/src/hooks/useGlobalState.ts index ee59f909..1997b458 100644 --- a/frontend/packages/core/src/hooks/useGlobalState.ts +++ b/frontend/packages/core/src/hooks/useGlobalState.ts @@ -3,13 +3,45 @@ import {createContext, useContext} from 'react'; import type {Dispatch} from 'react'; export interface GlobalState { - runs: string[]; - model: FileList | File[] | null; + scalar: { + runs: string[]; + }; + histogram: { + runs: string[]; + }; + image: { + runs: string[]; + }; + audio: { + runs: string[]; + }; + prCurve: { + runs: string[]; + }; + graph: { + model: FileList | File[] | null; + }; } export const globalState: GlobalState = { - runs: [], - model: null + scalar: { + runs: [] + }, + histogram: { + runs: [] + }, + image: { + runs: [] + }, + audio: { + runs: [] + }, + prCurve: { + runs: [] + }, + graph: { + model: null + } }; export const GlobalStateContext = createContext(globalState); diff --git a/frontend/packages/core/src/hooks/useTagFilter.ts b/frontend/packages/core/src/hooks/useTagFilter.ts index 9905bcfc..a81bfba8 100644 --- a/frontend/packages/core/src/hooks/useTagFilter.ts +++ b/frontend/packages/core/src/hooks/useTagFilter.ts @@ -2,6 +2,8 @@ import type {Run, Tag, TagWithSingleRun, TagsData} from '~/types'; import {color, colorAlt} from '~/utils/chart'; import {useCallback, useEffect, useMemo, useReducer} from 'react'; +import {cache} from 'swr'; +import camelCase from 'lodash/camelCase'; import groupBy from 'lodash/groupBy'; import intersection from 'lodash/intersection'; import intersectionBy from 'lodash/intersectionBy'; @@ -163,7 +165,16 @@ const useTagFilter = (type: string, running: boolean) => { const {data, loading, error} = useRunningRequest(`/${type}/tags`, running); + // clear cache in order to fully reload data when switching page + useEffect(() => () => cache.delete(`/${type}/tags`), [type]); + + const pageName = useMemo(() => camelCase(type), [type]); + const [globalState, globalDispatch] = useGlobalState(); + const storedRuns = useMemo( + () => ((globalState as unknown) as Record)[camelCase(pageName)]?.runs ?? [], + [pageName, globalState] + ); const runs: string[] = useMemo(() => data?.runs ?? [], [data]); const tags: Tags = useMemo( @@ -183,7 +194,7 @@ const useTagFilter = (type: string, running: boolean) => { const [state, dispatch] = useReducer(reducer, { initRuns: [], - globalRuns: globalState.runs, + globalRuns: storedRuns, runs: [], selectedRuns: [], initTags: {}, @@ -211,7 +222,15 @@ const useTagFilter = (type: string, running: boolean) => { }); } }, [queryRuns, state.runs]); - useEffect(() => globalDispatch({runs: state.globalRuns}), [state.globalRuns, globalDispatch]); + useEffect( + () => + globalDispatch({ + [pageName]: { + runs: state.globalRuns + } + }), + [pageName, state.globalRuns, globalDispatch] + ); const tagsWithSingleRun = useMemo( () => diff --git a/frontend/packages/core/src/pages/graph.tsx b/frontend/packages/core/src/pages/graph.tsx index 6982f8b7..0421f7c9 100644 --- a/frontend/packages/core/src/pages/graph.tsx +++ b/frontend/packages/core/src/pages/graph.tsx @@ -75,7 +75,7 @@ const Graph: FunctionComponent = () => { const graph = useRef(null); const file = useRef(null); - const [files, setFiles] = useState(globalState.model); + const [files, setFiles] = useState(globalState.graph.model); const onClickFile = useCallback(() => { if (file.current) { file.current.value = ''; @@ -86,7 +86,7 @@ const Graph: FunctionComponent = () => { (e: React.ChangeEvent) => { const target = e.target; if (target && target.files && target.files.length) { - globalDispatch({model: target.files}); + globalDispatch({graph: {model: target.files}}); setFiles(target.files); } }, -- GitLab