@samitouri / QOS-React / commits / 49979bbf52

Support Pointer Events (#12507)

* Support Pointer Events * Add Pointer Events DOM Fixture

Philipp Spieß committed May 16, 2018 at 15:34 UTC 49979bbf521102d0fb55c77323bcec6fd6871a03
13 files changed +373 -17
fixtures/dom/src/components/Header.js
+1
@@ -64,6 +64,7 @@ class Header extends React.Component {
64 <option value="/event-pooling">Event Pooling</option>
65 <option value="/custom-elements">Custom Elements</option>
66 <option value="/media-events">Media Events</option>
67 + <option value="/pointer-events">Pointer Events</option>
68 </select>
69 </label>
70 <label htmlFor="react_version">
fixtures/dom/src/components/fixtures/index.js
+3
@@ -11,6 +11,7 @@ import ErrorHandling from './error-handling';
11 import EventPooling from './event-pooling';
12 import CustomElementFixtures from './custom-elements';
13 import MediaEventsFixtures from './media-events';
14 +import PointerEventsFixtures from './pointer-events';
15
16 const React = window.React;
17
@@ -46,6 +47,8 @@ function FixturesPage() {
47 return <CustomElementFixtures />;
48 case '/media-events':
49 return <MediaEventsFixtures />;
50 + case '/pointer-events':
51 + return <PointerEventsFixtures />;
52 default:
53 return <p>Please select a test fixture.</p>;
54 }
fixtures/dom/src/components/fixtures/pointer-events/drag-box.js new
+90
@@ -0,0 +1,90 @@
1 +const React = window.React;
2 +
3 +const CIRCLE_SIZE = 80;
4 +
5 +class DragBox extends React.Component {
6 + state = {
7 + hasCapture: false,
8 + circleLeft: 80,
9 + circleTop: 80,
10 + };
11 + isDragging = false;
12 + previousLeft = 0;
13 + previousTop = 0;
14 +
15 + onDown = event => {
16 + this.isDragging = true;
17 + event.target.setPointerCapture(event.pointerId);
18 +
19 + // We store the initial coordinates to be able to calculate the changes
20 + // later on.
21 + this.extractPositionDelta(event);
22 + };
23 +
24 + onMove = event => {
25 + if (!this.isDragging) {
26 + return;
27 + }
28 + const {left, top} = this.extractPositionDelta(event);
29 +
30 + this.setState(({circleLeft, circleTop}) => ({
31 + circleLeft: circleLeft + left,
32 + circleTop: circleTop + top,
33 + }));
34 + };
35 +
36 + onUp = event => (this.isDragging = false);
37 + onGotCapture = event => this.setState({hasCapture: true});
38 + onLostCapture = event => this.setState({hasCapture: false});
39 +
40 + extractPositionDelta = event => {
41 + const left = event.pageX;
42 + const top = event.pageY;
43 + const delta = {
44 + left: left - this.previousLeft,
45 + top: top - this.previousTop,
46 + };
47 + this.previousLeft = left;
48 + this.previousTop = top;
49 + return delta;
50 + };
51 +
52 + render() {
53 + const {hasCapture, circleLeft, circleTop} = this.state;
54 +
55 + const boxStyle = {
56 + border: '1px solid #d9d9d9',
57 + margin: '10px 0 20px',
58 + minHeight: 400,
59 + width: '100%',
60 + position: 'relative',
61 + };
62 +
63 + const circleStyle = {
64 + width: CIRCLE_SIZE,
65 + height: CIRCLE_SIZE,
66 + borderRadius: CIRCLE_SIZE / 2,
67 + position: 'absolute',
68 + left: circleLeft,
69 + top: circleTop,
70 + backgroundColor: hasCapture ? 'blue' : 'green',
71 + touchAction: 'none',
72 + };
73 +
74 + return (
75 + <div style={boxStyle}>
76 + <div
77 + style={circleStyle}
78 + onPointerDown={this.onDown}
79 + onPointerMove={this.onMove}
80 + onPointerUp={this.onUp}
81 + onPointerCancel={this.onUp}
82 + onGotPointerCapture={this.onGotCapture}
83 + onLostPointerCapture={this.onLostCapture}
84 + />
85 + </div>
86 + );
87 + }
88 +}
89 +
90 +export default DragBox;
fixtures/dom/src/components/fixtures/pointer-events/drag.js new
+25
@@ -0,0 +1,25 @@
1 +import TestCase from '../../TestCase';
2 +import DragBox from './drag-box';
3 +
4 +const React = window.React;
5 +
6 +class Drag extends React.Component {
7 + render() {
8 + return (
9 + <TestCase title="Drag" description="">
10 + <TestCase.Steps>
11 + <li>Drag the circle below with any pointer tool</li>
12 + </TestCase.Steps>
13 +
14 + <TestCase.ExpectedResult>
15 + While dragging, the circle must have turn blue to indicate that a
16 + pointer capture was received.
17 + </TestCase.ExpectedResult>
18 +
19 + <DragBox />
20 + </TestCase>
21 + );
22 + }
23 +}
24 +
25 +export default Drag;
fixtures/dom/src/components/fixtures/pointer-events/hover-box.js new
+34
@@ -0,0 +1,34 @@
1 +const React = window.React;
2 +
3 +class DrawBox extends React.Component {
4 + render() {
5 + const boxStyle = {
6 + border: '1px solid #d9d9d9',
7 + margin: '10px 0 20px',
8 + padding: '20px 20px',
9 + touchAction: 'none',
10 + };
11 +
12 + const obstacleStyle = {
13 + border: '1px solid #d9d9d9',
14 + width: '25%',
15 + height: '200px',
16 + margin: '12.5%',
17 + display: 'inline-block',
18 + };
19 +
20 + return (
21 + <div
22 + style={boxStyle}
23 + onPointerOver={this.props.onOver}
24 + onPointerOut={this.props.onOut}
25 + onPointerEnter={this.props.onEnter}
26 + onPointerLeave={this.props.onLeave}>
27 + <div style={obstacleStyle} />
28 + <div style={obstacleStyle} />
29 + </div>
30 + );
31 + }
32 +}
33 +
34 +export default DrawBox;
fixtures/dom/src/components/fixtures/pointer-events/hover.js new
+51
@@ -0,0 +1,51 @@
1 +import TestCase from '../../TestCase';
2 +import HoverBox from './hover-box';
3 +
4 +const React = window.React;
5 +
6 +class Hover extends React.Component {
7 + state = {
8 + overs: 0,
9 + outs: 0,
10 + enters: 0,
11 + leaves: 0,
12 + };
13 +
14 + onOver = () => this.setState({overs: this.state.overs + 1});
15 + onOut = () => this.setState({outs: this.state.outs + 1});
16 + onEnter = () => this.setState({enters: this.state.enters + 1});
17 + onLeave = () => this.setState({leaves: this.state.leaves + 1});
18 +
19 + render() {
20 + const {overs, outs, enters, leaves} = this.state;
21 +
22 + return (
23 + <TestCase title="Hover" description="">
24 + <TestCase.Steps>
25 + <li>Hover over the above box and the obstacles</li>
26 + </TestCase.Steps>
27 +
28 + <TestCase.ExpectedResult>
29 + Overs and outs should increase when moving over the obstacles but
30 + enters and leaves should not.
31 + </TestCase.ExpectedResult>
32 +
33 + <HoverBox
34 + onOver={this.onOver}
35 + onOut={this.onOut}
36 + onEnter={this.onEnter}
37 + onLeave={this.onLeave}
38 + />
39 +
40 + <p>
41 + Pointer Overs: <b>{overs}</b> <br />
42 + Pointer Outs: <b>{outs}</b> <br />
43 + Pointer Enters: <b>{enters}</b> <br />
44 + Pointer Leaves: <b>{leaves}</b> <br />
45 + </p>
46 + </TestCase>
47 + );
48 + }
49 +}
50 +
51 +export default Hover;
fixtures/dom/src/components/fixtures/pointer-events/index.js new
+20
@@ -0,0 +1,20 @@
1 +import FixtureSet from '../../FixtureSet';
2 +import Drag from './drag';
3 +import Hover from './hover';
4 +
5 +const React = window.React;
6 +
7 +class PointerEvents extends React.Component {
8 + render() {
9 + return (
10 + <FixtureSet
11 + title="Pointer Events"
12 + description="Pointer Events are not supported in every browser. The examples below might not work in every browser. To test pointer events, make sure to use Google Chrome, Firefox, Internet Explorer, or Edge.">
13 + <Drag />
14 + <Hover />
15 + </FixtureSet>
16 + );
17 + }
18 +}
19 +
20 +export default PointerEvents;
packages/react-dom/src/__tests__/__snapshots__/ReactTestUtils-test.js.snap
+10
@@ -35,6 +35,7 @@ Array [
35 "ended",
36 "error",
37 "focus",
38 + "gotPointerCapture",
39 "input",
40 "invalid",
41 "keyDown",
@@ -44,6 +45,7 @@ Array [
45 "loadStart",
46 "loadedData",
47 "loadedMetadata",
48 + "lostPointerCapture",
49 "mouseDown",
50 "mouseEnter",
51 "mouseLeave",
@@ -55,6 +57,14 @@ Array [
57 "pause",
58 "play",
59 "playing",
60 + "pointerCancel",
61 + "pointerDown",
62 + "pointerEnter",
63 + "pointerLeave",
64 + "pointerMove",
65 + "pointerOut",
66 + "pointerOver",
67 + "pointerUp",
68 "progress",
69 "rateChange",
70 "reset",
packages/react-dom/src/events/DOMTopLevelEventTypes.js
+26
@@ -72,6 +72,9 @@ export const TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
72 export const TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
73 export const TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
74 export const TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
75 +export const TOP_GOT_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType(
76 + 'gotpointercapture',
77 +);
78 export const TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
79 export const TOP_INVALID = unsafeCastStringToDOMTopLevelType('invalid');
80 export const TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
@@ -83,6 +86,9 @@ export const TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
86 export const TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType(
87 'loadedmetadata',
88 );
89 +export const TOP_LOST_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType(
90 + 'lostpointercapture',
91 +);
92 export const TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
93 export const TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
94 export const TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
@@ -92,6 +98,26 @@ export const TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
98 export const TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
99 export const TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
100 export const TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
101 +export const TOP_POINTER_CANCEL = unsafeCastStringToDOMTopLevelType(
102 + 'pointercancel',
103 +);
104 +export const TOP_POINTER_DOWN = unsafeCastStringToDOMTopLevelType(
105 + 'pointerdown',
106 +);
107 +export const TOP_POINTER_ENTER = unsafeCastStringToDOMTopLevelType(
108 + 'pointerenter',
109 +);
110 +export const TOP_POINTER_LEAVE = unsafeCastStringToDOMTopLevelType(
111 + 'pointerleave',
112 +);
113 +export const TOP_POINTER_MOVE = unsafeCastStringToDOMTopLevelType(
114 + 'pointermove',
115 +);
116 +export const TOP_POINTER_OUT = unsafeCastStringToDOMTopLevelType('pointerout');
117 +export const TOP_POINTER_OVER = unsafeCastStringToDOMTopLevelType(
118 + 'pointerover',
119 +);
120 +export const TOP_POINTER_UP = unsafeCastStringToDOMTopLevelType('pointerup');
121 export const TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
122 export const TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
123 export const TOP_RESET = unsafeCastStringToDOMTopLevelType('reset');
packages/react-dom/src/events/EnterLeaveEventPlugin.js
+48 -14
@@ -7,8 +7,14 @@
7
8 import {accumulateEnterLeaveDispatches} from 'events/EventPropagators';
9
10 -import {TOP_MOUSE_OUT, TOP_MOUSE_OVER} from './DOMTopLevelEventTypes';
10 +import {
11 + TOP_MOUSE_OUT,
12 + TOP_MOUSE_OVER,
13 + TOP_POINTER_OUT,
14 + TOP_POINTER_OVER,
15 +} from './DOMTopLevelEventTypes';
16 import SyntheticMouseEvent from './SyntheticMouseEvent';
17 +import SyntheticPointerEvent from './SyntheticPointerEvent';
18 import {
19 getClosestInstanceFromNode,
20 getNodeFromInstance,
@@ -23,6 +29,14 @@ const eventTypes = {
29 registrationName: 'onMouseLeave',
30 dependencies: [TOP_MOUSE_OUT, TOP_MOUSE_OVER],
31 },
32 + pointerEnter: {
33 + registrationName: 'onPointerEnter',
34 + dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER],
35 + },
36 + pointerLeave: {
37 + registrationName: 'onPointerLeave',
38 + dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER],
39 + },
40 };
41
42 const EnterLeaveEventPlugin = {
@@ -41,14 +55,17 @@ const EnterLeaveEventPlugin = {
55 nativeEvent,
56 nativeEventTarget,
57 ) {
44 - if (
45 - topLevelType === TOP_MOUSE_OVER &&
46 - (nativeEvent.relatedTarget || nativeEvent.fromElement)
47 - ) {
58 + const isOverEvent =
59 + topLevelType === TOP_MOUSE_OVER || topLevelType === TOP_POINTER_OVER;
60 + const isOutEvent =
61 + topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_POINTER_OUT;
62 +
63 + if (isOverEvent && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
64 return null;
65 }
50 - if (topLevelType !== TOP_MOUSE_OUT && topLevelType !== TOP_MOUSE_OVER) {
51 - // Must not be a mouse in or mouse out - ignoring.
66 +
67 + if (!isOutEvent && !isOverEvent) {
68 + // Must not be a mouse or pointer in or out - ignoring.
69 return null;
70 }
71
@@ -68,7 +85,7 @@ const EnterLeaveEventPlugin = {
85
86 let from;
87 let to;
71 - if (topLevelType === TOP_MOUSE_OUT) {
88 + if (isOutEvent) {
89 from = targetInst;
90 const related = nativeEvent.relatedTarget || nativeEvent.toElement;
91 to = related ? getClosestInstanceFromNode(related) : null;
@@ -83,26 +100,43 @@ const EnterLeaveEventPlugin = {
100 return null;
101 }
102
103 + let eventInterface, leaveEventType, enterEventType, eventTypePrefix;
104 +
105 + if (topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_MOUSE_OVER) {
106 + eventInterface = SyntheticMouseEvent;
107 + leaveEventType = eventTypes.mouseLeave;
108 + enterEventType = eventTypes.mouseEnter;
109 + eventTypePrefix = 'mouse';
110 + } else if (
111 + topLevelType === TOP_POINTER_OUT ||
112 + topLevelType === TOP_POINTER_OVER
113 + ) {
114 + eventInterface = SyntheticPointerEvent;
115 + leaveEventType = eventTypes.pointerLeave;
116 + enterEventType = eventTypes.pointerEnter;
117 + eventTypePrefix = 'pointer';
118 + }
119 +
120 const fromNode = from == null ? win : getNodeFromInstance(from);
121 const toNode = to == null ? win : getNodeFromInstance(to);
122
89 - const leave = SyntheticMouseEvent.getPooled(
90 - eventTypes.mouseLeave,
123 + const leave = eventInterface.getPooled(
124 + leaveEventType,
125 from,
126 nativeEvent,
127 nativeEventTarget,
128 );
95 - leave.type = 'mouseleave';
129 + leave.type = eventTypePrefix + 'leave';
130 leave.target = fromNode;
131 leave.relatedTarget = toNode;
132
99 - const enter = SyntheticMouseEvent.getPooled(
100 - eventTypes.mouseEnter,
133 + const enter = eventInterface.getPooled(
134 + enterEventType,
135 to,
136 nativeEvent,
137 nativeEventTarget,
138 );
105 - enter.type = 'mouseenter';
139 + enter.type = eventTypePrefix + 'enter';
140 enter.target = toNode;
141 enter.relatedTarget = fromNode;
142
packages/react-dom/src/events/SimpleEventPlugin.js
+19
@@ -29,6 +29,7 @@ import SyntheticClipboardEvent from './SyntheticClipboardEvent';
29 import SyntheticFocusEvent from './SyntheticFocusEvent';
30 import SyntheticKeyboardEvent from './SyntheticKeyboardEvent';
31 import SyntheticMouseEvent from './SyntheticMouseEvent';
32 +import SyntheticPointerEvent from './SyntheticPointerEvent';
33 import SyntheticDragEvent from './SyntheticDragEvent';
34 import SyntheticTouchEvent from './SyntheticTouchEvent';
35 import SyntheticTransitionEvent from './SyntheticTransitionEvent';
@@ -78,6 +79,9 @@ const interactiveEventTypeNames: Array<EventTuple> = [
79 [DOMTopLevelEventTypes.TOP_PASTE, 'paste'],
80 [DOMTopLevelEventTypes.TOP_PAUSE, 'pause'],
81 [DOMTopLevelEventTypes.TOP_PLAY, 'play'],
82 + [DOMTopLevelEventTypes.TOP_POINTER_CANCEL, 'pointerCancel'],
83 + [DOMTopLevelEventTypes.TOP_POINTER_DOWN, 'pointerDown'],
84 + [DOMTopLevelEventTypes.TOP_POINTER_UP, 'pointerUp'],
85 [DOMTopLevelEventTypes.TOP_RATE_CHANGE, 'rateChange'],
86 [DOMTopLevelEventTypes.TOP_RESET, 'reset'],
87 [DOMTopLevelEventTypes.TOP_SEEKED, 'seeked'],
@@ -104,14 +108,19 @@ const nonInteractiveEventTypeNames: Array<EventTuple> = [
108 [DOMTopLevelEventTypes.TOP_ENCRYPTED, 'encrypted'],
109 [DOMTopLevelEventTypes.TOP_ENDED, 'ended'],
110 [DOMTopLevelEventTypes.TOP_ERROR, 'error'],
111 + [DOMTopLevelEventTypes.TOP_GOT_POINTER_CAPTURE, 'gotPointerCapture'],
112 [DOMTopLevelEventTypes.TOP_LOAD, 'load'],
113 [DOMTopLevelEventTypes.TOP_LOADED_DATA, 'loadedData'],
114 [DOMTopLevelEventTypes.TOP_LOADED_METADATA, 'loadedMetadata'],
115 [DOMTopLevelEventTypes.TOP_LOAD_START, 'loadStart'],
116 + [DOMTopLevelEventTypes.TOP_LOST_POINTER_CAPTURE, 'lostPointerCapture'],
117 [DOMTopLevelEventTypes.TOP_MOUSE_MOVE, 'mouseMove'],
118 [DOMTopLevelEventTypes.TOP_MOUSE_OUT, 'mouseOut'],
119 [DOMTopLevelEventTypes.TOP_MOUSE_OVER, 'mouseOver'],
120 [DOMTopLevelEventTypes.TOP_PLAYING, 'playing'],
121 + [DOMTopLevelEventTypes.TOP_POINTER_MOVE, 'pointerMove'],
122 + [DOMTopLevelEventTypes.TOP_POINTER_OUT, 'pointerOut'],
123 + [DOMTopLevelEventTypes.TOP_POINTER_OVER, 'pointerOver'],
124 [DOMTopLevelEventTypes.TOP_PROGRESS, 'progress'],
125 [DOMTopLevelEventTypes.TOP_SCROLL, 'scroll'],
126 [DOMTopLevelEventTypes.TOP_SEEKING, 'seeking'],
@@ -282,6 +291,16 @@ const SimpleEventPlugin: PluginModule<MouseEvent> & {
291 case DOMTopLevelEventTypes.TOP_PASTE:
292 EventConstructor = SyntheticClipboardEvent;
293 break;
294 + case DOMTopLevelEventTypes.TOP_GOT_POINTER_CAPTURE:
295 + case DOMTopLevelEventTypes.TOP_LOST_POINTER_CAPTURE:
296 + case DOMTopLevelEventTypes.TOP_POINTER_CANCEL:
297 + case DOMTopLevelEventTypes.TOP_POINTER_DOWN:
298 + case DOMTopLevelEventTypes.TOP_POINTER_MOVE:
299 + case DOMTopLevelEventTypes.TOP_POINTER_OUT:
300 + case DOMTopLevelEventTypes.TOP_POINTER_OVER:
301 + case DOMTopLevelEventTypes.TOP_POINTER_UP:
302 + EventConstructor = SyntheticPointerEvent;
303 + break;
304 default:
305 if (__DEV__) {
306 if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) {
packages/react-dom/src/events/SyntheticPointerEvent.js new
+25
@@ -0,0 +1,25 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import SyntheticMouseEvent from './SyntheticMouseEvent';
9 +
10 +/**
11 + * @interface PointerEvent
12 + * @see http://www.w3.org/TR/pointerevents/
13 + */
14 +const SyntheticPointerEvent = SyntheticMouseEvent.extend({
15 + pointerId: null,
16 + width: null,
17 + height: null,
18 + pressure: null,
19 + tiltX: null,
20 + tiltY: null,
21 + pointerType: null,
22 + isPrimary: null,
23 +});
24 +
25 +export default SyntheticPointerEvent;
packages/react-dom/src/events/TapEventPlugin.js
+21 -3
@@ -15,6 +15,10 @@ import {
15 TOP_MOUSE_DOWN,
16 TOP_MOUSE_MOVE,
17 TOP_MOUSE_UP,
18 + TOP_POINTER_CANCEL,
19 + TOP_POINTER_DOWN,
20 + TOP_POINTER_UP,
21 + TOP_POINTER_MOVE,
22 TOP_TOUCH_CANCEL,
23 TOP_TOUCH_END,
24 TOP_TOUCH_MOVE,
@@ -23,14 +27,20 @@ import {
27 import SyntheticUIEvent from './SyntheticUIEvent';
28
29 function isStartish(topLevelType) {
26 - return topLevelType === TOP_MOUSE_DOWN || topLevelType === TOP_TOUCH_START;
30 + return (
31 + topLevelType === TOP_MOUSE_DOWN ||
32 + topLevelType === TOP_TOUCH_START ||
33 + topLevelType === TOP_POINTER_DOWN
34 + );
35 }
36
37 function isEndish(topLevelType) {
38 return (
39 topLevelType === TOP_MOUSE_UP ||
32 - topLevelType === TOP_TOUCH_END ||
33 - topLevelType === TOP_TOUCH_CANCEL
40 + topLevelType === TOP_POINTER_CANCEL ||
41 + topLevelType === TOP_POINTER_UP ||
42 + topLevelType === TOP_TOUCH_CANCEL ||
43 + topLevelType === TOP_TOUCH_END
44 );
45 }
46
@@ -102,8 +112,16 @@ const touchEvents = [
112 TOP_TOUCH_MOVE,
113 ];
114
115 +const pointerEvents = [
116 + TOP_POINTER_CANCEL,
117 + TOP_POINTER_DOWN,
118 + TOP_POINTER_MOVE,
119 + TOP_POINTER_UP,
120 +];
121 +
122 const dependencies = [TOP_MOUSE_DOWN, TOP_MOUSE_MOVE, TOP_MOUSE_UP].concat(
123 touchEvents,
124 + pointerEvents,
125 );
126
127 const eventTypes = {