React events: add unmounting to Focus (#15396)
Nicolas Gallagher committed
Apr 12, 2019 at 04:23 UTC
805e7f87334551c559ab5162c701f82c105ad0b9
1 file changed
+65
-15
packages/react-events/src/Focus.js
+65
-15
@@ -22,6 +22,7 @@ type FocusProps = {
22
23
type FocusState = {
24
isFocused: boolean,
25
+ focusTarget: null | Element | Document,
26
};
27
28
type FocusEventType = 'focus' | 'blur' | 'focuschange';
@@ -47,54 +48,87 @@ function createFocusEvent(
48
}
49
50
function dispatchFocusInEvents(
50
- event: ReactResponderEvent,
51
+ event: null | ReactResponderEvent,
52
context: ReactResponderContext,
53
props: FocusProps,
54
+ state: FocusState,
55
) {
54
- const {nativeEvent, target} = event;
55
- if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
56
- return;
56
+ if (event != null) {
57
+ const {nativeEvent} = event;
58
+ if (
59
+ context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
60
+ ) {
61
+ return;
62
+ }
63
}
64
if (props.onFocus) {
59
- const syntheticEvent = createFocusEvent('focus', target);
65
+ const syntheticEvent = createFocusEvent(
66
+ 'focus',
67
+ ((state.focusTarget: any): Element | Document),
68
+ );
69
context.dispatchEvent(syntheticEvent, props.onFocus, {discrete: true});
70
}
71
if (props.onFocusChange) {
72
const listener = () => {
73
props.onFocusChange(true);
74
};
66
- const syntheticEvent = createFocusEvent('focuschange', target);
75
+ const syntheticEvent = createFocusEvent(
76
+ 'focuschange',
77
+ ((state.focusTarget: any): Element | Document),
78
+ );
79
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
80
}
81
}
82
83
function dispatchFocusOutEvents(
72
- event: ReactResponderEvent,
84
+ event: null | ReactResponderEvent,
85
context: ReactResponderContext,
86
props: FocusProps,
87
+ state: FocusState,
88
) {
76
- const {nativeEvent, target} = event;
77
- if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
78
- return;
89
+ if (event != null) {
90
+ const {nativeEvent} = event;
91
+ if (
92
+ context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)
93
+ ) {
94
+ return;
95
+ }
96
}
97
if (props.onBlur) {
81
- const syntheticEvent = createFocusEvent('blur', target);
98
+ const syntheticEvent = createFocusEvent(
99
+ 'blur',
100
+ ((state.focusTarget: any): Element | Document),
101
+ );
102
context.dispatchEvent(syntheticEvent, props.onBlur, {discrete: true});
103
}
104
if (props.onFocusChange) {
105
const listener = () => {
106
props.onFocusChange(false);
107
};
88
- const syntheticEvent = createFocusEvent('focuschange', target);
108
+ const syntheticEvent = createFocusEvent(
109
+ 'focuschange',
110
+ ((state.focusTarget: any): Element | Document),
111
+ );
112
context.dispatchEvent(syntheticEvent, listener, {discrete: true});
113
}
114
}
115
116
+function unmountResponder(
117
+ context: ReactResponderContext,
118
+ props: FocusProps,
119
+ state: FocusState,
120
+): void {
121
+ if (state.isFocused) {
122
+ dispatchFocusOutEvents(null, context, props, state);
123
+ }
124
+}
125
+
126
const FocusResponder = {
127
targetEventTypes,
128
createInitialState(): FocusState {
129
return {
130
isFocused: false,
131
+ focusTarget: null,
132
};
133
},
134
onEvent(
@@ -103,25 +137,41 @@ const FocusResponder = {
137
props: Object,
138
state: FocusState,
139
): void {
106
- const {type} = event;
140
+ const {type, target} = event;
141
142
switch (type) {
143
case 'focus': {
144
if (!state.isFocused && !context.hasOwnership()) {
111
- dispatchFocusInEvents(event, context, props);
145
+ state.focusTarget = target;
146
+ dispatchFocusInEvents(event, context, props, state);
147
state.isFocused = true;
148
}
149
break;
150
}
151
case 'blur': {
152
if (state.isFocused) {
118
- dispatchFocusOutEvents(event, context, props);
153
+ dispatchFocusOutEvents(event, context, props, state);
154
state.isFocused = false;
155
+ state.focusTarget = null;
156
}
157
break;
158
}
159
}
160
},
161
+ onUnmount(
162
+ context: ReactResponderContext,
163
+ props: FocusProps,
164
+ state: FocusState,
165
+ ) {
166
+ unmountResponder(context, props, state);
167
+ },
168
+ onOwnershipChange(
169
+ context: ReactResponderContext,
170
+ props: FocusProps,
171
+ state: FocusState,
172
+ ) {
173
+ unmountResponder(context, props, state);
174
+ },
175
};
176
177
export default {