import React from 'react'; import classNames from 'classnames'; import { groupBy, mapValues } from 'lodash'; import { intersperse } from '../../utils/array-utils'; import NodeDetailsTableNodeLink from './node-details-table-node-link'; import NodeDetailsTableNodeMetricLink from './node-details-table-node-metric-link'; import { formatDataType } from '../../utils/string-utils'; function getValuesForNode(node) { let values = {}; ['metrics', 'metadata'].forEach((collection) => { if (node[collection]) { node[collection].forEach((field) => { const result = Object.assign({}, field); result.valueType = collection; values[field.id] = result; }); } }); if (node.parents) { const byTopologyId = groupBy(node.parents, parent => parent.topologyId); const relativesByTopologyId = mapValues(byTopologyId, (relatives, topologyId) => ({ id: topologyId, label: topologyId, relatives, value: relatives.map(relative => relative.label).join(', '), valueType: 'relatives', })); values = { ...values, ...relativesByTopologyId, }; } return values; } function renderValues(node, columns = [], columnStyles = [], timestamp = null, topologyId = null) { const fields = getValuesForNode(node); return columns.map(({ id }, i) => { const field = fields[id]; const style = columnStyles[i]; if (field) { if (field.valueType === 'metadata') { const { value, title } = formatDataType(field, timestamp); return ( {field.dataType === 'link' ? ( {value} ) : value} ); } if (field.valueType === 'relatives') { return ( {intersperse(field.relatives.map(relative => ( )), ' ')} ); } // valueType === 'metrics' return ( ); } // empty cell to complete the row for proper hover return ( ); }); } export default class NodeDetailsTableRow extends React.Component { constructor(props, context) { super(props, context); // // We watch how far the mouse moves when click on a row, move to much and we assume that the // user is selecting some data in the row. In this case don't trigger the onClick event which // is most likely a details panel popping open. // this.state = { focused: false }; this.mouseDrag = {}; } onMouseEnter = () => { this.setState({ focused: true }); if (this.props.onMouseEnter) { this.props.onMouseEnter(this.props.index, this.props.node); } } onMouseLeave = () => { this.setState({ focused: false }); if (this.props.onMouseLeave) { this.props.onMouseLeave(); } } onMouseDown = (ev) => { this.mouseDrag = { originX: ev.pageX, originY: ev.pageY, }; } onClick = (ev) => { const thresholdPx = 2; const { pageX, pageY } = ev; const { originX, originY } = this.mouseDrag; const movedTheMouseTooMuch = ( Math.abs(originX - pageX) > thresholdPx || Math.abs(originY - pageY) > thresholdPx ); if (movedTheMouseTooMuch && originX && originY) { return; } this.props.onClick(ev, this.props.node); this.mouseDrag = {}; } render() { const { node, nodeIdKey, topologyId, columns, onClick, colStyles, timestamp } = this.props; const [firstColumnStyle, ...columnStyles] = colStyles; const values = renderValues(node, columns, columnStyles, timestamp, topologyId); const nodeId = node[nodeIdKey]; const className = classNames('tour-step-anchor node-details-table-node', { focused: this.state.focused, selected: this.props.selected, }); return ( {this.props.renderIdCell(Object.assign(node, { nodeId, topologyId }))} {values} ); } } NodeDetailsTableRow.defaultProps = { renderIdCell: props => };