Skip to content

Commit 23405c9

Browse files
authored
[react-events] Add ContextMenu responder (#16296)
A module for responding to contextmenu events. This functionality will be removed from the Press responder in the future.
1 parent 606f76b commit 23405c9

File tree

5 files changed

+414
-17
lines changed

5 files changed

+414
-17
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
module.exports = require('./src/dom/ContextMenu');

packages/react-events/package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
"files": [
1313
"LICENSE",
1414
"README.md",
15-
"press.js",
16-
"hover.js",
17-
"focus.js",
18-
"swipe.js",
15+
"context-menu.js",
1916
"drag.js",
20-
"scroll.js",
17+
"focus.js",
18+
"hover.js",
2119
"input.js",
2220
"keyboard.js",
21+
"press.js",
22+
"scroll.js",
23+
"swipe.js",
2324
"build-info.json",
2425
"cjs/",
2526
"umd/"
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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 {
11+
ReactDOMResponderEvent,
12+
ReactDOMResponderContext,
13+
PointerType,
14+
} from 'shared/ReactDOMTypes';
15+
import type {ReactEventResponderListener} from 'shared/ReactTypes';
16+
17+
import React from 'react';
18+
import {DiscreteEvent} from 'shared/ReactTypes';
19+
20+
type ContextMenuProps = {|
21+
disabled: boolean,
22+
onContextMenu: (e: ContextMenuEvent) => void,
23+
preventDefault: boolean,
24+
|};
25+
26+
type ContextMenuState = {
27+
pointerType: PointerType,
28+
};
29+
30+
type ContextMenuEvent = {|
31+
target: Element | Document,
32+
type: 'contextmenu',
33+
pointerType: PointerType,
34+
timeStamp: number,
35+
clientX: null | number,
36+
clientY: null | number,
37+
pageX: null | number,
38+
pageY: null | number,
39+
x: null | number,
40+
y: null | number,
41+
altKey: boolean,
42+
ctrlKey: boolean,
43+
metaKey: boolean,
44+
shiftKey: boolean,
45+
|};
46+
47+
const hasPointerEvents =
48+
typeof window !== 'undefined' && window.PointerEvent != null;
49+
50+
function dispatchContextMenuEvent(
51+
event: ReactDOMResponderEvent,
52+
context: ReactDOMResponderContext,
53+
props: ContextMenuProps,
54+
state: ContextMenuState,
55+
): void {
56+
const nativeEvent: any = event.nativeEvent;
57+
const target = event.target;
58+
const timeStamp = context.getTimeStamp();
59+
const pointerType = state.pointerType;
60+
61+
const gestureState = {
62+
altKey: nativeEvent.altKey,
63+
button: nativeEvent.button === 0 ? 'primary' : 'auxillary',
64+
ctrlKey: nativeEvent.altKey,
65+
metaKey: nativeEvent.altKey,
66+
pageX: nativeEvent.altKey,
67+
pageY: nativeEvent.altKey,
68+
pointerType,
69+
shiftKey: nativeEvent.altKey,
70+
target,
71+
timeStamp,
72+
type: 'contextmenu',
73+
x: nativeEvent.clientX,
74+
y: nativeEvent.clientY,
75+
};
76+
77+
context.dispatchEvent(gestureState, props.onContextMenu, DiscreteEvent);
78+
}
79+
80+
const contextMenuImpl = {
81+
targetEventTypes: hasPointerEvents
82+
? ['contextmenu_active', 'pointerdown']
83+
: ['contextmenu_active', 'touchstart', 'mousedown'],
84+
getInitialState(): ContextMenuState {
85+
return {
86+
pointerType: '',
87+
};
88+
},
89+
onEvent(
90+
event: ReactDOMResponderEvent,
91+
context: ReactDOMResponderContext,
92+
props: ContextMenuProps,
93+
state: ContextMenuState,
94+
): void {
95+
const nativeEvent: any = event.nativeEvent;
96+
const pointerType = event.pointerType;
97+
const type = event.type;
98+
99+
if (props.disabled) {
100+
return;
101+
}
102+
103+
if (type === 'contextmenu') {
104+
const onContextMenu = props.onContextMenu;
105+
const preventDefault = props.preventDefault;
106+
if (preventDefault !== false && !nativeEvent.defaultPrevented) {
107+
nativeEvent.preventDefault();
108+
}
109+
if (typeof onContextMenu === 'function') {
110+
dispatchContextMenuEvent(event, context, props, state);
111+
}
112+
state.pointerType = '';
113+
} else {
114+
state.pointerType = pointerType;
115+
}
116+
},
117+
};
118+
119+
export const ContextMenuResponder = React.unstable_createResponder(
120+
'ContextMenu',
121+
contextMenuImpl,
122+
);
123+
124+
export function useContextMenuResponder(
125+
props: ContextMenuProps,
126+
): ReactEventResponderListener<any, any> {
127+
return React.unstable_useResponder(ContextMenuResponder, props);
128+
}

0 commit comments

Comments
 (0)