Skip to content
This repository has been archived by the owner on Feb 19, 2022. It is now read-only.

make ZoomContainer work with real-time data #496

Merged
merged 1 commit into from
Jul 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 32 additions & 37 deletions demo/components/debug-demo.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
/*eslint-disable no-magic-numbers */
/*global setInterval:false */

import React from "react";
import {
VictoryChart, VictoryLine, VictoryGroup, VictoryBrushContainer
VictoryChart, VictoryLine, VictoryZoomContainer
} from "../../src/index";

const data = [
{ x: 1, y: -3 },
{ x: 2, y: 5 },
{ x: 3, y: 3 },
{ x: 4, y: 0 },
{ x: 5, y: -2 },
{ x: 6, y: -2 },
{ x: 7, y: 5 }
];
import { range, last } from "lodash";

const y = (x) => Math.sin(x / 10);
const initData = () => range(70).map((x) => ({ x, y: y(x) }));

class App extends React.Component {
constructor() {
super();
this.state = {};
}
handleSelectionChange(domain) {
this.setState({
selectedDomain: domain
});
}
changeSelection(a, b) {
this.setState({
selectedDomain: { x: [a, b] }
});
this.state = {
data: initData(),
zoomDomain: null
};

setInterval(() => {
const { data } = this.state;
const x = last(data).x + 1;

if (x > 1000) {
this.setState({ data: initData() });
} else {
data.push({ x, y: y(x) });
this.setState({ data });
}
}, 20);
}
render() {
return (
<div style={{ width: 300 }}>
<button onClick={() => this.changeSelection(1, 3)}>1-3</button>
<button onClick={() => this.changeSelection(3, 6)}>3-6</button>
<VictoryChart
containerComponent={
<VictoryBrushContainer
dimension="x"
onDomainChange={this.handleSelectionChange.bind(this)}
selectedDomain={this.state.selectedDomain}
/>
}
>
<VictoryGroup data={data}>
<VictoryLine/>
</VictoryGroup>
</VictoryChart>
<div style={{ maxWidth: 500 }}>
<VictoryChart containerComponent={<VictoryZoomContainer dimension="x" />}>
<VictoryLine
data={this.state.data}
/>
<VictoryLine
data={this.state.data}
/>
</VictoryChart>
</div>
);
}
Expand Down
20 changes: 16 additions & 4 deletions src/components/containers/victory-zoom-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const zoomContainerMixin = (base) => class VictoryZoomContainer extends b
static defaultProps = {
...VictoryContainer.defaultProps,
clipContainerComponent: <VictoryClipContainer/>,
allowZoom: true
allowZoom: true,
zoomActive: false
};

static defaultEvents = [{
Expand Down Expand Up @@ -131,12 +132,23 @@ export const zoomContainerMixin = (base) => class VictoryZoomContainer extends b
const childComponents = React.Children.toArray(props.children);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix must be in modifyChildren or another part of the VictoryZoomContainer; it cannot just go in the event handlers (found in zoom-helpers.js). This is because if new data is added to a chart, the handlers don't get fired, but we must know whether to show the new data or stay "zoomed in."


return childComponents.map((child) => {
const { currentDomain } = props;
const { currentDomain, zoomActive } = props;
const originalDomain = defaults({}, props.originalDomain, props.domain);
const zoomDomain = defaults({}, props.zoomDomain, props.domain);
const cachedZoomDomain = defaults({}, props.cachedZoomDomain, props.domain);
const domain = isEqual(zoomDomain, cachedZoomDomain) ?
defaults({}, currentDomain, originalDomain) : zoomDomain;

let domain;
if (!isEqual(zoomDomain, cachedZoomDomain)) {
// if zoomDomain has been changed, use it
domain = zoomDomain;
} else if (!zoomActive) {
// if user has zoomed all the way out, use the child domain
domain = child.props.domain;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

child.props.domain is already calculated for us, so this approach has no new computational overhead.

} else {
// default: use currentDomain, set by the event handlers
domain = defaults({}, currentDomain, originalDomain);
}

let newDomain = props.polar ? this.modifyPolarDomain(domain, originalDomain) : domain;
if (props.dimension) {
// if zooming is restricted to a dimension, don't squash changes to zoomDomain in other dim
Expand Down
14 changes: 12 additions & 2 deletions src/components/containers/zoom-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ const Helpers = {
];
},

zoommingOut(evt) {
return evt.deltaY > 0;
},

getScaleFactor(evt) {
const sign = evt.deltaY > 0 ? 1 : -1;
const sign = this.zoommingOut(evt) ? 1 : -1;
// eslint-disable-next-line no-magic-numbers
const delta = Math.min(Math.abs(evt.deltaY / 300), 0.5); // TODO: Check scale factor
return Math.abs(1 + sign * delta);
Expand Down Expand Up @@ -232,16 +236,22 @@ const Helpers = {
y: dimension === "x" ? lastDomain.y : this.scale(y, evt, targetProps, "y")
};
const resumeAnimation = this.handleAnimation(ctx);

const zoomActive = !this.zoommingOut(evt) // if zoomming in or
// if zoomActive is already set AND user hasn't zoommed out all the way
|| (targetProps.zoomActive && !isEqual(originalDomain, lastDomain));

if (isFunction(onDomainChange)) {
onDomainChange(currentDomain);
}

return [{
target: "parent",
callback: resumeAnimation,
mutation: () => {
return {
domain: currentDomain, currentDomain, originalDomain, cachedZoomDomain: zoomDomain,
parentControlledProps: ["domain"], panning: false
parentControlledProps: ["domain"], panning: false, zoomActive
};
}
}];
Expand Down