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);