[TSVB] change how the resize component works to not use timeouts (#14320)

* [WIP] Atempt to use a new resize component that does not use timeouts

* remove unused var

* add copyright notices
This commit is contained in:
Matt Apperson 2017-10-18 15:45:42 +02:00 committed by Chris Cowan
parent 21f22d3334
commit 0638bcac6e
3 changed files with 208 additions and 75 deletions

View file

@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import _ from 'lodash';
import $ from '../lib/flot';
import eventBus from '../lib/events';
@ -9,7 +8,6 @@ import calculateBarWidth from '../lib/calculate_bar_width';
import colors from '../lib/colors';
class FlotChart extends Component {
constructor(props) {
super(props);
this.handleResize = this.handleResize.bind(this);
@ -28,11 +26,13 @@ class FlotChart extends Component {
// We need to rerender if the axis change
const valuesChanged = props.yaxes.some((axis, i) => {
if (this.props.yaxes[i]) {
return axis.position !== this.props.yaxes[i].position ||
axis.max !== this.props.yaxes[i].max ||
axis.min !== this.props.yaxes[i].min ||
axis.axisFormatter !== this.props.yaxes[i].axisFormatter ||
axis.axisFormatterTemplate !== this.props.yaxes[i].axisFormatterTemplate;
return (
axis.position !== this.props.yaxes[i].position ||
axis.max !== this.props.yaxes[i].max ||
axis.min !== this.props.yaxes[i].min ||
axis.axisFormatter !== this.props.yaxes[i].axisFormatter ||
axis.axisFormatterTemplate !== this.props.yaxes[i].axisFormatterTemplate
);
}
});
if (props.yaxes.length !== this.props.yaxes.length || valuesChanged) {
@ -62,7 +62,7 @@ class FlotChart extends Component {
filterByShow(show) {
if (show) {
return (metric) => {
return metric => {
return show.some(id => _.startsWith(id, metric.id));
};
}
@ -95,7 +95,7 @@ class FlotChart extends Component {
calculateData(data, show) {
return _(data)
.filter(this.filterByShow(show))
.map((set) => {
.map(set => {
if (_.isPlainObject(set)) {
return set;
}
@ -103,7 +103,9 @@ class FlotChart extends Component {
color: '#990000',
data: set
};
}).reverse().value();
})
.reverse()
.value();
}
handleDraw(plot) {
@ -153,7 +155,7 @@ class FlotChart extends Component {
borderWidth,
borderColor: lineColor,
hoverable: true,
mouseActiveRadius: 200,
mouseActiveRadius: 200
}
};
@ -172,15 +174,17 @@ class FlotChart extends Component {
return _.assign(opts, props.options);
}
handleResize() {
const resize = findDOMNode(this.resize);
handleResize(width, height) {
this.size = { width, height };
console.log(this.size);
if (!this.rendered) {
this.renderChart();
return;
}
if (resize && resize.clientHeight > 0 && resize.clientHeight > 0) {
if (this.size.height > 0 && this.size.width > 0) {
if (!this.plot) return;
console.log('here');
this.plot.resize();
this.plot.setupGrid();
this.plot.draw();
@ -189,19 +193,24 @@ class FlotChart extends Component {
}
renderChart() {
const resize = findDOMNode(this.resize);
console.log('here 2');
if (this.size.height > 0 && this.size.width > 0) {
console.log('here 2.3');
if (resize.clientWidth > 0 && resize.clientHeight > 0) {
this.rendered = true;
const { series } = this.props;
const data = this.calculateData(series, this.props.show);
console.log('here 2.3.3.2');
this.plot = $.plot(this.target, data, this.getOptions(this.props));
console.log('here 2.3.3');
this.handleDraw(this.plot);
console.log('here 3');
_.defer(() => this.handleResize());
this.handleMouseOver = (...args) => {
if (this.props.onMouseOver) this.props.onMouseOver(...args, this.plot);
};
@ -214,8 +223,6 @@ class FlotChart extends Component {
$(this.target).on('mouseleave', this.handleMouseLeave);
if (this.props.crosshair) {
this.handleThorPlotover = (e, pos, item, originalPlot) => {
if (this.plot !== originalPlot) {
this.plot.setCrosshair({ x: _.get(pos, 'x') });
@ -225,7 +232,7 @@ class FlotChart extends Component {
this.handlePlotover = (e, pos, item) => eventBus.trigger('thorPlotover', [pos, item, this.plot]);
this.handlePlotleave = () => eventBus.trigger('thorPlotleave');
this.handleThorPlotleave = (e) => {
this.handleThorPlotleave = e => {
this.plot.clearCrosshair();
if (this.props.plothover) this.props.plothover(e);
};
@ -256,20 +263,14 @@ class FlotChart extends Component {
}
render() {
console.log('render');
return (
<Resize
onResize={this.handleResize}
ref={(el) => this.resize = el}
className="rhythm_chart__timeseries-container"
>
<div
ref={(el) => this.target = el}
className="rhythm_chart__timeseries-container"
/>
</Resize>
<div className="rhythm_chart__timeseries-container">
<div ref={el => (this.target = el)} className="rhythm_chart__timeseries-container" />
<Resize onResize={this.handleResize} />
</div>
);
}
}
FlotChart.defaultProps = {
@ -289,7 +290,7 @@ FlotChart.propTypes = {
show: PropTypes.array,
tickFormatter: PropTypes.func,
showGrid: PropTypes.bool,
yaxes: PropTypes.array,
yaxes: PropTypes.array
};
export default FlotChart;

View file

@ -1,70 +1,176 @@
/*
* Please note: Much of this code is taken from the MIT licensed react-resize-detector module written by @maslianok
*/
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
class Resize extends Component {
constructor(props) {
super(props);
this.state = {};
this.handleResize = this.handleResize.bind(this);
const parentStyle = {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
overflow: 'hidden',
zIndex: -1,
visibility: 'hidden'
};
const shrinkChildStyle = {
position: 'absolute',
left: 0,
top: 0,
width: '200%',
height: '200%'
};
const expandChildStyle = {
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%'
};
class Resize extends Component {
constructor() {
super();
this.state = {
expandChildHeight: 0,
expandChildWidth: 0,
expandScrollLeft: 0,
expandScrollTop: 0,
shrinkScrollTop: 0,
shrinkScrollLeft: 0,
lastWidth: 0,
lastHeight: 0
};
this.reset = this.reset.bind(this);
this.handleScroll = this.handleScroll.bind(this);
}
checkSize() {
const el = findDOMNode(this.el);
if (!el) return;
this.timeout = setTimeout(() => {
const { currentHeight, currentWidth } = this.state;
if (currentHeight !== el.parentNode.clientHeight || currentWidth !== el.parentNode.clientWidth) {
this.setState({
currentWidth: el.parentNode.clientWidth,
currentHeight: el.parentNode.clientHeight
});
this.handleResize();
}
clearTimeout(this.timeout);
this.checkSize();
}, this.props.frequency);
componentWillMount() {
this.forceUpdate();
}
componentDidMount() {
const el = findDOMNode(this.el);
const currentWidth = el.parentNode.clientWidth;
const currentHeight = el.parentNode.clientHeight;
this.setState({ currentHeight, currentWidth }); // eslint-disable-line react/no-did-mount-set-state
this.checkSize();
const [width, height] = this.containerSize();
this.reset(width, height);
this.props.onResize(width, height);
}
componentWillUnmount() {
clearTimeout(this.timeout);
shouldComponentUpdate(nextProps, nextState) {
return this.props !== nextProps || this.state !== nextState;
}
handleResize() {
if (this.props.onResize) this.props.onResize();
componentDidUpdate() {
this.expand.scrollLeft = this.expand.scrollWidth;
this.expand.scrollTop = this.expand.scrollHeight;
this.shrink.scrollLeft = this.shrink.scrollWidth;
this.shrink.scrollTop = this.shrink.scrollHeight;
}
containerSize() {
return [
this.props.handleWidth && this.container.parentElement.offsetWidth,
this.props.handleHeight && this.container.parentElement.offsetHeight
];
}
reset(containerWidth, containerHeight) {
if (typeof window === 'undefined') {
return;
}
const parent = this.container.parentElement;
let position = 'static';
if (parent.currentStyle) {
position = parent.currentStyle.position;
} else if (window.getComputedStyle) {
position = window.getComputedStyle(parent).position;
}
if (position === 'static') {
parent.style.position = 'relative';
}
this.setState({
expandChildHeight: this.expand.offsetHeight + 10,
expandChildWidth: this.expand.offsetWidth + 10,
lastWidth: containerWidth,
lastHeight: containerHeight
});
}
handleScroll(e) {
if (typeof window === 'undefined') {
return;
}
e.preventDefault();
e.stopPropagation();
const { state } = this;
const [width, height] = this.containerSize();
if (width !== state.lastWidth || height !== state.lastHeight) {
this.props.onResize(width, height);
}
this.reset(width, height);
}
render() {
const style = this.props.style || {};
const className = this.props.className || '';
return(
const { state } = this;
const expandStyle = Object.assign({}, expandChildStyle, {
width: state.expandChildWidth,
height: state.expandChildHeight
});
return (
<div
style={style}
className={className}
ref={(el) => this.el = el}
style={parentStyle}
ref={e => {
this.container = e;
}}
>
{this.props.children}
<div
style={parentStyle}
onScroll={this.handleScroll}
ref={e => {
this.expand = e;
}}
>
<div style={expandStyle} />
</div>
<div
style={parentStyle}
onScroll={this.handleScroll}
ref={e => {
this.shrink = e;
}}
>
<div style={shrinkChildStyle} />
</div>
</div>
);
}
}
Resize.defaultProps = {
frequency: 500
};
Resize.propTypes = {
frequency: PropTypes.number,
handleWidth: PropTypes.bool,
handleHeight: PropTypes.bool,
onResize: PropTypes.func
};
Resize.defaultProps = {
handleWidth: true,
handleHeight: true,
onResize: e => e
};
export default Resize;

View file

@ -1,6 +1,32 @@
Kibana
Copyright 2012-2017 Elasticsearch
---
This product bundles react-resize-detector@0.6.0 which is available under a
"MIT" license.
The MIT License
Copyright (c) 2017 Vitalii Maslianok <maslianok@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---
This product bundles angular-ui-bootstrap@0.12.1 which is available under a
"MIT" license.