Skip to content

Commit

Permalink
Create ZoomHandler
Browse files Browse the repository at this point in the history
Due to the way that the Highlight component handles the mouse events,
it is pretty much required that it is one of the last children of the XYPlot.
Since it renders a transparent `rect` over the entire `XYPlot` it currently disables
any mouse events for controls lower in the dom tree.
If the `Highlight` component is moved higher in the dom tree, then the `onMouseLeave`
event will fire when a lower component is hovered over.

The ZoomHandler delegates the handling of the mouse events to the XYPlot itself.
The XYPlot now holds a collection of `Event` objects that will be passed to the children,
where they can subscribe to the ones that they are interested in.
When the appropriate Event in the XYPlot is fired, it will execute the callbacks for all listeners.
This approach does not cause the `XYPlot` to re-render, and only the listeners that perform an
operation in their callback will be re-rendered.

Also created a `useStateWithGet` that wraps a `useState` call and also provides a memoized `getState` method.
Since the `getState` method is only created once for the component, it will not trigger re-renders when
listed in the dependencies of `useEffect` / `useCallback`. This drastically cuts down the number of
event handlers that are subscribed / unsubscribed.
  • Loading branch information
Chris Thomas committed Jun 10, 2020
1 parent 8e56d05 commit 58a1ee2
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 125 deletions.
20 changes: 14 additions & 6 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jest/recommended",
"prettier",
"prettier/react"
Expand All @@ -19,8 +20,8 @@
"**/dist/",
"**/es/"
],
"settings":{
"react":{
"settings": {
"react": {
"version": "detect"
}
},
Expand All @@ -31,12 +32,19 @@
},
"rules": {
"consistent-return": 0,
"max-len": [1, 110, 4],
"max-params": ["error", 6],
"max-len": [
1,
110,
4
],
"max-params": [
"error",
6
],
"object-curly-spacing": 0,
"babel/object-curly-spacing": 2,
"jest/require-top-level-describe":"error",
"jest/require-top-level-describe": "error",
"react/prop-types": "off",
"prettier/prettier": "warn"
}
}
}
1 change: 1 addition & 0 deletions packages/react-vis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.5.0",
"eslint-plugin-jest": "^23.13.2",
"eslint-plugin-react-hooks": "^4.0.4",
"jest": "^25.5.4",
"jsdom": "^9.9.1",
"node-sass": "^4.9.3",
Expand Down
2 changes: 2 additions & 0 deletions packages/react-vis/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export Treemap from 'treemap';

export ContentClipPath from './plot/content-clip-path';

export ZoomHandler from './plot/zoom-handler';

