Skip to content

Commit a41b217

Browse files
authored
Add additional event API responder surfaces (#15248)
* Add rest of event modules + small fixes
1 parent 700f17b commit a41b217

File tree

14 files changed

+659
-14
lines changed

14 files changed

+659
-14
lines changed

packages/events/EventTypes.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import SyntheticEvent from 'events/SyntheticEvent';
1111
import type {AnyNativeEvent} from 'events/PluginModuleType';
12+
import type {ReactEventResponderEventType} from 'shared/ReactTypes';
1213

1314
export type EventResponderContext = {
1415
event: AnyNativeEvent,
@@ -30,8 +31,12 @@ export type EventResponderContext = {
3031
isTargetOwned: EventTarget => boolean,
3132
isTargetWithinEventComponent: EventTarget => boolean,
3233
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
33-
addRootEventTypes: (rootEventTypes: Array<string>) => void,
34-
removeRootEventTypes: (rootEventTypes: Array<string>) => void,
34+
addRootEventTypes: (
35+
rootEventTypes: Array<ReactEventResponderEventType>,
36+
) => void,
37+
removeRootEventTypes: (
38+
rootEventTypes: Array<ReactEventResponderEventType>,
39+
) => void,
3540
requestOwnership: (target: EventTarget | null) => boolean,
3641
releaseOwnership: (target: EventTarget | null) => boolean,
3742
};

packages/react-dom/src/events/DOMEventResponderSystem.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const targetEventTypeCached: Map<
4444
Array<ReactEventResponderEventType>,
4545
Set<DOMTopLevelEventType>,
4646
> = new Map();
47+
const targetOwnership: Map<EventTarget, Fiber> = new Map();
4748

4849
type EventListener = (event: SyntheticEvent) => void;
4950

@@ -204,16 +205,37 @@ DOMEventResponderContext.prototype.isPositionWithinTouchHitTarget = function() {
204205
// TODO
205206
};
206207

207-
DOMEventResponderContext.prototype.isTargetOwned = function() {
208-
// TODO
208+
DOMEventResponderContext.prototype.isTargetOwned = function(
209+
targetElement: Element | Node,
210+
): boolean {
211+
const targetDoc = targetElement.ownerDocument;
212+
return targetOwnership.has(targetDoc);
209213
};
210214

211-
DOMEventResponderContext.prototype.requestOwnership = function() {
212-
// TODO
215+
DOMEventResponderContext.prototype.requestOwnership = function(
216+
targetElement: Element | Node,
217+
): boolean {
218+
const targetDoc = targetElement.ownerDocument;
219+
if (targetOwnership.has(targetDoc)) {
220+
return false;
221+
}
222+
targetOwnership.set(targetDoc, this._fiber);
223+
return true;
213224
};
214225

215-
DOMEventResponderContext.prototype.releaseOwnership = function() {
216-
// TODO
226+
DOMEventResponderContext.prototype.releaseOwnership = function(
227+
targetElement: Element | Node,
228+
): boolean {
229+
const targetDoc = targetElement.ownerDocument;
230+
if (!targetOwnership.has(targetDoc)) {
231+
return false;
232+
}
233+
const owner = targetOwnership.get(targetDoc);
234+
if (owner === this._fiber || owner === this._fiber.alternate) {
235+
targetOwnership.delete(targetDoc);
236+
return true;
237+
}
238+
return false;
217239
};
218240

219241
function getTargetEventTypes(

packages/react-events/drag.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
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+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
const Drag = require('./src/Drag');
13+
14+
module.exports = Drag.default || Drag;

packages/react-events/focus.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
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+
* @flow
8+
*/
9+
10+
'use strict';
11+
12+
const Focus = require('./src/Focus');
13+
14+
module.exports = Focus.default || Focus;

packages/react-events/npm/drag.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('./cjs/react-events-drag.production.min.js');
5+
} else {
6+
module.exports = require('./cjs/react-events-drag.development.js');
7+
}

packages/react-events/npm/focus.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('./cjs/react-events-focus.production.min.js');
5+
} else {
6+
module.exports = require('./cjs/react-events-focus.development.js');
7+
}

packages/react-events/npm/swipe.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
if (process.env.NODE_ENV === 'production') {
4+
module.exports = require('./cjs/react-events-swipe.production.min.js');
5+
} else {
6+
module.exports = require('./cjs/react-events-swipe.development.js');
7+
}

packages/react-events/package.json

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
"LICENSE",
1313
"README.md",
1414
"press.js",
15+
"hover.js",
16+
"focus.js",
17+
"swipe.js",
18+
"drag.js",
19+
"index.js",
1520
"build-info.json",
1621
"cjs/",
1722
"umd/"

packages/react-events/src/Drag.js

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
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+
* @flow
8+
*/
9+
10+
import type {EventResponderContext} from 'events/EventTypes';
11+
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12+
13+
const targetEventTypes = ['pointerdown', 'pointercancel'];
14+
const rootEventTypes = ['pointerup', {name: 'pointermove', passive: false}];
15+
16+
type DragState = {
17+
dragTarget: null | EventTarget,
18+
isPointerDown: boolean,
19+
isDragging: boolean,
20+
startX: number,
21+
startY: number,
22+
x: number,
23+
y: number,
24+
};
25+
26+
// In the case we don't have PointerEvents (Safari), we listen to touch events
27+
// too
28+
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
29+
targetEventTypes.push('touchstart', 'touchend', 'mousedown', 'touchcancel');
30+
rootEventTypes.push('mouseup', 'mousemove', {
31+
name: 'touchmove',
32+
passive: false,
33+
});
34+
}
35+
36+
function dispatchDragEvent(
37+
context: EventResponderContext,
38+
name: string,
39+
listener: (e: Object) => void,
40+
state: DragState,
41+
discrete: boolean,
42+
eventData?: {
43+
diffX: number,
44+
diffY: number,
45+
},
46+
): void {
47+
context.dispatchEvent(name, listener, state.dragTarget, discrete, eventData);
48+
}
49+
50+
const DragResponder = {
51+
targetEventTypes,
52+
createInitialState(): DragState {
53+
return {
54+
dragTarget: null,
55+
isPointerDown: false,
56+
isDragging: false,
57+
startX: 0,
58+
startY: 0,
59+
x: 0,
60+
y: 0,
61+
};
62+
},
63+
handleEvent(
64+
context: EventResponderContext,
65+
props: Object,
66+
state: DragState,
67+
): void {
68+
const {eventTarget, eventType, event} = context;
69+
70+
switch (eventType) {
71+
case 'touchstart':
72+
case 'mousedown':
73+
case 'pointerdown': {
74+
if (!state.isDragging) {
75+
const obj =
76+
eventType === 'touchstart' ? (event: any).changedTouches[0] : event;
77+
const x = (state.startX = (obj: any).screenX);
78+
const y = (state.startY = (obj: any).screenY);
79+
state.x = x;
80+
state.y = y;
81+
state.dragTarget = eventTarget;
82+
state.isPointerDown = true;
83+
context.addRootEventTypes(rootEventTypes);
84+
}
85+
break;
86+
}
87+
case 'touchmove':
88+
case 'mousemove':
89+
case 'pointermove': {
90+
if (context.isPassive()) {
91+
return;
92+
}
93+
if (state.isPointerDown) {
94+
const obj =
95+
eventType === 'touchmove' ? (event: any).changedTouches[0] : event;
96+
const x = (obj: any).screenX;
97+
const y = (obj: any).screenY;
98+
state.x = x;
99+
state.y = y;
100+
if (!state.isDragging && x !== state.startX && y !== state.startY) {
101+
let shouldEnableDragging = true;
102+
103+
if (
104+
props.onShouldClaimOwnership &&
105+
props.onShouldClaimOwnership()
106+
) {
107+
shouldEnableDragging = context.requestOwnership(state.dragTarget);
108+
}
109+
if (shouldEnableDragging) {
110+
state.isDragging = true;
111+
if (props.onDragChange) {
112+
const dragChangeEventListener = () => {
113+
props.onDragChange(true);
114+
};
115+
context.dispatchEvent(
116+
'dragchange',
117+
dragChangeEventListener,
118+
state.dragTarget,
119+
true,
120+
);
121+
}
122+
} else {
123+
state.dragTarget = null;
124+
state.isPointerDown = false;
125+
context.removeRootEventTypes(rootEventTypes);
126+
}
127+
} else {
128+
if (props.onDragMove) {
129+
const eventData = {
130+
diffX: x - state.startX,
131+
diffY: y - state.startY,
132+
};
133+
dispatchDragEvent(
134+
context,
135+
'dragmove',
136+
props.onDragMove,
137+
state,
138+
false,
139+
eventData,
140+
);
141+
}
142+
(event: any).preventDefault();
143+
}
144+
}
145+
break;
146+
}
147+
case 'pointercancel':
148+
case 'touchcancel':
149+
case 'touchend':
150+
case 'mouseup':
151+
case 'pointerup': {
152+
if (state.isDragging) {
153+
if (props.onShouldClaimOwnership) {
154+
context.releaseOwnership(state.dragTarget);
155+
}
156+
if (props.onDragEnd) {
157+
dispatchDragEvent(context, 'dragend', props.onDragEnd, state, true);
158+
}
159+
if (props.onDragChange) {
160+
const dragChangeEventListener = () => {
161+
props.onDragChange(false);
162+
};
163+
context.dispatchEvent(
164+
'dragchange',
165+
dragChangeEventListener,
166+
state.dragTarget,
167+
true,
168+
);
169+
}
170+
state.isDragging = false;
171+
}
172+
if (state.isPointerDown) {
173+
state.dragTarget = null;
174+
state.isPointerDown = false;
175+
context.removeRootEventTypes(rootEventTypes);
176+
}
177+
break;
178+
}
179+
}
180+
},
181+
};
182+
183+
export default {
184+
$$typeof: REACT_EVENT_COMPONENT_TYPE,
185+
props: null,
186+
responder: DragResponder,
187+
};

0 commit comments

Comments
 (0)