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 { NODE_BASE_SIZE } from '../constants/styles'; import NodeShapeCircle from './node-shape-circle'; import NodeShapeStack from './node-shape-stack'; import NodeShapeRoundedSquare from './node-shape-rounded-square'; import NodeShapeHexagon from './node-shape-hexagon'; import NodeShapeHeptagon from './node-shape-heptagon'; import NodeShapeCloud from './node-shape-cloud'; import NodeNetworksOverlay from './node-networks-overlay'; const labelWidth = 1.4 * NODE_BASE_SIZE; const nodeShapes = { circle: NodeShapeCircle, hexagon: NodeShapeHexagon, heptagon: NodeShapeHeptagon, square: NodeShapeRoundedSquare, 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, matched: 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); } componentWillReceiveProps(nextProps) { // marks as matched only when search query changes if (nextProps.searchQuery !== this.props.searchQuery) { this.setState({ matched: nextProps.matched }); } else { this.setState({ matched: false }); } } renderSvgLabels(labelClassName, subLabelClassName, labelOffsetY) { const { label, subLabel } = this.props; return ( {label} {subLabel} ); } renderStandardLabels(labelClassName, subLabelClassName, labelOffsetY, mouseEvents) { const { label, subLabel, blurred, matches = makeMap() } = this.props; const matchedMetadata = matches.get('metadata', makeList()); const matchedParents = matches.get('parents', makeList()); const matchedNodeDetails = matchedMetadata.concat(matchedParents); return (
{!blurred && }
); } render() { const { blurred, focused, highlighted, networks, pseudo, rank, label, transform, exportingGraph, showingNetworks, stack } = this.props; const { hovered, matched } = this.state; const color = getNodeColor(rank, label, pseudo); const truncate = !focused && !hovered; const labelOffsetY = (showingNetworks && networks) ? 40 : 28; const nodeClassName = classnames('node', { highlighted, blurred: blurred && !focused, hovered, matched, pseudo }); const labelClassName = classnames('node-label', { truncate }); const subLabelClassName = classnames('node-sublabel', { truncate }); const NodeShapeType = getNodeShape(this.props); const useSvgLabels = exportingGraph; const mouseEvents = { onClick: this.handleMouseClick, onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, }; return ( {useSvgLabels ? this.renderSvgLabels(labelClassName, subLabelClassName, labelOffsetY) : this.renderStandardLabels(labelClassName, subLabelClassName, labelOffsetY, mouseEvents)} {showingNetworks && } ); } saveShapeRef(ref) { this.shapeRef = ref; } handleMouseClick(ev) { ev.stopPropagation(); 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 }); } } export default connect( state => ({ searchQuery: state.get('searchQuery'), exportingGraph: state.get('exportingGraph'), showingNetworks: state.get('showingNetworks'), }), { clickNode, enterNode, leaveNode } )(Node);