mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-20 16:13:02 +00:00
Initial version of the resource view (#2296)
* Added resource view selector button * Showing resource boxes in the resource view * Crude CPU resource view prototype * Improved the viewMode state logic * Extracted zooming into a separate wrapper component * Split the layout selectors between graph-view and resource-view * Proper zooming logic for the resource view * Moved all node networks utils to selectors * Improved the zoom caching logic * Further refactoring of selectors * Added sticky labels to the resource boxes * Added panning translation limits in the resource view * Renamed GridModeSelector -> ViewModeSelector * Polished the topology resource view selection logic * Search bar hidden in the resource view * Added per-layer topology names to the resource view * Made metric selectors work for the resource view * Adjusted the viewport selectors * Renamed viewport selector to canvas (+ maximal zoom fix) * Showing more useful metric info in the resource box labels * Fetching only necessary nodes for the resource view * Refactored the resource view layer component * Addressed first batch UI comments (from the Scope meeting) * Switch to deep zooming transform in the resource view to avoid SVG precision errors * Renamed and moved resource view components * Polished all the resource view components * Changing the available metrics selection * Improved and polished the state transition logic for the resource view * Separated zoom limits from the zoom active state * Renaming and bunch of comments * Addressed all the UI comments (@davkal + @fons) * Made graph view selectors independent from resource view selectors
This commit is contained in:
190
client/app/scripts/components/zoom-wrapper.js
Normal file
190
client/app/scripts/components/zoom-wrapper.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { debounce, pick } from 'lodash';
|
||||
import { fromJS } from 'immutable';
|
||||
|
||||
import { event as d3Event, select } from 'd3-selection';
|
||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
||||
|
||||
import { cacheZoomState } from '../actions/app-actions';
|
||||
import { transformToString } from '../utils/transform-utils';
|
||||
import { activeTopologyZoomCacheKeyPathSelector } from '../selectors/zooming';
|
||||
import {
|
||||
canvasMarginsSelector,
|
||||
canvasWidthSelector,
|
||||
canvasHeightSelector,
|
||||
} from '../selectors/canvas';
|
||||
|
||||
import { ZOOM_CACHE_DEBOUNCE_INTERVAL } from '../constants/timer';
|
||||
|
||||
|
||||
class ZoomWrapper extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
minTranslateX: 0,
|
||||
maxTranslateX: 0,
|
||||
minTranslateY: 0,
|
||||
maxTranslateY: 0,
|
||||
translateX: 0,
|
||||
translateY: 0,
|
||||
minScale: 1,
|
||||
maxScale: 1,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
};
|
||||
|
||||
this.debouncedCacheZoom = debounce(this.cacheZoom.bind(this), ZOOM_CACHE_DEBOUNCE_INTERVAL);
|
||||
this.zoomed = this.zoomed.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.zoomRestored = false;
|
||||
this.zoom = zoom().on('zoom', this.zoomed);
|
||||
this.svg = select(`svg#${this.props.svg}`);
|
||||
|
||||
this.setZoomTriggers(!this.props.disabled);
|
||||
this.updateZoomLimits(this.props);
|
||||
this.restoreZoomState(this.props);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.setZoomTriggers(false);
|
||||
this.debouncedCacheZoom.cancel();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const layoutChanged = nextProps.layoutId !== this.props.layoutId;
|
||||
const disabledChanged = nextProps.disabled !== this.props.disabled;
|
||||
|
||||
// If the layout has changed (either active topology or its options) or
|
||||
// relayouting has been requested, stop pending zoom caching event and
|
||||
// ask for the new zoom settings to be restored again from the cache.
|
||||
if (layoutChanged || nextProps.forceRelayout) {
|
||||
this.debouncedCacheZoom.cancel();
|
||||
this.zoomRestored = false;
|
||||
}
|
||||
|
||||
// If the zooming has been enabled/disabled, update its triggers.
|
||||
if (disabledChanged) {
|
||||
this.setZoomTriggers(!nextProps.disabled);
|
||||
}
|
||||
|
||||
this.updateZoomLimits(nextProps);
|
||||
if (!this.zoomRestored) {
|
||||
this.restoreZoomState(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// `forwardTransform` says whether the zoom transform is forwarded to the child
|
||||
// component. The advantage of that is more control rendering control in the
|
||||
// children, while the disadvantage is that it's slower, as all the children
|
||||
// get updated on every zoom/pan action.
|
||||
const { children, forwardTransform } = this.props;
|
||||
const transform = forwardTransform ? '' : transformToString(this.state);
|
||||
|
||||
return (
|
||||
<g className="cachable-zoom-wrapper" transform={transform}>
|
||||
{forwardTransform ? children(this.state) : children}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
setZoomTriggers(zoomingEnabled) {
|
||||
if (zoomingEnabled) {
|
||||
this.svg.call(this.zoom);
|
||||
} else {
|
||||
this.svg.on('.zoom', null);
|
||||
}
|
||||
}
|
||||
|
||||
// Decides which part of the zoom state is cachable depending
|
||||
// on the horizontal/vertical degrees of freedom.
|
||||
cachableState(state = this.state) {
|
||||
const cachableFields = []
|
||||
.concat(this.props.fixHorizontal ? [] : ['scaleX', 'translateX'])
|
||||
.concat(this.props.fixVertical ? [] : ['scaleY', 'translateY']);
|
||||
|
||||
return pick(state, cachableFields);
|
||||
}
|
||||
|
||||
cacheZoom() {
|
||||
this.props.cacheZoomState(fromJS(this.cachableState()));
|
||||
}
|
||||
|
||||
updateZoomLimits(props) {
|
||||
const zoomLimits = props.layoutZoomLimits.toJS();
|
||||
|
||||
this.zoom = this.zoom.scaleExtent([zoomLimits.minScale, zoomLimits.maxScale]);
|
||||
|
||||
if (props.bounded) {
|
||||
this.zoom = this.zoom
|
||||
// Translation limits are only set if explicitly demanded (currently we are using them
|
||||
// in the resource view, but not in the graph view, although I think the idea would be
|
||||
// to use them everywhere).
|
||||
.translateExtent([
|
||||
[zoomLimits.minTranslateX, zoomLimits.minTranslateY],
|
||||
[zoomLimits.maxTranslateX, zoomLimits.maxTranslateY],
|
||||
])
|
||||
// This is to ensure that the translation limits are properly
|
||||
// centered, so that the canvas margins are respected.
|
||||
.extent([
|
||||
[props.canvasMargins.left, props.canvasMargins.top],
|
||||
[props.canvasMargins.left + props.width, props.canvasMargins.top + props.height]
|
||||
]);
|
||||
}
|
||||
|
||||
this.setState(zoomLimits);
|
||||
}
|
||||
|
||||
// Restore the zooming settings
|
||||
restoreZoomState(props) {
|
||||
if (!props.layoutZoomState.isEmpty()) {
|
||||
const zoomState = props.layoutZoomState.toJS();
|
||||
|
||||
// After the limits have been set, update the zoom.
|
||||
this.svg.call(this.zoom.transform, zoomIdentity
|
||||
.translate(zoomState.translateX, zoomState.translateY)
|
||||
.scale(zoomState.scaleX, zoomState.scaleY));
|
||||
|
||||
// Update the state variables.
|
||||
this.setState(zoomState);
|
||||
this.zoomRestored = true;
|
||||
}
|
||||
}
|
||||
|
||||
zoomed() {
|
||||
if (!this.props.disabled) {
|
||||
const updatedState = this.cachableState({
|
||||
scaleX: d3Event.transform.k,
|
||||
scaleY: d3Event.transform.k,
|
||||
translateX: d3Event.transform.x,
|
||||
translateY: d3Event.transform.y,
|
||||
});
|
||||
|
||||
this.setState(updatedState);
|
||||
this.debouncedCacheZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
width: canvasWidthSelector(state),
|
||||
height: canvasHeightSelector(state),
|
||||
canvasMargins: canvasMarginsSelector(state),
|
||||
layoutZoomState: props.zoomStateSelector(state),
|
||||
layoutZoomLimits: props.zoomLimitsSelector(state),
|
||||
layoutId: JSON.stringify(activeTopologyZoomCacheKeyPathSelector(state)),
|
||||
forceRelayout: state.get('forceRelayout'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{ cacheZoomState }
|
||||
)(ZoomWrapper);
|
||||
Reference in New Issue
Block a user