From 09dd30f53da9fb3ebe375b12bc6aefd0b2dae4ce Mon Sep 17 00:00:00 2001 From: raintoway <3023704769@qq.com> Date: Fri, 18 Nov 2022 17:55:42 +0800 Subject: [PATCH 1/3] feature# add the treeVisualization of ast --- website/package.json | 4 +- .../visualization/TreeVisualization.js | 207 ++++++++++++++++++ .../visualization/css/treeVisualization.css | 81 +++++++ website/src/components/visualization/index.js | 2 + 4 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 website/src/components/visualization/TreeVisualization.js create mode 100644 website/src/components/visualization/css/treeVisualization.css diff --git a/website/package.json b/website/package.json index 282cc9dc..90b0a8f6 100644 --- a/website/package.json +++ b/website/package.json @@ -63,7 +63,7 @@ "acorn-to-esprima": "^2.0.8", "astexplorer-go": "^1.0.0", "astexplorer-refmt": "^1.0.1", - "astexplorer-syn": "^1.0.48", + "astexplorer-syn": "1.0.48", "babel-core": "npm:babel-core@^6", "babel-eslint": "^7.2.3", "babel-eslint8": "npm:babel-eslint@^8", @@ -112,6 +112,7 @@ "jscodeshift": "^0.11.0", "json-stringify-safe": "^5.0.1", "json-to-ast": "^2.1.0", + "lodash": "^4.17.21", "lodash.isequal": "^4.5.0", "luaparse": "^0.3.0", "lucene": "^2.1.1", @@ -132,6 +133,7 @@ "pug-lexer": "^4.1.0", "pug-parser": "^5.0.1", "react": "16", + "react-d3-tree": "^3.3.5", "react-dom": "^16.0.1", "react-redux": "^7.2.0", "recast": "^0.21.1", diff --git a/website/src/components/visualization/TreeVisualization.js b/website/src/components/visualization/TreeVisualization.js new file mode 100644 index 00000000..f82a9bf0 --- /dev/null +++ b/website/src/components/visualization/TreeVisualization.js @@ -0,0 +1,207 @@ +import PropTypes from 'prop-types'; +import React, { Fragment } from 'react'; +import {publish} from '../../utils/pubsub.js'; +import {logEvent} from '../../utils/logger'; +import {treeAdapterFromParseResult} from '../../core/TreeAdapter.js'; +import TreeD3 from 'react-d3-tree'; +import _ from 'lodash' + +import './css/treeVisualization.css' + +const {useReducer, useMemo, useEffect,createRef} = React; + +const STORAGE_KEY = 'tree_settings'; + +function initSettings() { + const storedSettings = global.localStorage.getItem(STORAGE_KEY); + return storedSettings ? + JSON.parse(storedSettings) : + { + autofocus: true, + hideFunctions: true, + hideEmptyKeys: false, + hideLocationData: false, + hideTypeKeys: false, + }; +} + +function reducer(state, element) { + const newState = {...state, [element.name]: element.checked}; + + global.localStorage.setItem(STORAGE_KEY, JSON.stringify(newState)); + logEvent( + 'tree_view_settings', + element.checked ? 'enabled' : 'disabled', + element.name, + ); + + return newState; +} + +function makeCheckbox(name, settings, updateSettings) { + return ( + updateSettings(event.target)} + /> + ); +} + +export default function TreeVisualization({parseResult, position}) { + const [settings, updateSettings] = useReducer(reducer, null, initSettings); + const treeAdapter = useMemo( + () => treeAdapterFromParseResult(parseResult, settings), + [parseResult.treeAdapter, settings], + ); + + let TreeRef = createRef(); + + // is the node in the range + function isInRange(node){ + if(node === null || position === null) return false + return treeAdapter.isInRange(node, node.name, position) || treeAdapter.hasChildrenInRange(node, node.name, position) + } + + function walk(node,parentNode,preName){ + let rsl = {} + rsl.name = preName === undefined ? treeAdapter.getNodeName(node) : preName + rsl.name = rsl.name === undefined ? '' : rsl.name + '' + let keysObj = Array.from(treeAdapter.walkNode(node)) + .filter(({key}) => key !== 'length') || [] + rsl.node = parentNode || node + rsl.children = [] + if(typeof node === 'object') { + keysObj.forEach(item=>{ + if(Array.isArray(item.value)){ + let tempArr = [] + item.value.forEach(secItem=>{ + secItem !== null && secItem !== undefined && tempArr.push(walk(secItem)) + }) + rsl.children.push({name:item.key,children:tempArr,node:node}) + }else if(typeof item.value === 'object') { + rsl.children.push(walk(item.value,undefined,item.key)) + } else { + rsl.children.push({name:item.key + ':' + item.value,node:node}) + } + }) + } else { + rsl.name = node === undefined ? '' : node + '' + rsl.node = parentNode || null + delete rsl.children + } + return rsl + } + let cloneParseResult = _.cloneDeep(parseResult) + const orgChart = [walk(cloneParseResult.ast,cloneParseResult.ast)] + + function nodeMouseOver(node,event){ + event.stopPropagation(); + let data = node.nodeDatum + let range = treeAdapter.getRange(data.node); + if(range && data.__rd3t.depth !== 0) publish('HIGHLIGHT', {node: data.node, range}); + } + + function nodeMouseOut(node,event){ + event.stopPropagation(); + let data = node.nodeDatum + let range = treeAdapter.getRange(data.node); + if(range && data.__rd3t.depth !== 0) publish('CLEAR_HIGHLIGHT', {node: data.node, range}); + } + + function nodeClick(node,event){ + event.stopPropagation(); + node.toggleNode() + } + + let top = null + let inRangeNodes = [] + useEffect(()=>{ + let targetNode = null + inRangeNodes.forEach(item=>{ + if(targetNode === null) { + targetNode = item + }else if(targetNode.hierarchyPointNode.depth < item.hierarchyPointNode.depth ) { + targetNode = item + } + item.nodeDatum.__rd3t.collapsed = false + }) + TreeRef && targetNode && TreeRef.current.centerNode(targetNode.hierarchyPointNode) + inRangeNodes.length === 0 && top !== null && TreeRef.current.centerNode(top.hierarchyPointNode) + }) + + function renderNode(node){ + if(top === null) top = node + let nodeData = node.nodeDatum.node + if(node.nodeDatum.node && isInRange(nodeData)){ + inRangeNodes.push(node) + } + return ( + + + + +

