Compare commits
8 Commits
before-ref
...
react-svg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba1cab4fcb | ||
|
|
d40df8622c | ||
|
|
578e33eb4b | ||
|
|
95e0cf5918 | ||
|
|
c8a1f9c3b8 | ||
|
|
23eed87017 | ||
|
|
dce00ac8b2 | ||
|
|
9352424aa7 |
@@ -23,6 +23,7 @@
|
|||||||
"babel-traverse": "^6.26.0",
|
"babel-traverse": "^6.26.0",
|
||||||
"babylon": "^6.18.0",
|
"babylon": "^6.18.0",
|
||||||
"chokidar": "^2.0.3",
|
"chokidar": "^2.0.3",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"d3-flextree": "^2.1.1",
|
"d3-flextree": "^2.1.1",
|
||||||
"directory-tree": "^2.1.0",
|
"directory-tree": "^2.1.0",
|
||||||
@@ -38,7 +39,6 @@
|
|||||||
"redux-saga": "^0.16.0",
|
"redux-saga": "^0.16.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"style-loader": "^0.21.0",
|
"style-loader": "^0.21.0",
|
||||||
"svg.js": "^2.6.4",
|
|
||||||
"webpack": "^4.6.0",
|
"webpack": "^4.6.0",
|
||||||
"websocket": "^1.0.26"
|
"websocket": "^1.0.26"
|
||||||
},
|
},
|
||||||
|
|||||||
7771
src/public/dist/bundle.js
vendored
7771
src/public/dist/bundle.js
vendored
File diff suppressed because it is too large
Load Diff
2
src/public/dist/bundle.js.map
vendored
2
src/public/dist/bundle.js.map
vendored
File diff suppressed because one or more lines are too long
1
src/public/dist/resources/right-arrow.svg
vendored
1
src/public/dist/resources/right-arrow.svg
vendored
@@ -3,6 +3,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 448.011 448.011" style="enable-background:new 0 0 448.011 448.011;" xml:space="preserve" width="512px" height="512px">
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 448.011 448.011" style="enable-background:new 0 0 448.011 448.011;" xml:space="preserve" width="512px" height="512px">
|
||||||
<g>
|
<g>
|
||||||
<g>
|
<g>
|
||||||
|
<rect x="120" y="0" width="320" height="445" style="fill:#fff" />
|
||||||
<path d="M438.731,209.463l-416-192c-6.624-3.008-14.528-1.216-19.136,4.48c-4.64,5.696-4.8,13.792-0.384,19.648l136.8,182.4 l-136.8,182.4c-4.416,5.856-4.256,13.984,0.352,19.648c3.104,3.872,7.744,5.952,12.448,5.952c2.272,0,4.544-0.48,6.688-1.472 l416-192c5.696-2.624,9.312-8.288,9.312-14.528S444.395,212.087,438.731,209.463z" fill="#1890ff"/>
|
<path d="M438.731,209.463l-416-192c-6.624-3.008-14.528-1.216-19.136,4.48c-4.64,5.696-4.8,13.792-0.384,19.648l136.8,182.4 l-136.8,182.4c-4.416,5.856-4.256,13.984,0.352,19.648c3.104,3.872,7.744,5.952,12.448,5.952c2.272,0,4.544-0.48,6.688-1.472 l416-192c5.696-2.624,9.312-8.288,9.312-14.528S444.395,212.087,438.731,209.463z" fill="#1890ff"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 838 B |
@@ -47,7 +47,7 @@ const DefaultState = {
|
|||||||
key: CONTROLS_KEYS.CODE_CRUMBS_MINIMIZE
|
key: CONTROLS_KEYS.CODE_CRUMBS_MINIMIZE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'show details',
|
name: 'show details (remove it)',
|
||||||
title: 'Show all Details',
|
title: 'Show all Details',
|
||||||
key: CONTROLS_KEYS.CODE_CRUMBS_DETAILS
|
key: CONTROLS_KEYS.CODE_CRUMBS_DETAILS
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { withSvgDraw } from '../utils/SvgDraw';
|
|
||||||
import { drawCodeCrumbEdge, drawPartEdge, drawCodeCrumbLoc, drawPopOver } from './drawHelpers';
|
|
||||||
import { drawFileText, drawFileIcon } from '../SourceTree/drawHelpers';
|
|
||||||
|
|
||||||
import { getFilesList } from '../../../../utils/treeLayout';
|
|
||||||
import { createSet } from '../utils/SvgSet';
|
|
||||||
|
|
||||||
class CodeCrumbsTree extends React.Component {
|
|
||||||
componentDidMount() {
|
|
||||||
this.drawSet = createSet(this.props.primaryDraw);
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.clearDraw();
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.clearDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
|
||||||
return true;
|
|
||||||
//TODO: missing overlapping elements: text&icons
|
|
||||||
/*const oldProps = this.props;
|
|
||||||
return oldProps.filesTreeLayoutNodes !== nextProps.filesTreeLayoutNodes;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
clearDraw() {
|
|
||||||
this.drawSet.clearAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
drawTree() {
|
|
||||||
const {
|
|
||||||
primaryDraw,
|
|
||||||
filesTreeLayoutNodes,
|
|
||||||
shiftToCenterPoint,
|
|
||||||
sourceDiagramOn,
|
|
||||||
dependenciesDiagramOn,
|
|
||||||
codeCrumbsMinimize,
|
|
||||||
codeCrumbsDetails,
|
|
||||||
onCodeCrumbSelect
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { add } = this.drawSet;
|
|
||||||
|
|
||||||
const filesList = getFilesList(filesTreeLayoutNodes);
|
|
||||||
filesList.forEach(node => {
|
|
||||||
const [nX, nY] = [node.y, node.x];
|
|
||||||
|
|
||||||
if (node.children) {
|
|
||||||
if (!sourceDiagramOn && !dependenciesDiagramOn) {
|
|
||||||
add(
|
|
||||||
drawFileText(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
name: node.data.name
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
add(
|
|
||||||
drawFileIcon(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
codeCrumbsMinimize
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
!codeCrumbsMinimize &&
|
|
||||||
add(
|
|
||||||
drawPartEdge(primaryDraw, shiftToCenterPoint, {
|
|
||||||
source: {
|
|
||||||
x: nX,
|
|
||||||
y: nY
|
|
||||||
},
|
|
||||||
parentName: node.data.name
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
!codeCrumbsMinimize &&
|
|
||||||
node.children.forEach((crumb, i, list) => {
|
|
||||||
const [cX, cY] = [crumb.y, crumb.x];
|
|
||||||
const singleCrumb = list.length === 1;
|
|
||||||
|
|
||||||
!singleCrumb &&
|
|
||||||
add(
|
|
||||||
drawCodeCrumbEdge(primaryDraw, shiftToCenterPoint, {
|
|
||||||
source: {
|
|
||||||
x: nX,
|
|
||||||
y: nY
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
x: cX,
|
|
||||||
y: cY
|
|
||||||
},
|
|
||||||
parentName: node.data.name
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
//TODO: refactor mess
|
|
||||||
const loc = crumb.data.crumbedLineNode.loc.start;
|
|
||||||
add(
|
|
||||||
drawCodeCrumbLoc(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: cX,
|
|
||||||
y: cY,
|
|
||||||
loc: `(${loc.line},${loc.column})`,
|
|
||||||
name: crumb.data.name,
|
|
||||||
singleCrumb,
|
|
||||||
onMouseOver() {
|
|
||||||
if (!crumb.data.params.details || codeCrumbsDetails) return null;
|
|
||||||
|
|
||||||
return drawPopOver(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: cX,
|
|
||||||
y: cY,
|
|
||||||
name: crumb.data.params.details,
|
|
||||||
singleCrumb
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onClick() {
|
|
||||||
onCodeCrumbSelect(node.data, crumb.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (codeCrumbsDetails && crumb.data.params.details) {
|
|
||||||
add(
|
|
||||||
drawPopOver(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: cX,
|
|
||||||
y: cY,
|
|
||||||
name: crumb.data.params.details,
|
|
||||||
singleCrumb
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withSvgDraw(CodeCrumbsTree);
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
import { PURPLE_COLOR, BLUE_COLOR, SYMBOL_WIDTH } from '../../store/constants';
|
|
||||||
|
|
||||||
export const drawCodeCrumbEdge = (draw, shiftToCenterPoint, { target, source, parentName }) => {
|
|
||||||
const nameWidth = SYMBOL_WIDTH * parentName.length;
|
|
||||||
const padding = 40;
|
|
||||||
const edgeTurnDistance = 20;
|
|
||||||
|
|
||||||
const P1 = shiftToCenterPoint(source.x + nameWidth + padding, source.y);
|
|
||||||
|
|
||||||
const P2 = shiftToCenterPoint(target.x - edgeTurnDistance, source.y);
|
|
||||||
const P3 = shiftToCenterPoint(target.x - edgeTurnDistance, target.y);
|
|
||||||
const P4 = shiftToCenterPoint(target.x, target.y);
|
|
||||||
|
|
||||||
const polyline = draw.polyline([[P1.x, P1.y], [P2.x, P2.y], [P3.x, P3.y], [P4.x, P4.y]]);
|
|
||||||
|
|
||||||
polyline.fill('none').stroke({
|
|
||||||
color: PURPLE_COLOR
|
|
||||||
});
|
|
||||||
|
|
||||||
return polyline;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawPartEdge = (draw, shiftToCenterPoint, { source, parentName }) => {
|
|
||||||
const nameWidth = SYMBOL_WIDTH * parentName.length;
|
|
||||||
const padding = 17;
|
|
||||||
|
|
||||||
const P1 = shiftToCenterPoint(source.x + nameWidth + padding, source.y);
|
|
||||||
const P2 = { x: P1.x + padding + 6, y: P1.y };
|
|
||||||
|
|
||||||
const polyline = draw.polyline([[P1.x, P1.y], [P2.x, P2.y]]);
|
|
||||||
|
|
||||||
polyline.fill('none').stroke({
|
|
||||||
color: PURPLE_COLOR
|
|
||||||
});
|
|
||||||
|
|
||||||
const smallLine = draw.line(P1.x, P1.y - 2, P1.x, P1.y + 2).stroke({ color: PURPLE_COLOR });
|
|
||||||
|
|
||||||
return [polyline, smallLine];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawCodeCrumbLoc = (
|
|
||||||
draw,
|
|
||||||
shiftToCenterPoint,
|
|
||||||
{ x, y, name = '', loc, singleCrumb, onClick, onMouseOver }
|
|
||||||
) => {
|
|
||||||
const textPointShiftX = 3;
|
|
||||||
const textPointShiftY = 5;
|
|
||||||
const textPoint = shiftToCenterPoint(singleCrumb ? x - 20 : x, y);
|
|
||||||
|
|
||||||
const locWidth = loc.length * 6;
|
|
||||||
const locRec = draw
|
|
||||||
.rect(locWidth, 12)
|
|
||||||
.fill('#fff')
|
|
||||||
.stroke(PURPLE_COLOR)
|
|
||||||
.move(textPoint.x, textPoint.y - 6);
|
|
||||||
|
|
||||||
const locText = draw.text(loc);
|
|
||||||
locText
|
|
||||||
.font({ fill: '#595959', family: 'Menlo', size: '8px' })
|
|
||||||
.style({ cursor: 'pointer' })
|
|
||||||
.move(textPoint.x + textPointShiftX, textPoint.y - textPointShiftY);
|
|
||||||
|
|
||||||
if (onMouseOver) {
|
|
||||||
let popOver = null;
|
|
||||||
locText.on('mouseover', () => {
|
|
||||||
popOver = onMouseOver();
|
|
||||||
});
|
|
||||||
locText.on('mouseout', () => {
|
|
||||||
popOver && popOver[0].remove() && popOver[1].remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
locText.on('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
const nameText = draw.text(':' + name);
|
|
||||||
nameText.font({ fill: '#595959', family: 'Menlo', size: '12px' });
|
|
||||||
//TODO: refactor to use one way, plus or minus
|
|
||||||
nameText.move(textPoint.x + textPointShiftX + locWidth - 1, textPoint.y - textPointShiftY - 2);
|
|
||||||
|
|
||||||
return [locRec, locText, nameText];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [locRec, locText];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawPopOver = (draw, shiftToCenterPoint, { x, y, name = '', singleCrumb }) => {
|
|
||||||
const tPt = shiftToCenterPoint(x - 15 + (singleCrumb ? 0 : 20), y - 24);
|
|
||||||
const nameWidth = name.length * 6;
|
|
||||||
const nameHeight = 8;
|
|
||||||
const padding = 5;
|
|
||||||
|
|
||||||
const polyline = draw.polyline([
|
|
||||||
[tPt.x - padding, tPt.y + nameHeight + padding + 3],
|
|
||||||
[tPt.x - padding, tPt.y - padding],
|
|
||||||
[tPt.x + nameWidth + 2 * padding, tPt.y - padding],
|
|
||||||
[tPt.x + nameWidth + 2 * padding, tPt.y + nameHeight + padding],
|
|
||||||
[tPt.x - padding + 3, tPt.y + nameHeight + padding],
|
|
||||||
[tPt.x - padding, tPt.y + nameHeight + padding + 3]
|
|
||||||
]);
|
|
||||||
|
|
||||||
polyline.fill('#fff').stroke({
|
|
||||||
color: PURPLE_COLOR
|
|
||||||
});
|
|
||||||
|
|
||||||
const text = draw.text(name);
|
|
||||||
text.font({ fill: '#595959', family: 'Menlo', size: '10px' });
|
|
||||||
text.move(tPt.x + 2, tPt.y - 1);
|
|
||||||
|
|
||||||
return [text, polyline];
|
|
||||||
};
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { drawDependenciesEdge } from './drawHelpers';
|
|
||||||
import { drawFileText, drawFileIcon } from '../SourceTree/drawHelpers';
|
|
||||||
import { getFilesList } from '../../../../utils/treeLayout';
|
|
||||||
import { withSvgDraw } from '../utils/SvgDraw';
|
|
||||||
|
|
||||||
class DependenciesTree extends React.Component {
|
|
||||||
componentDidMount() {
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
const { primaryDraw } = this.props;
|
|
||||||
|
|
||||||
primaryDraw.clear();
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
//move to utils
|
|
||||||
findNodeByPathName = (list = [], pathName) => {
|
|
||||||
return list.find(l => l.data.path === pathName);
|
|
||||||
};
|
|
||||||
|
|
||||||
getFilteredDependenciesList() {
|
|
||||||
const { dependenciesList, dependenciesEntryPoint, dependenciesShowOneModule } = this.props;
|
|
||||||
|
|
||||||
const entryPoint = dependenciesEntryPoint || {
|
|
||||||
path: dependenciesList[0].moduleName
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dependenciesShowOneModule) {
|
|
||||||
return [dependenciesList.find(d => d.moduleName === entryPoint.path)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.collectDependencies(entryPoint.path, dependenciesList);
|
|
||||||
}
|
|
||||||
|
|
||||||
collectDependencies(entryModuleName, dependenciesList) {
|
|
||||||
let queue = [].concat(entryModuleName),
|
|
||||||
store = [];
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
let moduleName = queue.shift(),
|
|
||||||
entryModule = dependenciesList.find(d => d.moduleName === moduleName);
|
|
||||||
|
|
||||||
store.push(entryModule);
|
|
||||||
|
|
||||||
const nodeBody = entryModule.importedModuleNames;
|
|
||||||
if (nodeBody) {
|
|
||||||
queue = [...queue, ...nodeBody];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawTree() {
|
|
||||||
const { primaryDraw, filesTreeLayoutNodes, shiftToCenterPoint, sourceDiagramOn } = this.props;
|
|
||||||
|
|
||||||
const moduleFilesList = getFilesList(filesTreeLayoutNodes);
|
|
||||||
const filteredDependenciesList = this.getFilteredDependenciesList();
|
|
||||||
|
|
||||||
filteredDependenciesList.forEach(({ moduleName, importedModuleNames }) => {
|
|
||||||
const moduleNode = this.findNodeByPathName(moduleFilesList, moduleName);
|
|
||||||
|
|
||||||
if (!moduleNode) return;
|
|
||||||
|
|
||||||
const [mX, mY] = [moduleNode.y, moduleNode.x];
|
|
||||||
|
|
||||||
if (!sourceDiagramOn) {
|
|
||||||
drawFileText(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: mX,
|
|
||||||
y: mY,
|
|
||||||
name: moduleNode.data.name
|
|
||||||
});
|
|
||||||
drawFileIcon(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: mX,
|
|
||||||
y: mY
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
importedModuleNames.reduce((prevSource, name) => {
|
|
||||||
const importedNode = this.findNodeByPathName(moduleFilesList, name);
|
|
||||||
|
|
||||||
if (!importedNode) return;
|
|
||||||
|
|
||||||
const [iX, iY] = [importedNode.y, importedNode.x];
|
|
||||||
//TODO: implementation iterations:
|
|
||||||
//1) done: first with sharp angles + overlay
|
|
||||||
//2) done: without overlaying, not fot all cases
|
|
||||||
//3) rounded angles
|
|
||||||
const source = { x: iX, y: iY };
|
|
||||||
drawDependenciesEdge(primaryDraw, shiftToCenterPoint, {
|
|
||||||
source,
|
|
||||||
target: { x: mX, y: mY },
|
|
||||||
prevSource
|
|
||||||
});
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}, null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withSvgDraw(DependenciesTree);
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import { getCurvedPath } from '../../../../utils/svgPrimitives';
|
|
||||||
import { BLUE_COLOR, PURPLE_COLOR } from '../../store/constants';
|
|
||||||
//TODO: move numbers to config per function
|
|
||||||
|
|
||||||
const COLOR = BLUE_COLOR;
|
|
||||||
|
|
||||||
export const drawDependenciesEdge = (draw, shiftToCenterPoint, { source, target, prevSource }) => {
|
|
||||||
const padding = 30;
|
|
||||||
const halfPadding = padding / 2 - 5;
|
|
||||||
|
|
||||||
const sourcePt = shiftToCenterPoint(
|
|
||||||
target.y > source.y ? source.x + 10 : source.x + 8,
|
|
||||||
target.y > source.y ? source.y + 7 : source.y - 12
|
|
||||||
);
|
|
||||||
drawSourceDotLine(draw, sourcePt);
|
|
||||||
|
|
||||||
if (!prevSource) {
|
|
||||||
const targetPt = shiftToCenterPoint(target.x, target.y);
|
|
||||||
|
|
||||||
const P1 = { x: sourcePt.x, y: targetPt.y + padding - 6 };
|
|
||||||
const P2 = { x: targetPt.x - halfPadding, y: targetPt.y + padding - 6 };
|
|
||||||
const P3 = { x: targetPt.x - halfPadding, y: targetPt.y };
|
|
||||||
|
|
||||||
drawConnectionLine(draw, [
|
|
||||||
[sourcePt.x, sourcePt.y],
|
|
||||||
[P1.x, P1.y],
|
|
||||||
[P2.x, P2.y],
|
|
||||||
[P3.x, P3.y],
|
|
||||||
[targetPt.x, targetPt.y]
|
|
||||||
]);
|
|
||||||
|
|
||||||
drawArrow(draw, shiftToCenterPoint, target.x, target.y + 6);
|
|
||||||
} else {
|
|
||||||
if (prevSource.x < sourcePt.x) {
|
|
||||||
//TODO: handle other cases
|
|
||||||
const prevSourcePt = shiftToCenterPoint(prevSource.x, prevSource.y);
|
|
||||||
|
|
||||||
const P1 = { x: sourcePt.x, y: sourcePt.y + halfPadding - 3 };
|
|
||||||
const P2 = {
|
|
||||||
x: prevSourcePt.x + halfPadding,
|
|
||||||
y: sourcePt.y + halfPadding - 3
|
|
||||||
};
|
|
||||||
|
|
||||||
drawConnectionLine(draw, [[sourcePt.x, sourcePt.y], [P1.x, P1.y], [P2.x, P2.y]]);
|
|
||||||
|
|
||||||
drawDot(draw, P2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawDot = (draw, { x, y }) => {
|
|
||||||
const radius = 4;
|
|
||||||
const halfRadius = radius / 2;
|
|
||||||
|
|
||||||
draw
|
|
||||||
.circle(radius)
|
|
||||||
.fill(BLUE_COLOR)
|
|
||||||
.move(x - halfRadius, y - halfRadius);
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawConnectionLine = (draw, points) => {
|
|
||||||
const polyline = draw.polyline(points);
|
|
||||||
|
|
||||||
polyline.fill('none').stroke({
|
|
||||||
color: COLOR
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawSourceDotLine = (draw, { x, y }) => {
|
|
||||||
draw.line(x - 3, y, x + 3, y).stroke({ width: 1, color: COLOR });
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawArrow = (draw, shiftToCenterPoint, nX, nY) => {
|
|
||||||
const fileIconPath = 'resources/right-arrow.svg';
|
|
||||||
const fileIconSize = 7;
|
|
||||||
const fileIconPointShiftX = -4;
|
|
||||||
const fileIconPointShiftY = 9.5;
|
|
||||||
const fileIconPoint = shiftToCenterPoint(nX + fileIconPointShiftX, nY - fileIconPointShiftY);
|
|
||||||
|
|
||||||
draw
|
|
||||||
.rect(5, 6)
|
|
||||||
.fill('#fff')
|
|
||||||
.move(fileIconPoint.x + 2, fileIconPoint.y);
|
|
||||||
|
|
||||||
draw.image(fileIconPath, fileIconSize, fileIconSize).move(fileIconPoint.x, fileIconPoint.y);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.Dot {
|
||||||
|
fill: #BFBFBF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Dot-disabled {
|
||||||
|
fill: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Dot-highlighted {
|
||||||
|
fill: #1890ff;
|
||||||
|
}
|
||||||
21
src/public/js/components/tree-diagram/component/Dot/index.js
Normal file
21
src/public/js/components/tree-diagram/component/Dot/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
export const Dot = props => {
|
||||||
|
const { position, disabled, highlighted } = props;
|
||||||
|
const radius = 2.5;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<circle
|
||||||
|
r={radius}
|
||||||
|
cx={position.x}
|
||||||
|
cy={position.y}
|
||||||
|
className={classNames('Dot', {
|
||||||
|
'Dot-disabled': !!disabled,
|
||||||
|
'Dot-highlighted': !!highlighted
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
import { SYMBOL_WIDTH } from '../../store/constants';
|
||||||
|
|
||||||
|
export const PartEdge = props => {
|
||||||
|
const { sourcePosition, parentName } = props;
|
||||||
|
|
||||||
|
const nameWidth = SYMBOL_WIDTH * parentName.length;
|
||||||
|
const padding = 17;
|
||||||
|
|
||||||
|
const P1 = { x: sourcePosition.x + nameWidth + padding, y: sourcePosition.y };
|
||||||
|
const P2 = { x: P1.x + padding + 6, y: P1.y };
|
||||||
|
|
||||||
|
const polylinePoints = [[P1.x, P1.y], [P2.x, P2.y]];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<polyline points={polylinePoints.join(', ')} className={'CodeCrumbEdge'} />
|
||||||
|
<line x1={P1.x} y1={P1.y - 2} x2={P1.x} y2={P1.y + 2} className={'CodeCrumbEdge'} />
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CodeCrumbEdge = props => {
|
||||||
|
const { sourcePosition, targetPosition, parentName } = props;
|
||||||
|
|
||||||
|
const nameWidth = SYMBOL_WIDTH * parentName.length;
|
||||||
|
const padding = 40;
|
||||||
|
const edgeTurnDistance = 20;
|
||||||
|
|
||||||
|
const P1 = { x: sourcePosition.x + nameWidth + padding, y: sourcePosition.y };
|
||||||
|
|
||||||
|
const P2 = { x: targetPosition.x - edgeTurnDistance, y: sourcePosition.y };
|
||||||
|
const P3 = { x: targetPosition.x - edgeTurnDistance, y: targetPosition.y };
|
||||||
|
const P4 = targetPosition;
|
||||||
|
|
||||||
|
const polylinePoints = [[P1.x, P1.y], [P2.x, P2.y], [P3.x, P3.y], [P4.x, P4.y]];
|
||||||
|
|
||||||
|
return <polyline points={polylinePoints.join(', ')} className={'CodeCrumbEdge'} />;
|
||||||
|
};
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const PADDING = 30;
|
||||||
|
const HALF_PADDING = PADDING / 2 - 5;
|
||||||
|
|
||||||
|
export const getSourcePt = (sourcePosition, targetPosition) => ({
|
||||||
|
x: targetPosition.y > sourcePosition.y ? sourcePosition.x + 10 : sourcePosition.x + 8,
|
||||||
|
y: targetPosition.y > sourcePosition.y ? sourcePosition.y + 7 : sourcePosition.y - 12
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getSourceDotLinePoints = sourcePt => [
|
||||||
|
[sourcePt.x - 3, sourcePt.y],
|
||||||
|
[sourcePt.x + 3, sourcePt.y]
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getConnectionLinePoints = (targetPosition, prevSourcePosition, sourcePt) => {
|
||||||
|
if (!prevSourcePosition) {
|
||||||
|
const P1 = { x: sourcePt.x, y: targetPosition.y + PADDING - 6 };
|
||||||
|
const P2 = { x: targetPosition.x - HALF_PADDING, y: targetPosition.y + PADDING - 6 };
|
||||||
|
const P3 = { x: targetPosition.x - HALF_PADDING, y: targetPosition.y };
|
||||||
|
|
||||||
|
return [
|
||||||
|
[sourcePt.x, sourcePt.y],
|
||||||
|
[P1.x, P1.y],
|
||||||
|
[P2.x, P2.y],
|
||||||
|
[P3.x, P3.y],
|
||||||
|
[targetPosition.x, targetPosition.y]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevSourcePosition.x < sourcePt.x) {
|
||||||
|
//TODO: handle other cases
|
||||||
|
const P1 = { x: sourcePt.x, y: sourcePt.y + HALF_PADDING - 3 };
|
||||||
|
const P2 = {
|
||||||
|
x: prevSourcePosition.x + HALF_PADDING,
|
||||||
|
y: sourcePt.y + HALF_PADDING - 3
|
||||||
|
};
|
||||||
|
|
||||||
|
return [[sourcePt.x, sourcePt.y], [P1.x, P1.y], [P2.x, P2.y]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DependenciesEdge = props => {
|
||||||
|
const {
|
||||||
|
targetPosition,
|
||||||
|
sourcePosition,
|
||||||
|
prevSourcePosition,
|
||||||
|
onClick = () => console.log('on dependencies edge')
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const sourcePt = getSourcePt(sourcePosition, targetPosition);
|
||||||
|
const sourceDotLinePoints = getSourceDotLinePoints(sourcePt);
|
||||||
|
const connectionLinePoints = getConnectionLinePoints(
|
||||||
|
targetPosition,
|
||||||
|
prevSourcePosition,
|
||||||
|
sourcePt
|
||||||
|
);
|
||||||
|
if (!connectionLinePoints) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastPt = connectionLinePoints[connectionLinePoints.length - 1];
|
||||||
|
const endPointConfig = {
|
||||||
|
radius: 2,
|
||||||
|
x: lastPt[0],
|
||||||
|
y: lastPt[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prevSourcePosition) {
|
||||||
|
endPointConfig.radius = 2; // TODO: maybe we can use right away in SVG? it's static anyway!!
|
||||||
|
} else {
|
||||||
|
endPointConfig.x -= 5;
|
||||||
|
endPointConfig.y -= 4;
|
||||||
|
endPointConfig.iconSize = 8;
|
||||||
|
endPointConfig.iconPath = 'resources/right-arrow.svg'; // TODO: move to getter
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<polyline points={sourceDotLinePoints.join(', ')} className={'DependenciesEdge'} />
|
||||||
|
<polyline points={connectionLinePoints.join(', ')} className={'DependenciesEdge'} />
|
||||||
|
<polyline
|
||||||
|
onClick={onClick}
|
||||||
|
points={connectionLinePoints.join(', ')}
|
||||||
|
className={'EdgeMouseHandler'}
|
||||||
|
/>
|
||||||
|
{prevSourcePosition ? (
|
||||||
|
<circle
|
||||||
|
className={'DependenciesEdge-end-dot'}
|
||||||
|
r={endPointConfig.radius}
|
||||||
|
cx={endPointConfig.x}
|
||||||
|
cy={endPointConfig.y}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<image
|
||||||
|
x={endPointConfig.x}
|
||||||
|
y={endPointConfig.y}
|
||||||
|
xlinkHref={endPointConfig.iconPath}
|
||||||
|
height={endPointConfig.iconSize}
|
||||||
|
width={endPointConfig.iconSize}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
export const SourceEdge = props => {
|
||||||
|
const {
|
||||||
|
targetPosition,
|
||||||
|
sourcePosition,
|
||||||
|
disabled,
|
||||||
|
singleChild,
|
||||||
|
onClick = () => console.log('on source edge')
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const edgeTurnDistance = 20;
|
||||||
|
|
||||||
|
const START_PT = sourcePosition;
|
||||||
|
const P2 = { x: targetPosition.x - edgeTurnDistance, y: sourcePosition.y };
|
||||||
|
const P3 = { x: targetPosition.x - edgeTurnDistance, y: targetPosition.y };
|
||||||
|
const END_PT = targetPosition;
|
||||||
|
|
||||||
|
const points = singleChild
|
||||||
|
? [[START_PT.x, START_PT.y], [END_PT.x, END_PT.y]]
|
||||||
|
: [[START_PT.x, START_PT.y], [P2.x, P2.y], [P3.x, P3.y], [END_PT.x, END_PT.y]];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<polyline
|
||||||
|
points={points.join(', ')}
|
||||||
|
className={classNames('SourceEdge', {
|
||||||
|
'SourceEdge-disabled': disabled
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<polyline onClick={onClick} points={points.join(', ')} className={'EdgeMouseHandler'} />
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.EdgeMouseHandler {
|
||||||
|
cursor: pointer;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 8px;
|
||||||
|
stroke: rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SourceEdge {
|
||||||
|
fill: none;
|
||||||
|
stroke: #BFBFBF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SourceEdge-disabled {
|
||||||
|
stroke: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DependenciesEdge {
|
||||||
|
fill: none;
|
||||||
|
stroke: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DependenciesEdge-end-dot {
|
||||||
|
fill: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeCrumbEdge {
|
||||||
|
fill: none;
|
||||||
|
stroke: #ff18a6;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
export const CodeCrumbName = props => {
|
||||||
|
// onMouseOver maybe use onMouseOver to show crumb details in popover
|
||||||
|
const { position, loc, name, singleCrumb, onMouseOver, onClick } = props;
|
||||||
|
|
||||||
|
const textPoint = { x: singleCrumb ? position.x - 20 : position.x, y: position.y };
|
||||||
|
const symbolWidth = 6;
|
||||||
|
const locWidth = loc.length * symbolWidth;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<rect
|
||||||
|
x={textPoint.x}
|
||||||
|
y={textPoint.y - 6}
|
||||||
|
width={locWidth}
|
||||||
|
height={12}
|
||||||
|
className={'CodeCrumbName-rect'}
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x={textPoint.x + 3}
|
||||||
|
y={textPoint.y + 3}
|
||||||
|
onClick={onClick}
|
||||||
|
className={'CodeCrumbName-loc'}
|
||||||
|
>
|
||||||
|
{loc}
|
||||||
|
</text>
|
||||||
|
{(name && (
|
||||||
|
<text
|
||||||
|
x={textPoint.x + 3 + locWidth - 1}
|
||||||
|
y={textPoint.y + 4}
|
||||||
|
onClick={onClick}
|
||||||
|
className={'CodeCrumbName-text'}
|
||||||
|
>
|
||||||
|
:{name}
|
||||||
|
</text>
|
||||||
|
)) ||
|
||||||
|
null}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
37
src/public/js/components/tree-diagram/component/Node/File.js
Normal file
37
src/public/js/components/tree-diagram/component/Node/File.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const ICONS_DIR = 'resources/';
|
||||||
|
|
||||||
|
export const FileName = props => {
|
||||||
|
const { position, name, onTextClick, onIconClick, purple } = props;
|
||||||
|
|
||||||
|
const iconPath = ICONS_DIR + (purple ? 'js-file-purple.svg' : 'js-file.svg');
|
||||||
|
const iconSize = 15;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<image
|
||||||
|
x={position.x + 2}
|
||||||
|
y={position.y - 10}
|
||||||
|
onClick={onIconClick}
|
||||||
|
xlinkHref={iconPath}
|
||||||
|
height={iconSize}
|
||||||
|
width={iconSize}
|
||||||
|
className={'NodeIcon'}
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x={position.x + 16}
|
||||||
|
y={position.y + 5}
|
||||||
|
onClick={onTextClick}
|
||||||
|
className={classNames('NodeText-file-name', {
|
||||||
|
'NodeText-file-name-purple': purple
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</text>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const ICONS_DIR = 'resources/';
|
||||||
|
|
||||||
|
export const FolderName = props => {
|
||||||
|
const { position, name, disabled, closed, onClick } = props;
|
||||||
|
|
||||||
|
const iconPath = `${ICONS_DIR}${closed ? 'closed-' : ''}folder${disabled ? '-disabled' : ''}.svg`;
|
||||||
|
const iconSize = closed ? 14 : 15;
|
||||||
|
|
||||||
|
const iconPositionX = position.x + 3;
|
||||||
|
const iconPositionY = position.y + (closed ? -16 : -17);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{closed ? (
|
||||||
|
<polyline
|
||||||
|
points={[
|
||||||
|
iconPositionX - 1,
|
||||||
|
iconPositionY + 16,
|
||||||
|
iconPositionX + 16,
|
||||||
|
iconPositionY + 16,
|
||||||
|
iconPositionX + 16,
|
||||||
|
iconPositionY + 14
|
||||||
|
].join(', ')}
|
||||||
|
className={classNames('NodeIcon-folder-line', {
|
||||||
|
'NodeIcon-folder-line-disabled': disabled
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<image
|
||||||
|
x={iconPositionX}
|
||||||
|
y={iconPositionY}
|
||||||
|
onClick={onClick}
|
||||||
|
xlinkHref={iconPath}
|
||||||
|
height={iconSize}
|
||||||
|
width={iconSize}
|
||||||
|
className={'NodeIcon'}
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x={position.x + 20}
|
||||||
|
y={position.y - 3}
|
||||||
|
className={classNames('NodeText-folder-name', {
|
||||||
|
'NodeText-folder-name-disabled': disabled
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</text>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
.NodeIcon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeIcon-folder-line {
|
||||||
|
fill: none;
|
||||||
|
stroke: #BFBFBF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeIcon-folder-line-disabled {
|
||||||
|
stroke: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeText-file-name {
|
||||||
|
fill: #595959;
|
||||||
|
font-family: 'Menlo';
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeText-file-name-purple {
|
||||||
|
fill: #ff18a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeText-folder-name {
|
||||||
|
fill: #595959;
|
||||||
|
font-family: 'Menlo';
|
||||||
|
}
|
||||||
|
|
||||||
|
.NodeText-folder-name-disabled {
|
||||||
|
fill: #A9A8A8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeCrumbName-rect {
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #ff18a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeCrumbName-loc {
|
||||||
|
fill: #595959;
|
||||||
|
font-family: 'Menlo';
|
||||||
|
font-size: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeCrumbName-text {
|
||||||
|
fill: #ff18a6;
|
||||||
|
font-family: 'Menlo';
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { withSvgDraw } from '../utils/SvgDraw';
|
|
||||||
import {
|
|
||||||
drawDot,
|
|
||||||
drawSourceEdge,
|
|
||||||
drawFileText,
|
|
||||||
drawFileIcon,
|
|
||||||
drawFolderText,
|
|
||||||
drawFolderIcon
|
|
||||||
} from './drawHelpers';
|
|
||||||
|
|
||||||
import { FILE_NODE_TYPE, DIR_NODE_TYPE } from '../../../../../../shared/constants';
|
|
||||||
import { createSet } from '../utils/SvgSet';
|
|
||||||
|
|
||||||
class SourceTree extends React.Component {
|
|
||||||
componentDidMount() {
|
|
||||||
this.drawSet = createSet(this.props.primaryDraw);
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.clearPrimaryDraw();
|
|
||||||
this.clearSecondaryDraw();
|
|
||||||
this.drawTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.clearPrimaryDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearPrimaryDraw() {
|
|
||||||
this.drawSet.clearAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearSecondaryDraw() {
|
|
||||||
this.props.secondaryDraw.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
drawTree() {
|
|
||||||
const {
|
|
||||||
primaryDraw,
|
|
||||||
secondaryDraw,
|
|
||||||
layoutNodes,
|
|
||||||
closedFolders,
|
|
||||||
shiftToCenterPoint,
|
|
||||||
dependenciesDiagramOn,
|
|
||||||
codeCrumbsMinimize,
|
|
||||||
onFileSelect,
|
|
||||||
onFileIconClick,
|
|
||||||
onFolderClick
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { add } = this.drawSet;
|
|
||||||
|
|
||||||
//note: instance from d3-flex tree, not Array
|
|
||||||
layoutNodes.each(node => {
|
|
||||||
const [nX, nY] = [node.y, node.x];
|
|
||||||
const parent = node.parent;
|
|
||||||
|
|
||||||
if (parent && parent.data.type === DIR_NODE_TYPE) {
|
|
||||||
const [pX, pY] = [parent.y, parent.x];
|
|
||||||
|
|
||||||
drawSourceEdge(secondaryDraw, shiftToCenterPoint, {
|
|
||||||
disabled: dependenciesDiagramOn,
|
|
||||||
target: {
|
|
||||||
x: nX,
|
|
||||||
y: nY
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
x: pX,
|
|
||||||
y: pY
|
|
||||||
},
|
|
||||||
singleChild: parent.children.length === 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.data.type === FILE_NODE_TYPE) {
|
|
||||||
drawDot(secondaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
disabled: dependenciesDiagramOn
|
|
||||||
});
|
|
||||||
|
|
||||||
add(
|
|
||||||
drawFileText(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
purple: node.children && codeCrumbsMinimize,
|
|
||||||
name: node.data.name,
|
|
||||||
onClick() {
|
|
||||||
onFileSelect(node.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
add(
|
|
||||||
drawFileIcon(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
purple: node.children && codeCrumbsMinimize,
|
|
||||||
onClick() {
|
|
||||||
dependenciesDiagramOn && onFileIconClick(node.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.data.type === DIR_NODE_TYPE) {
|
|
||||||
drawDot(secondaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
disabled: dependenciesDiagramOn
|
|
||||||
});
|
|
||||||
|
|
||||||
add(
|
|
||||||
drawFolderText(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
name: node.data.name,
|
|
||||||
disabled: dependenciesDiagramOn
|
|
||||||
})
|
|
||||||
);
|
|
||||||
add(
|
|
||||||
drawFolderIcon(primaryDraw, shiftToCenterPoint, {
|
|
||||||
x: nX,
|
|
||||||
y: nY,
|
|
||||||
disabled: dependenciesDiagramOn,
|
|
||||||
closed: closedFolders[node.data.path],
|
|
||||||
onClick() {
|
|
||||||
onFolderClick(node.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withSvgDraw(SourceTree);
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import { getCurvedPath } from '../../../../utils/svgPrimitives';
|
|
||||||
import { BLUE_COLOR, PURPLE_COLOR, SYMBOL_WIDTH } from '../../store/constants';
|
|
||||||
|
|
||||||
//TODO: move numbers to config per function
|
|
||||||
//create object instead
|
|
||||||
const ICONS_DIR = 'resources/';
|
|
||||||
|
|
||||||
export const drawDot = (draw, shiftToCenterPoint, { x, y, disabled, highlighted }) => {
|
|
||||||
const radius = 5;
|
|
||||||
const halfRadius = radius / 2;
|
|
||||||
const circlePoint = shiftToCenterPoint(x - halfRadius, y - halfRadius);
|
|
||||||
|
|
||||||
let color = '#BFBFBF';
|
|
||||||
if (disabled) {
|
|
||||||
color = '#ccc';
|
|
||||||
}
|
|
||||||
if (highlighted) {
|
|
||||||
color = BLUE_COLOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return draw
|
|
||||||
.circle(radius)
|
|
||||||
.fill(color)
|
|
||||||
.move(circlePoint.x, circlePoint.y);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawSourceEdge = (
|
|
||||||
draw,
|
|
||||||
shiftToCenterPoint,
|
|
||||||
{ target, source, disabled, singleChild }
|
|
||||||
) => {
|
|
||||||
const edgeTurnDistance = 20;
|
|
||||||
|
|
||||||
const START_PT = shiftToCenterPoint(source.x, source.y);
|
|
||||||
const P2 = shiftToCenterPoint(target.x - edgeTurnDistance, source.y);
|
|
||||||
const P3 = shiftToCenterPoint(target.x - edgeTurnDistance, target.y);
|
|
||||||
const END_PT = shiftToCenterPoint(target.x, target.y);
|
|
||||||
|
|
||||||
const points = singleChild
|
|
||||||
? [[START_PT.x, START_PT.y], [END_PT.x, END_PT.y]]
|
|
||||||
: [[START_PT.x, START_PT.y], [P2.x, P2.y], [P3.x, P3.y], [END_PT.x, END_PT.y]];
|
|
||||||
|
|
||||||
const polyline = draw.polyline(points);
|
|
||||||
|
|
||||||
const color = !disabled ? '#BFBFBF' : '#ccc';
|
|
||||||
polyline.fill('none').stroke({
|
|
||||||
color
|
|
||||||
});
|
|
||||||
|
|
||||||
return polyline;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawFileText = (draw, shiftToCenterPoint, { x, y, purple, name = '', onClick }) => {
|
|
||||||
const text = draw.text(name);
|
|
||||||
text.font({ fill: purple ? PURPLE_COLOR : '#595959', family: 'Menlo' });
|
|
||||||
|
|
||||||
const fileTextPointShiftX = 16;
|
|
||||||
const fileTextPointShiftY = 8;
|
|
||||||
const fileTextPoint = shiftToCenterPoint(x + fileTextPointShiftX, y - fileTextPointShiftY);
|
|
||||||
text.move(fileTextPoint.x, fileTextPoint.y);
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
text.style({ cursor: 'pointer' }).on('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawFileIcon = (draw, shiftToCenterPoint, { x, y, purple, onClick }) => {
|
|
||||||
const fileIconPath = ICONS_DIR + (purple ? 'js-file-purple.svg' : 'js-file.svg');
|
|
||||||
const fileIconSize = 15;
|
|
||||||
const fileIconPointShiftX = 2;
|
|
||||||
const fileIconPointShiftY = 10;
|
|
||||||
const fileIconPoint = shiftToCenterPoint(x + fileIconPointShiftX, y - fileIconPointShiftY);
|
|
||||||
|
|
||||||
const icon = draw
|
|
||||||
.image(fileIconPath, fileIconSize, fileIconSize)
|
|
||||||
.move(fileIconPoint.x, fileIconPoint.y);
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
icon.style({ cursor: 'pointer' }).on('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawFolderText = (draw, shiftToCenterPoint, { x, y, name = '', disabled }) => {
|
|
||||||
const folderTextPointShiftX = 20;
|
|
||||||
const folderTextPointShiftY = 16;
|
|
||||||
|
|
||||||
const folderTextPoint = shiftToCenterPoint(x + folderTextPointShiftX, y - folderTextPointShiftY);
|
|
||||||
|
|
||||||
const fill = !disabled ? '#595959' : '#A9A8A8';
|
|
||||||
const text = draw.text(name);
|
|
||||||
|
|
||||||
text.font({ fill, family: 'Menlo' });
|
|
||||||
text.move(folderTextPoint.x, folderTextPoint.y);
|
|
||||||
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const drawFolderIcon = (draw, shiftToCenterPoint, { x, y, disabled, closed, onClick }) => {
|
|
||||||
const folderIconPath = `${ICONS_DIR}${closed ? 'closed-' : ''}folder${
|
|
||||||
disabled ? '-disabled' : ''
|
|
||||||
}.svg`;
|
|
||||||
|
|
||||||
const folderIconSize = closed ? 14 : 15;
|
|
||||||
const folderIconPointShiftX = closed ? 3 : 3;
|
|
||||||
const folderIconPointShiftY = closed ? 16 : 17;
|
|
||||||
const folderIconPoint = shiftToCenterPoint(x + folderIconPointShiftX, y - folderIconPointShiftY);
|
|
||||||
|
|
||||||
let polyline = null;
|
|
||||||
if (closed) {
|
|
||||||
polyline = draw.polyline([
|
|
||||||
folderIconPoint.x - 1,
|
|
||||||
folderIconPoint.y + 16,
|
|
||||||
folderIconPoint.x + 16,
|
|
||||||
folderIconPoint.y + 16,
|
|
||||||
folderIconPoint.x + 16,
|
|
||||||
folderIconPoint.y + 14
|
|
||||||
]);
|
|
||||||
|
|
||||||
const color = !disabled ? '#BFBFBF' : '#ccc';
|
|
||||||
polyline.fill('none').stroke({
|
|
||||||
color
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const icon = draw
|
|
||||||
.image(folderIconPath, folderIconSize, folderIconSize)
|
|
||||||
.move(folderIconPoint.x, folderIconPoint.y);
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
icon.style({ cursor: 'pointer' }).on('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!polyline) return icon;
|
|
||||||
|
|
||||||
return [icon, polyline];
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { getFilesList } from '../../../../utils/treeLayout';
|
||||||
|
import { CodeCrumbName } from '../Node/CodeCrumb';
|
||||||
|
import { FileName } from '../Node/File';
|
||||||
|
import { PartEdge, CodeCrumbEdge } from '../Edge/CodeCrumbEdge';
|
||||||
|
|
||||||
|
class CodeCrumbsTree extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
filesTreeLayoutNodes,
|
||||||
|
shiftToCenterPoint,
|
||||||
|
sourceDiagramOn,
|
||||||
|
dependenciesDiagramOn,
|
||||||
|
codeCrumbsMinimize,
|
||||||
|
codeCrumbsDetails,
|
||||||
|
onCodeCrumbSelect
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const filesList = getFilesList(filesTreeLayoutNodes);
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{filesList.map(node => {
|
||||||
|
const [nX, nY] = [node.y, node.x];
|
||||||
|
const position = shiftToCenterPoint(nX, nY);
|
||||||
|
|
||||||
|
if (!node.children) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={`code-crumb-${node.data.name}`}>
|
||||||
|
{!sourceDiagramOn && !dependenciesDiagramOn ? (
|
||||||
|
<FileName position={position} name={node.data.name} purple={codeCrumbsMinimize} />
|
||||||
|
) : null}
|
||||||
|
{(!codeCrumbsMinimize && (
|
||||||
|
<PartEdge sourcePosition={position} parentName={node.data.name} />
|
||||||
|
)) ||
|
||||||
|
null}
|
||||||
|
|
||||||
|
{!codeCrumbsMinimize &&
|
||||||
|
node.children.map((crumb, i, list) => {
|
||||||
|
const [cX, cY] = [crumb.y, crumb.x];
|
||||||
|
const crumbPosition = shiftToCenterPoint(cX, cY);
|
||||||
|
const singleCrumb = list.length === 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={`code-crumb-edge-${i}`}>
|
||||||
|
{(!singleCrumb && (
|
||||||
|
<CodeCrumbEdge
|
||||||
|
sourcePosition={position}
|
||||||
|
targetPosition={crumbPosition}
|
||||||
|
parentName={node.data.name}
|
||||||
|
/>
|
||||||
|
)) ||
|
||||||
|
null}
|
||||||
|
<CodeCrumbName
|
||||||
|
position={crumbPosition}
|
||||||
|
loc={crumb.data.displayLoc}
|
||||||
|
name={crumb.data.name}
|
||||||
|
singleCrumb={singleCrumb}
|
||||||
|
onClick={() => onCodeCrumbSelect(node.data, crumb.data)}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CodeCrumbsTree;
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { getFilesList } from '../../../../utils/treeLayout';
|
||||||
|
|
||||||
|
import { DependenciesEdge } from '../Edge/DepenenciesEdge';
|
||||||
|
import { FileName } from '../Node/File';
|
||||||
|
|
||||||
|
//move to utils
|
||||||
|
export const findNodeByPathName = (list = [], pathName) => {
|
||||||
|
return list.find(l => l.data.path === pathName);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFilteredDependenciesList = ({
|
||||||
|
dependenciesList,
|
||||||
|
dependenciesEntryPoint,
|
||||||
|
dependenciesShowOneModule
|
||||||
|
}) => {
|
||||||
|
const entryPoint = dependenciesEntryPoint || {
|
||||||
|
path: dependenciesList[0].moduleName
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dependenciesShowOneModule) {
|
||||||
|
return [dependenciesList.find(d => d.moduleName === entryPoint.path)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return collectDependencies(entryPoint.path, dependenciesList);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const collectDependencies = (entryModuleName, dependenciesList) => {
|
||||||
|
let queue = [].concat(entryModuleName),
|
||||||
|
store = [];
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
let moduleName = queue.shift(),
|
||||||
|
entryModule = dependenciesList.find(d => d.moduleName === moduleName);
|
||||||
|
|
||||||
|
store.push(entryModule);
|
||||||
|
|
||||||
|
const nodeBody = entryModule.importedModuleNames;
|
||||||
|
if (nodeBody) {
|
||||||
|
queue = [...queue, ...nodeBody];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return store;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DependenciesTree extends React.Component {
|
||||||
|
render() {
|
||||||
|
const { filesTreeLayoutNodes, shiftToCenterPoint, sourceDiagramOn } = this.props;
|
||||||
|
|
||||||
|
const moduleFilesList = getFilesList(filesTreeLayoutNodes);
|
||||||
|
const filteredDependenciesList = getFilteredDependenciesList(this.props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{filteredDependenciesList.map(({ moduleName, importedModuleNames }, i) => {
|
||||||
|
const moduleNode = findNodeByPathName(moduleFilesList, moduleName);
|
||||||
|
|
||||||
|
if (!moduleNode) return;
|
||||||
|
|
||||||
|
const [mX, mY] = [moduleNode.y, moduleNode.x];
|
||||||
|
const targetPosition = shiftToCenterPoint(mX, mY);
|
||||||
|
|
||||||
|
let prevSourcePosition = null;
|
||||||
|
return (
|
||||||
|
<React.Fragment key={moduleName + i}>
|
||||||
|
{!sourceDiagramOn ? (
|
||||||
|
<FileName position={targetPosition} name={moduleNode.data.name} />
|
||||||
|
) : null}
|
||||||
|
{importedModuleNames.map((name, i) => {
|
||||||
|
const importedNode = findNodeByPathName(moduleFilesList, name);
|
||||||
|
|
||||||
|
if (!importedNode) return null;
|
||||||
|
|
||||||
|
const [iX, iY] = [importedNode.y, importedNode.x];
|
||||||
|
//TODO: implementation iterations:
|
||||||
|
//1) done: first with sharp angles + overlay
|
||||||
|
//2) done: without overlaying, not fot all cases
|
||||||
|
//3) rounded angles
|
||||||
|
const sourcePosition = shiftToCenterPoint(iX, iY);
|
||||||
|
|
||||||
|
const dependenciesEdge = (
|
||||||
|
<DependenciesEdge
|
||||||
|
key={name + i}
|
||||||
|
sourcePosition={sourcePosition}
|
||||||
|
targetPosition={targetPosition}
|
||||||
|
prevSourcePosition={prevSourcePosition}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
prevSourcePosition = sourcePosition;
|
||||||
|
|
||||||
|
return dependenciesEdge;
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DependenciesTree;
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// TODO: add webpack resolve to not have these ../ .../ ../
|
||||||
|
import { FILE_NODE_TYPE, DIR_NODE_TYPE } from '../../../../../../shared/constants';
|
||||||
|
import { FileName } from '../Node/File';
|
||||||
|
import { FolderName } from '../Node/Folder';
|
||||||
|
import { Dot } from '../Dot/';
|
||||||
|
import { SourceEdge } from '../Edge/SourceEdge';
|
||||||
|
|
||||||
|
import DependenciesTree from './DependenciesTree';
|
||||||
|
import CodeCrumbsTree from './CodeCrumbsTree';
|
||||||
|
|
||||||
|
class SourceTree extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
sourceDiagramOn,
|
||||||
|
dependenciesDiagramOn,
|
||||||
|
codeCrumbsDiagramOn,
|
||||||
|
|
||||||
|
filesTreeLayoutNodes,
|
||||||
|
closedFolders,
|
||||||
|
shiftToCenterPoint,
|
||||||
|
codeCrumbsMinimize,
|
||||||
|
onFileSelect,
|
||||||
|
onFileIconClick,
|
||||||
|
onFolderClick,
|
||||||
|
dependenciesList
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const sourceEdges = [];
|
||||||
|
const sourceNodes = [];
|
||||||
|
const sourceDotes = [];
|
||||||
|
|
||||||
|
// TODO: add normal id generators for keys to not use i
|
||||||
|
let i = 0;
|
||||||
|
filesTreeLayoutNodes.each(node => {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
const [nX, nY] = [node.y, node.x];
|
||||||
|
const position = shiftToCenterPoint(nX, nY);
|
||||||
|
const name = node.data.name;
|
||||||
|
|
||||||
|
const parent = node.parent;
|
||||||
|
if (parent && parent.data.type === DIR_NODE_TYPE) {
|
||||||
|
const [pX, pY] = [parent.y, parent.x];
|
||||||
|
const sourcePosition = shiftToCenterPoint(pX, pY);
|
||||||
|
|
||||||
|
sourceEdges.push(
|
||||||
|
<SourceEdge
|
||||||
|
key={`edge-${i}`}
|
||||||
|
targetPosition={position}
|
||||||
|
sourcePosition={sourcePosition}
|
||||||
|
disabled={dependenciesDiagramOn}
|
||||||
|
singleChild={parent.children.length === 1}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([FILE_NODE_TYPE, DIR_NODE_TYPE].includes(node.data.type)) {
|
||||||
|
sourceDotes.push(
|
||||||
|
<Dot key={`dot-${i}`} position={position} disabled={dependenciesDiagramOn} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodeBasedOnType = null;
|
||||||
|
if (node.data.type === FILE_NODE_TYPE) {
|
||||||
|
nodeBasedOnType = (
|
||||||
|
<FileName
|
||||||
|
position={position}
|
||||||
|
name={name}
|
||||||
|
purple={node.children && codeCrumbsMinimize}
|
||||||
|
onTextClick={() => onFileSelect(node.data)}
|
||||||
|
onIconClick={() => dependenciesDiagramOn && onFileIconClick(node.data)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (node.data.type === DIR_NODE_TYPE) {
|
||||||
|
nodeBasedOnType = (
|
||||||
|
<FolderName
|
||||||
|
position={position}
|
||||||
|
name={name}
|
||||||
|
disabled={dependenciesDiagramOn}
|
||||||
|
closed={closedFolders[node.data.path]}
|
||||||
|
onClick={() => onFolderClick(node.data)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceNodes.push(<React.Fragment key={name + i}>{nodeBasedOnType}</React.Fragment>);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{(sourceDiagramOn && sourceEdges) || null}
|
||||||
|
{(sourceDiagramOn && sourceDotes) || null}
|
||||||
|
|
||||||
|
{dependenciesList && dependenciesDiagramOn && <DependenciesTree {...this.props} />}
|
||||||
|
|
||||||
|
{(sourceDiagramOn && sourceNodes) || null}
|
||||||
|
|
||||||
|
{(codeCrumbsDiagramOn && <CodeCrumbsTree {...this.props} />) || null}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SourceTree;
|
||||||
@@ -1,119 +1,33 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SourceTree from './SourceTree/SourceTree';
|
import SourceTree from './Tree/SourceTree';
|
||||||
import DependenciesTree from './DependenciesTree/DependenciesTree';
|
|
||||||
import CodeCrumbsTree from './CodeCrumbsTree/CodeCrumbsTree';
|
|
||||||
import './TreeDiagram.css';
|
import './TreeDiagram.css';
|
||||||
|
|
||||||
|
import { buildShiftToPoint } from '../../../utils/geometry';
|
||||||
|
|
||||||
|
export const BOX_SIZE = { W: 1000, H: 800 };
|
||||||
|
export const DOT = {
|
||||||
|
x: 50,
|
||||||
|
y: 500
|
||||||
|
};
|
||||||
|
|
||||||
class TreeDiagram extends React.Component {
|
class TreeDiagram extends React.Component {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.setState({ layersReady: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
//cc: layers
|
|
||||||
renderLayers() {
|
|
||||||
return (
|
|
||||||
<div className="TreeDiagram-layers">
|
|
||||||
<div
|
|
||||||
data-name="sourceEdgesLayer"
|
|
||||||
className="TreeDiagram-layer"
|
|
||||||
ref={ref => (this.sourceEdgesLayer = ref)}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
data-name="dependenciesEdgesLayer"
|
|
||||||
className="TreeDiagram-layer"
|
|
||||||
ref={ref => (this.dependenciesEdgesLayer = ref)}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
data-name="iconsAndTextLayer"
|
|
||||||
className="TreeDiagram-layer"
|
|
||||||
ref={ref => (this.iconsAndTextLayer = ref)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDiagrams() {
|
|
||||||
const {
|
|
||||||
filesTreeLayoutNodes,
|
|
||||||
dependenciesList,
|
|
||||||
closedFolders,
|
|
||||||
dependenciesEntryPoint,
|
|
||||||
|
|
||||||
sourceDiagramOn,
|
|
||||||
dependenciesDiagramOn,
|
|
||||||
dependenciesShowOneModule,
|
|
||||||
codeCrumbsDiagramOn,
|
|
||||||
codeCrumbsMinimize,
|
|
||||||
codeCrumbsDetails,
|
|
||||||
|
|
||||||
onFileSelect,
|
|
||||||
onFileIconClick,
|
|
||||||
onFolderClick,
|
|
||||||
onCodeCrumbSelect
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const sharedProps = {
|
|
||||||
sourceDiagramOn,
|
|
||||||
dependenciesDiagramOn,
|
|
||||||
codeCrumbsDiagramOn,
|
|
||||||
codeCrumbsMinimize,
|
|
||||||
codeCrumbsDetails
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{filesTreeLayoutNodes &&
|
|
||||||
sourceDiagramOn && (
|
|
||||||
<SourceTree
|
|
||||||
layoutNodes={filesTreeLayoutNodes}
|
|
||||||
closedFolders={closedFolders}
|
|
||||||
secondaryLayer={this.sourceEdgesLayer}
|
|
||||||
primaryLayer={this.iconsAndTextLayer}
|
|
||||||
onFileSelect={onFileSelect}
|
|
||||||
onFileIconClick={onFileIconClick}
|
|
||||||
onFolderClick={onFolderClick}
|
|
||||||
{...sharedProps}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{dependenciesList &&
|
|
||||||
filesTreeLayoutNodes &&
|
|
||||||
dependenciesDiagramOn && (
|
|
||||||
<DependenciesTree
|
|
||||||
dependenciesList={dependenciesList}
|
|
||||||
filesTreeLayoutNodes={filesTreeLayoutNodes}
|
|
||||||
dependenciesEntryPoint={dependenciesEntryPoint}
|
|
||||||
dependenciesShowOneModule={dependenciesShowOneModule}
|
|
||||||
primaryLayer={this.dependenciesEdgesLayer}
|
|
||||||
{...sharedProps}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{filesTreeLayoutNodes &&
|
|
||||||
codeCrumbsDiagramOn && (
|
|
||||||
<CodeCrumbsTree
|
|
||||||
filesTreeLayoutNodes={filesTreeLayoutNodes}
|
|
||||||
primaryLayer={this.iconsAndTextLayer}
|
|
||||||
onCodeCrumbSelect={onCodeCrumbSelect}
|
|
||||||
{...sharedProps}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { layersReady } = this.state;
|
const { width = BOX_SIZE.W, height = BOX_SIZE.H, dot = DOT } = this.props;
|
||||||
|
const shiftToCenterPoint = buildShiftToPoint(dot);
|
||||||
|
|
||||||
|
const { filesTreeLayoutNodes, ...otherProps } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="TreeDiagram-container">
|
<div className="TreeDiagram-container">
|
||||||
{this.renderLayers()}
|
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg">
|
||||||
{layersReady && this.renderDiagrams()}
|
{filesTreeLayoutNodes && (
|
||||||
|
<SourceTree
|
||||||
|
filesTreeLayoutNodes={filesTreeLayoutNodes}
|
||||||
|
shiftToCenterPoint={shiftToCenterPoint}
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import SVG from 'svg.js';
|
|
||||||
import { buildShiftToPoint } from '../../../../utils/geometry';
|
|
||||||
|
|
||||||
const IconsAndTextLayer = 'iconsAndTextLayer';
|
|
||||||
const cachedSvgDraws = {};
|
|
||||||
|
|
||||||
const BOX_SIZE = { W: 1000, H: 800 };
|
|
||||||
const DOT = {
|
|
||||||
x: 50,
|
|
||||||
y: 500
|
|
||||||
};
|
|
||||||
|
|
||||||
const shiftToCenterPoint = buildShiftToPoint(DOT);
|
|
||||||
|
|
||||||
export const withSvgDraw = Component =>
|
|
||||||
class extends React.Component {
|
|
||||||
state = {};
|
|
||||||
|
|
||||||
createSvg(layer) {
|
|
||||||
const { width = BOX_SIZE.W, height = BOX_SIZE.H } = this.props;
|
|
||||||
|
|
||||||
return SVG(layer).size(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrimaryDraw() {
|
|
||||||
const { primaryLayer } = this.props;
|
|
||||||
|
|
||||||
const primaryLayerName = primaryLayer.dataset.name;
|
|
||||||
if (primaryLayerName !== IconsAndTextLayer) {
|
|
||||||
return this.createSvg(primaryLayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedSvgDraws[primaryLayerName]) {
|
|
||||||
return cachedSvgDraws[primaryLayerName];
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedSvgDraws[primaryLayerName] = this.createSvg(primaryLayer);
|
|
||||||
return cachedSvgDraws[primaryLayerName];
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { secondaryLayer } = this.props;
|
|
||||||
|
|
||||||
let subState = {
|
|
||||||
primaryDraw: this.getPrimaryDraw()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (secondaryLayer) {
|
|
||||||
subState = {
|
|
||||||
...subState,
|
|
||||||
secondaryDraw: this.createSvg(secondaryLayer)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(subState);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
const { primaryLayer, secondaryLayer } = this.props;
|
|
||||||
|
|
||||||
if (primaryLayer.dataset.name !== IconsAndTextLayer) {
|
|
||||||
primaryLayer.removeChild(this.state.primaryDraw.node);
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryLayer && secondaryLayer.removeChild(this.state.secondaryDraw.node);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { primaryDraw, secondaryDraw } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
(primaryDraw && (
|
|
||||||
<Component
|
|
||||||
{...this.props}
|
|
||||||
primaryDraw={primaryDraw}
|
|
||||||
secondaryDraw={secondaryDraw}
|
|
||||||
shiftToCenterPoint={shiftToCenterPoint}
|
|
||||||
/>
|
|
||||||
)) ||
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
export const createSet = draw => {
|
|
||||||
const drawSet = draw.set();
|
|
||||||
|
|
||||||
return {
|
|
||||||
add(list) {
|
|
||||||
drawSet.add.apply(drawSet, [].concat(list));
|
|
||||||
},
|
|
||||||
clearAll() {
|
|
||||||
drawSet.each(function() {
|
|
||||||
this.off();
|
|
||||||
this.remove();
|
|
||||||
});
|
|
||||||
drawSet.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -59,8 +59,10 @@ const getCrumbs = fileCode => {
|
|||||||
if (comment && isCodecrumb(comment)) {
|
if (comment && isCodecrumb(comment)) {
|
||||||
const params = parseCodecrumbComment(comment);
|
const params = parseCodecrumbComment(comment);
|
||||||
|
|
||||||
|
const loc = node.loc.start;
|
||||||
crumbsList.push({
|
crumbsList.push({
|
||||||
name: params.name || '', //TODO: check, can be bug with layout calc
|
name: params.name || '', //TODO: check, can be bug with layout calc
|
||||||
|
displayLoc: `(${loc.line},${loc.column})`,
|
||||||
crumbedLineNode: node,
|
crumbedLineNode: node,
|
||||||
crumbNode: comment,
|
crumbNode: comment,
|
||||||
params
|
params
|
||||||
|
|||||||
@@ -1273,6 +1273,10 @@ classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classna
|
|||||||
version "2.2.5"
|
version "2.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
||||||
|
|
||||||
|
classnames@^2.2.6:
|
||||||
|
version "2.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||||
|
|
||||||
cli-boxes@^1.0.0:
|
cli-boxes@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
|
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
|
||||||
@@ -6014,10 +6018,6 @@ supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^3.0.0"
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
svg.js@^2.6.4:
|
|
||||||
version "2.6.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/svg.js/-/svg.js-2.6.4.tgz#034085b13391c6fcca1a0185a34dbea6c3e78dc3"
|
|
||||||
|
|
||||||
svgo@^0.7.0:
|
svgo@^0.7.0:
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
||||||
|
|||||||
Reference in New Issue
Block a user