Experimental Event API: Add Hover onUnmount support (#15394)
Dominic Gannaway committed
Apr 12, 2019 at 13:26 UTC
3438e5ce879883590f68370b9ad6448a8ffdfdc1
2 files changed
+59
-14
packages/react-events/src/Hover.js
+59
-13
@@ -24,6 +24,7 @@ type HoverProps = {
24
};
25
26
type HoverState = {
27
+ hoverTarget: null | Element | Document,
28
isActiveHovered: boolean,
29
isHovered: boolean,
30
isInHitSlop: boolean,
@@ -66,7 +67,6 @@ function createHoverEvent(
67
}
68
69
function dispatchHoverChangeEvent(
69
- event: ReactResponderEvent,
70
context: ReactResponderContext,
71
props: HoverProps,
72
state: HoverState,
@@ -75,7 +75,10 @@ function dispatchHoverChangeEvent(
75
const listener = () => {
76
props.onHoverChange(bool);
77
};
78
- const syntheticEvent = createHoverEvent('hoverchange', event.target);
78
+ const syntheticEvent = createHoverEvent(
79
+ 'hoverchange',
80
+ ((state.hoverTarget: any): Element | Document),
81
+ );
82
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
83
}
84
@@ -85,9 +88,14 @@ function dispatchHoverStartEvents(
88
props: HoverProps,
89
state: HoverState,
90
): void {
88
- const {nativeEvent, target} = event;
89
- if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
90
- return;
91
+ const target = state.hoverTarget;
92
+ if (event !== null) {
93
+ const {nativeEvent} = event;
94
+ if (
95
+ context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
96
+ ) {
97
+ return;
98
+ }
99
}
100
101
state.isHovered = true;
@@ -101,13 +109,16 @@ function dispatchHoverStartEvents(
109
state.isActiveHovered = true;
110
111
if (props.onHoverStart) {
104
- const syntheticEvent = createHoverEvent('hoverstart', target);
112
+ const syntheticEvent = createHoverEvent(
113
+ 'hoverstart',
114
+ ((target: any): Element | Document),
115
+ );
116
context.dispatchEvent(syntheticEvent, props.onHoverStart, {
117
discrete: true,
118
});
119
}
120
if (props.onHoverChange) {
110
- dispatchHoverChangeEvent(event, context, props, state);
121
+ dispatchHoverChangeEvent(context, props, state);
122
}
123
};
124
@@ -129,14 +140,19 @@ function dispatchHoverStartEvents(
140
}
141
142
function dispatchHoverEndEvents(
132
- event: ReactResponderEvent,
143
+ event: null | ReactResponderEvent,
144
context: ReactResponderContext,
145
props: HoverProps,
146
state: HoverState,
147
) {
137
- const {nativeEvent, target} = event;
138
- if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
139
- return;
148
+ const target = state.hoverTarget;
149
+ if (event !== null) {
150
+ const {nativeEvent} = event;
151
+ if (
152
+ context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
153
+ ) {
154
+ return;
155
+ }
156
}
157
158
state.isHovered = false;
@@ -150,11 +166,14 @@ function dispatchHoverEndEvents(
166
state.isActiveHovered = false;
167
168
if (props.onHoverEnd) {
153
- const syntheticEvent = createHoverEvent('hoverend', target);
169
+ const syntheticEvent = createHoverEvent(
170
+ 'hoverend',
171
+ ((target: any): Element | Document),
172
+ );
173
context.dispatchEvent(syntheticEvent, props.onHoverEnd, {discrete: true});
174
}
175
if (props.onHoverChange) {
157
- dispatchHoverChangeEvent(event, context, props, state);
176
+ dispatchHoverChangeEvent(context, props, state);
177
}
178
};
179
@@ -179,6 +198,16 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
198
return Math.max(min, maybeNumber != null ? maybeNumber : fallback);
199
}
200
201
+function unmountResponder(
202
+ context: ReactResponderContext,
203
+ props: HoverProps,
204
+ state: HoverState,
205
+): void {
206
+ if (state.isHovered) {
207
+ dispatchHoverEndEvents(null, context, props, state);
208
+ }
209
+}
210
+
211
const HoverResponder = {
212
targetEventTypes,
213
createInitialState() {
@@ -231,6 +260,7 @@ const HoverResponder = {
260
state.isInHitSlop = true;
261
return;
262
}
263
+ state.hoverTarget = target;
264
dispatchHoverStartEvents(event, context, props, state);
265
}
266
break;
@@ -241,6 +271,7 @@ const HoverResponder = {
271
dispatchHoverEndEvents(event, context, props, state);
272
}
273
state.isInHitSlop = false;
274
+ state.hoverTarget = null;
275
state.isTouched = false;
276
state.skipMouseAfterPointer = false;
277
break;
@@ -293,12 +324,27 @@ const HoverResponder = {
324
case 'pointercancel': {
325
if (state.isHovered && !state.isTouched) {
326
dispatchHoverEndEvents(event, context, props, state);
327
+ state.hoverTarget = null;
328
state.isTouched = false;
329
}
330
break;
331
}
332
}
333
},
334
+ onUnmount(
335
+ context: ReactResponderContext,
336
+ props: HoverProps,
337
+ state: HoverState,
338
+ ) {
339
+ unmountResponder(context, props, state);
340
+ },
341
+ onOwnershipChange(
342
+ context: ReactResponderContext,
343
+ props: HoverProps,
344
+ state: HoverState,
345
+ ) {
346
+ unmountResponder(context, props, state);
347
+ },
348
};
349
350
export default {
packages/react-events/src/Press.js
-1
@@ -699,7 +699,6 @@ const PressResponder = {
699
) {
700
unmountResponder(context, props, state);
701
},
702
- // TODO This method doesn't work as of yet
702
onOwnershipChange(
703
context: ReactResponderContext,
704
props: PressProps,