From b72a989d55a63ae17d1658c412824a14ff86a941 Mon Sep 17 00:00:00 2001 From: andrei-cacio Date: Tue, 21 Apr 2020 12:14:49 +0300 Subject: [PATCH 1/2] Added event normalizer --- src/MultiSlider/MultiSliderHandle.js | 3 + src/Position/Position.js | 9 +++ src/Surface/Surface.js | 7 ++- src/util/normalize-event.js | 85 ++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/util/normalize-event.js diff --git a/src/MultiSlider/MultiSliderHandle.js b/src/MultiSlider/MultiSliderHandle.js index aefa11c..b53594e 100644 --- a/src/MultiSlider/MultiSliderHandle.js +++ b/src/MultiSlider/MultiSliderHandle.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { noop } from '../util/functions'; +import normalizeEvent from '../util/normalize-event'; import './MultiSliderHandle.css'; @@ -12,6 +13,7 @@ class MultiSliderHandle extends React.PureComponent { } startInteraction(e) { + e = normalizeEvent(e); this.props.onStart(e, this.props.property); } @@ -26,6 +28,7 @@ class MultiSliderHandle extends React.PureComponent {
); diff --git a/src/Position/Position.js b/src/Position/Position.js index d72c33f..0960604 100644 --- a/src/Position/Position.js +++ b/src/Position/Position.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { noop } from '../util/functions'; +import normalizeEvent from '../util/normalize-event'; /* Component: Position @@ -22,16 +23,23 @@ class Position extends React.PureComponent { } componentDidMount() { + console.log('attached'); document.addEventListener('mousemove', this.onChange); document.addEventListener('mouseup', this.onEnd); + document.addEventListener('touchstart', this.onChange); + document.addEventListener('touchend', this.onEnd); } componentWillUnmount() { document.removeEventListener('mousemove', this.onChange); document.removeEventListener('mouseup', this.onEnd); + document.removeEventListener('touchstart', this.onChange); + document.removeEventListener('touchend', this.onEnd); } onChange(e) { + console.log(e); + e = normalizeEvent(e); this.props.onChange( { x: e.clientX, @@ -44,6 +52,7 @@ class Position extends React.PureComponent { } onEnd(e) { + e = normalizeEvent(e); this.props.onEnd( { x: e.clientX, diff --git a/src/Surface/Surface.js b/src/Surface/Surface.js index b4f05a3..c91725a 100644 --- a/src/Surface/Surface.js +++ b/src/Surface/Surface.js @@ -5,6 +5,7 @@ import { scaleLinear } from 'd3-scale'; import Position from '../Position/Position'; import { noop } from '../util/functions'; +import normalizeEvent, { isTouchEnabledDevice } from '../util/normalize-event'; import './Surface.css'; @@ -22,7 +23,6 @@ const initial_state = { for the X and Y coordinates in percentages (interval [0..100]). */ - class Surface extends React.Component { constructor(props) { super(props); @@ -42,6 +42,7 @@ class Surface extends React.Component { } start(e) { + e = normalizeEvent(e); this.setState({ interacting: true }); @@ -54,6 +55,7 @@ class Surface extends React.Component { } end(e) { + e = normalizeEvent(e); this.setState({ interacting: false }); @@ -75,6 +77,7 @@ class Surface extends React.Component { } insert(e) { + e = normalizeEvent(e); this.props.onInsert( this.scale({ x: e.clientX, @@ -92,6 +95,8 @@ class Surface extends React.Component { let events = {}; if (passive) { events['onDoubleClick'] = this.insert; + } else if (isTouchEnabledDevice()) { + events['onTouchMove'] = this.start; } else { events['onMouseDownCapture'] = this.start; } diff --git a/src/util/normalize-event.js b/src/util/normalize-event.js new file mode 100644 index 0000000..46486b5 --- /dev/null +++ b/src/util/normalize-event.js @@ -0,0 +1,85 @@ +const proxiedValues = ['clientX', 'clientY', 'pageX', 'pageY', 'screenX', 'screenY']; + +const proxiedFunctions = ['preventDefault', 'stopPropagation', 'stopImmediatePropagation']; + +var EventProxy = { + apply: function(obj, thisArg, argumentList) { + return obj.apply(obj, argumentList); + }, + + get: function(obj, prop) { + if (obj.changedTouches && obj.changedTouches[0] && proxiedValues.indexOf(prop) > -1) { + return obj.changedTouches[0][prop]; + } + + if ( + obj.detail && + obj.detail.changedTouches && + obj.detail.changedTouches[0] && + proxiedValues.indexOf(prop) > -1 + ) { + return obj.detail.changedTouches[0][prop]; + } + + if (prop == 'original') { + if (obj.detail && obj.detail.original) { + return obj.detail.original; + } + return obj; + } + + if (obj.type === 'touchend' && prop === 'target') { + let clientX, clientY; + if (obj.changedTouches && obj.changedTouches[0]) { + clientX = obj.changedTouches[0].clientX; + clientY = obj.changedTouches[0].clientY; + } else if (obj.detail && obj.detail.changedTouches && obj.detail.changedTouches[0]) { + clientX = obj.detail.changedTouches[0].clientX; + clientY = obj.detail.changedTouches[0].clientY; + } + return document.elementFromPoint(clientX, clientY); + } + + if (proxiedFunctions.indexOf(prop) > -1) { + if (obj.detail && obj.detail.original && obj.detail.original[prop]) { + return obj.detail.original[prop].bind(obj); + } + return obj[prop].bind(obj); + } + + return obj[prop]; + } +}; + +let __isTouchEnabledDevice; +function isTouchEnabledDevice() { + return __isTouchEnabledDevice !== undefined + ? __isTouchEnabledDevice + : (__isTouchEnabledDevice = (function() { + // we rely on Proxy for events that come from touch-enabled devices, + // so if we don't have it we need to assume this is not such a device. + if (!window.Proxy) { + return false; + } + try { + new CustomEvent('longtap'); + } catch (e) { + return false; + } + /* + As per https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints + a device should expose how many touch points are supported; unfortunately + mobile Safari does not support it, so we have to also taste for ontouchstart for iPads. + */ + return ( + window.ontouchstart !== undefined || + navigator.maxTouchPoints || + navigator.msMaxTouchPoints + ); + })()); +} + +const normalizeEvent = e => (isTouchEnabledDevice() ? new Proxy(e, EventProxy) : e); + +export default normalizeEvent; +export { isTouchEnabledDevice }; From 269cc34002021d3d3378486e0579096d22066c6f Mon Sep 17 00:00:00 2001 From: Darius Ungurean Date: Thu, 9 Jul 2020 23:39:55 +0300 Subject: [PATCH 2/2] Small fixes and adjustments for touch enabled devices --- src/Pad/stories/index.stories.js | 2 +- src/Position/Position.js | 16 +++++++++------- src/Position/stories/index.stories.js | 13 +++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Pad/stories/index.stories.js b/src/Pad/stories/index.stories.js index 50c858b..585f011 100644 --- a/src/Pad/stories/index.stories.js +++ b/src/Pad/stories/index.stories.js @@ -2,7 +2,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import State from '../../Husk/Husk'; +import Husk from '../../Husk/Husk'; import Pad from '../Pad'; import PadGrid from '../PadGrid'; diff --git a/src/Position/Position.js b/src/Position/Position.js index 0960604..c0c7561 100644 --- a/src/Position/Position.js +++ b/src/Position/Position.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { noop } from '../util/functions'; -import normalizeEvent from '../util/normalize-event'; +import normalizeEvent, { isTouchEnabledDevice } from '../util/normalize-event'; /* Component: Position @@ -23,22 +23,24 @@ class Position extends React.PureComponent { } componentDidMount() { - console.log('attached'); document.addEventListener('mousemove', this.onChange); document.addEventListener('mouseup', this.onEnd); - document.addEventListener('touchstart', this.onChange); - document.addEventListener('touchend', this.onEnd); + if (isTouchEnabledDevice()) { + document.addEventListener('touchmove', this.onChange); + document.addEventListener('touchend', this.onEnd); + } } componentWillUnmount() { document.removeEventListener('mousemove', this.onChange); document.removeEventListener('mouseup', this.onEnd); - document.removeEventListener('touchstart', this.onChange); - document.removeEventListener('touchend', this.onEnd); + if (isTouchEnabledDevice()) { + document.removeEventListener('touchmove', this.onChange); + document.removeEventListener('touchend', this.onEnd); + } } onChange(e) { - console.log(e); e = normalizeEvent(e); this.props.onChange( { diff --git a/src/Position/stories/index.stories.js b/src/Position/stories/index.stories.js index 5caacb5..1ca9703 100644 --- a/src/Position/stories/index.stories.js +++ b/src/Position/stories/index.stories.js @@ -32,16 +32,17 @@ storiesOf('Position', module) this.state = { interacting: false }; + this.startInteraction = this.startInteraction.bind(this); + } + + startInteraction(e) { + this.setState({ interacting: true }); + e.preventDefault(); } render() { return ( -
{ - this.setState({ interacting: true }); - e.preventDefault(); - }} - > +

Hold down the mouse here and move it to read its coordinates:{' '} {this.state.x}: {this.state.y}