import React from 'react';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { Map as makeMap, List as makeList } from 'immutable';
import { clickNode, enterNode, leaveNode } from '../actions/app-actions';
import { getNodeColor } from '../utils/color-utils';
import MatchedText from '../components/matched-text';
import MatchedResults from '../components/matched-results';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { GRAPH_VIEW_MODE } from '../constants/naming';
import { NODE_BASE_SIZE } from '../constants/styles';
import NodeShapeStack from './node-shape-stack';
import NodeNetworksOverlay from './node-networks-overlay';
import {
NodeShapeCircle,
NodeShapeTriangle,
NodeShapeSquare,
NodeShapePentagon,
NodeShapeHexagon,
NodeShapeHeptagon,
NodeShapeOctagon,
NodeShapeCloud,
} from './node-shapes';
const labelWidth = 1.2 * NODE_BASE_SIZE;
const nodeShapes = {
circle: NodeShapeCircle,
triangle: NodeShapeTriangle,
square: NodeShapeSquare,
pentagon: NodeShapePentagon,
hexagon: NodeShapeHexagon,
heptagon: NodeShapeHeptagon,
octagon: NodeShapeOctagon,
cloud: NodeShapeCloud,
};
function stackedShape(Shape) {
const factory = React.createFactory(NodeShapeStack);
return props => factory(Object.assign({}, props, {shape: Shape}));
}
function getNodeShape({ shape, stack }) {
const nodeShape = nodeShapes[shape];
if (!nodeShape) {
throw new Error(`Unknown shape: ${shape}!`);
}
return stack ? stackedShape(nodeShape) : nodeShape;
}
class Node extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
hovered: false,
};
this.handleMouseClick = this.handleMouseClick.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
this.saveShapeRef = this.saveShapeRef.bind(this);
}
renderSvgLabels(labelClassName, labelMinorClassName, labelOffsetY) {
const { label, labelMinor } = this.props;
return (
{label}
{labelMinor}
);
}
renderStandardLabels(labelClassName, labelMinorClassName, labelOffsetY, mouseEvents) {
const { label, labelMinor, matches = makeMap() } = this.props;
const matchedMetadata = matches.get('metadata', makeList());
const matchedParents = matches.get('parents', makeList());
const matchedNodeDetails = matchedMetadata.concat(matchedParents);
return (
);
}
render() {
const {
focused, highlighted, networks, pseudo, rank, label, transform,
exportingGraph, showingNetworks, stack, id, metric
} = this.props;
const { hovered } = this.state;
const color = getNodeColor(rank, label, pseudo);
const truncate = !focused && !hovered;
const labelOffsetY = (showingNetworks && networks) ? 40 : 28;
const nodeClassName = classnames('node', { highlighted, hovered, pseudo });
const labelClassName = classnames('node-label', { truncate });
const labelMinorClassName = classnames('node-label-minor', { truncate });
const NodeShapeType = getNodeShape(this.props);
const mouseEvents = {
onClick: this.handleMouseClick,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
};
return (
{exportingGraph ?
this.renderSvgLabels(labelClassName, labelMinorClassName, labelOffsetY) :
this.renderStandardLabels(labelClassName, labelMinorClassName, labelOffsetY, mouseEvents)}
{showingNetworks && }
);
}
saveShapeRef(ref) {
this.shapeRef = ref;
}
handleMouseClick(ev) {
ev.stopPropagation();
trackAnalyticsEvent('scope.node.click', {
layout: GRAPH_VIEW_MODE,
topologyId: this.props.currentTopology.get('id'),
parentTopologyId: this.props.currentTopology.get('parentId'),
});
this.props.clickNode(this.props.id, this.props.label, this.shapeRef.getBoundingClientRect());
}
handleMouseEnter() {
this.props.enterNode(this.props.id);
this.setState({ hovered: true });
}
handleMouseLeave() {
this.props.leaveNode(this.props.id);
this.setState({ hovered: false });
}
}
function mapStateToProps(state) {
return {
exportingGraph: state.get('exportingGraph'),
showingNetworks: state.get('showingNetworks'),
currentTopology: state.get('currentTopology'),
contrastMode: state.get('contrastMode'),
};
}
export default connect(
mapStateToProps,
{ clickNode, enterNode, leaveNode }
)(Node);