export {
makeHeightFlexible,
makeVisFlexible,
Expand Down
135 changes: 23 additions & 112 deletions packages/react-vis/src/plot/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {getAttributeScale} from 'utils/scales-utils';
import {getCombinedClassName} from 'utils/styling-utils';

function getLocs(evt) {
// const xLoc = evt.type === 'touchstart' ? evt.pageX : evt.offsetX;
// const yLoc = evt.type === 'touchstart' ? evt.pageY : evt.offsetY;
const xLoc = evt.offsetX ?? evt.pageX;
const yLoc = evt.offsetY ?? evt.pageY;
const xLoc = evt.type === 'touchstart' ? evt.pageX : evt.offsetX;
const yLoc = evt.type === 'touchstart' ? evt.pageY : evt.offsetY;
return {xLoc, yLoc};
}

Expand All @@ -22,7 +20,7 @@ class Highlight extends AbstractSeries {
startLocY: 0,
dragArea: null
};
ref = React.createRef();

_getDrawArea(xLoc, yLoc) {
const {startLocX, startLocY} = this.state;
const {
Expand Down Expand Up @@ -118,69 +116,10 @@ class Highlight extends AbstractSeries {
return {};
}

_captureMouse = e => {
console.log('capture', e.type, e.target, e);

document.addEventListener('mouseup', this.stopBrushing, {capture: true});
document.addEventListener('mousemove', this.onBrush, {capture: true});
// document.body.style['pointer-events'] = 'none';

if (this.ref.current) {
this.ref.current.addEventListener('mouseleave', this._mouseLeave, {
capture: true
});
}

e.preventDefault();
e.stopPropagation();
// this.startBrushing(e);
};

_releaseMouse = e => {
console.log('release', e.type, e.target, e);

document.removeEventListener('mouseup', this.stopBrushing, {capture: true});
document.removeEventListener('mousemove', this.onBrush, {capture: true});

if (this.ref.current) {
this.ref.current.removeEventListener('mouseleave', this._mouseLeave, {
capture: true
});
}
// document.body.style['pointer-events'] = 'auto';
e.stopPropagation();
};

_mouseLeave = e => {
const {toElement} = e;
if (toElement === document.documentElement) {
this.stopBrushing(e);
return;
}
if (toElement === this.ref.current.parentNode.parentNode) {
this.stopBrushing(e);
return;
}
console.log('didnt really leave', toElement, this.ref.current.parentNode);
// this.ref.current.
};

startBrushing = e => {
// e.preventDefault();
this._captureMouse(e);
startBrushing(e) {
const {onBrushStart, onDragStart, drag} = this.props;
const {dragArea} = this.state;
const {xLoc, yLoc} = getLocs(e);
console.log(
'start',
xLoc,
yLoc,
e.type,
e.offsetX,
e.offsetY,
e.pageX,
e.pageY
);
const {xLoc, yLoc} = getLocs(e.nativeEvent);

const startArea = (dragging, resetDrag) => {
const emptyBrush = {
Expand Down Expand Up @@ -214,23 +153,9 @@ class Highlight extends AbstractSeries {
onDragStart(e);
}
}
};
}

stopBrushing = e => {
if (e.toElement === document.documentElement) {
console.log('is document');
// return;
}
console.log(
'stop',
e.type,
e.target,
e.currentTarget,
e.toElement,
document,
e
);
this._releaseMouse(e);
stopBrushing() {
const {brushing, dragging, brushArea} = this.state;
// Quickly short-circuit if the user isn't brushing in our component
if (!brushing && !dragging) {
Expand Down Expand Up @@ -258,18 +183,14 @@ class Highlight extends AbstractSeries {
if (drag && onDragEnd) {
onDragEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null);
}
};
}

onBrush = e => {
e.preventDefault();
e.stopPropagation();
onBrush(e) {
const {onBrush, onDrag, drag} = this.props;
const {brushing, dragging} = this.state;
const {xLoc, yLoc} = getLocs(e);
// console.log('brush', xLoc, yLoc);
const {xLoc, yLoc} = getLocs(e.nativeEvent);
if (brushing) {
const brushArea = this._getDrawArea(xLoc, yLoc);
// console.log('brush area', brushArea);
this.setState({brushArea});

if (onBrush) {
Expand All @@ -284,7 +205,7 @@ class Highlight extends AbstractSeries {
onDrag(this._convertAreaToCoordinates(brushArea));
}
}
};
}

render() {
const {
Expand Down Expand Up @@ -329,36 +250,26 @@ class Highlight extends AbstractSeries {
className={getCombinedClassName(className, 'rv-highlight-container')}
>
<rect
ref={this.ref}
className="rv-mouse-target"
fill="black"
opacity="0"
x="0"
y="0"
width={Math.max(touchWidth, 0)}
height={Math.max(touchHeight, 0)}
onMouseDownCapture={e => this.startBrushing(e.nativeEvent)}
// onMouseMoveCapture={e => this.onBrush(e)}
// onMouseUpCapture={e => this.stopBrushing(e)}
// onMouseLeave={e => {
// console.log(
// 'mouse leave',
// e.target,
// e.currentTarget,
// getLocs(e.nativeEvent)
// );
// // this._releaseMouse(e);
// // this.stopBrushing(e);
// }}
onMouseDown={e => this.startBrushing(e)}
onMouseMove={e => this.onBrush(e)}
onMouseUp={e => this.stopBrushing(e)}
onMouseLeave={e => this.stopBrushing(e)}
// preventDefault() so that mouse event emulation does not happen
// onTouchEnd={e => {
// e.preventDefault();
// this.stopBrushing(e);
// }}
// onTouchCancel={e => {
// e.preventDefault();
// this.stopBrushing(e);
// }}
onTouchEnd={e => {
e.preventDefault();
this.stopBrushing(e);
}}
onTouchCancel={e => {
e.preventDefault();
this.stopBrushing(e);
}}
onContextMenu={e => e.preventDefault()}
onContextMenuCapture={e => e.preventDefault()}
/>
Expand Down
19 changes: 17 additions & 2 deletions packages/react-vis/src/plot/xy-plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import {

import CanvasWrapper from './series/canvas-wrapper';

import {Event} from '../utils/events';

const ATTRIBUTES = [
'x',
'y',
Expand Down Expand Up @@ -140,7 +142,14 @@ class XYPlot extends React.Component {
const data = getStackedData(children, stackBy);
this.state = {
scaleMixins: this._getScaleMixins(data, props),
data
data,
events: {
mouseMove: new Event('move'),
mouseDown: new Event('down'),
mouseUp: new Event('up'),
mouseLeave: new Event('leave'),
mouseEnter: new Event('enter')
}
};
}

Expand Down Expand Up @@ -222,7 +231,8 @@ class XYPlot extends React.Component {
...scaleMixins,
...child.props,
...XYPlotValues[index],
...dataProps
...dataProps,
events: this.state.events
});
});
}
Expand Down Expand Up @@ -348,6 +358,7 @@ class XYPlot extends React.Component {
component.onParentMouseDown(event);
}
});
this.state.events.mouseDown.fire(event);
};

/**
Expand All @@ -367,6 +378,7 @@ class XYPlot extends React.Component {
component.onParentMouseEnter(event);
}
});
this.state.events.mouseEnter.fire(event);
};

/**
Expand All @@ -386,6 +398,7 @@ class XYPlot extends React.Component {
component.onParentMouseLeave(event);
}
});
this.state.events.mouseLeave.fire(event);
};

/**
Expand All @@ -405,6 +418,7 @@ class XYPlot extends React.Component {
component.onParentMouseMove(event);
}
});
this.state.events.mouseMove.fire(event);
};

/**
Expand All @@ -424,6 +438,7 @@ class XYPlot extends React.Component {
component.onParentMouseUp(event);
}
});
this.state.events.mouseUp.fire(event);
};

/**
Expand Down
Loading

0 comments on commit 58a1ee2

Please sign in to comment.