Skip to content
This repository was archived by the owner on Aug 13, 2021. It is now read-only.

Commit cf2f609

Browse files
andrei-caciodarung
andauthored
WIP Add touch events for all components (#62)
* Added event normalizer * Small fixes and adjustments for touch enabled devices Co-authored-by: Darius Ungurean <[email protected]>
1 parent ede3019 commit cf2f609

File tree

6 files changed

+113
-8
lines changed

6 files changed

+113
-8
lines changed

src/MultiSlider/MultiSliderHandle.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33

44
import { noop } from '../util/functions';
5+
import normalizeEvent from '../util/normalize-event';
56

67
import './MultiSliderHandle.css';
78

@@ -12,6 +13,7 @@ class MultiSliderHandle extends React.PureComponent {
1213
}
1314

1415
startInteraction(e) {
16+
e = normalizeEvent(e);
1517
this.props.onStart(e, this.props.property);
1618
}
1719

@@ -26,6 +28,7 @@ class MultiSliderHandle extends React.PureComponent {
2628
<div
2729
className="uix-multislider__handle"
2830
onMouseDown={this.startInteraction}
31+
onTouchStart={this.startInteraction}
2932
style={styles}
3033
/>
3134
);

src/Pad/stories/index.stories.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22

33
import { storiesOf } from '@storybook/react';
44
import { action } from '@storybook/addon-actions';
5-
import State from '../../Husk/Husk';
5+
import Husk from '../../Husk/Husk';
66

77
import Pad from '../Pad';
88
import PadGrid from '../PadGrid';

src/Position/Position.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { noop } from '../util/functions';
4+
import normalizeEvent, { isTouchEnabledDevice } from '../util/normalize-event';
45

56
/*
67
Component: Position
@@ -24,14 +25,23 @@ class Position extends React.PureComponent {
2425
componentDidMount() {
2526
document.addEventListener('mousemove', this.onChange);
2627
document.addEventListener('mouseup', this.onEnd);
28+
if (isTouchEnabledDevice()) {
29+
document.addEventListener('touchmove', this.onChange);
30+
document.addEventListener('touchend', this.onEnd);
31+
}
2732
}
2833

2934
componentWillUnmount() {
3035
document.removeEventListener('mousemove', this.onChange);
3136
document.removeEventListener('mouseup', this.onEnd);
37+
if (isTouchEnabledDevice()) {
38+
document.removeEventListener('touchmove', this.onChange);
39+
document.removeEventListener('touchend', this.onEnd);
40+
}
3241
}
3342

3443
onChange(e) {
44+
e = normalizeEvent(e);
3545
this.props.onChange(
3646
{
3747
x: e.clientX,
@@ -44,6 +54,7 @@ class Position extends React.PureComponent {
4454
}
4555

4656
onEnd(e) {
57+
e = normalizeEvent(e);
4758
this.props.onEnd(
4859
{
4960
x: e.clientX,

src/Position/stories/index.stories.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ storiesOf('Position', module)
3232
this.state = {
3333
interacting: false
3434
};
35+
this.startInteraction = this.startInteraction.bind(this);
36+
}
37+
38+
startInteraction(e) {
39+
this.setState({ interacting: true });
40+
e.preventDefault();
3541
}
3642

3743
render() {
3844
return (
39-
<div
40-
onMouseDown={e => {
41-
this.setState({ interacting: true });
42-
e.preventDefault();
43-
}}
44-
>
45+
<div onMouseDown={this.startInteraction} onTouchStart={this.startInteraction}>
4546
<p>
4647
Hold down the mouse here and move it to read its coordinates:{' '}
4748
{this.state.x}: {this.state.y}

src/Surface/Surface.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { scaleLinear } from 'd3-scale';
55

66
import Position from '../Position/Position';
77
import { noop } from '../util/functions';
8+
import normalizeEvent, { isTouchEnabledDevice } from '../util/normalize-event';
89

910
import './Surface.css';
1011

@@ -22,7 +23,6 @@ const initial_state = {
2223
for the X and Y coordinates in percentages (interval [0..100]).
2324
2425
*/
25-
2626
class Surface extends React.Component {
2727
constructor(props) {
2828
super(props);
@@ -42,6 +42,7 @@ class Surface extends React.Component {
4242
}
4343

4444
start(e) {
45+
e = normalizeEvent(e);
4546
this.setState({
4647
interacting: true
4748
});
@@ -54,6 +55,7 @@ class Surface extends React.Component {
5455
}
5556

5657
end(e) {
58+
e = normalizeEvent(e);
5759
this.setState({
5860
interacting: false
5961
});
@@ -75,6 +77,7 @@ class Surface extends React.Component {
7577
}
7678

7779
insert(e) {
80+
e = normalizeEvent(e);
7881
this.props.onInsert(
7982
this.scale({
8083
x: e.clientX,
@@ -92,6 +95,8 @@ class Surface extends React.Component {
9295
let events = {};
9396
if (passive) {
9497
events['onDoubleClick'] = this.insert;
98+
} else if (isTouchEnabledDevice()) {
99+
events['onTouchMove'] = this.start;
95100
} else {
96101
events['onMouseDownCapture'] = this.start;
97102
}

src/util/normalize-event.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const proxiedValues = ['clientX', 'clientY', 'pageX', 'pageY', 'screenX', 'screenY'];
2+
3+
const proxiedFunctions = ['preventDefault', 'stopPropagation', 'stopImmediatePropagation'];
4+
5+
var EventProxy = {
6+
apply: function(obj, thisArg, argumentList) {
7+
return obj.apply(obj, argumentList);
8+
},
9+
10+
get: function(obj, prop) {
11+
if (obj.changedTouches && obj.changedTouches[0] && proxiedValues.indexOf(prop) > -1) {
12+
return obj.changedTouches[0][prop];
13+
}
14+
15+
if (
16+
obj.detail &&
17+
obj.detail.changedTouches &&
18+
obj.detail.changedTouches[0] &&
19+
proxiedValues.indexOf(prop) > -1
20+
) {
21+
return obj.detail.changedTouches[0][prop];
22+
}
23+
24+
if (prop == 'original') {
25+
if (obj.detail && obj.detail.original) {
26+
return obj.detail.original;
27+
}
28+
return obj;
29+
}
30+
31+
if (obj.type === 'touchend' && prop === 'target') {
32+
let clientX, clientY;
33+
if (obj.changedTouches && obj.changedTouches[0]) {
34+
clientX = obj.changedTouches[0].clientX;
35+
clientY = obj.changedTouches[0].clientY;
36+
} else if (obj.detail && obj.detail.changedTouches && obj.detail.changedTouches[0]) {
37+
clientX = obj.detail.changedTouches[0].clientX;
38+
clientY = obj.detail.changedTouches[0].clientY;
39+
}
40+
return document.elementFromPoint(clientX, clientY);
41+
}
42+
43+
if (proxiedFunctions.indexOf(prop) > -1) {
44+
if (obj.detail && obj.detail.original && obj.detail.original[prop]) {
45+
return obj.detail.original[prop].bind(obj);
46+
}
47+
return obj[prop].bind(obj);
48+
}
49+
50+
return obj[prop];
51+
}
52+
};
53+
54+
let __isTouchEnabledDevice;
55+
function isTouchEnabledDevice() {
56+
return __isTouchEnabledDevice !== undefined
57+
? __isTouchEnabledDevice
58+
: (__isTouchEnabledDevice = (function() {
59+
// we rely on Proxy for events that come from touch-enabled devices,
60+
// so if we don't have it we need to assume this is not such a device.
61+
if (!window.Proxy) {
62+
return false;
63+
}
64+
try {
65+
new CustomEvent('longtap');
66+
} catch (e) {
67+
return false;
68+
}
69+
/*
70+
As per https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints
71+
a device should expose how many touch points are supported; unfortunately
72+
mobile Safari does not support it, so we have to also taste for ontouchstart for iPads.
73+
*/
74+
return (
75+
window.ontouchstart !== undefined ||
76+
navigator.maxTouchPoints ||
77+
navigator.msMaxTouchPoints
78+
);
79+
})());
80+
}
81+
82+
const normalizeEvent = e => (isTouchEnabledDevice() ? new Proxy(e, EventProxy) : e);
83+
84+
export default normalizeEvent;
85+
export { isTouchEnabledDevice };

0 commit comments

Comments
 (0)