+ {node.nodeDatum && node.nodeDatum.name} +

+
+
+ ) + } + + return ( +
+
+ + ​ + {treeAdapter.getConfigurableFilters().map(filter => ( + + + ​ + + ))} +
+
+ +
+
+ ); +} + +TreeVisualization.propTypes = { + parseResult: PropTypes.object, + position: PropTypes.number, +}; diff --git a/website/src/components/visualization/css/treeVisualization.css b/website/src/components/visualization/css/treeVisualization.css new file mode 100644 index 00000000..23738866 --- /dev/null +++ b/website/src/components/visualization/css/treeVisualization.css @@ -0,0 +1,81 @@ +.tree-visualization { + display: flex; + flex-direction: column; +} + +.tree-visualization>.toolbar { + padding: 5px; + flex-shrink: 0; +} + +.tree-visualization>.toolbar label { + cursor: pointer; + margin-right: 5px; + -webkit-touch-callout: none; + user-select: none; + white-space: nowrap; +} + +.tree-visualization ul { + margin: 0; + padding-left: 20px; + overflow: auto; +} + +.tree-visualization>ul { + cursor: default; + box-sizing: border-box; + font-family: monospace; + -webkit-touch-callout: none; + user-select: none; + flex: 1; +} + +.tree-visualization .value-body { + min-width: 300px; + width: fit-content; +} + + +.node__root>circle { + fill: #aaa; + stroke: none +} + +.node__root>circle:hover { + fill: yellow; +} + +.node__root>.isInRange { + fill: yellow; +} + +.node__branch>circle { + fill: #aaa; + stroke: none +} + +.node__branch>circle:hover { + fill: yellow; +} + +.node__branch>.isInRange { + fill: yellow; +} + +.node__leaf>circle { + fill: white; + stroke: none +} + +.node__leaf>circle:hover { + fill: yellow; +} + +.node__leaf>.isInRange { + fill: yellow; +} + +.moreInfoHidden { + display: none; +} \ No newline at end of file diff --git a/website/src/components/visualization/index.js b/website/src/components/visualization/index.js index 04967a4c..2788b079 100644 --- a/website/src/components/visualization/index.js +++ b/website/src/components/visualization/index.js @@ -1,7 +1,9 @@ import JSON from './JSON'; import Tree from './Tree'; +import TreeVisualization from './TreeVisualization'; export default [ Tree, JSON, + TreeVisualization ]; From 0fb932c39238b6b02a24aaad65b7a20f52def173 Mon Sep 17 00:00:00 2001 From: raintoway <3023704769@qq.com> Date: Mon, 21 Nov 2022 10:57:18 +0800 Subject: [PATCH 2/3] fix# adjust the tree's position --- .../visualization/TreeVisualization.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/website/src/components/visualization/TreeVisualization.js b/website/src/components/visualization/TreeVisualization.js index f82a9bf0..442852ef 100644 --- a/website/src/components/visualization/TreeVisualization.js +++ b/website/src/components/visualization/TreeVisualization.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -import React, { Fragment } from 'react'; -import {publish} from '../../utils/pubsub.js'; +import React, { cloneElement, Fragment } from 'react'; +import {publish, subscribe} from '../../utils/pubsub.js'; import {logEvent} from '../../utils/logger'; import {treeAdapterFromParseResult} from '../../core/TreeAdapter.js'; import TreeD3 from 'react-d3-tree'; @@ -117,6 +117,14 @@ export default function TreeVisualization({parseResult, position}) { let top = null let inRangeNodes = [] + let treeVisualizationContainer = document.getElementById('treeVisualizationContainer') + let width = document.documentElement.clientWidth/2 + let height = document.documentElement.clientHeight/2 + if(treeVisualizationContainer) { + width = document.getElementById('treeVisualizationContainer').clientWidth + height = document.getElementById('treeVisualizationContainer').clientHeight + } + useEffect(()=>{ let targetNode = null inRangeNodes.forEach(item=>{ @@ -167,7 +175,7 @@ export default function TreeVisualization({parseResult, position}) { } return ( -
+
From ffd104625dd593f23e259da67fcf3ad186ee6948 Mon Sep 17 00:00:00 2001 From: raintoway <3023704769@qq.com> Date: Mon, 21 Nov 2022 16:47:44 +0800 Subject: [PATCH 3/3] fix# adjust the position of selectedNode --- .../visualization/TreeVisualization.js | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/website/src/components/visualization/TreeVisualization.js b/website/src/components/visualization/TreeVisualization.js index 442852ef..5423026f 100644 --- a/website/src/components/visualization/TreeVisualization.js +++ b/website/src/components/visualization/TreeVisualization.js @@ -117,9 +117,9 @@ export default function TreeVisualization({parseResult, position}) { let top = null let inRangeNodes = [] - let treeVisualizationContainer = document.getElementById('treeVisualizationContainer') let width = document.documentElement.clientWidth/2 let height = document.documentElement.clientHeight/2 + let treeVisualizationContainer = document.getElementById('treeVisualizationContainer') if(treeVisualizationContainer) { width = document.getElementById('treeVisualizationContainer').clientWidth height = document.getElementById('treeVisualizationContainer').clientHeight @@ -135,8 +135,26 @@ export default function TreeVisualization({parseResult, position}) { } item.nodeDatum.__rd3t.collapsed = false }) - TreeRef && targetNode && TreeRef.current.centerNode(targetNode.hierarchyPointNode) - inRangeNodes.length === 0 && top !== null && TreeRef.current.centerNode(top.hierarchyPointNode) + setTimeout(() => { + if(targetNode) { + let [targetG,transform,position] = [null,'',''] + targetG = document.getElementById(targetNode.nodeDatum.__rd3t.id) + targetG && (transform = targetG.getAttribute('transform') || '') + transform && (position = transform.replace(/^translate\(/,'').replace(/\)$/,'')) + position = position.split(',').map(item=>Number(item)) + targetNode.hierarchyPointNode.x = position[0] + targetNode.hierarchyPointNode.y = position[1] + TreeRef && targetNode && TreeRef.current.centerNode(targetNode.hierarchyPointNode) + inRangeNodes.length === 0 && top !== null && TreeRef.current.centerNode(top.hierarchyPointNode) + } + }, 100); + subscribe('PANEL_RESIZE', () => { + let treeVisualizationContainer = document.getElementById('treeVisualizationContainer') + if(treeVisualizationContainer) { + width = document.getElementById('treeVisualizationContainer').clientWidth + height = document.getElementById('treeVisualizationContainer').clientHeight + } + }) }) function renderNode(node){