From bfa23c2576877ee0239ebf63de0412f0232f6f7b Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Sat, 29 Feb 2020 23:52:35 +0800 Subject: [PATCH] VisualDL 2.0 development in progress (#571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: initialize VisualDL 2.0 * refactor: fix dev server problem * refactor: add i18n * refactor: fix i18n * infra i18n支持 * styled-components for debug * infra. essential hack for styled comopnent * use yaml for translation * fix: navbar url problem * feat: add nuxt module to build locales * fix: index route redirect error * feat: add page title * refactor: move i18n to module * feat: add html lang attribute * R.I.P * Hello React * refactor: initialize VisualDL 2.0 * fix: layout rerender * add favicon * add page title * add meta tags * feat: finish tag filter * refactor: hook tastes good * add single select * finish components * add api server * scalars segregate metrics * json-server sucks * echarts * add eslint * bug fix * finish scalars page * change layout, fix aside * add commit hook * use tag filter hook * add chart loading * encapsulate run select * samples page under construction * finish images * feat: graph page, still need some polishment * finish high-dimensional * fix mock data problem * update readme * fix build * fix: use Buffer.from instead of constractor * update Readme Co-authored-by: Niandalu --- .travis.yml | 117 +- frontend/.babelrc | 7 - frontend/.editorconfig | 13 + frontend/.eslintrc.js | 55 +- frontend/.fecsignore | 6 - frontend/.fecsrc | 26 - frontend/.github/semantic.yml | 5 + frontend/.gitignore | 27 + frontend/.nvmrc | 1 + frontend/README.md | 49 + frontend/babel.config.js | 14 + frontend/components/AsideDivider.tsx | 18 + frontend/components/Button.tsx | 58 + frontend/components/Chart.tsx | 31 + frontend/components/ChartPage.tsx | 48 + frontend/components/Checkbox.tsx | 113 + frontend/components/Content.tsx | 42 + frontend/components/Field.tsx | 26 + frontend/components/GraphPage/NodeInfo.tsx | 71 + frontend/components/Icon.tsx | 12 + frontend/components/Image.tsx | 44 + frontend/components/Input.tsx | 56 + frontend/components/Layout.tsx | 29 + frontend/components/LineChart.tsx | 100 + frontend/components/Navbar.tsx | 85 + frontend/components/Pagination.tsx | 127 + frontend/components/RadioButton.tsx | 86 + frontend/components/RadioGroup.tsx | 42 + frontend/components/RangeSlider.tsx | 106 + frontend/components/RunSelect.tsx | 36 + frontend/components/RunningToggle.tsx | 30 + frontend/components/SampleChart.tsx | 111 + frontend/components/ScalarChart.tsx | 182 + frontend/components/ScatterChart.tsx | 79 + frontend/components/SearchInput.tsx | 36 + frontend/components/Select.tsx | 223 + frontend/components/SmoothingSlider.tsx | 34 + frontend/components/StepSlider.tsx | 42 + frontend/components/Tag.tsx | 46 + frontend/components/TagFilter.tsx | 111 + frontend/components/Title.tsx | 13 + frontend/deprecated/App.san | 125 - .../deprecated/common/component/AppMenu.san | 94 - .../common/component/CheckBoxGroup.san | 114 - .../common/component/DropDownMenu.san | 43 - .../common/component/ExpandPanel.san | 73 - .../common/component/RadioGroup.san | 43 - .../deprecated/common/component/Slider.san | 65 - frontend/deprecated/graph/Graph.san | 69 - frontend/deprecated/graph/index.js | 9 - frontend/deprecated/graph/ui/chart.san | 190 - frontend/deprecated/graph/ui/config.san | 84 - frontend/deprecated/histogram/Histogram.san | 148 - frontend/deprecated/histogram/index.js | 9 - frontend/deprecated/histogram/ui/chart.san | 534 - .../deprecated/histogram/ui/chartPage.san | 83 - frontend/deprecated/histogram/ui/config.san | 99 - frontend/deprecated/home/Home.san | 17 - frontend/deprecated/home/index.js | 8 - frontend/deprecated/images/Images.san | 160 - frontend/deprecated/images/index.js | 9 - frontend/deprecated/images/ui/chartPage.san | 93 - frontend/deprecated/images/ui/config.san | 68 - frontend/deprecated/images/ui/image.san | 165 - frontend/deprecated/scalars/Scalars.san | 165 - frontend/deprecated/scalars/index.js | 15 - frontend/deprecated/scalars/ui/chart.san | 610 -- frontend/deprecated/scalars/ui/chartPage.san | 90 - frontend/deprecated/scalars/ui/config.san | 139 - frontend/hooks/useClickOutside.ts | 40 + frontend/hooks/useECharts.ts | 46 + frontend/hooks/useTagFilter.ts | 143 + frontend/lint-staged.config.js | 4 + frontend/mock/API.md | 27 - frontend/mock/data/plugin/audio/audio.js | 39 - .../mock/data/plugin/audio/individualaudio.js | 22 - frontend/mock/data/plugin/audio/tags.js | 36 - .../mock/data/plugin/embeddings/embeddings.js | 91 - frontend/mock/data/plugin/graphs/graph.js | 704 -- .../mock/data/plugin/histograms/histograms.js | 244 - frontend/mock/data/plugin/histograms/tags.js | 47 - frontend/mock/data/plugin/images/images.js | 39 - .../data/plugin/images/individualImage.js | 22 - frontend/mock/data/plugin/images/tags.js | 36 - frontend/mock/data/plugin/scalars/scalars.js | 38 - frontend/mock/data/plugin/scalars/tags.js | 43 - frontend/mock/data/plugin/texts/tags.js | 39 - frontend/mock/data/plugin/texts/texts.js | 38 - frontend/mock/data/runs.js | 22 - frontend/mock/embeddings/embeddings.ts | 38 + frontend/mock/graphs/graph.ts | 1 + frontend/mock/images/images.ts | 42 + frontend/mock/images/individualImage.ts | 19 + frontend/mock/images/tags.ts | 25 + frontend/mock/runs.ts | 1 + frontend/mock/scalars/scalars.ts | 1010 ++ frontend/mock/scalars/tags.ts | 4 + frontend/next-env.d.ts | 2 + frontend/next.config.js | 32 + frontend/package.json | 180 +- frontend/pages/_app.tsx | 58 + frontend/pages/_document.tsx | 58 + frontend/pages/_error.tsx | 27 + frontend/pages/graphs.tsx | 249 + frontend/pages/high-dimensional.tsx | 144 + frontend/pages/highDimension.tsx | 24 + frontend/pages/index.tsx | 18 + frontend/pages/samples.tsx | 140 + frontend/pages/scalars.tsx | 120 + frontend/prettier.config.js | 14 + frontend/public/favicon.ico | Bin 0 -> 1370 bytes frontend/public/images/logo.svg | 1 + frontend/public/static/locales/en/common.json | 20 + frontend/public/static/locales/en/errors.json | 4 + frontend/public/static/locales/en/graphs.json | 10 + .../static/locales/en/high-dimensional.json | 9 + .../static/locales/en/highDimension.json | 1 + .../public/static/locales/en/samples.json | 7 + .../public/static/locales/en/scalars.json | 17 + frontend/public/static/locales/zh/common.json | 16 + frontend/public/static/locales/zh/errors.json | 4 + frontend/public/static/locales/zh/graphs.json | 1 + .../static/locales/zh/high-dimensional.json | 9 + .../static/locales/zh/highDimension.json | 1 + .../public/static/locales/zh/samples.json | 7 + .../public/static/locales/zh/scalars.json | 22 + frontend/public/style/fonts/vdl-icon.svg | 22 + frontend/public/style/fonts/vdl-icon.ttf | Bin 0 -> 3444 bytes frontend/public/style/fonts/vdl-icon.woff | Bin 0 -> 3520 bytes frontend/public/style/vdl-icon.css | 61 + frontend/resource/graph/collectDagFacts.ts | 226 + frontend/resource/graph/index.ts | 2 + frontend/resource/graph/style.ts | 26 + frontend/resource/graph/types.ts | 44 + frontend/scripts/build.sh | 21 + frontend/server/index.ts | 33 + frontend/src/App.vue | 78 - frontend/src/assets/favicon.png | Bin 680 -> 0 bytes frontend/src/assets/ic_download.svg | 4 - frontend/src/assets/ic_fullscreen_off.svg | 5 - frontend/src/assets/ic_fullscreen_on.svg | 5 - frontend/src/assets/ic_logo.svg | 3 - frontend/src/assets/ic_undo.svg | 5 - frontend/src/assets/ic_zoom_select_off.svg | 5 - frontend/src/assets/ic_zoom_select_on.svg | 5 - frontend/src/assets/language/en.json | 53 - frontend/src/assets/language/zh.json | 53 - frontend/src/audio/Audio.vue | 160 - frontend/src/audio/ui/AudioPanel.vue | 166 - frontend/src/audio/ui/AudioPanelContainer.vue | 101 - frontend/src/audio/ui/Config.vue | 55 - frontend/src/common/component/AppMenu.vue | 177 - frontend/src/common/component/ExpandPanel.vue | 72 - .../component/Notification/Notification.js | 98 - .../component/Notification/Notification.md | 176 - .../component/Notification/Notification.styl | 51 - .../Notification/NotificationItem.js | 106 - .../common/component/Notification/index.js | 3 - frontend/src/common/component/TagsTab.vue | 53 - frontend/src/common/component/ui-common.styl | 108 - frontend/src/common/util/autoAdjustHeight.js | 13 - frontend/src/common/util/downLoadFile.js | 70 - frontend/src/common/util/http.js | 52 - frontend/src/common/util/index.js | 5 - frontend/src/common/util/number.js | 3 - frontend/src/common/util/quantile.js | 21 - frontend/src/common/util/routeTo.js | 8 - frontend/src/graph/Graph.vue | 59 - frontend/src/graph/ui/Chart.vue | 377 - frontend/src/graph/ui/Config.vue | 102 - frontend/src/graph/ui/dragHelper.js | 27 - .../src/graph/ui/svgToPngDownloadHelper.js | 501 - .../src/high-dimensional/HighDimensional.vue | 133 - frontend/src/high-dimensional/ui/Chart.vue | 347 - frontend/src/high-dimensional/ui/Config.vue | 99 - frontend/src/histogram/Histogram.vue | 149 - frontend/src/histogram/histogramHelper.js | 67 - frontend/src/histogram/ui/Chart.vue | 573 -- frontend/src/histogram/ui/ChartPage.vue | 103 - frontend/src/histogram/ui/Config.vue | 77 - frontend/src/images/Images.vue | 160 - frontend/src/images/ui/ChartPage.vue | 102 - frontend/src/images/ui/Config.vue | 60 - frontend/src/images/ui/Image.vue | 183 - frontend/src/index.js | 39 - frontend/src/metrics/Metrics.vue | 262 - frontend/src/metrics/histogramHelper.js | 67 - frontend/src/metrics/ui/ChartPage.vue | 103 - frontend/src/metrics/ui/Config.vue | 210 - frontend/src/metrics/ui/HistogramChart.vue | 580 -- frontend/src/metrics/ui/ScalarChart.vue | 739 -- frontend/src/router/index.js | 43 - frontend/src/samples/Samples.vue | 260 - frontend/src/samples/ui/Audio.vue | 171 - frontend/src/samples/ui/Config.vue | 93 - frontend/src/samples/ui/Image.vue | 187 - frontend/src/samples/ui/SamplePage.vue | 128 - frontend/src/samples/ui/Text.vue | 172 - frontend/src/scalars/Scalars.vue | 152 - frontend/src/scalars/ui/Chart.vue | 739 -- frontend/src/scalars/ui/ChartPage.vue | 97 - frontend/src/scalars/ui/Config.vue | 137 - frontend/src/service.js | 29 - frontend/src/style/main.styl | 11 - frontend/src/style/variables.styl | 13 - frontend/src/texts/Texts.vue | 159 - frontend/src/texts/ui/Chart.vue | 167 - frontend/src/texts/ui/ChartPage.vue | 94 - frontend/src/texts/ui/Config.vue | 68 - frontend/template/index.html | 15 - frontend/tool/HtmlReplacePlugin.js | 25 - frontend/tool/build.js | 74 - frontend/tool/demo-server.js | 94 - frontend/tool/dev-client.js | 8 - frontend/tool/dev-server.js | 93 - frontend/tool/entry.js | 77 - frontend/tool/webpack.config.js | 137 - frontend/tool/webpack.demo.config.js | 33 - frontend/tool/webpack.dev.config.js | 33 - frontend/tool/webpack.prod.config.js | 71 - frontend/tsconfig.json | 39 + frontend/tsconfig.server.json | 12 + frontend/types/app-shim.d.ts | 49 + frontend/types/dagre-d3.d.ts | 30 + frontend/types/echarts.d.ts | 1 + frontend/types/index.d.ts | 6 + frontend/types/next.d.ts | 10 + frontend/types/saveSvgAsPng.d.ts | 3 + frontend/utils/chart.ts | 127 + frontend/utils/fetch.ts | 26 + frontend/utils/i18n.ts | 27 + frontend/utils/mock.ts | 48 + frontend/utils/scalars.ts | 139 + frontend/utils/style.ts | 142 + frontend/yarn.lock | 8957 +++++++++++++++++ requirements.txt | 2 +- scripts/build.sh | 34 +- scripts/start_demo_server.sh | 14 +- scripts/start_dev_server.sh | 13 +- scripts/tests.sh | 18 - 240 files changed, 14793 insertions(+), 15285 deletions(-) delete mode 100644 frontend/.babelrc create mode 100644 frontend/.editorconfig delete mode 100644 frontend/.fecsignore delete mode 100644 frontend/.fecsrc create mode 100644 frontend/.github/semantic.yml create mode 100644 frontend/.gitignore create mode 100644 frontend/.nvmrc create mode 100644 frontend/README.md create mode 100644 frontend/babel.config.js create mode 100644 frontend/components/AsideDivider.tsx create mode 100644 frontend/components/Button.tsx create mode 100644 frontend/components/Chart.tsx create mode 100644 frontend/components/ChartPage.tsx create mode 100644 frontend/components/Checkbox.tsx create mode 100644 frontend/components/Content.tsx create mode 100644 frontend/components/Field.tsx create mode 100644 frontend/components/GraphPage/NodeInfo.tsx create mode 100644 frontend/components/Icon.tsx create mode 100644 frontend/components/Image.tsx create mode 100644 frontend/components/Input.tsx create mode 100644 frontend/components/Layout.tsx create mode 100644 frontend/components/LineChart.tsx create mode 100644 frontend/components/Navbar.tsx create mode 100644 frontend/components/Pagination.tsx create mode 100644 frontend/components/RadioButton.tsx create mode 100644 frontend/components/RadioGroup.tsx create mode 100644 frontend/components/RangeSlider.tsx create mode 100644 frontend/components/RunSelect.tsx create mode 100644 frontend/components/RunningToggle.tsx create mode 100644 frontend/components/SampleChart.tsx create mode 100644 frontend/components/ScalarChart.tsx create mode 100644 frontend/components/ScatterChart.tsx create mode 100644 frontend/components/SearchInput.tsx create mode 100644 frontend/components/Select.tsx create mode 100644 frontend/components/SmoothingSlider.tsx create mode 100644 frontend/components/StepSlider.tsx create mode 100644 frontend/components/Tag.tsx create mode 100644 frontend/components/TagFilter.tsx create mode 100644 frontend/components/Title.tsx delete mode 100644 frontend/deprecated/App.san delete mode 100644 frontend/deprecated/common/component/AppMenu.san delete mode 100644 frontend/deprecated/common/component/CheckBoxGroup.san delete mode 100644 frontend/deprecated/common/component/DropDownMenu.san delete mode 100644 frontend/deprecated/common/component/ExpandPanel.san delete mode 100644 frontend/deprecated/common/component/RadioGroup.san delete mode 100644 frontend/deprecated/common/component/Slider.san delete mode 100644 frontend/deprecated/graph/Graph.san delete mode 100644 frontend/deprecated/graph/index.js delete mode 100644 frontend/deprecated/graph/ui/chart.san delete mode 100644 frontend/deprecated/graph/ui/config.san delete mode 100644 frontend/deprecated/histogram/Histogram.san delete mode 100644 frontend/deprecated/histogram/index.js delete mode 100644 frontend/deprecated/histogram/ui/chart.san delete mode 100644 frontend/deprecated/histogram/ui/chartPage.san delete mode 100644 frontend/deprecated/histogram/ui/config.san delete mode 100644 frontend/deprecated/home/Home.san delete mode 100644 frontend/deprecated/home/index.js delete mode 100644 frontend/deprecated/images/Images.san delete mode 100644 frontend/deprecated/images/index.js delete mode 100644 frontend/deprecated/images/ui/chartPage.san delete mode 100644 frontend/deprecated/images/ui/config.san delete mode 100644 frontend/deprecated/images/ui/image.san delete mode 100644 frontend/deprecated/scalars/Scalars.san delete mode 100644 frontend/deprecated/scalars/index.js delete mode 100644 frontend/deprecated/scalars/ui/chart.san delete mode 100644 frontend/deprecated/scalars/ui/chartPage.san delete mode 100644 frontend/deprecated/scalars/ui/config.san create mode 100644 frontend/hooks/useClickOutside.ts create mode 100644 frontend/hooks/useECharts.ts create mode 100644 frontend/hooks/useTagFilter.ts create mode 100644 frontend/lint-staged.config.js delete mode 100644 frontend/mock/API.md delete mode 100644 frontend/mock/data/plugin/audio/audio.js delete mode 100644 frontend/mock/data/plugin/audio/individualaudio.js delete mode 100644 frontend/mock/data/plugin/audio/tags.js delete mode 100644 frontend/mock/data/plugin/embeddings/embeddings.js delete mode 100644 frontend/mock/data/plugin/graphs/graph.js delete mode 100644 frontend/mock/data/plugin/histograms/histograms.js delete mode 100644 frontend/mock/data/plugin/histograms/tags.js delete mode 100644 frontend/mock/data/plugin/images/images.js delete mode 100644 frontend/mock/data/plugin/images/individualImage.js delete mode 100644 frontend/mock/data/plugin/images/tags.js delete mode 100644 frontend/mock/data/plugin/scalars/scalars.js delete mode 100644 frontend/mock/data/plugin/scalars/tags.js delete mode 100644 frontend/mock/data/plugin/texts/tags.js delete mode 100644 frontend/mock/data/plugin/texts/texts.js delete mode 100644 frontend/mock/data/runs.js create mode 100644 frontend/mock/embeddings/embeddings.ts create mode 100644 frontend/mock/graphs/graph.ts create mode 100644 frontend/mock/images/images.ts create mode 100644 frontend/mock/images/individualImage.ts create mode 100644 frontend/mock/images/tags.ts create mode 100644 frontend/mock/runs.ts create mode 100644 frontend/mock/scalars/scalars.ts create mode 100644 frontend/mock/scalars/tags.ts create mode 100644 frontend/next-env.d.ts create mode 100644 frontend/next.config.js create mode 100644 frontend/pages/_app.tsx create mode 100644 frontend/pages/_document.tsx create mode 100644 frontend/pages/_error.tsx create mode 100644 frontend/pages/graphs.tsx create mode 100644 frontend/pages/high-dimensional.tsx create mode 100644 frontend/pages/highDimension.tsx create mode 100644 frontend/pages/index.tsx create mode 100644 frontend/pages/samples.tsx create mode 100644 frontend/pages/scalars.tsx create mode 100644 frontend/prettier.config.js create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/images/logo.svg create mode 100644 frontend/public/static/locales/en/common.json create mode 100644 frontend/public/static/locales/en/errors.json create mode 100644 frontend/public/static/locales/en/graphs.json create mode 100644 frontend/public/static/locales/en/high-dimensional.json create mode 100644 frontend/public/static/locales/en/highDimension.json create mode 100644 frontend/public/static/locales/en/samples.json create mode 100644 frontend/public/static/locales/en/scalars.json create mode 100644 frontend/public/static/locales/zh/common.json create mode 100644 frontend/public/static/locales/zh/errors.json create mode 100644 frontend/public/static/locales/zh/graphs.json create mode 100644 frontend/public/static/locales/zh/high-dimensional.json create mode 100644 frontend/public/static/locales/zh/highDimension.json create mode 100644 frontend/public/static/locales/zh/samples.json create mode 100644 frontend/public/static/locales/zh/scalars.json create mode 100755 frontend/public/style/fonts/vdl-icon.svg create mode 100755 frontend/public/style/fonts/vdl-icon.ttf create mode 100755 frontend/public/style/fonts/vdl-icon.woff create mode 100755 frontend/public/style/vdl-icon.css create mode 100644 frontend/resource/graph/collectDagFacts.ts create mode 100644 frontend/resource/graph/index.ts create mode 100644 frontend/resource/graph/style.ts create mode 100644 frontend/resource/graph/types.ts create mode 100755 frontend/scripts/build.sh create mode 100644 frontend/server/index.ts delete mode 100644 frontend/src/App.vue delete mode 100644 frontend/src/assets/favicon.png delete mode 100755 frontend/src/assets/ic_download.svg delete mode 100755 frontend/src/assets/ic_fullscreen_off.svg delete mode 100755 frontend/src/assets/ic_fullscreen_on.svg delete mode 100644 frontend/src/assets/ic_logo.svg delete mode 100755 frontend/src/assets/ic_undo.svg delete mode 100755 frontend/src/assets/ic_zoom_select_off.svg delete mode 100755 frontend/src/assets/ic_zoom_select_on.svg delete mode 100644 frontend/src/assets/language/en.json delete mode 100644 frontend/src/assets/language/zh.json delete mode 100644 frontend/src/audio/Audio.vue delete mode 100644 frontend/src/audio/ui/AudioPanel.vue delete mode 100644 frontend/src/audio/ui/AudioPanelContainer.vue delete mode 100644 frontend/src/audio/ui/Config.vue delete mode 100644 frontend/src/common/component/AppMenu.vue delete mode 100644 frontend/src/common/component/ExpandPanel.vue delete mode 100644 frontend/src/common/component/Notification/Notification.js delete mode 100644 frontend/src/common/component/Notification/Notification.md delete mode 100644 frontend/src/common/component/Notification/Notification.styl delete mode 100644 frontend/src/common/component/Notification/NotificationItem.js delete mode 100644 frontend/src/common/component/Notification/index.js delete mode 100644 frontend/src/common/component/TagsTab.vue delete mode 100644 frontend/src/common/component/ui-common.styl delete mode 100644 frontend/src/common/util/autoAdjustHeight.js delete mode 100644 frontend/src/common/util/downLoadFile.js delete mode 100644 frontend/src/common/util/http.js delete mode 100644 frontend/src/common/util/index.js delete mode 100644 frontend/src/common/util/number.js delete mode 100644 frontend/src/common/util/quantile.js delete mode 100644 frontend/src/common/util/routeTo.js delete mode 100644 frontend/src/graph/Graph.vue delete mode 100644 frontend/src/graph/ui/Chart.vue delete mode 100644 frontend/src/graph/ui/Config.vue delete mode 100644 frontend/src/graph/ui/dragHelper.js delete mode 100644 frontend/src/graph/ui/svgToPngDownloadHelper.js delete mode 100644 frontend/src/high-dimensional/HighDimensional.vue delete mode 100644 frontend/src/high-dimensional/ui/Chart.vue delete mode 100644 frontend/src/high-dimensional/ui/Config.vue delete mode 100644 frontend/src/histogram/Histogram.vue delete mode 100644 frontend/src/histogram/histogramHelper.js delete mode 100644 frontend/src/histogram/ui/Chart.vue delete mode 100644 frontend/src/histogram/ui/ChartPage.vue delete mode 100644 frontend/src/histogram/ui/Config.vue delete mode 100644 frontend/src/images/Images.vue delete mode 100644 frontend/src/images/ui/ChartPage.vue delete mode 100644 frontend/src/images/ui/Config.vue delete mode 100644 frontend/src/images/ui/Image.vue delete mode 100644 frontend/src/index.js delete mode 100644 frontend/src/metrics/Metrics.vue delete mode 100644 frontend/src/metrics/histogramHelper.js delete mode 100644 frontend/src/metrics/ui/ChartPage.vue delete mode 100644 frontend/src/metrics/ui/Config.vue delete mode 100644 frontend/src/metrics/ui/HistogramChart.vue delete mode 100644 frontend/src/metrics/ui/ScalarChart.vue delete mode 100644 frontend/src/router/index.js delete mode 100644 frontend/src/samples/Samples.vue delete mode 100644 frontend/src/samples/ui/Audio.vue delete mode 100644 frontend/src/samples/ui/Config.vue delete mode 100644 frontend/src/samples/ui/Image.vue delete mode 100644 frontend/src/samples/ui/SamplePage.vue delete mode 100644 frontend/src/samples/ui/Text.vue delete mode 100644 frontend/src/scalars/Scalars.vue delete mode 100644 frontend/src/scalars/ui/Chart.vue delete mode 100644 frontend/src/scalars/ui/ChartPage.vue delete mode 100644 frontend/src/scalars/ui/Config.vue delete mode 100644 frontend/src/service.js delete mode 100644 frontend/src/style/main.styl delete mode 100644 frontend/src/style/variables.styl delete mode 100644 frontend/src/texts/Texts.vue delete mode 100644 frontend/src/texts/ui/Chart.vue delete mode 100644 frontend/src/texts/ui/ChartPage.vue delete mode 100644 frontend/src/texts/ui/Config.vue delete mode 100755 frontend/template/index.html delete mode 100644 frontend/tool/HtmlReplacePlugin.js delete mode 100644 frontend/tool/build.js delete mode 100644 frontend/tool/demo-server.js delete mode 100644 frontend/tool/dev-client.js delete mode 100644 frontend/tool/dev-server.js delete mode 100644 frontend/tool/entry.js delete mode 100644 frontend/tool/webpack.config.js delete mode 100644 frontend/tool/webpack.demo.config.js delete mode 100644 frontend/tool/webpack.dev.config.js delete mode 100644 frontend/tool/webpack.prod.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.server.json create mode 100644 frontend/types/app-shim.d.ts create mode 100644 frontend/types/dagre-d3.d.ts create mode 100644 frontend/types/echarts.d.ts create mode 100644 frontend/types/index.d.ts create mode 100644 frontend/types/next.d.ts create mode 100644 frontend/types/saveSvgAsPng.d.ts create mode 100644 frontend/utils/chart.ts create mode 100644 frontend/utils/fetch.ts create mode 100644 frontend/utils/i18n.ts create mode 100644 frontend/utils/mock.ts create mode 100644 frontend/utils/scalars.ts create mode 100644 frontend/utils/style.ts create mode 100644 frontend/yarn.lock diff --git a/.travis.yml b/.travis.yml index cffd65a4..30cfd382 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,61 +1,72 @@ -language: cpp +matrix: + include: + - language: cpp + compiler: clang + cache: + - pip + - ccache + sudo: required + dist: trusty -compiler: clang + os: + - linux + # much bug with osx environment + # TODO(ChunweiYan) support osx in the future + #- osx -cache: - - pip - - ccache - - yarn - - npm -sudo: required -dist: trusty + env: + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=check_style" + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=test" + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=test_python3" + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=build_doc" -os: - - linux - # much bug with osx environment - # TODO(ChunweiYan) support osx in the future - #- osx + addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + - gcc-4.8 + - g++-4.8 + - git + - python + - python-pip + - python2.7-dev + - python-wheel + - python3-pip + - python3-dev + - python3-wheel + - clang-format-3.8 + - ccache + ssh_known_hosts: 13.229.163.131 -env: - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=check_style" - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=test" - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=test_python3" - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && JOB=build_doc" + before_install: + - eval "${MATRIX_EVAL}" + - if [[ "$JOB" == "check_style" ]]; then sudo ln -s /usr/bin/clang-format-3.8 /usr/bin/clang-format; sudo pip install pre-commit flake8; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade python; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install brew-pip; fi -addons: - apt: - sources: - - llvm-toolchain-trusty-5.0 - packages: - - clang-5.0 - - gcc-4.8 - - g++-4.8 - - git - - python - - python-pip - - python2.7-dev - - python-wheel - - python3-pip - - python3-dev - - python3-wheel - - clang-format-3.8 - - ccache - - npm - - nodejs - ssh_known_hosts: 13.229.163.131 - -before_install: - - eval "${MATRIX_EVAL}" - - if [[ "$JOB" == "check_style" ]]; then sudo ln -s /usr/bin/clang-format-3.8 /usr/bin/clang-format; sudo pip install pre-commit flake8; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade python; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install brew-pip; fi - -script: - - if [[ "$JOB" == "check_style" ]]; then ./scripts/check_style.sh; fi - - if [[ "$JOB" == "test" ]]; then ./scripts/tests.sh all; fi - - if [[ "$JOB" == "test_python3" ]]; then WITH_PYTHON3=ON ./scripts/tests.sh all; fi - - if [[ "$JOB" == "build_doc" ]]; then ./scripts/deploy_docs_on_travis.sh; fi; + script: + - if [[ "$JOB" == "check_style" ]]; then ./scripts/check_style.sh; fi + - if [[ "$JOB" == "test" ]]; then ./scripts/tests.sh all; fi + - if [[ "$JOB" == "test_python3" ]]; then WITH_PYTHON3=ON ./scripts/tests.sh all; fi + - if [[ "$JOB" == "build_doc" ]]; then ./scripts/deploy_docs_on_travis.sh; fi; + - language: node_js + dist: trusty + node_js: + - 12 + sudo: false + cache: + - npm + - yarn + env: + - NODE_ENV=production + before_script: + - cd frontend + script: + - if [[ "$JOB" == "check_style" ]]; then yarn lint; fi + - if [[ "$JOB" == "test" ]]; then yarn test; fi notifications: email: diff --git a/frontend/.babelrc b/frontend/.babelrc deleted file mode 100644 index f35b67b9..00000000 --- a/frontend/.babelrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presets": [ - ["es2015", {"modules": false}], - "stage-0" - ], - "plugins": ["transform-class-properties"] -} diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 00000000..e9a9bff8 --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index d9d63bb7..4ca5f799 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,23 +1,34 @@ module.exports = { - extends: [ - 'google', - 'plugin:vue/base', - 'plugin:vue/essential', - 'plugin:vue/strongly-recommended', - ], - parserOptions: { - "sourceType": "module", - }, - rules: { - // override/add rules settings here, such as: - 'vue/no-unused-vars': 'warn', - 'max-len': ["warn", 120], - "vue/prop-name-casing": ["error"], - 'vue/script-indent': 'error', - - // The following rules should apply eventually. Turn them off for now - // so we can have pre-commit running - 'no-invalid-this': 'off', - 'require-jsdoc': 'off', - } -} + env: { + browser: true, + es6: true, + node: true + }, + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'prettier/@typescript-eslint', + 'plugin:prettier/recommended' + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 2018, + sourceType: 'module' + }, + plugins: ['react', 'react-hooks', '@typescript-eslint'], + settings: { + react: { + version: 'detect' + } + }, + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn' + } +}; diff --git a/frontend/.fecsignore b/frontend/.fecsignore deleted file mode 100644 index 77d57a3a..00000000 --- a/frontend/.fecsignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/**/* -dep/**/* -test/**/* -mock/**/* -example/**/* -output/**/* diff --git a/frontend/.fecsrc b/frontend/.fecsrc deleted file mode 100644 index c5bd3591..00000000 --- a/frontend/.fecsrc +++ /dev/null @@ -1,26 +0,0 @@ -{ - "files": [ - "./src/**/*.san", - "./src/**/*.js", - "./template/**/*.html" - ], - "eslint": { - "rules": { - "fecs-esnext-ext": [ - "2", - [ - "js", - "san" - ] - ], - "fecs-valid-jsdoc": [ - "0" - ] - } - }, - "csshint": {}, - "htmlcs": {}, - "jformatter": {}, - "esformatter": {}, - "csscomb": {} -} diff --git a/frontend/.github/semantic.yml b/frontend/.github/semantic.yml new file mode 100644 index 00000000..40b69276 --- /dev/null +++ b/frontend/.github/semantic.yml @@ -0,0 +1,5 @@ +# Always validate the PR title AND all the commits +titleAndCommits: true +# Allows use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns") +# this is only relevant when using commitsOnly: true (or titleAndCommits: true) +allowMergeCommits: true diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..1e227ae3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build +/dist + +# misc +.DS_Store +.env* +.vscode + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 00000000..66df3b7a --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +12.16.1 diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..f7635da2 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,49 @@ +# FrontEnd of VisualDL + +A platform to visualize the deep learning process and result. + +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![License](https://img.shields.io/github/license/PaddlePaddle/VisualDL?style=flat-square)](https://github.com/PaddlePaddle/VisualDL/blob/develop/LICENSE) [![GitHub top language](https://img.shields.io/github/languages/top/PaddlePaddle/VisualDL?style=flat-square)](https://github.com/PaddlePaddle/VisualDL) [![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/PaddlePaddle/VisualDL?style=flat-square)](https://github.com/PaddlePaddle/VisualDL) [![GitHub issues](https://img.shields.io/github/issues/PaddlePaddle/VisualDL?style=flat-square)](https://github.com/PaddlePaddle/VisualDL/issues) [![GitHub All Releases](https://img.shields.io/github/downloads/PaddlePaddle/VisualDL/total?style=flat-square)](https://github.com/PaddlePaddle/VisualDL/releases) [![GitHub stars](https://img.shields.io/github/stars/PaddlePaddle/VisualDL?style=social)](https://github.com/PaddlePaddle/VisualDL/stargazers) + +**🚧UNDER CONSTRUCTION🚧** + +**🚧SOME FEATURE MAY NOT WORK PROPERLY🚧** + +**🚧PULL REQUESTS WELCOMED🚧** + +## Development + +> nodejs ≥ 10 and npm ≥ 6 is required. + +First, install all dependencies: + +```bash +npm install +# or +yarn +``` + +Then you can start the development server: + +```bash +yarn dev +``` + +Now open [http://localhost:8999](http://localhost:8999) with your browser. + +You can change the port with `PORT` environment variable. + +```bash +PORT=3000 yarn dev +``` + +## Learn More + +This project is based on following projects: + +- [Next.js](https://nextjs.org/) +- [React](https://reactjs.org/) +- [ECharts](https://echarts.apache.org/) + +## License + +Apache-2.0 diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 00000000..dd680565 --- /dev/null +++ b/frontend/babel.config.js @@ -0,0 +1,14 @@ +module.exports = { + presets: ['next/babel'], + plugins: [ + [ + 'styled-components', + { + ssr: true, + displayName: true, + preprocess: false + } + ], + ...(process.env.NODE_ENV !== 'production' ? ['babel-plugin-typescript-to-proptypes'] : []) + ] +}; diff --git a/frontend/components/AsideDivider.tsx b/frontend/components/AsideDivider.tsx new file mode 100644 index 00000000..f47d34f7 --- /dev/null +++ b/frontend/components/AsideDivider.tsx @@ -0,0 +1,18 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {rem} from '~/utils/style'; + +const Divider = styled.hr<{height?: string | number}>` + background-color: transparent; + margin: 0; + border: none; + height: ${({height}) => (height ? ('number' === height ? rem(height) : height) : rem(30))}; +`; + +type AsideDividerProps = { + height?: string | number; +}; + +const AsideDivider: FunctionComponent = ({height}) => ; + +export default AsideDivider; diff --git a/frontend/components/Button.tsx b/frontend/components/Button.tsx new file mode 100644 index 00000000..ee548fbb --- /dev/null +++ b/frontend/components/Button.tsx @@ -0,0 +1,58 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + em, + half, + textInvertColor, + primaryColor, + primaryFocusedColor, + primaryActiveColor, + duration, + easing, + ellipsis, + transitions +} from '~/utils/style'; +import RawIcon from '~/components/Icon'; + +const height = em(36); + +const Wrapper = styled.a` + cursor: pointer; + height: ${height}; + line-height: ${height}; + border-radius: ${half(height)}; + background-color: ${primaryColor}; + color: ${textInvertColor}; + display: block; + text-align: center; + ${transitions('background-color', `${duration} ${easing}`)} + ${ellipsis()} + + &:hover, + &:focus { + background-color: ${primaryFocusedColor}; + } + + &:active { + background-color: ${primaryActiveColor}; + } +`; + +const Icon = styled(RawIcon)` + margin-right: 4px; +`; + +type ButtonProps = { + icon?: string; + onClick?: () => unknown; +}; + +const Button: FunctionComponent = ({icon, children, className, onClick}) => ( + + {icon && } + {children} + +); + +export default Button; diff --git a/frontend/components/Chart.tsx b/frontend/components/Chart.tsx new file mode 100644 index 00000000..975f112b --- /dev/null +++ b/frontend/components/Chart.tsx @@ -0,0 +1,31 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + primaryColor, + backgroundColor, + borderColor, + borderRadius, + duration, + easing, + transitions, + math +} from '~/utils/style'; + +const Div = styled.div` + background-color: ${backgroundColor}; + border: 1px solid ${borderColor}; + border-radius: ${math(`${borderRadius} * 2`)}; + ${transitions(['border-color', 'box-shadow'], `${duration} ${easing}`)} + + &:hover { + border-color: ${primaryColor}; + box-shadow: 0 5px 6px 0 rgba(0, 0, 0, 0.05); + } +`; + +const Chart: FunctionComponent = ({className, children}) => { + return
{children}
; +}; + +export default Chart; diff --git a/frontend/components/ChartPage.tsx b/frontend/components/ChartPage.tsx new file mode 100644 index 00000000..3ddc0313 --- /dev/null +++ b/frontend/components/ChartPage.tsx @@ -0,0 +1,48 @@ +import React, {FunctionComponent, useState} from 'react'; +import styled from 'styled-components'; +import {WithStyled, rem} from '~/utils/style'; +import Chart from '~/components/Chart'; +import Pagination from '~/components/Pagination'; + +const Wrapper = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + align-items: stretch; + align-content: flex-start; + + > * { + margin: 0 ${rem(20)} ${rem(20)} 0; + flex-shrink: 0; + flex-grow: 0; + } +`; + +// TODO: add types +// eslint-disable-next-line +type ChartPageProps = { + items?: T[]; + withChart?: (item: T) => React.ReactNode; +}; + +const ChartPage: FunctionComponent = ({items, withChart, className}) => { + const pageSize = 12; + const total = Math.ceil((items?.length ?? 0) / pageSize); + + const [page, setPage] = useState(1); + + const pageItems = items?.slice((page - 1) * pageSize, page * pageSize) ?? []; + + return ( +
+ + {pageItems.map((item, index) => ( + {withChart?.(item)} + ))} + + +
+ ); +}; + +export default ChartPage; diff --git a/frontend/components/Checkbox.tsx b/frontend/components/Checkbox.tsx new file mode 100644 index 00000000..fa335fed --- /dev/null +++ b/frontend/components/Checkbox.tsx @@ -0,0 +1,113 @@ +import React, {FunctionComponent, useState} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + em, + textLighterColor, + textInvertColor, + backgroundColor, + primaryColor, + duration, + size, + easing, + ellipsis, + transitions, + math, + darken, + lighten +} from '~/utils/style'; + +const height = em(20); +const checkSize = em(16); +const checkMark = + // eslint-disable-next-line + 'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjgiIHZpZXdCb3g9IjAgMCAxMSA4IiB3aWR0aD0iMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTkuNDc5NDI3MDggMTAuMTg3NWgtNS4yNXYtMS4zMTI1aDMuOTM3bC4wMDA1LTcuODc1aDEuMzEyNXoiIGZpbGw9IiNmNWY1ZjUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJtYXRyaXgoLjcwNzEwNjc4IC43MDcxMDY3OCAtLjcwNzEwNjc4IC43MDcxMDY3OCA0Ljk2Mjk5NCAtNi4yMDg0NCkiLz48L3N2Zz4='; + +const Wrapper = styled.label<{disabled?: boolean}>` + position: relative; + display: inline-flex; + align-items: flex-start; + cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')}; +`; + +const Input = styled.input.attrs<{disabled?: boolean}>(props => ({ + type: 'checkbox', + disabled: !!props.disabled +}))` + ${size(0, 0)} + position: absolute; + left: 0; + top: 0; + opacity: 0; + pointer-events: none; +`; + +const Inner = styled.div<{checked?: boolean; size?: string; disabled?: boolean}>` + color: ${props => (props.checked ? textInvertColor : 'transparent')}; + flex-shrink: 0; + ${props => size(math(`${checkSize} * ${props.size === 'small' ? 0.875 : 1}`))} + margin: ${math(`(${height} - ${checkSize}) / 2`)} 0; + margin-right: ${em(4)}; + border: 1px solid ${props => (props.disabled || !props.checked ? textLighterColor : primaryColor)}; + background-color: ${props => + props.disabled + ? props.checked + ? textLighterColor + : lighten(0.333, textLighterColor) + : props.checked + ? primaryColor + : backgroundColor}; + background-image: ${props => (props.checked ? `url("${checkMark}")` : 'none')}; + background-repeat: no-repeat; + background-position: center center; + background-size: ${em(10)} ${em(8)}; + position: relative; + ${transitions(['border-color', 'background-color', 'color'], `${duration} ${easing}`)} + + ${Wrapper}:hover > & { + border-color: ${props => + props.disabled ? textLighterColor : props.checked ? primaryColor : darken(0.1, textLighterColor)}; + } +`; + +const Content = styled.div<{disabled?: boolean}>` + line-height: ${height}; + flex-grow: 1; + ${props => (props.disabled ? `color: ${textLighterColor};` : '')} + ${ellipsis()} +`; + +type CheckboxProps = { + value?: boolean; + onChange?: (value: boolean) => unknown; + size?: 'small'; + disabled?: boolean; +}; + +const Checkbox: FunctionComponent = ({ + value, + children, + size, + disabled, + className, + onChange +}) => { + const [checked, setChecked] = useState(!!value); + const onChangeInput = (e: React.ChangeEvent) => { + if (disabled) { + return; + } + setChecked(e.target.checked); + onChange?.(e.target.checked); + }; + + return ( + + + + {children} + + ); +}; + +export default Checkbox; diff --git a/frontend/components/Content.tsx b/frontend/components/Content.tsx new file mode 100644 index 00000000..8f2b413b --- /dev/null +++ b/frontend/components/Content.tsx @@ -0,0 +1,42 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {rem, math, headerHeight, asideWidth, backgroundColor} from '~/utils/style'; + +const margin = rem(20); +const padding = rem(20); + +const Section = styled.section` + /* trigger BFC */ + overflow: hidden; +`; + +const Article = styled.article<{aside?: boolean}>` + margin: ${margin}; + margin-right: ${props => (props.aside ? math(`${margin} + ${asideWidth}`) : margin)}; + padding: ${padding}; + background-color: ${backgroundColor}; + min-height: calc(100vh - ${math(`${margin} * 2 + ${headerHeight}`)}); +`; + +const Aside = styled.aside` + width: ${asideWidth}; + padding: ${padding}; + background-color: ${backgroundColor}; + height: calc(100vh - ${headerHeight}); + position: fixed; + top: ${headerHeight}; + right: 0; +`; + +type ContentProps = { + aside?: React.ReactNode; +}; + +const Content: FunctionComponent = ({children, aside}) => ( +
+
{children}
+ {aside && } +
+); + +export default Content; diff --git a/frontend/components/Field.tsx b/frontend/components/Field.tsx new file mode 100644 index 00000000..8f6a31b4 --- /dev/null +++ b/frontend/components/Field.tsx @@ -0,0 +1,26 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {WithStyled, rem} from '~/utils/style'; + +const Wrapper = styled.div` + & + & { + margin-top: ${rem(20)}; + } +`; + +const Label = styled.div` + margin-bottom: ${rem(10)}; +`; + +type FieldProps = { + label?: string; +}; + +const Field: FunctionComponent = ({label, children, className}) => ( + + {label && } + {children} + +); + +export default Field; diff --git a/frontend/components/GraphPage/NodeInfo.tsx b/frontend/components/GraphPage/NodeInfo.tsx new file mode 100644 index 00000000..1e39204e --- /dev/null +++ b/frontend/components/GraphPage/NodeInfo.tsx @@ -0,0 +1,71 @@ +import React, {FunctionComponent} from 'react'; +import {useTranslation, NextI18NextPage} from '~/utils/i18n'; +import {NodeType, TypedNode} from '~/resource/graph'; + +const typeName: {[k in NodeType]: string} = { + [NodeType.Input]: 'input', + [NodeType.Output]: 'output', + [NodeType.Op]: 'operator' +}; + +export interface NodeInfoProps { + node?: TypedNode | {type: 'unknown'; guessType: NodeType; msg: string}; +} + +const NodeInfo: FunctionComponent = props => { + const {t} = useTranslation(['graphs']); + if (!props.node) { + return

{t('click-node')}

; + } + + const node = props.node; + switch (node.type) { + case NodeType.Input: + case NodeType.Output: + return ( +
    +
  • + {t('node-type')}: {typeName[node.type]} +
  • +
  • + {t('node-name')}: {node.name} +
  • +
  • + {t('node-data-shape')}: {node.shape} +
  • +
  • + {t('node-data-type')}: {node.data_type} +
  • +
+ ); + case NodeType.Op: + return ( +
    +
  • + {t('node-type')}: {typeName[node.type]} +
  • +
  • + {t('input')}: {node.input} +
  • +
  • + {t('op-type')}: {node.opType} +
  • +
  • + {t('output')}: {node.output} +
  • +
+ ); + case 'unknown': + return ( +
    +
  • + {t('node-type')}: {typeName[node.guessType]} +
  • +
+ ); + default: + return <>; + } +}; + +export default NodeInfo; diff --git a/frontend/components/Icon.tsx b/frontend/components/Icon.tsx new file mode 100644 index 00000000..c856236c --- /dev/null +++ b/frontend/components/Icon.tsx @@ -0,0 +1,12 @@ +import React, {FunctionComponent} from 'react'; +import {WithStyled} from '~/utils/style'; + +type IconProps = { + type: string; +}; + +const Icon: FunctionComponent = ({type, className}) => { + return ; +}; + +export default Icon; diff --git a/frontend/components/Image.tsx b/frontend/components/Image.tsx new file mode 100644 index 00000000..5f541477 --- /dev/null +++ b/frontend/components/Image.tsx @@ -0,0 +1,44 @@ +import React, {FunctionComponent, useEffect, useState, useRef} from 'react'; +import {useTranslation} from '~/utils/i18n'; +import fetch from 'isomorphic-unfetch'; + +type ImageProps = { + src?: string; +}; + +const Image: FunctionComponent = ({src}) => { + const {t} = useTranslation('common'); + + const [url, setUrl] = useState(''); + const [loading, setLoading] = useState(false); + + const controller = useRef(null as AbortController | null); + + useEffect(() => { + if (process.browser) { + let objectUrl: string | null = null; + (async () => { + setLoading(true); + controller.current?.abort(); + controller.current = new AbortController(); + try { + const result = await fetch(src ?? '', {signal: controller.current.signal}); + const blob = await result.blob(); + objectUrl = URL.createObjectURL(blob); + setUrl(objectUrl); + } catch { + // ignore abort error + } finally { + setLoading(false); + } + })(); + return () => { + objectUrl && URL.revokeObjectURL(objectUrl); + }; + } + }, [src]); + + return loading ? {t('loading')} : ; +}; + +export default Image; diff --git a/frontend/components/Input.tsx b/frontend/components/Input.tsx new file mode 100644 index 00000000..6844b6bb --- /dev/null +++ b/frontend/components/Input.tsx @@ -0,0 +1,56 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + em, + textLighterColor, + borderColor, + borderFocusedColor, + borderRadius, + duration, + easing, + math +} from '~/utils/style'; + +export const padding = em(10); +export const height = em(36); + +const StyledInput = styled.input<{rounded?: boolean}>` + padding: ${padding}; + height: ${height}; + line-height: ${height}; + display: inline-block; + border: 1px solid ${borderColor}; + border-radius: ${props => (props.rounded ? math(`${height} / 2`) : borderRadius)}; + transition: border-color ${duration} ${easing}; + outline: none; + + &:hover, + &:focus { + border-color: ${borderFocusedColor}; + } + + &::placeholder { + color: ${textLighterColor}; + } +`; + +export type InputProps = { + rounded?: boolean; + placeholder?: string; + value?: string; + onChange?: (value: string) => unknown; +}; + +const Input: FunctionComponent = ({rounded, placeholder, value, onChange, className}) => ( + onChange?.(e.target.value)} + > +); + +export default Input; diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx new file mode 100644 index 00000000..bde1cdaf --- /dev/null +++ b/frontend/components/Layout.tsx @@ -0,0 +1,29 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {headerHeight} from '~/utils/style'; +import Navbar from '~/components/Navbar'; + +const Main = styled.main` + padding-top: ${headerHeight}; +`; + +const Header = styled.header` + position: fixed; + z-index: 10000; + width: 100%; + height: ${headerHeight}; + top: 0; + left: 0; + right: 0; +`; + +const Layout: FunctionComponent = ({children}) => ( +
+
+ +
+ {children} +
+); + +export default Layout; diff --git a/frontend/components/LineChart.tsx b/frontend/components/LineChart.tsx new file mode 100644 index 00000000..0540c023 --- /dev/null +++ b/frontend/components/LineChart.tsx @@ -0,0 +1,100 @@ +import React, {FunctionComponent, useEffect, useCallback} from 'react'; +import {EChartOption} from 'echarts'; +import {WithStyled} from '~/utils/style'; +import useECharts from '~/hooks/useECharts'; +import * as chart from '~/utils/chart'; + +type LineChartProps = { + title?: string; + legend?: string[]; + data?: Partial['series']>>; + xAxis?: string; + type?: EChartOption.BasicComponents.CartesianAxis.Type; + yRange?: { + min: number; + max: number; + }; + tooltip?: string | EChartOption.Tooltip.Formatter; + loading?: boolean; +}; + +const LineChart: FunctionComponent = ({ + title, + legend, + data, + xAxis, + type, + yRange, + tooltip, + loading, + className +}) => { + const [ref, echart] = useECharts(!!loading); + + const xAxisFormatter = useCallback( + (value: number) => (type === 'time' ? new Date(value).toLocaleTimeString() : value), + [type] + ); + + useEffect(() => { + if (process.browser) { + echart.current?.setOption( + { + color: chart.color, + title: { + ...chart.title, + text: title ?? '' + }, + tooltip: { + ...chart.tooltip, + ...(tooltip + ? { + formatter: tooltip + } + : {}) + }, + toolbox: chart.toolbox, + legend: { + ...chart.legend, + data: legend ?? [] + }, + grid: chart.grid, + xAxis: { + ...chart.xAxis, + name: xAxis || '', + type: type || 'value', + axisLabel: { + ...chart.xAxis.axisLabel, + formatter: xAxisFormatter + } + }, + yAxis: { + ...chart.yAxis, + ...(yRange || {}) + }, + series: data?.map(item => ({ + ...chart.series, + ...item + })) + } as EChartOption, + {notMerge: true} + ); + } + }, [data, title, legend, xAxis, type, xAxisFormatter, yRange, tooltip, echart]); + + useEffect(() => { + if (process.browser) { + setTimeout(() => { + echart.current?.dispatchAction({ + type: 'takeGlobalCursor', + key: 'dataZoomSelect', + dataZoomSelectActive: true + }); + }, 0); + } + }, [echart]); + + return
; +}; + +export default LineChart; diff --git a/frontend/components/Navbar.tsx b/frontend/components/Navbar.tsx new file mode 100644 index 00000000..68821c27 --- /dev/null +++ b/frontend/components/Navbar.tsx @@ -0,0 +1,85 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {useRouter} from 'next/router'; +import {useTranslation, Link} from '~/utils/i18n'; +import {rem, headerColor, duration, easing, lighten, transitions} from '~/utils/style'; + +const navItems = ['scalars', 'samples', 'graphs', 'high-dimensional']; + +const Nav = styled.nav` + background-color: ${headerColor}; + color: #fff; + height: 100%; + width: 100%; + padding: 0 ${rem(20)}; + display: flex; + justify-content: flex-start; + align-items: center; +`; + +const Logo = styled.a` + font-size: ${rem(20)}; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + font-weight: 600; + margin-right: ${rem(40)}; + + > img { + width: ${rem(98)}; + height: ${rem(31)}; + vertical-align: middle; + margin-right: ${rem(8)}; + } + + > span { + vertical-align: middle; + } +`; + +const NavItem = styled.a<{active: boolean}>` + padding: 0 ${rem(20)}; + height: 100%; + display: inline-flex; + justify-content: center; + align-items: center; + background-color: ${headerColor}; + ${transitions(['background-color'], `${duration} ${easing}`)} + + &:hover { + background-color: ${lighten(0.05, headerColor)}; + } + + > span { + padding: ${rem(10)} 0 ${rem(7)}; + border-bottom: ${rem(3)} solid ${props => (props.active ? '#596cd6' : 'transparent')}; + ${transitions(['border-bottom'], `${duration} ${easing}`)} + text-transform: uppercase; + } +`; + +const Navbar: FunctionComponent = () => { + const {t} = useTranslation('common'); + const {pathname} = useRouter(); + + return ( + + ); +}; + +export default Navbar; diff --git a/frontend/components/Pagination.tsx b/frontend/components/Pagination.tsx new file mode 100644 index 00000000..6d7c516a --- /dev/null +++ b/frontend/components/Pagination.tsx @@ -0,0 +1,127 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + em, + primaryColor, + backgroundColor, + borderColor, + textInvertColor, + textColor, + borderRadius, + borderFocusedColor, + duration, + easing, + transitions, + size +} from '~/utils/style'; + +const height = em(36); + +const Wrapper = styled.nav` + display: flex; + user-select: none; +`; + +const Ul = styled.ul` + display: inline-flex; + list-style: none; + margin: 0; + padding: 0; +`; + +const Li = styled.li` + list-style: none; + margin-left: ${em(10)}; + + &:first-child { + margin-left: 0; + } +`; + +const A = styled.a<{current?: boolean}>` + cursor: pointer; + display: block; + background-color: ${props => (props.current ? primaryColor : backgroundColor)}; + color: ${props => (props.current ? textInvertColor : textColor)}; + height: ${height}; + line-height: calc(${height} - 2px); + min-width: ${height}; + padding: 0 ${em(10)}; + text-align: center; + border: 1px solid ${props => (props.current ? primaryColor : borderColor)}; + border-radius: ${borderRadius}; + ${transitions(['color', 'border-color', 'background-color'], `${duration} ${easing}`)} + + &:hover { + border-color: ${props => (props.current ? primaryColor : borderFocusedColor)}; + } +`; + +const Span = styled.span` + display: block; + ${size(height)} + line-height: ${height}; + text-align: center; +`; + +type PaginationProps = { + page: number; + total: number; + onChange?: (page: number) => unknown; +}; + +const Pagination: FunctionComponent = ({page, total, className, onChange}) => { + const padding = 2; + const around = 2; + + const startEllipsis = page - padding - around - 1 > 0; + const endEllipsis = page + padding + around < total; + const start = + page - around - 1 <= 0 ? [] : Array.from(new Array(Math.min(padding, page - around - 1)), (_v, i) => i + 1); + const end = + page + around >= total + ? [] + : Array.from( + new Array(Math.min(padding, total - page - around)), + (_v, i) => total - padding + i + 1 + Math.max(padding - total + page + around, 0) + ); + const before = + page - 1 <= 0 + ? [] + : Array.from( + new Array(Math.min(around, page - 1)), + (_v, i) => page - around + i + Math.max(around - page + 1, 0) + ); + const after = page >= total ? [] : Array.from(new Array(Math.min(around, total - page)), (_v, i) => page + i + 1); + + const genLink = (arr: number[]) => + arr.map(i => ( +
  • + onChange?.(i)}>{i} +
  • + )); + const hellip = ( +
  • + +
  • + ); + + return ( + +
      + {genLink(start)} + {startEllipsis && hellip} + {genLink(before)} +
    • + {page} +
    • + {genLink(after)} + {endEllipsis && hellip} + {genLink(end)} +
    +
    + ); +}; + +export default Pagination; diff --git a/frontend/components/RadioButton.tsx b/frontend/components/RadioButton.tsx new file mode 100644 index 00000000..19338f3d --- /dev/null +++ b/frontend/components/RadioButton.tsx @@ -0,0 +1,86 @@ +import React, {FunctionComponent, useContext} from 'react'; +import styled from 'styled-components'; +import { + WithStyled, + em, + textColor, + textInvertColor, + borderColor, + borderRadius, + backgroundColor, + primaryColor, + duration, + easing, + ellipsis, + transitions, + borderFocusedColor +} from '~/utils/style'; +import {ValueContext, EventContext} from '~/components/RadioGroup'; + +const height = em(36); +const minWidth = em(72); +const maxWidth = em(144); + +const Button = styled.a<{selected?: boolean}>` + cursor: pointer; + background-color: ${props => (props.selected ? primaryColor : backgroundColor)}; + color: ${props => (props.selected ? textInvertColor : textColor)}; + height: ${height}; + line-height: calc(${height} - 2px); + min-width: ${minWidth}; + ${ellipsis(maxWidth)} + text-align: center; + border: 1px solid ${props => (props.selected ? primaryColor : borderColor)}; + ${transitions(['color', 'border-color', 'background-color'], `${duration} ${easing}`)} + /* bring selected one to top in order to cover the sibling's border */ + ${props => (props.selected ? 'position: relative;' : '')} + + &:hover { + border-color: ${props => (props.selected ? primaryColor : borderFocusedColor)}; + } + + &:first-of-type { + border-top-left-radius: ${borderRadius}; + border-bottom-left-radius: ${borderRadius}; + } + + &:last-of-type { + border-top-right-radius: ${borderRadius}; + border-bottom-right-radius: ${borderRadius}; + } + + & + & { + margin-left: -1px; + } +`; + +type RadioButtonProps = { + selected?: boolean; + title?: string; + value?: string | number | symbol; +}; + +const RadioButton: FunctionComponent = ({ + className, + value, + selected, + title, + children +}) => { + const groupValue = useContext(ValueContext); + const onChange = useContext(EventContext); + + const onClick = () => { + if (value && onChange && groupValue !== value) { + onChange(value); + } + }; + + return ( + + ); +}; + +export default RadioButton; diff --git a/frontend/components/RadioGroup.tsx b/frontend/components/RadioGroup.tsx new file mode 100644 index 00000000..3577d6a3 --- /dev/null +++ b/frontend/components/RadioGroup.tsx @@ -0,0 +1,42 @@ +import React, {FunctionComponent, createContext, useState, useCallback} from 'react'; +import styled from 'styled-components'; +import {WithStyled} from '~/utils/style'; + +const Wrapper = styled.div` + display: inline-flex; + + > * { + flex-shrink: 0; + align-items: flex-start; + } +`; + +export const ValueContext = createContext(null as string | number | symbol | undefined | null); +// eslint-disable-next-line @typescript-eslint/no-empty-function +export const EventContext = createContext((() => {}) as ((value: string | number | symbol) => unknown) | undefined); + +type RadioGroupProps = { + value?: string | number | symbol; + onChange?: (value: string | number | symbol) => unknown; +}; + +const RadioGroup: FunctionComponent = ({value, onChange, children, className}) => { + const [selected, setSelected] = useState(value); + const onSelectedChange = useCallback( + (value: string | number | symbol) => { + setSelected(value); + onChange?.(value); + }, + [onChange] + ); + + return ( + + + {children} + + + ); +}; + +export default RadioGroup; diff --git a/frontend/components/RangeSlider.tsx b/frontend/components/RangeSlider.tsx new file mode 100644 index 00000000..505d2464 --- /dev/null +++ b/frontend/components/RangeSlider.tsx @@ -0,0 +1,106 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {WithStyled, em, size, half, math, primaryColor, textLighterColor, backgroundColor} from '~/utils/style'; +import InputRange, {Range} from 'react-input-range'; + +const height = em(20); +const railHeight = em(4); +const thumbSize = em(12); +const railColor = '#DBDEEB'; + +const Wrapper = styled.div<{disabled?: boolean}>` + height: ${height}; + + .input-range { + height: 100%; + position: relative; + + &__label { + display: none; + } + + &__track { + cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')}; + + &--background { + height: ${railHeight}; + width: 100%; + position: absolute; + top: 50%; + margin-top: -${half(railHeight)}; + background-color: ${railColor}; + border-radius: ${half(railHeight)}; + } + + &--active { + height: ${railHeight}; + position: absolute; + background-color: ${props => (props.disabled ? textLighterColor : primaryColor)}; + border-radius: ${half(railHeight)}; + outline: none; + } + } + + &__slider-container { + top: -${math(`(${thumbSize} - ${railHeight}) / 2`)}; + margin-left: -${half(thumbSize)}; + } + + &__slider { + ${size(thumbSize)} + border-radius: ${half(thumbSize)}; + border: ${em(3)} solid ${props => (props.disabled ? textLighterColor : primaryColor)}; + background-color: ${backgroundColor}; + } + } +`; + +type RangeSliderProps = { + min?: number; + max?: number; + step?: number; + value?: number; + disabled?: boolean; + onChange?: (value: number) => unknown; + onChangeComplete?: () => unknown; +}; + +const RangeSlider: FunctionComponent = ({ + onChange, + onChangeComplete, + className, + min, + max, + step, + value, + disabled +}) => { + const onChangeRange = (range: number | Range) => onChange?.(range as number); + + return ( + + onChangeComplete?.()} + /> + + ); +}; + +RangeSlider.defaultProps = { + min: 0, + max: 100, + step: 1, + value: 50 +}; + +export default RangeSlider; diff --git a/frontend/components/RunSelect.tsx b/frontend/components/RunSelect.tsx new file mode 100644 index 00000000..2d216569 --- /dev/null +++ b/frontend/components/RunSelect.tsx @@ -0,0 +1,36 @@ +import React, {FunctionComponent} from 'react'; +import styled from 'styled-components'; +import {rem} from '~/utils/style'; +import {useTranslation} from '~/utils/i18n'; +import Select, {SelectValueType} from '~/components/Select'; + +const Title = styled.div` + font-size: ${rem(16)}; + line-height: ${rem(16)}; + font-weight: 700; + margin-bottom: ${rem(10)}; +`; + +type RunSelectProps = { + runs?: string[]; + value?: string[]; + onChange?: (value: string[]) => unknown; +}; + +const RunSelect: FunctionComponent = ({runs, value, onChange}) => { + const {t} = useTranslation('common'); + + return ( + <> + {t('select-runs')} + setRun(value as string)} + /> + + + + + + {t('display-all-label')} + + + + + {t('dimension')} + + + setDimension(value as Dimension)}> + {dimensions.map(item => ( + + {t(item)} + + ))} + + + + + + {t('reduction-method')} + + + setReduction(value as string)}> + {reductions.map(item => ( + + {t(item)} + + ))} + + + + + ); + + return ( + <> + {t('common:high-dimensional')} + + + + + ); +}; + +HighDimensional.defaultProps = { + runs: [] +}; + +HighDimensional.getInitialProps = withFetcher(async ({query}, fetcher) => { + const runs = await fetcher('/runs').then(uniq); + return { + runs, + selectedRun: runs.includes(query.run) ? query.run : runs[0], + namespacesRequired: ['high-dimensional', 'common'] + }; +}); + +export default HighDimensional; diff --git a/frontend/pages/highDimension.tsx b/frontend/pages/highDimension.tsx new file mode 100644 index 00000000..89dd5097 --- /dev/null +++ b/frontend/pages/highDimension.tsx @@ -0,0 +1,24 @@ +import Title from '~/components/Title'; +import Content from '~/components/Content'; +import {useTranslation} from '~/utils/i18n'; + +const HighDimension = () => { + const {t} = useTranslation(['highDimension', 'common']); + + const aside =
    ; + + return ( + <> + {t('common:highDimension')} + + + ); +}; + +HighDimension.getInitialProps = () => { + return { + namespacesRequired: ['highDimension', 'common'] + }; +}; + +export default HighDimension; diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx new file mode 100644 index 00000000..c9241f73 --- /dev/null +++ b/frontend/pages/index.tsx @@ -0,0 +1,18 @@ +import {useEffect} from 'react'; +import {NextI18NextPage, Router} from '~/utils/i18n'; + +const Index: NextI18NextPage = () => { + useEffect(() => { + Router.replace('/scalars'); + }, []); + + return null; +}; + +Index.getInitialProps = () => { + return { + namespacesRequired: [] + }; +}; + +export default Index; diff --git a/frontend/pages/samples.tsx b/frontend/pages/samples.tsx new file mode 100644 index 00000000..c1394687 --- /dev/null +++ b/frontend/pages/samples.tsx @@ -0,0 +1,140 @@ +import React, {useState, useCallback, useMemo} from 'react'; +import styled from 'styled-components'; +import {rem, em} from '~/utils/style'; +import {useTranslation, NextI18NextPage} from '~/utils/i18n'; +import useTagFilter, { + getInitialProps as getTagFilterInitialProps, + defaultProps as defaultTagFilterProps, + Props as TagFilterProps +} from '~/hooks/useTagFilter'; +import {withFetcher} from '~/utils/fetch'; +import Title from '~/components/Title'; +import Content from '~/components/Content'; +import RunSelect from '~/components/RunSelect'; +import TagFilter from '~/components/TagFilter'; +import Icon from '~/components/Icon'; +import Field from '~/components/Field'; +import Checkbox from '~/components/Checkbox'; +import RunningToggle from '~/components/RunningToggle'; +import AsideDivider from '~/components/AsideDivider'; +import ChartPage from '~/components/ChartPage'; +import SampleChart from '~/components/SampleChart'; + +const StyledIcon = styled(Icon)` + font-size: ${rem(16)}; + margin-left: ${em(6)}; + margin-right: ${em(4)}; + vertical-align: middle; +`; + +const CheckboxTitle = styled.span` + font-size: ${rem(16)}; + font-weight: 700; + vertical-align: text-top; +`; + +const SubField = styled(Field)` + margin-left: ${rem(26)}; +`; + +type Item = { + run: string; + label: string; +}; + +type SamplesProps = TagFilterProps & {}; + +const Samples: NextI18NextPage = ({tags: propTags, runs: propRuns, selectedRuns: propSelectedRuns}) => { + const {t} = useTranslation(['samples', 'common']); + + const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter( + 'images', + propSelectedRuns, + { + runs: propRuns, + tags: propTags + } + ); + const ungroupedSelectedTags = useMemo( + () => + selectedTags.reduce((prev, {runs, ...item}) => { + Array.prototype.push.apply( + prev, + runs.map(run => ({...item, run})) + ); + return prev; + }, [] as Item[]), + [selectedTags] + ); + + const [showImage, setShowImage] = useState(true); + // const [showAudio, setShowAudio] = useState(true); + // const [showText, setShowText] = useState(true); + + const [showActualSize, setShowActualSize] = useState(false); + + const [running, setRunning] = useState(true); + + const aside = ( +
    + + + + + + {t('image')} + + + {showImage && ( + + + {t('show-actual-size')} + + + )} + {/* + + + + {t('audio')} + + + + + + + {t('text')} + + */} + +
    + ); + + const withChart = useCallback( + ({run, label}: Item) => , + [showActualSize, running] + ); + + return ( + <> + {t('common:samples')} + + + + + + ); +}; + +Samples.defaultProps = { + ...defaultTagFilterProps +}; + +Samples.getInitialProps = withFetcher(async (context, fetcher) => { + return { + ...(await getTagFilterInitialProps('images', context, fetcher)), + namespacesRequired: ['samples', 'common'] + }; +}); + +export default Samples; diff --git a/frontend/pages/scalars.tsx b/frontend/pages/scalars.tsx new file mode 100644 index 00000000..d47a23c9 --- /dev/null +++ b/frontend/pages/scalars.tsx @@ -0,0 +1,120 @@ +import React, {useState, useCallback} from 'react'; +import {useTranslation, NextI18NextPage} from '~/utils/i18n'; +import useTagFilter, { + getInitialProps as getTagFilterInitialProps, + defaultProps as defaultTagFilterProps, + Props as TagFilterProps +} from '~/hooks/useTagFilter'; +import {withFetcher} from '~/utils/fetch'; +import Title from '~/components/Title'; +import Content from '~/components/Content'; +import RunSelect from '~/components/RunSelect'; +import TagFilter from '~/components/TagFilter'; +import Select, {SelectValueType} from '~/components/Select'; +import Field from '~/components/Field'; +import Checkbox from '~/components/Checkbox'; +import SmoothingSlider from '~/components/SmoothingSlider'; +import RunningToggle from '~/components/RunningToggle'; +import AsideDivider from '~/components/AsideDivider'; +import ChartPage from '~/components/ChartPage'; +import ScalarChart, {xAxisMap, sortingMethodMap} from '~/components/ScalarChart'; +import {Tag} from '~/types'; + +type XAxis = keyof typeof xAxisMap; +const xAxisValues = ['step', 'relative', 'wall']; +type TooltiopSorting = keyof typeof sortingMethodMap; +const toolTipSortingValues = ['default', 'descending', 'ascending', 'nearest']; + +type ScalarsProps = TagFilterProps & {}; + +const Scalars: NextI18NextPage = ({tags: propTags, runs: propRuns, selectedRuns: propSelectedRuns}) => { + const {t} = useTranslation(['scalars', 'common']); + + const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter( + 'scalars', + propSelectedRuns, + { + runs: propRuns, + tags: propTags + } + ); + + const [smoothing, setSmoothing] = useState(0.6); + + const [xAxis, setXAxis] = useState(xAxisValues[0] as XAxis); + const onChangeXAxis = (value: SelectValueType | SelectValueType[]) => setXAxis(value as XAxis); + + const [tooltipSorting, setTooltipSorting] = useState(toolTipSortingValues[0] as TooltiopSorting); + const onChangeTooltipSorting = (value: SelectValueType | SelectValueType[]) => + setTooltipSorting(value as TooltiopSorting); + + const [ignoreOutliers, setIgnoreOutliers] = useState(false); + + const [running, setRunning] = useState(true); + + const aside = ( +
    + + + + + + + +