diff --git a/package.json b/package.json index 4aa8110..21d49ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactjs-graphs", - "version": "1.0.4", + "version": "1.1.0", "description": "Create branched graphs in ReactJS", "main": "build/index.js", "scripts": { diff --git a/source/Graph.js b/source/Graph.js index 7fc43b8..fa458fa 100644 --- a/source/Graph.js +++ b/source/Graph.js @@ -11,11 +11,13 @@ export default class Graph extends React.Component { super(props) this.state = { list: [], + backtrackList: [], vertexCoordinates: {} } props.vertices.forEach((vertex, index) => { this.state.list.push({ name: vertex.label, next: [] }) + this.state.backtrackList.push({ name: vertex.label, prev: [] }) }) props.edges.map(edge => { @@ -24,12 +26,14 @@ export default class Graph extends React.Component { let mainIndex = 1 - let totalYAllowed = props.height + let totalYAllowed = props.orientation === "horizontal" ? props.height : props.width let vertexGap = props.vertexGap let horizontalMin = 0, horizontalMax = 0 let verticalMin = props.height/2, verticalMax = props.height/2 + console.log(this.state.list) + this.state.list.map(mainVertex => { const edgesTo = mainVertex.next @@ -55,6 +59,8 @@ export default class Graph extends React.Component { for(let i=0;i obj.name == edgesTo[i]).prev.push(mainVertex.name) + const childVertex = edgesTo[i] const childY = (i + 1)*partitionLength @@ -97,10 +103,10 @@ export default class Graph extends React.Component { if(vertex.label in this.state.vertexCoordinates) { // ok } else { - if(flag) throw new Error("Initializing the graph with multiple vertices? Only 1 vertex is supported for now") + if(flag) throw new Error(`Initializing the graph with multiple vertices (${vertex.label})? Only 1 vertex is supported for now`) // this is the first vertex which was not registered in the for-loop above flag = true - if(this.props.orientation === 'horizontal') { + if(props.orientation === 'horizontal') { this.state.vertexCoordinates[vertex.label] = { x: 0, y: totalYAllowed/2 @@ -114,15 +120,40 @@ export default class Graph extends React.Component { } }) - console.log(this.state.vertexCoordinates) + console.log(this.state.backtrackList) console.log(verticalMin, verticalMax) console.log(horizontalMin, horizontalMax) + this.state.backtrackList.forEach(mainVertex => { + // this is for correcting positions of multiple vertices joined to single vertex + const edgeTo = mainVertex.prev + if(edgeTo.length < 2) return + + //debugger + + //const { y:oldY } = this.state.vertexCoordinates[mainVertex.name] + + let sumY = 0 + + edgeTo.forEach(vertex => { + sumY += this.state.vertexCoordinates[vertex].y + }) + + sumY /= edgeTo.length + + this.state.vertexCoordinates[mainVertex.name].y = sumY + + }) + //this.state.horizontalShift = -(horizontalMin + horizontalMax)/2 this.state.verticalShift = ((verticalMin + verticalMax) - (props.height))/2 this.state.horizontalShift = ((horizontalMin + horizontalMax) - (props.width))/2 - + + if(!props.perfectlyCenter) { + if(props.orientation === "vertical") this.state.horizontalShift = 0 + else this.state.verticalShift = 0 + } } getEdges(edgeProps) { @@ -140,7 +171,7 @@ export default class Graph extends React.Component { const { x, y } = vertexCoordinates[edgesTo[i]] elems.push() } @@ -151,26 +182,29 @@ export default class Graph extends React.Component { render() { const { vertexCoordinates, horizontalShift, verticalShift } = this.state - const { vertexStroke, vertexStrokeWidth, inactiveVertexFill, activeVertexFill, vertexRadius } = this.props - const vertexProps = { vertexStroke, vertexStrokeWidth, inactiveVertexFill, activeVertexFill, vertexRadius } + const { labelFontSize, vertexStroke, vertexStrokeWidth, inactiveVertexFill, activeVertexFill, vertexRadius } = this.props + const vertexProps = { labelFontSize, vertexStroke, vertexStrokeWidth, inactiveVertexFill, activeVertexFill, vertexRadius } const { edgeStroke, edgeWidth } = this.props const edgeProps = { edgeStroke, edgeWidth } - const { vertices, width, height } = this.props + const { vertices, width, height, orientation } = this.props return ( this.group = k} offsetX={horizontalShift} offsetY={verticalShift}> - {this.getEdges(edgeProps)} + + {this.getEdges(edgeProps)} {vertices.map((vertex, index) => { return vertex.onClick(vertex.label, index, vertex.extras)} + disabled={vertex.disabled} {...vertexProps} /> }) @@ -186,10 +220,12 @@ Graph.propTypes = { orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, - vertexGap: PropTypes.number.isRequired + vertexGap: PropTypes.number.isRequired, + perfectlyCenter: PropTypes.bool.isRequired } Graph.defaultProps = { orientation: 'horizontal', - vertexGap: 100 + vertexGap: 100, + perfectlyCenter: false } \ No newline at end of file diff --git a/source/Vertex.js b/source/Vertex.js index b7eb1f0..619631b 100644 --- a/source/Vertex.js +++ b/source/Vertex.js @@ -1,5 +1,5 @@ import React from 'react' -import { Circle, Text } from 'react-konva' +import { Circle, Text, Group } from 'react-konva' import PropTypes from 'prop-types' export default class Vertex extends React.Component { @@ -10,55 +10,79 @@ export default class Vertex extends React.Component { this.mouseOutVertex = this.mouseOutVertex.bind(this) this.state = { vertexFill: props.inactiveVertexFill, - textOffsetX: 0 + textOffsetX: 0, + textOffsetY: 0, + showTextNode: true } } componentDidMount() { this.setState({ - textOffsetX: this.text.width() / 2 + textOffsetX: this.text.width() / 2, + textOffsetY: this.text.height() / 2, + showTextNode: !this.props.disabled }) } + shouldComponentUpdate(nextProps, nextState) { + return this.state != nextState + } + mouseOutVertex() { document.body.style.cursor = 'default' - this.setState({ - vertexFill: this.props.inactiveVertexFill - }) + + if(this.props.disabled) { + // disabled vertex + this.setState({ + showTextNode: false + }) + } else { + this.setState({ + vertexFill: this.props.inactiveVertexFill + }) + } } mouseInVertex() { document.body.style.cursor = 'pointer' - this.setState({ - vertexFill: this.props.activeVertexFill - }) + if(this.props.disabled) { + this.setState({ + showTextNode: true + }) + } else { + this.setState({ + vertexFill: this.props.activeVertexFill + }) + } } render() { - const { x, y, label, onClick, vertexStroke, vertexStrokeWidth, vertexRadius } = this.props - const { vertexFill, textOffsetX } = this.state + const { disabled, x, y, label, onClick, vertexStroke, vertexStrokeWidth, vertexRadius, orientation, labelFontSize } = this.props + const { vertexFill, textOffsetX, textOffsetY, showTextNode } = this.state return ( - <> - + {showTextNode ? this.text = node } - x={x} - y={y-30} + x={orientation === "horizontal" ? x : x - textOffsetX - vertexRadius - vertexStrokeWidth - 5 } + y={orientation === "horizontal" ? y - textOffsetY * 2 - vertexRadius - vertexStrokeWidth : y - textOffsetY } offsetX={textOffsetX} - text={label} /> + fontSize={labelFontSize} + text={label} /> : null } - + ) } } @@ -70,7 +94,8 @@ Vertex.propTypes = { vertexStrokeWidth: PropTypes.number.isRequired, inactiveVertexFill: PropTypes.string.isRequired, activeVertexFill: PropTypes.string.isRequired, - vertexRadius: PropTypes.number.isRequired + vertexRadius: PropTypes.number.isRequired, + labelFontSize: PropTypes.number.isRequired } Vertex.defaultProps ={ @@ -78,5 +103,6 @@ Vertex.defaultProps ={ vertexStrokeWidth: 3, inactiveVertexFill: "white", activeVertexFill: "#df6766", - vertexRadius: 10 + vertexRadius: 10, + labelFontSize: 12 } \ No newline at end of file diff --git a/source/index.js b/source/index.js index 34e67d5..f733ff2 100644 --- a/source/index.js +++ b/source/index.js @@ -7,36 +7,39 @@ const onClick = (label, index, extras) => { } const vertices = [ - { label: "Here it begins", onClick, extras: "Some helper data" }, { label: "HTML", onClick }, { label: "CSS", onClick }, - { label: "Sass", onClick }, - { label: "JavaScript", onClick }, - { label: "Bootstrap", onClick }, - { label: "jQuery", onClick }, - { label: "ReactJS", onClick }, - { label: "Jest", onClick }, - { label: "Angular", onClick }, - { label: "Vue", onClick }, - { label: "Redux", onClick }, - { label: "React Material", onClick }, - { label: "Vuetify", onClick }, + { label: "Sass", onClick, disabled: false }, + { label: "JavaScript", onClick, disabled: false }, +// { label: "Bootstrap", onClick, disabled: false }, +// { label: "jQuery", onClick, disabled: false }, + { label: "React.js", onClick, disabled: false }, + { label: "Angular", onClick, disabled: false }, + { label: "Vue", onClick, disabled: false }, +// { label: "Redux", onClick, disabled: false }, +// { label: "Vuetify", onClick, disabled: false }, + { label: "Node.js", }, + //{ label: "GitHub", }, + /*{ label: "MongoDB", }, + { label: "MySQL", }, + { label: "PHP", }, + { label: "nginx", }, + { label: "Apache", },*/ ] const edges = [ - ["Here it begins", "HTML"], ["HTML", "CSS"], ["CSS", "JavaScript"], ["CSS", "Sass"], - ["JavaScript", "jQuery"], - ["jQuery", "Bootstrap"], - ["jQuery", "ReactJS"], - ["jQuery", "Angular"], - ["jQuery", "Vue"], - ["ReactJS", "Redux"], - ["Redux", "React Material"], - ["React Material", "Jest"], - ["Vue", "Vuetify"], + ["Sass", "Vue"], + ["JavaScript", "React.js"], + ["JavaScript", "Angular"], + ["JavaScript", "Vue"], + ["Angular", "Node.js"], + ["React.js", "Node.js"], + ["Vue", "Node.js"], + //["React.js", "Redux"], + //["Vue", "Vuetify"], ] @@ -49,5 +52,8 @@ ReactDOM.render(, document.getElementById('root')) \ No newline at end of file diff --git a/source/konva.js b/source/konva.js index 328f9f4..34c7e7d 100644 --- a/source/konva.js +++ b/source/konva.js @@ -3,6 +3,7 @@ const Konva = require("../node_modules/konva/src/Core") require("../node_modules/konva/src/shapes/Circle") require("../node_modules/konva/src/shapes/Line") require("../node_modules/konva/src/shapes/Text") +require("../node_modules/konva/src/shapes/Text") require("../node_modules/konva/src/Animation")