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",
|
||||
"babylon": "^6.18.0",
|
||||
"chokidar": "^2.0.3",
|
||||
"classnames": "^2.2.6",
|
||||
"css-loader": "^0.28.11",
|
||||
"d3-flextree": "^2.1.1",
|
||||
"directory-tree": "^2.1.0",
|
||||
@@ -38,7 +39,6 @@
|
||||
"redux-saga": "^0.16.0",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"svg.js": "^2.6.4",
|
||||
"webpack": "^4.6.0",
|
||||
"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">
|
||||
<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"/>
|
||||
</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
|
||||
},
|
||||
{
|
||||
name: 'show details',
|
||||
name: 'show details (remove it)',
|
||||
title: 'Show all 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 SourceTree from './SourceTree/SourceTree';
|
||||
import DependenciesTree from './DependenciesTree/DependenciesTree';
|
||||
import CodeCrumbsTree from './CodeCrumbsTree/CodeCrumbsTree';
|
||||
import SourceTree from './Tree/SourceTree';
|
||||
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 {
|
||||
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() {
|
||||
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 (
|
||||
<div className="TreeDiagram-container">
|
||||
{this.renderLayers()}
|
||||
{layersReady && this.renderDiagrams()}
|
||||
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg">
|
||||
{filesTreeLayoutNodes && (
|
||||
<SourceTree
|
||||
filesTreeLayoutNodes={filesTreeLayoutNodes}
|
||||
shiftToCenterPoint={shiftToCenterPoint}
|
||||
{...otherProps}
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
</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)) {
|
||||
const params = parseCodecrumbComment(comment);
|
||||
|
||||
const loc = node.loc.start;
|
||||
crumbsList.push({
|
||||
name: params.name || '', //TODO: check, can be bug with layout calc
|
||||
displayLoc: `(${loc.line},${loc.column})`,
|
||||
crumbedLineNode: node,
|
||||
crumbNode: comment,
|
||||
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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
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:
|
||||
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:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
|
||||
|
||||
Reference in New Issue
Block a user