diff --git a/client/.eslintrc b/client/.eslintrc index 9a85980f0..6fae61983 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -33,6 +33,5 @@ "jsx-a11y/no-static-element-interactions": 0, "react/jsx-no-target-blank": 0, "react/no-find-dom-node": 0, - "class-methods-use-this": 0, } } diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index ff1cd2cb4..4acfaf366 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -115,6 +115,80 @@ function updateLayout(width, height, nodes, baseOptions) { } +function centerSelectedNode(props, state) { + let stateNodes = state.nodes; + let stateEdges = state.edges; + if (!stateNodes.has(props.selectedNodeId)) { + return {}; + } + + const adjacentNodes = props.adjacentNodes; + const adjacentLayoutNodeIds = []; + + adjacentNodes.forEach((adjacentId) => { + // filter loopback + if (adjacentId !== props.selectedNodeId) { + adjacentLayoutNodeIds.push(adjacentId); + } + }); + + // move origin node to center of viewport + const zoomScale = state.scale; + const translate = [state.panTranslateX, state.panTranslateY]; + const viewportHalfWidth = ((state.width + props.margins.left) - DETAILS_PANEL_WIDTH) / 2; + const viewportHalfHeight = (state.height + props.margins.top) / 2; + const centerX = (-translate[0] + viewportHalfWidth) / zoomScale; + const centerY = (-translate[1] + viewportHalfHeight) / zoomScale; + stateNodes = stateNodes.mergeIn([props.selectedNodeId], { + x: centerX, + y: centerY + }); + + // circle layout for adjacent nodes + const adjacentCount = adjacentLayoutNodeIds.length; + const density = radiusDensity(adjacentCount); + const radius = Math.min(state.width, state.height) / density / zoomScale; + const offsetAngle = Math.PI / 4; + + stateNodes = stateNodes.map((node, nodeId) => { + const index = adjacentLayoutNodeIds.indexOf(nodeId); + if (index > -1) { + const angle = offsetAngle + ((Math.PI * 2 * index) / adjacentCount); + return node.merge({ + x: centerX + (radius * Math.sin(angle)), + y: centerY + (radius * Math.cos(angle)) + }); + } + return node; + }); + + // fix all edges for circular nodes + stateEdges = stateEdges.map((edge) => { + if (edge.get('source') === props.selectedNodeId + || edge.get('target') === props.selectedNodeId + || _.includes(adjacentLayoutNodeIds, edge.get('source')) + || _.includes(adjacentLayoutNodeIds, edge.get('target'))) { + const source = stateNodes.get(edge.get('source')); + const target = stateNodes.get(edge.get('target')); + return edge.set('points', fromJS([ + {x: source.get('x'), y: source.get('y')}, + {x: target.get('x'), y: target.get('y')} + ])); + } + return edge; + }); + + // auto-scale node size for selected nodes + const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height); + + return { + selectedNodeScale, + edges: stateEdges, + nodes: stateNodes + }; +} + + class NodesChart extends React.Component { constructor(props, context) { @@ -180,7 +254,7 @@ class NodesChart extends React.Component { assign(state, this.restoreLayout(state)); } if (nextProps.selectedNodeId) { - assign(state, this.centerSelectedNode(nextProps, state)); + assign(state, centerSelectedNode(nextProps, state)); } this.setState(state); @@ -246,79 +320,6 @@ class NodesChart extends React.Component { } } - centerSelectedNode(props, state) { - let stateNodes = state.nodes; - let stateEdges = state.edges; - if (!stateNodes.has(props.selectedNodeId)) { - return {}; - } - - const adjacentNodes = props.adjacentNodes; - const adjacentLayoutNodeIds = []; - - adjacentNodes.forEach((adjacentId) => { - // filter loopback - if (adjacentId !== props.selectedNodeId) { - adjacentLayoutNodeIds.push(adjacentId); - } - }); - - // move origin node to center of viewport - const zoomScale = state.scale; - const translate = [state.panTranslateX, state.panTranslateY]; - const viewportHalfWidth = ((state.width + props.margins.left) - DETAILS_PANEL_WIDTH) / 2; - const viewportHalfHeight = (state.height + props.margins.top) / 2; - const centerX = (-translate[0] + viewportHalfWidth) / zoomScale; - const centerY = (-translate[1] + viewportHalfHeight) / zoomScale; - stateNodes = stateNodes.mergeIn([props.selectedNodeId], { - x: centerX, - y: centerY - }); - - // circle layout for adjacent nodes - const adjacentCount = adjacentLayoutNodeIds.length; - const density = radiusDensity(adjacentCount); - const radius = Math.min(state.width, state.height) / density / zoomScale; - const offsetAngle = Math.PI / 4; - - stateNodes = stateNodes.map((node, nodeId) => { - const index = adjacentLayoutNodeIds.indexOf(nodeId); - if (index > -1) { - const angle = offsetAngle + ((Math.PI * 2 * index) / adjacentCount); - return node.merge({ - x: centerX + (radius * Math.sin(angle)), - y: centerY + (radius * Math.cos(angle)) - }); - } - return node; - }); - - // fix all edges for circular nodes - stateEdges = stateEdges.map((edge) => { - if (edge.get('source') === props.selectedNodeId - || edge.get('target') === props.selectedNodeId - || includes(adjacentLayoutNodeIds, edge.get('source')) - || includes(adjacentLayoutNodeIds, edge.get('target'))) { - const source = stateNodes.get(edge.get('source')); - const target = stateNodes.get(edge.get('target')); - return edge.set('points', fromJS([ - {x: source.get('x'), y: source.get('y')}, - {x: target.get('x'), y: target.get('y')} - ])); - } - return edge; - }); - - // auto-scale node size for selected nodes - const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height); - - return { - selectedNodeScale, - edges: stateEdges, - nodes: stateNodes - }; - } - restoreLayout(state) { // undo any pan/zooming that might have happened this.setZoom(state); diff --git a/client/app/scripts/components/grid-mode-selector.js b/client/app/scripts/components/grid-mode-selector.js index e27d849ee..c81a76ae1 100644 --- a/client/app/scripts/components/grid-mode-selector.js +++ b/client/app/scripts/components/grid-mode-selector.js @@ -4,6 +4,21 @@ import classNames from 'classnames'; import { toggleGridMode } from '../actions/app-actions'; + +function renderItem(icons, label, isSelected, onClick) { + const className = classNames('grid-mode-selector-action', { + 'grid-mode-selector-action-selected': isSelected + }); + return ( +
+ + {label} +
+ ); +} + class GridModeSelector extends React.Component { constructor(props, context) { @@ -21,28 +36,14 @@ class GridModeSelector extends React.Component { return this.props.toggleGridMode(false); } - renderItem(icons, label, isSelected, onClick) { - const className = classNames('grid-mode-selector-action', { - 'grid-mode-selector-action-selected': isSelected - }); - return ( -
- - {label} -
- ); - } - render() { const { gridMode } = this.props; return (
- {this.renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)} - {this.renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)} + {renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)} + {renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)}
); diff --git a/client/app/scripts/components/matched-results.js b/client/app/scripts/components/matched-results.js index 24c23dbec..9fd0d53c3 100644 --- a/client/app/scripts/components/matched-results.js +++ b/client/app/scripts/components/matched-results.js @@ -6,26 +6,24 @@ import MatchedText from './matched-text'; const SHOW_ROW_COUNT = 2; const MAX_MATCH_LENGTH = 24; -class MatchedResults extends React.Component { - renderMatch(matches, field) { - const match = matches.get(field); - const text = match.text; - - return ( -
-
- - {match.label}: - - -
+function renderMatch(match) { + return ( +
+
+ + {match.label}: + +
- ); - } +
+ ); +} + +class MatchedResults extends React.Component { render() { const { matches, style } = this.props; @@ -46,7 +44,7 @@ class MatchedResults extends React.Component { return (
- {matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => this.renderMatch(matches, fieldId))} + {matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => renderMatch(matches.get(fieldId)))} {moreFieldMatches &&
{`${moreFieldMatches.size} more matches`}
} diff --git a/client/app/scripts/components/node-details/node-details-labels.js b/client/app/scripts/components/node-details/node-details-labels.js index fa3fb0850..a8ae1aac7 100644 --- a/client/app/scripts/components/node-details/node-details-labels.js +++ b/client/app/scripts/components/node-details/node-details-labels.js @@ -6,6 +6,16 @@ import MatchedText from '../matched-text'; import NodeDetailsControlButton from './node-details-control-button'; import ShowMore from '../show-more'; + +function renderControls(controls) { + return ( +
+ {sortBy(controls, 'rank').map(control => )} +
+ ); +} + export default class NodeDetailsLabels extends React.Component { constructor(props, context) { @@ -15,7 +25,6 @@ export default class NodeDetailsLabels extends React.Component { limit: this.DEFAULT_LIMIT, }; this.handleLimitClick = this.handleLimitClick.bind(this); - this.renderControls = this.renderControls.bind(this); } handleLimitClick() { @@ -23,15 +32,6 @@ export default class NodeDetailsLabels extends React.Component { this.setState({limit}); } - renderControls(controls) { - return ( -
- {sortBy(controls, 'rank').map(control => )} -
- ); - } - render() { const { controls, matches = makeMap() } = this.props; let rows = this.props.rows; @@ -49,7 +49,7 @@ export default class NodeDetailsLabels extends React.Component { return (
- {controls && this.renderControls(controls)} + {controls && renderControls(controls)} {rows.map(field => (
); diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js index 4eadf7b6a..7fc4f551d 100644 --- a/client/app/scripts/components/terminal.js +++ b/client/app/scripts/components/terminal.js @@ -123,7 +123,7 @@ class Terminal extends React.Component { } this.socket = null; const wereConnected = this.state.connected; - if (this.isMounted) { + if (this.isComponentMounted) { // Calling setState on an unmounted component will throw a warning. // `connected` will get set to false by `componentWillUnmount`. this.setState({connected: false}); @@ -176,7 +176,7 @@ class Terminal extends React.Component { } componentDidMount() { - this.isMounted = true; + this.isComponentMounted = true; if (this.props.connect) { this.mountTerminal(); } @@ -217,7 +217,7 @@ class Terminal extends React.Component { } componentWillUnmount() { - this.isMounted = false; + this.isComponentMounted = false; this.setState({connected: false}); log('cwu terminal'); diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js index 08cb6fec2..df180d319 100644 --- a/client/app/scripts/components/topologies.js +++ b/client/app/scripts/components/topologies.js @@ -4,6 +4,18 @@ import classnames from 'classnames'; import { clickTopology } from '../actions/app-actions'; + +function basicTopologyInfo(topology, searchMatchCount) { + const info = [ + `Nodes: ${topology.getIn(['stats', 'node_count'])}`, + `Connections: ${topology.getIn(['stats', 'edge_count'])}` + ]; + if (searchMatchCount) { + info.push(`Search Matches: ${searchMatchCount}`); + } + return info.join('\n'); +} + class Topologies extends React.Component { constructor(props, context) { @@ -21,7 +33,7 @@ class Topologies extends React.Component { const topologyId = subTopology.get('id'); const searchMatches = this.props.searchNodeMatches.get(subTopology.get('id')); const searchMatchCount = searchMatches ? searchMatches.size : 0; - const title = this.renderTitle(subTopology, searchMatchCount); + const title = basicTopologyInfo(subTopology, searchMatchCount); const className = classnames('topologies-sub-item', { 'topologies-sub-item-active': isActive, 'topologies-sub-item-matched': searchMatchCount @@ -38,15 +50,6 @@ class Topologies extends React.Component { ); } - renderTitle(topology, searchMatchCount) { - let title = `Nodes: ${topology.getIn(['stats', 'node_count'])}\n` - + `Connections: ${topology.getIn(['stats', 'node_count'])}`; - if (searchMatchCount) { - title = `${title}\nSearch Matches: ${searchMatchCount}`; - } - return title; - } - renderTopology(topology) { const isActive = topology === this.props.currentTopology; const searchMatches = this.props.searchNodeMatches.get(topology.get('id')); @@ -56,7 +59,7 @@ class Topologies extends React.Component { 'topologies-item-main-matched': searchMatchCount }); const topologyId = topology.get('id'); - const title = this.renderTitle(topology, searchMatchCount); + const title = basicTopologyInfo(topology, searchMatchCount); return (