diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index f876206b2..0a4714c76 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -21,7 +21,7 @@ const MARGINS = { // make sure circular layouts a bit denser with 3-6 nodes const radiusDensity = d3.scale.threshold() - .domain([3, 6]).range([3, 4, 3]); + .domain([3, 6]).range([2.5, 3.5, 3]); const NodesChart = React.createClass({ @@ -30,7 +30,7 @@ const NodesChart = React.createClass({ nodes: {}, edges: {}, nodeScale: d3.scale.linear(), - translate: [0, 0], + shiftTranslate: [0, 0], panTranslate: [0, 0], scale: 1, hasZoomed: false, @@ -49,8 +49,7 @@ const NodesChart = React.createClass({ .scaleExtent([0.1, 2]) .on('zoom', this.zoomed); - d3.select('.canvas') - .on('click', this.handleBackgroundClick) + d3.select('.nodes-chart svg') .call(this.zoom); }, @@ -61,6 +60,7 @@ const NodesChart = React.createClass({ // wipe node states when showing different topology if (nextProps.topologyId !== this.props.topologyId) { _.assign(state, { + autoShifted: false, nodes: {}, edges: {} }); @@ -82,8 +82,7 @@ const NodesChart = React.createClass({ componentWillUnmount: function() { // undoing .call(zoom) - d3.select('.canvas') - .on('click', null) + d3.select('.nodes-chart svg') .on('mousedown.zoom', null) .on('onwheel', null) .on('onmousewheel', null) @@ -159,7 +158,7 @@ const NodesChart = React.createClass({ // only animate shift behavior, not panning const panTranslate = this.state.panTranslate; - const shiftTranslate = this.state.translate; + const shiftTranslate = this.state.shiftTranslate; let translate = panTranslate; let wasShifted = false; if (shiftTranslate[0] !== panTranslate[0] || shiftTranslate[1] !== panTranslate[1]) { @@ -175,7 +174,7 @@ const NodesChart = React.createClass({
Too many nodes to show in the browser.
We're working on it, but for now, try a different view?
- + {function(interpolated) { let interpolatedTranslate = wasShifted ? interpolated.val : panTranslate; @@ -287,9 +286,10 @@ const NodesChart = React.createClass({ const adjacentCount = adjacentLayoutNodes.length; const density = radiusDensity(adjacentCount); const radius = Math.min(props.width, props.height) / density; + const offsetAngle = Math.PI / 4; _.each(adjacentLayoutNodes, function(node, i) { - const angle = Math.PI * 2 * i / adjacentCount; + const angle = offsetAngle + Math.PI * 2 * i / adjacentCount; node.x = centerX + radius * Math.sin(angle); node.y = centerY + radius * Math.cos(angle); }); @@ -310,37 +310,43 @@ const NodesChart = React.createClass({ // shift canvas selected node out of view if it has not been shifted already let autoShifted = this.state.autoShifted; - const translate = state.translate; + const shiftTranslate = state.shiftTranslate; if (!autoShifted) { const visibleWidth = Math.max(props.width - props.detailsWidth, 0); - const offsetX = translate[0]; + const offsetX = shiftTranslate[0]; // normalize graph coordinates by zoomScale const zoomScale = state.scale; const outerRadius = radius + this.state.nodeScale(1.5); - if (offsetX + (centerX + outerRadius) * zoomScale > visibleWidth) { + if (2 * outerRadius * zoomScale > props.width) { + // radius too big, centering center node on canvas + shiftTranslate[0] = -(centerX * zoomScale - (props.width + MARGINS.left) / 2); + } else if (offsetX + (centerX + outerRadius) * zoomScale > visibleWidth) { // shift left if blocked by details const shift = (centerX + outerRadius) * zoomScale - visibleWidth; - translate[0] = -shift; + shiftTranslate[0] = -shift; } else if (offsetX + (centerX - outerRadius) * zoomScale < 0) { // shift right if off canvas const shift = offsetX - offsetX + (centerX - outerRadius) * zoomScale; - translate[0] = -shift; + shiftTranslate[0] = -shift; } - const offsetY = translate[1]; - if (offsetY + (centerY + outerRadius) * zoomScale > props.height) { + const offsetY = shiftTranslate[1]; + if (2 * outerRadius * zoomScale > props.height) { + // radius too big, centering center node on canvas + shiftTranslate[1] = -(centerY * zoomScale - (props.height + MARGINS.top) / 2); + } else if (offsetY + (centerY + outerRadius) * zoomScale > props.height) { // shift up if past bottom const shift = (centerY + outerRadius) * zoomScale - props.height; - translate[1] = -shift; + shiftTranslate[1] = -shift; } else if (offsetY + (centerY - outerRadius) * zoomScale - props.topMargin < 0) { // shift down if off canvas const shift = offsetY - offsetY + (centerY - outerRadius) * zoomScale - props.topMargin; - translate[1] = -shift; + shiftTranslate[1] = -shift; } - // debug('shift', centerX, centerY, outerRadius, translate); + // debug('shift', centerX, centerY, outerRadius, shiftTranslate); // saving translate in d3's panning cache - this.zoom.translate(translate); + this.zoom.translate(shiftTranslate); autoShifted = true; } @@ -348,16 +354,22 @@ const NodesChart = React.createClass({ autoShifted: autoShifted, edges: layoutEdges, nodes: layoutNodes, - translate: translate + shiftTranslate: shiftTranslate }; }, - handleBackgroundClick: function() { - AppActions.clickCloseDetails(); - // allow shifts again - this.setState({ - autoShifted: false - }); + isZooming: false, // distinguish pan/zoom from click + + handleMouseUp: function() { + if (!this.isZooming) { + AppActions.clickCloseDetails(); + // allow shifts again + this.setState({ + autoShifted: false + }); + } else { + this.isZooming = false; + } }, restoreLayout: function(state) { @@ -443,11 +455,12 @@ const NodesChart = React.createClass({ zoomed: function() { // debug('zoomed', d3.event.scale, d3.event.translate); + this.isZooming = true; this.setState({ autoShifted: false, hasZoomed: true, panTranslate: d3.event.translate.slice(), - translate: d3.event.translate.slice(), + shiftTranslate: d3.event.translate.slice(), scale: d3.event.scale }); }