@react-aria/interactions 3.20.0 → 3.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/import.mjs +81 -33
- package/dist/main.js +80 -31
- package/dist/main.js.map +1 -1
- package/dist/module.js +81 -33
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/DOMPropsContext.ts +1 -1
- package/src/index.ts +1 -0
- package/src/textSelection.ts +2 -2
- package/src/useFocusVisible.ts +103 -27
- package/src/usePress.ts +12 -13
package/dist/types.d.ts
CHANGED
|
@@ -69,6 +69,24 @@ export interface FocusVisibleResult {
|
|
|
69
69
|
/** Whether keyboard focus is visible globally. */
|
|
70
70
|
isFocusVisible: boolean;
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* EXPERIMENTAL
|
|
74
|
+
* Adds a window (i.e. iframe) to the list of windows that are being tracked for focus visible.
|
|
75
|
+
*
|
|
76
|
+
* Sometimes apps render portions of their tree into an iframe. In this case, we cannot accurately track if the focus
|
|
77
|
+
* is visible because we cannot see interactions inside the iframe. If you have this in your application's architecture,
|
|
78
|
+
* then this function will attach event listeners inside the iframe. You should call `addWindowFocusTracking` with an
|
|
79
|
+
* element from inside the window you wish to add. We'll retrieve the relevant elements based on that.
|
|
80
|
+
* Note, you do not need to call this for the default window, as we call it for you.
|
|
81
|
+
*
|
|
82
|
+
* When you are ready to stop listening, but you do not wish to unmount the iframe, you may call the cleanup function
|
|
83
|
+
* returned by `addWindowFocusTracking`. Otherwise, when you unmount the iframe, all listeners and state will be cleaned
|
|
84
|
+
* up automatically for you.
|
|
85
|
+
*
|
|
86
|
+
* @param element @default document.body - The element provided will be used to get the window to add.
|
|
87
|
+
* @returns A function to remove the event listeners and cleanup the state.
|
|
88
|
+
*/
|
|
89
|
+
export function addWindowFocusTracking(element?: HTMLElement | null): () => void;
|
|
72
90
|
/**
|
|
73
91
|
* If true, keyboard focus is visible.
|
|
74
92
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AEuBA,2BAA4B,SAAQ,WAAW;IAC7C,+FAA+F;IAC/F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,yEAAyE;IACzE,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC;AAED,+BAAgC,SAAQ,UAAU;IAChD,mCAAmC;IACnC,GAAG,CAAC,EAAE,UAAU,OAAO,CAAC,CAAA;CACzB;AAwBD;IACE,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,UAAU,EAAE,aAAa,CAAA;CAC1B;AA8CD;;;;GAIG;AACH,yBAAyB,KAAK,EAAE,cAAc,GAAG,WAAW,
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/textSelection.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/context.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/usePress.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/Pressable.tsx","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/PressResponder.tsx","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/utils.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useFocus.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useFocusVisible.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useFocusWithin.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useHover.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useInteractOutside.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/createEventHandler.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useKeyboard.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useMove.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useScrollWheel.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/useLongPress.ts","packages/@react-aria/interactions/src/packages/@react-aria/interactions/src/index.ts","packages/@react-aria/interactions/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {Pressable} from './Pressable';\nexport {PressResponder, ClearPressResponder} from './PressResponder';\nexport {useFocus} from './useFocus';\nexport {\n isFocusVisible,\n getInteractionModality,\n setInteractionModality,\n addWindowFocusTracking,\n useInteractionModality,\n useFocusVisible,\n useFocusVisibleListener\n} from './useFocusVisible';\nexport {useFocusWithin} from './useFocusWithin';\nexport {useHover} from './useHover';\nexport {useInteractOutside} from './useInteractOutside';\nexport {useKeyboard} from './useKeyboard';\nexport {useMove} from './useMove';\nexport {usePress} from './usePress';\nexport {useScrollWheel} from './useScrollWheel';\nexport {useLongPress} from './useLongPress';\n\nexport type {FocusProps, FocusResult} from './useFocus';\nexport type {FocusVisibleHandler, FocusVisibleProps, FocusVisibleResult, Modality} from './useFocusVisible';\nexport type {FocusWithinProps, FocusWithinResult} from './useFocusWithin';\nexport type {HoverProps, HoverResult} from './useHover';\nexport type {InteractOutsideProps} from './useInteractOutside';\nexport type {KeyboardProps, KeyboardResult} from './useKeyboard';\nexport type {PressProps, PressHookProps, PressResult} from './usePress';\nexport type {PressEvent, PressEvents, MoveStartEvent, MoveMoveEvent, MoveEndEvent, MoveEvents, HoverEvent, HoverEvents, FocusEvents, KeyboardEvents} from '@react-types/shared';\nexport type {MoveResult} from './useMove';\nexport type {LongPressProps, LongPressResult} from './useLongPress';\nexport type {ScrollWheelProps} from './useScrollWheel';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/interactions",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"url": "https://github.com/adobe/react-spectrum"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@react-aria/ssr": "^3.9.
|
|
26
|
-
"@react-aria/utils": "^3.
|
|
25
|
+
"@react-aria/ssr": "^3.9.1",
|
|
26
|
+
"@react-aria/utils": "^3.23.1",
|
|
27
27
|
"@react-types/shared": "^3.22.0",
|
|
28
28
|
"@swc/helpers": "^0.5.0"
|
|
29
29
|
},
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "f040ff62678e6a31375b96c05396df0bae660350"
|
|
37
37
|
}
|
package/src/DOMPropsContext.ts
CHANGED
|
@@ -20,7 +20,7 @@ interface DOMPropsResponderProps extends DOMAttributes {
|
|
|
20
20
|
|
|
21
21
|
interface IDOMPropsResponderContext extends DOMAttributes {
|
|
22
22
|
register(): void,
|
|
23
|
-
ref?: MutableRefObject<Element>
|
|
23
|
+
ref?: MutableRefObject<Element | null>
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const DOMPropsResponderContext = React.createContext<IDOMPropsResponderContext | null>(null);
|
package/src/index.ts
CHANGED
package/src/textSelection.ts
CHANGED
|
@@ -84,9 +84,9 @@ export function restoreTextSelection(target?: Element) {
|
|
|
84
84
|
// If not iOS, restore the target's original user-select if any
|
|
85
85
|
// Ignore state since it doesn't apply for non iOS
|
|
86
86
|
if (target && modifiedElementMap.has(target)) {
|
|
87
|
-
let targetOldUserSelect = modifiedElementMap.get(target);
|
|
87
|
+
let targetOldUserSelect = modifiedElementMap.get(target) as string;
|
|
88
88
|
|
|
89
|
-
if (target.style.userSelect === 'none'
|
|
89
|
+
if (target.style.userSelect === 'none') {
|
|
90
90
|
target.style.userSelect = targetOldUserSelect;
|
|
91
91
|
}
|
|
92
92
|
|
package/src/useFocusVisible.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// NOTICE file in the root directory of this source tree.
|
|
16
16
|
// See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
|
|
17
17
|
|
|
18
|
-
import {isMac, isVirtualClick} from '@react-aria/utils';
|
|
18
|
+
import {getOwnerDocument, getOwnerWindow, isMac, isVirtualClick} from '@react-aria/utils';
|
|
19
19
|
import {useEffect, useState} from 'react';
|
|
20
20
|
import {useIsSSR} from '@react-aria/ssr';
|
|
21
21
|
|
|
@@ -37,7 +37,10 @@ export interface FocusVisibleResult {
|
|
|
37
37
|
|
|
38
38
|
let currentModality: null | Modality = null;
|
|
39
39
|
let changeHandlers = new Set<Handler>();
|
|
40
|
-
|
|
40
|
+
interface GlobalListenerData {
|
|
41
|
+
focus: () => void
|
|
42
|
+
}
|
|
43
|
+
export let hasSetupGlobalListeners = new Map<Window, GlobalListenerData>(); // We use a map here to support setting event listeners across multiple document objects.
|
|
41
44
|
let hasEventBeforeFocus = false;
|
|
42
45
|
let hasBlurredWindowRecently = false;
|
|
43
46
|
|
|
@@ -114,49 +117,117 @@ function handleWindowBlur() {
|
|
|
114
117
|
/**
|
|
115
118
|
* Setup global event listeners to control when keyboard focus style should be visible.
|
|
116
119
|
*/
|
|
117
|
-
function setupGlobalFocusEvents() {
|
|
118
|
-
if (typeof window === 'undefined' || hasSetupGlobalListeners) {
|
|
120
|
+
function setupGlobalFocusEvents(element?: HTMLElement | null) {
|
|
121
|
+
if (typeof window === 'undefined' || hasSetupGlobalListeners.get(getOwnerWindow(element))) {
|
|
119
122
|
return;
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
const windowObject = getOwnerWindow(element);
|
|
126
|
+
const documentObject = getOwnerDocument(element);
|
|
127
|
+
|
|
122
128
|
// Programmatic focus() calls shouldn't affect the current input modality.
|
|
123
129
|
// However, we need to detect other cases when a focus event occurs without
|
|
124
130
|
// a preceding user event (e.g. screen reader focus). Overriding the focus
|
|
125
131
|
// method on HTMLElement.prototype is a bit hacky, but works.
|
|
126
|
-
let focus = HTMLElement.prototype.focus;
|
|
127
|
-
HTMLElement.prototype.focus = function () {
|
|
132
|
+
let focus = windowObject.HTMLElement.prototype.focus;
|
|
133
|
+
windowObject.HTMLElement.prototype.focus = function () {
|
|
128
134
|
hasEventBeforeFocus = true;
|
|
129
135
|
focus.apply(this, arguments as unknown as [options?: FocusOptions | undefined]);
|
|
130
136
|
};
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
documentObject.addEventListener('keydown', handleKeyboardEvent, true);
|
|
139
|
+
documentObject.addEventListener('keyup', handleKeyboardEvent, true);
|
|
140
|
+
documentObject.addEventListener('click', handleClickEvent, true);
|
|
135
141
|
|
|
136
142
|
// Register focus events on the window so they are sure to happen
|
|
137
143
|
// before React's event listeners (registered on the document).
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
windowObject.addEventListener('focus', handleFocusEvent, true);
|
|
145
|
+
windowObject.addEventListener('blur', handleWindowBlur, false);
|
|
140
146
|
|
|
141
147
|
if (typeof PointerEvent !== 'undefined') {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
documentObject.addEventListener('pointerdown', handlePointerEvent, true);
|
|
149
|
+
documentObject.addEventListener('pointermove', handlePointerEvent, true);
|
|
150
|
+
documentObject.addEventListener('pointerup', handlePointerEvent, true);
|
|
145
151
|
} else {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
documentObject.addEventListener('mousedown', handlePointerEvent, true);
|
|
153
|
+
documentObject.addEventListener('mousemove', handlePointerEvent, true);
|
|
154
|
+
documentObject.addEventListener('mouseup', handlePointerEvent, true);
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
|
|
157
|
+
// Add unmount handler
|
|
158
|
+
windowObject.addEventListener('beforeunload', () => {
|
|
159
|
+
tearDownWindowFocusTracking(element);
|
|
160
|
+
}, {once: true});
|
|
161
|
+
|
|
162
|
+
hasSetupGlobalListeners.set(windowObject, {focus});
|
|
152
163
|
}
|
|
153
164
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
const tearDownWindowFocusTracking = (element, loadListener?: () => void) => {
|
|
166
|
+
const windowObject = getOwnerWindow(element);
|
|
167
|
+
const documentObject = getOwnerDocument(element);
|
|
168
|
+
if (loadListener) {
|
|
169
|
+
documentObject.removeEventListener('DOMContentLoaded', loadListener);
|
|
170
|
+
}
|
|
171
|
+
if (!hasSetupGlobalListeners.has(windowObject)) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
windowObject.HTMLElement.prototype.focus = hasSetupGlobalListeners.get(windowObject)!.focus;
|
|
175
|
+
|
|
176
|
+
documentObject.removeEventListener('keydown', handleKeyboardEvent, true);
|
|
177
|
+
documentObject.removeEventListener('keyup', handleKeyboardEvent, true);
|
|
178
|
+
documentObject.removeEventListener('click', handleClickEvent, true);
|
|
179
|
+
windowObject.removeEventListener('focus', handleFocusEvent, true);
|
|
180
|
+
windowObject.removeEventListener('blur', handleWindowBlur, false);
|
|
181
|
+
|
|
182
|
+
if (typeof PointerEvent !== 'undefined') {
|
|
183
|
+
documentObject.removeEventListener('pointerdown', handlePointerEvent, true);
|
|
184
|
+
documentObject.removeEventListener('pointermove', handlePointerEvent, true);
|
|
185
|
+
documentObject.removeEventListener('pointerup', handlePointerEvent, true);
|
|
157
186
|
} else {
|
|
158
|
-
|
|
187
|
+
documentObject.removeEventListener('mousedown', handlePointerEvent, true);
|
|
188
|
+
documentObject.removeEventListener('mousemove', handlePointerEvent, true);
|
|
189
|
+
documentObject.removeEventListener('mouseup', handlePointerEvent, true);
|
|
159
190
|
}
|
|
191
|
+
|
|
192
|
+
hasSetupGlobalListeners.delete(windowObject);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* EXPERIMENTAL
|
|
197
|
+
* Adds a window (i.e. iframe) to the list of windows that are being tracked for focus visible.
|
|
198
|
+
*
|
|
199
|
+
* Sometimes apps render portions of their tree into an iframe. In this case, we cannot accurately track if the focus
|
|
200
|
+
* is visible because we cannot see interactions inside the iframe. If you have this in your application's architecture,
|
|
201
|
+
* then this function will attach event listeners inside the iframe. You should call `addWindowFocusTracking` with an
|
|
202
|
+
* element from inside the window you wish to add. We'll retrieve the relevant elements based on that.
|
|
203
|
+
* Note, you do not need to call this for the default window, as we call it for you.
|
|
204
|
+
*
|
|
205
|
+
* When you are ready to stop listening, but you do not wish to unmount the iframe, you may call the cleanup function
|
|
206
|
+
* returned by `addWindowFocusTracking`. Otherwise, when you unmount the iframe, all listeners and state will be cleaned
|
|
207
|
+
* up automatically for you.
|
|
208
|
+
*
|
|
209
|
+
* @param element @default document.body - The element provided will be used to get the window to add.
|
|
210
|
+
* @returns A function to remove the event listeners and cleanup the state.
|
|
211
|
+
*/
|
|
212
|
+
export function addWindowFocusTracking(element?: HTMLElement | null): () => void {
|
|
213
|
+
const documentObject = getOwnerDocument(element);
|
|
214
|
+
let loadListener;
|
|
215
|
+
if (documentObject.readyState !== 'loading') {
|
|
216
|
+
setupGlobalFocusEvents(element);
|
|
217
|
+
} else {
|
|
218
|
+
loadListener = () => {
|
|
219
|
+
setupGlobalFocusEvents(element);
|
|
220
|
+
};
|
|
221
|
+
documentObject.addEventListener('DOMContentLoaded', loadListener);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return () => tearDownWindowFocusTracking(element, loadListener);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Server-side rendering does not have the document object defined
|
|
228
|
+
// eslint-disable-next-line no-restricted-globals
|
|
229
|
+
if (typeof document !== 'undefined') {
|
|
230
|
+
addWindowFocusTracking();
|
|
160
231
|
}
|
|
161
232
|
|
|
162
233
|
/**
|
|
@@ -213,11 +284,16 @@ const nonTextInputTypes = new Set([
|
|
|
213
284
|
* focus visible style can be properly set.
|
|
214
285
|
*/
|
|
215
286
|
function isKeyboardFocusEvent(isTextInput: boolean, modality: Modality, e: HandlerEvent) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
287
|
+
const IHTMLInputElement = typeof window !== 'undefined' ? getOwnerWindow(e?.target as Element).HTMLInputElement : HTMLInputElement;
|
|
288
|
+
const IHTMLTextAreaElement = typeof window !== 'undefined' ? getOwnerWindow(e?.target as Element).HTMLTextAreaElement : HTMLTextAreaElement;
|
|
289
|
+
const IHTMLElement = typeof window !== 'undefined' ? getOwnerWindow(e?.target as Element).HTMLElement : HTMLElement;
|
|
290
|
+
const IKeyboardEvent = typeof window !== 'undefined' ? getOwnerWindow(e?.target as Element).KeyboardEvent : KeyboardEvent;
|
|
291
|
+
|
|
292
|
+
isTextInput = isTextInput ||
|
|
293
|
+
(e?.target instanceof IHTMLInputElement && !nonTextInputTypes.has(e?.target?.type)) ||
|
|
294
|
+
e?.target instanceof IHTMLTextAreaElement ||
|
|
295
|
+
(e?.target instanceof IHTMLElement && e?.target.isContentEditable);
|
|
296
|
+
return !(isTextInput && modality === 'keyboard' && e instanceof IKeyboardEvent && !FOCUS_VISIBLE_INPUT_KEYS[e.key]);
|
|
221
297
|
}
|
|
222
298
|
|
|
223
299
|
/**
|
package/src/usePress.ts
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
// NOTICE file in the root directory of this source tree.
|
|
16
16
|
// See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
|
|
17
17
|
|
|
18
|
+
import {chain, focusWithoutScrolling, getOwnerDocument, getOwnerWindow, isMac, isVirtualClick, isVirtualPointerEvent, mergeProps, openLink, useEffectEvent, useGlobalListeners, useSyncRef} from '@react-aria/utils';
|
|
18
19
|
import {disableTextSelection, restoreTextSelection} from './textSelection';
|
|
19
20
|
import {DOMAttributes, FocusableElement, PressEvent as IPressEvent, PointerType, PressEvents} from '@react-types/shared';
|
|
20
|
-
import {focusWithoutScrolling, getOwnerDocument, getOwnerWindow, isMac, isVirtualClick, isVirtualPointerEvent, mergeProps, openLink, useEffectEvent, useGlobalListeners, useSyncRef} from '@react-aria/utils';
|
|
21
21
|
import {PressResponderContext} from './context';
|
|
22
22
|
import {RefObject, useContext, useEffect, useMemo, useRef, useState} from 'react';
|
|
23
23
|
|
|
@@ -270,8 +270,16 @@ export function usePress(props: PressHookProps): PressResult {
|
|
|
270
270
|
shouldStopPropagation = triggerPressStart(e, 'keyboard');
|
|
271
271
|
|
|
272
272
|
// Focus may move before the key up event, so register the event on the document
|
|
273
|
-
// instead of the same element where the key down event occurred.
|
|
274
|
-
|
|
273
|
+
// instead of the same element where the key down event occurred. Make it capturing so that it will trigger
|
|
274
|
+
// before stopPropagation from useKeyboard on a child element may happen and thus we can still call triggerPress for the parent element.
|
|
275
|
+
let originalTarget = e.currentTarget;
|
|
276
|
+
let pressUp = (e) => {
|
|
277
|
+
if (isValidKeyboardEvent(e, originalTarget) && !e.repeat && originalTarget.contains(e.target as Element) && state.target) {
|
|
278
|
+
triggerPressUp(createEvent(state.target, e), 'keyboard');
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
addGlobalListener(getOwnerDocument(e.currentTarget), 'keyup', chain(pressUp, onKeyUp), true);
|
|
275
283
|
}
|
|
276
284
|
|
|
277
285
|
if (shouldStopPropagation) {
|
|
@@ -292,11 +300,6 @@ export function usePress(props: PressHookProps): PressResult {
|
|
|
292
300
|
state.metaKeyEvents = new Map();
|
|
293
301
|
}
|
|
294
302
|
},
|
|
295
|
-
onKeyUp(e) {
|
|
296
|
-
if (isValidKeyboardEvent(e.nativeEvent, e.currentTarget) && !e.repeat && e.currentTarget.contains(e.target as Element) && state.target) {
|
|
297
|
-
triggerPressUp(createEvent(state.target, e), 'keyboard');
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
303
|
onClick(e) {
|
|
301
304
|
if (e && !e.currentTarget.contains(e.target as Element)) {
|
|
302
305
|
return;
|
|
@@ -338,13 +341,9 @@ export function usePress(props: PressHookProps): PressResult {
|
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
let target = e.target as Element;
|
|
341
|
-
|
|
344
|
+
triggerPressEnd(createEvent(state.target, e), 'keyboard', state.target.contains(target));
|
|
342
345
|
removeAllGlobalListeners();
|
|
343
346
|
|
|
344
|
-
if (shouldStopPropagation) {
|
|
345
|
-
e.stopPropagation();
|
|
346
|
-
}
|
|
347
|
-
|
|
348
347
|
// If a link was triggered with a key other than Enter, open the URL ourselves.
|
|
349
348
|
// This means the link has a role override, and the default browser behavior
|
|
350
349
|
// only applies when using the Enter key.
|