mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-19 15:46:41 +00:00
The initial idea was to keep it separate since the unattached links were also to be displayed distinctively from the metrics. With the new design, unattached links are rendered in the same list as metrics with attached links. Therefore, we treat unattached metric links as an empty metric.
175 lines
5.0 KiB
JavaScript
175 lines
5.0 KiB
JavaScript
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,
|
|
value: relatives.map(relative => relative.label).join(', '),
|
|
valueType: 'relatives',
|
|
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 (
|
|
<td
|
|
className="node-details-table-node-value truncate"
|
|
title={title}
|
|
style={style}
|
|
key={field.id}>
|
|
{value}
|
|
</td>
|
|
);
|
|
}
|
|
if (field.valueType === 'relatives') {
|
|
return (
|
|
<td
|
|
className="node-details-table-node-value truncate"
|
|
title={field.value}
|
|
style={style}
|
|
key={field.id}>
|
|
{intersperse(field.relatives.map(relative =>
|
|
<NodeDetailsTableNodeLink
|
|
key={relative.id}
|
|
linkable
|
|
nodeId={relative.id}
|
|
{...relative}
|
|
/>
|
|
), ' ')}
|
|
</td>
|
|
);
|
|
}
|
|
// valueType === 'metrics'
|
|
return (
|
|
<NodeDetailsTableNodeMetricLink
|
|
style={style} key={field.id} topologyId={topologyId} {...field} />
|
|
);
|
|
}
|
|
// empty cell to complete the row for proper hover
|
|
return (
|
|
<td className="node-details-table-node-value" style={style} key={id} />
|
|
);
|
|
});
|
|
}
|
|
|
|
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.mouseDragOrigin = [0, 0];
|
|
|
|
this.onMouseDown = this.onMouseDown.bind(this);
|
|
this.onMouseUp = this.onMouseUp.bind(this);
|
|
this.onMouseEnter = this.onMouseEnter.bind(this);
|
|
this.onMouseLeave = this.onMouseLeave.bind(this);
|
|
}
|
|
|
|
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) {
|
|
const { pageX, pageY } = ev;
|
|
this.mouseDragOrigin = [pageX, pageY];
|
|
}
|
|
|
|
onMouseUp(ev) {
|
|
const [originX, originY] = this.mouseDragOrigin;
|
|
const { pageX, pageY } = ev;
|
|
const thresholdPx = 2;
|
|
const movedTheMouseTooMuch = (
|
|
Math.abs(originX - pageX) > thresholdPx ||
|
|
Math.abs(originY - pageY) > thresholdPx
|
|
);
|
|
if (movedTheMouseTooMuch) {
|
|
return;
|
|
}
|
|
|
|
this.props.onClick(ev, this.props.node);
|
|
}
|
|
|
|
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('node-details-table-node', {
|
|
selected: this.props.selected,
|
|
focused: this.state.focused,
|
|
});
|
|
|
|
return (
|
|
<tr
|
|
onMouseDown={onClick && this.onMouseDown}
|
|
onMouseUp={onClick && this.onMouseUp}
|
|
onMouseEnter={this.onMouseEnter}
|
|
onMouseLeave={this.onMouseLeave}
|
|
className={className}>
|
|
<td className="node-details-table-node-label truncate" style={firstColumnStyle}>
|
|
{this.props.renderIdCell(Object.assign(node, {topologyId, nodeId}))}
|
|
</td>
|
|
{values}
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
NodeDetailsTableRow.defaultProps = {
|
|
renderIdCell: props => <NodeDetailsTableNodeLink {...props} />
|
|
};
|