diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 4ca5f79974d3a39de7033ec71328019cd3f1659f..228d946dac3f2943be47d926b616195eeda4c59f 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -29,6 +29,8 @@ module.exports = { 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off', 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn' + 'react-hooks/exhaustive-deps': 'warn', + '@typescript-eslint/no-explicit-any': 'error', + 'no-console': 'warn' } }; diff --git a/frontend/components/GraphPage/NodeInfo.tsx b/frontend/components/GraphPage/NodeInfo.tsx index 793cc9e7e1d409dc291f2b268b6389ea15daa33d..3f5cf56d92a04cf12dfb6a53217983f7a2cdb927 100644 --- a/frontend/components/GraphPage/NodeInfo.tsx +++ b/frontend/components/GraphPage/NodeInfo.tsx @@ -1,6 +1,8 @@ import React, {FunctionComponent} from 'react'; import {useTranslation} from '~/utils/i18n'; import {NodeType, TypedNode} from '~/resource/graph'; +import styled from 'styled-components'; +import {WithStyled} from '~/utils/style'; const typeName: {[k in NodeType]: string} = { [NodeType.Input]: 'input', @@ -12,6 +14,27 @@ export interface NodeInfoProps { node?: TypedNode | {type: 'unknown'; guessType: NodeType; msg: string}; } +const DataList: FunctionComponent<{items: {key: string; value: string | string[]}[]} & WithStyled> = props => { + return ( + + ); +}; + +const PropertyList = styled(DataList)` + padding: 0; + list-style: none; + color: #666; + li + li { + margin-top: 1em; + } +`; + const NodeInfo: FunctionComponent = props => { const {t} = useTranslation(['graphs']); if (!props.node) { @@ -23,46 +46,28 @@ const NodeInfo: FunctionComponent = props => { case NodeType.Input: case NodeType.Output: return ( - + ); 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]} -
  • -
- ); + return ; default: return <>; } diff --git a/frontend/pages/graphs.tsx b/frontend/pages/graphs.tsx index e3a9f4463e08ae24d9548984b7d0d1845bf673a7..0a3b0f2dca28fb5bf785213dbdf5332116ff4b0d 100644 --- a/frontend/pages/graphs.tsx +++ b/frontend/pages/graphs.tsx @@ -41,6 +41,41 @@ const GraphSvg = styled('svg')` .node { cursor: pointer; + + .label-container { + stroke-width: 3px; + stroke: #e6e6e6; + &.rect { + rx: 10; + ry: 10; + } + } + + &.operator { + .label-container { + fill: #cdd9da; + } + } + + &.output { + .label-container { + stroke-dasharray: 5, 5; + stroke: #e6e6e6; + fill: #cad2d0; + } + } + + &.input { + .label-container { + fill: #d5d3d8; + } + } + + &.active { + .label-container { + stroke: #25c9ff; + } + } } .edgePath path.path { @@ -55,9 +90,9 @@ const MAX_SCALE = 4; const useDag = (graph?: Graph) => { const [displaySwitch, setDisplaySwitch] = useState({ - detail: true, - input: true, - output: true + detail: false, + input: false, + output: false }); const facts = useMemo(() => collectDagFacts(graph), [graph]); @@ -134,17 +169,23 @@ const useDagreD3 = (graph: Graph | undefined) => { .on('end', () => svg.classed('grabbing', false)); svg.call(zoom); + let prevDom: HTMLElement | undefined; // install event listeners svg.selectAll('g.node').on('click', v => { const uid = v as string; - const {type} = g.node(uid); - const dagNode = dagInfo.findNode(type, uid); - if (!dagNode) { + const {type, elem: dom} = g.node(uid); + if (prevDom) { + prevDom.classList.remove('active'); + } + dom.classList.add('active'); + prevDom = dom; + const node = dagInfo.findNode(type, uid); + if (!node) { setCurrentNode({type: 'unknown', guessType: type, msg: uid}); return; } - setCurrentNode({...dagNode, type}); + setCurrentNode({...node, type}); }); const fitScreen = () => { @@ -209,21 +250,21 @@ const Graphs: NextI18NextPage = () => {
- + - +
diff --git a/frontend/public/static/locales/en/common.json b/frontend/public/static/locales/en/common.json index 5b47327d35afb142b34495d37a35dfff17c42452..6a7b98d6984d7bc04c969420612cfe8ee4260806 100644 --- a/frontend/public/static/locales/en/common.json +++ b/frontend/public/static/locales/en/common.json @@ -10,10 +10,6 @@ "select": "Please Select", "runs": "Runs", "select-runs": "Select Runs", - "scale": "Scale", - "download-image": "Download Image", - "restore-image": "Restore Image", - "node-info": "Node Info", "running": "Running", "stopped": "Stopped", "loading": "Loading" diff --git a/frontend/public/static/locales/en/graphs.json b/frontend/public/static/locales/en/graphs.json index 6d8675162d2b9ee4fb455f0af2e1b233aed17101..75b39d521f2666fa8b7fab8cb58d6f6059785498 100644 --- a/frontend/public/static/locales/en/graphs.json +++ b/frontend/public/static/locales/en/graphs.json @@ -1,4 +1,8 @@ { + "scale": "Scale", + "download-image": "Download Image", + "restore-image": "Restore Image", + "node-info": "Node Info", "node-type": "Node Type", "node-name": "Node Name", "node-data-shape": "Shape", @@ -6,5 +10,5 @@ "output": "Output", "op-type": "Operator Type", "node-data-type": "Data Type", - "click-node": "Click a node to display" + "click-node": "Click a node to view its detail" } diff --git a/frontend/public/static/locales/zh/graphs.json b/frontend/public/static/locales/zh/graphs.json index 0967ef424bce6791893e9a57bb952f80fd536e93..12493b89c8be0634cc60d4523f19a903e9a2a009 100644 --- a/frontend/public/static/locales/zh/graphs.json +++ b/frontend/public/static/locales/zh/graphs.json @@ -1 +1,14 @@ -{} +{ + "scale": "比例", + "download-image": "下载图片", + "restore-image": "还原图片", + "node-info": "节点信息", + "node-type": "节点类型", + "node-name": "节点名称", + "node-data-shape": "数据类型", + "input": "输入", + "output": "输出", + "op-type": "算子类型", + "node-data-type": "数据类型", + "click-node": "点击左侧节点,查看节点信息" +} diff --git a/frontend/resource/graph/collectDagFacts.ts b/frontend/resource/graph/collectDagFacts.ts index d21565f67322b3902ef06a2b04d059f3a93b06ca..b6e2501f4b93777034e8a781e27980571f613ccc 100644 --- a/frontend/resource/graph/collectDagFacts.ts +++ b/frontend/resource/graph/collectDagFacts.ts @@ -1,5 +1,4 @@ import {Graph, Node, NodeUID, InputNode, NodeType} from './types'; -import {OpNodeStyle, OutputNodeStyle, InputNodeStyle} from './style'; interface DagNode { key: string; @@ -7,7 +6,6 @@ interface DagNode { label: string; shape: string; class: string; - style: string; } type DagEdge = [string, string]; @@ -124,8 +122,7 @@ const expandRelations = (nodeMapping: NodeRelationMapping) => { label: bridge, shape: 'diamond', class: 'output', - type: NodeType.Output, - style: OutputNodeStyle + type: NodeType.Output }); detailLayer.edges.push([inputTo, bridge]); @@ -158,8 +155,7 @@ type: ${inputNode.data_type} dims: ${inputNode.shape.join(' × ')} `, shape: 'rect', - class: 'input', - style: InputNodeStyle + class: 'input' }); relations.output.forEach(o => edges.push([inputNodeUID, o])); @@ -180,8 +176,7 @@ const extractOutputLayer = (nodeRelationMapping: NodeRelationMapping) => { type: NodeType.Output, label: nodeUID, shape: 'diamond', - class: 'output', - style: OutputNodeStyle + class: 'output' }); for (const inputNode of relations.input) { @@ -208,8 +203,7 @@ export const collectDagFacts = (graph?: Graph) => { type: NodeType.Op, label: n.opType, shape: 'rect', - class: 'operator', - style: OpNodeStyle + class: 'operator' })); const {briefLayer: bl, detailLayer: dl} = expandRelations(nodeRelationMapping); diff --git a/frontend/resource/graph/style.ts b/frontend/resource/graph/style.ts deleted file mode 100644 index d3483721e5dd58afa2a4c28c9359039633ee2f8d..0000000000000000000000000000000000000000 --- a/frontend/resource/graph/style.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const OpNodeStyle = ` -stroke-width: 3px; -opacity: 0.1; -rx: 10; -ry: 10; -stroke: #333; -stroke-color: #41b3a3; -fill: #008c99; -`; - -export const OutputNodeStyle = ` -opacity: 0.1; -stroke-width: 3px; -stroke-dasharray: 5, 5; -stroke: #333; -stroke-color: #41b3a3; -fill: #015249; -`; - -export const InputNodeStyle = ` -opacity: 0.1; -stroke-width: 3px; -stroke: #333; -stroke-color: #41b3a3; -fill: #6c648b; -`;