react-network-diagrams
Version:
457 lines (399 loc) • 16.5 kB
JavaScript
/**
* Copyright (c) 2018, The Regents of the University of California,
* through Lawrence Berkeley National Laboratory (subject to receipt
* of any required approvals from the U.S. Dept. of Energy).
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from "react";
import PropTypes from "prop-types";
import { Endpoint } from "./Endpoint";
import { SimpleEdge as Line } from "./SimpleEdge";
/**
* The `x1`, `x2`, `y1`, and `y2` properties determine the position of the endpoints on the `<svg>` element.
* A path is then drawn betwween the endpoints. If the lineShape property is set to "square",
* the width of the square will be the distance between x1 and x2, with the height of the square
* determined by the size prop.
*
* The `labelPosition`, determines where the label will be positioned around the line. The `xOffset` and
* `yOffset` properties allow you to customize the distance the label is from the `x` and `y` properties.
*
* The `label` property is the name that will be displayed on the line. If you want to display multiple
* lines, the label can take an array of strings, with each array element displayed on a separate line.
*/
export class Connection extends React.Component {
constructor(props) {
super(props);
this.state = {
highlighted: false
};
this.handleMouseOut = this.handleMouseOut.bind(this);
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleSelectionChanged = this.handleSelectionChanged.bind(this);
}
/**
* User hovers over the circuit
*/
handleMouseOver() {
if (!this.props.noNavigate) {
this.setState({ highlighted: true });
}
}
/**
* Use stops hovering over circuit
*/
handleMouseOut() {
if (!this.props.noNavigate) {
this.setState({ highlighted: false });
}
}
handleSelectionChanged(e, value) {
if (!this.props.noNavigate) {
this.props.onSelectionChange(e, value);
}
}
renderEndpoints() {
if (this.props.arrow) {
return <g />;
} else {
return (
<g>
<Endpoint
x={this.props.x1}
y={this.props.y1}
key={"line-begin"}
style={this.props.style}
radius={this.props.radius}
shape={this.props.endpointShape}
roundedX={this.props.endPointRoundedX}
roundedY={this.props.endPointRoundedY}
highlighted={this.state.highlighted}
muted={this.props.muted}
selected={this.props.selected}
/>
<Endpoint
x={this.props.x2}
y={this.props.y2}
key={"line-end"}
style={this.props.style}
radius={this.props.radius}
shape={this.props.endpointShape}
roundedX={this.props.endPointRoundedX}
roundedY={this.props.endPointRoundedY}
highlighted={this.state.highlighted}
muted={this.props.muted}
selected={this.props.selected}
/>
</g>
);
}
}
render() {
let xOffset;
let yOffset;
if (this.props.labelOffsetX === undefined) {
xOffset = this.props.radius * 1.33;
} else {
xOffset = this.props.labelOffsetX;
}
if (this.props.labelOffsetY === undefined) {
yOffset = this.props.radius * 1.33;
} else {
yOffset = this.props.labelOffsetY;
}
const hitStyle = {
cursor: this.props.noNavigate ? "default" : "pointer",
stroke: "#FFF",
strokeWidth: 8
};
const navTo = this.props.navTo;
let width;
let stroke;
let fill;
let offset;
if (this.props.lineShape === "angled") {
offset = this.props.bendOffset;
} else {
offset = this.props.curveOffset;
}
if (this.state.highlighted) {
width = this.props.style.line.highlighted.strokeWidth;
stroke = this.props.style.line.highlighted.stroke;
fill = this.props.style.line.highlighted.fill;
} else {
width = this.props.style.line.normal.strokeWidth;
stroke = this.props.style.line.normal.stroke;
fill = this.props.style.line.normal.fill;
}
return (
<g>
<g>
<Line
x1={this.props.x1}
x2={this.props.x2}
y1={this.props.y1}
y2={this.props.y2}
shape={this.props.lineShape}
key={"line-path"}
label={this.props.label}
labelPosition={this.props.labelPosition}
labelStyle={this.props.style.label}
labelOffsetX={xOffset}
labelOffsetY={yOffset}
textAnchor={this.props.textAnchor}
color={stroke}
width={width}
muted={this.props.muted}
selected={this.props.selected}
classed={this.props.classed}
roundedX={this.props.roundedX}
roundedY={this.props.roundedY}
fillColor={fill}
size={this.props.size}
centerLine={this.props.centerLine}
arrow={this.props.arrow}
arrowWidth={this.props.arrowWidth}
arrowHeight={this.props.arrowHeight}
position={this.props.position}
offset={offset}
curveDirection={this.props.curveDirection}
name={navTo}
/>
</g>
<g onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
<Line
// Positional Props used by all shapes
x1={this.props.x1}
x2={this.props.x2}
y1={this.props.y1}
y2={this.props.y2}
// Identity Props used by all shapes
shape={this.props.lineShape} // controls shape of the line
key={"line-path-hit"} // needed for react element
// Label Props used by all shapes
label={this.props.label} // provides label to be displayed
labelPosition={this.props.labelPosition} // controls where label
// is situated around the line
labelStyle={this.props.style.label} // controls the
// style of the label
labelOffsetX={xOffset} // controls the +/- x offset from labelPosition
labelOffsetY={yOffset} // controls the +/- y offset from labelPosition
textAnchor={this.props.textAnchor} // controls the positioning of the text
// Style Props
color={hitStyle.stroke} // controls color of the line
width={hitStyle.strokeWidth} // controls the stroke thickness
muted={this.props.muted} // controls style
selected={this.props.selected} // controls style
classed={this.props.classed} // provides a psuedo css class for the line
// Square props
roundedX={this.props.roundedX} // controls corner rounding
roundedY={this.props.roundedY} // controls corner rounding
fillColor={fill} // controls color of the fill
size={this.props.size} // controls height of square
centerLine={this.props.centerLine} // controls display of horizontal center line
// Arrow props, not used by square
arrow={this.props.arrow} // determines whether to
// display nodes or arrows at ends of line
arrowWidth={this.props.arrowWidth} // controls width of arrow
arrowHeight={this.props.arrowHeight} // controls height of arrow
// Line offset props, used by angle and arc
position={this.props.position} // controls angle of offset
offset={offset} // controls length of offset line
curveDirection={this.props.curveDirection} // controls left / right
// line path with reference
// to line center
// Navigation props
name={navTo} // returned value for _onSelection change - all
onSelectionChange={this.handleSelectionChanged} // callback function to
// control what happens if the
// element is clicked
invisible={true} // Internal prop for hiding this line
/>
</g>
<g>{this.renderEndpoints()}</g>
</g>
);
}
}
Connection.propTypes = {
/**
* Controls shape of the line, can be "linear", "square", "angled", "arc".
*/
lineShape: PropTypes.oneOf(["linear", "square", "angled", "arc"]),
//
// Positional Props used by all shapes
//
/** Controls the x-coordinate of the line beginning. */
x1: PropTypes.number,
/** Controls the x-coordinate of the line end. */
x2: PropTypes.number,
/** Controls the y-coordinate of the line beginning. */
y1: PropTypes.number,
/** Controls the y-coordinate of the line end. */
y2: PropTypes.number,
//
// Label Props used by all shapes
//
/**
* Provides label to be displayed; Takes either a string, or an array of
* strings for multi-line labels.
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
/**
* Controls where label is situated around the line.
*/
labelPosition: PropTypes.oneOf(["top", "bottom", "center"]),
/**
* Controls the x pixel offset from labelPosition
*/
labelOffsetX: PropTypes.number,
/**
* Controls the y pixel offset from labelPosition
*/
labelOffsetY: PropTypes.number,
/**
* Controls the justification of the text label
*/
textAnchor: PropTypes.oneOf(["begin", "middle", "end"]),
//
// Style Props, used by all shapes
//
/**
* Object prop that controls the inline style for the react element.
*
* The style has three components, one for the two Line-caps (`node`),
* one for the label (`label`) and one for the line (`line`). Each group
* has up to four different possible options depending on the way the
* line and endpoint should be rendered:
* * `normal` - provides the standard view of the component
* * `selected` - for when the component is clicked
* * `muted` - for when the component is in the background
* * `highlighted` - is used when the component is hovered over
*
* The muted and selected props are boolean values that tell the lower
* level primitive that you want to use these styles. They will default
* to false unless specified. The `fill` css style on each category is used
* for line-caps and square connections, allowing different colors to be
* specified for the interior of the shapes.
*/
style: PropTypes.object,
/** Display the endpoint muted */
muted: PropTypes.bool,
/** Display the endpoint selected */
selected: PropTypes.bool,
//
// Props for "square" shape
//
/** When the endpoint shape is a `square`, this controls the radius of corners. */
roundedX: PropTypes.number,
/** When the endpoint shape is a `square`, this controls the radius of corners. */
roundedY: PropTypes.number,
/**
* Used to determine the height of the square if the endpoint shape is a `square`,
* as well as when calculating the label position around a square.
*/
size: PropTypes.number,
/** Boolean value that controls if a horizontal line is drawn down the center of a square. */
centerLine: PropTypes.bool,
//
// Line offset Props, used by "angle" and "curved" shapes
//
/**
* Controls the angle of the offset from the center of the line.
*/
position: PropTypes.number,
/**
* Controls the distance from the center x axis the curve will arc through
*/
curveOffset: PropTypes.number,
/**
* Controls the length of the offset line
*/
bendOffset: PropTypes.number,
/**
* The curveDirection property determines whether the curve moves to the
* left or the right of the non-horizontal vector between x1,y1 and x2,y2.
* The curveOffset property specifies the distance of the curve from the vector
* between x1, y1 and x2, y2. When position is specified, this will offset a linear,
* or curved line from the x1, y1, x2, y2 corrdinates using a combination of
* vectors.
*
* This functions slightly differently for angled connections and
* will instead rotate a point offset from the x and y by an angle. If the
* curveDirection is left, this will move clockwise, and will move counterClockwise if right.
*/
curveDirection: PropTypes.oneOf(["left", "right"]),
//
// Linecap props, used by all shapes
//
/**
* Controls the size of the Line-cap
*/
radius: PropTypes.number,
/**
* Controls the shape of the line-cap.
*/
endpointShape: PropTypes.oneOf(["circle", "square", "cloud"]),
/**
* If a square endpoint shape is used, controls the corner rounding of the x-axis of the square
*/
endPointRoundedX: PropTypes.number,
/**
* If a square endpoint shape is used, controls the corner rounding of the y-axis of the square
*/
endPointRoundedY: PropTypes.number,
//
// Arrow props, not used by "square"
//
/**
* Boolean value that controls if a directional arrow is drawn instead of line-caps.
* When arrow is set to "true", the vector between x1, y1 and x2, y2 will have the
* Line-caps replaced with a directional arrow. Arrowheads can be sized using the
* arrowWidth and arrowHeight property.
*/
arrow: PropTypes.bool,
/**
* Controls the width of an arrow head
*/
arrowWidth: PropTypes.number,
/**
* Controls the height of an arrow head
*/
arrowHeight: PropTypes.number,
//
// Navigation Props, used by all shapes
//
/**
* Boolean value that determines if the element uses the onSelectionChange change and can be clicked
*/
noNavigate: PropTypes.bool,
/**
* Callback specified to handle selection of the circuit. The value supplied
* to the callback is whatever was specified in the navTo prop.
*/
onSelectionChange: PropTypes.func,
/**
* Value passed down to the click handler at the lowest level primitive.
* Will return to the onSelectionChange its value.
*/
navTo: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
Connection.defaultProps = {
noNavigate: false,
labelPosition: "top",
radius: 2,
endpointShape: "circle",
classed: "circuit",
lineShape: "linear",
selected: false,
muted: false,
position: 0,
arrow: false,
arrowWidth: 10,
arrowHeight: 10,
curveDirection: "right",
curveOffset: 20,
size: 40
};