-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathContextMenuWrapper.tsx
More file actions
122 lines (109 loc) · 3.41 KB
/
Copy pathContextMenuWrapper.tsx
File metadata and controls
122 lines (109 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* @author Timur Kuzhagaliyev <tim.kuzh@gmail.com>
* @copyright 2020
* @license MIT
*/
import React, { CSSProperties, useRef, useState } from 'react';
import { Nullable } from 'tsdef';
import { ContextMenuEvent } from './handlers';
import { ContextMenuEventContext, useInternalHandlers, useMenuPlacementStyle, useMenuToggleMethods } from './hooks';
export interface ContextMenuWrapperProps {
/**
* A unique ID that will be used to trigger this context menu from other components on the page. Global menus don't
* use IDs.
*/
id?: string;
/**
* Determines whether this context menu should replace the default context meu of your browser.
* If this is set, `id` is ignored.
*/
global?: boolean;
children?: React.ReactElement;
/**
* Callback that is triggered after the menu has appeared.
*/
onShow?: (event: ContextMenuEvent) => void;
/**
* Callback that is triggered after the menu has been dismissed.
*/
onHide?: () => void;
/**
* Makes context menu disappear when user clicks (or taps) anywhere inside of the menu.
*/
hideOnSelfClick?: boolean;
/**
* Makes context menu disappear when user clicks (or taps) anywhere outside of the menu.
*/
hideOnOutsideClick?: boolean;
/**
* Makes context menu disappear when user presses the Escape key.
*/
hideOnEscape?: boolean;
/**
* Makes context menu disappear when page is scrolled.
*/
hideOnScroll?: boolean;
/**
* Makes context menu disappear when window is resized.
*/
hideOnWindowResize?: boolean;
}
export const ContextMenuWrapper: React.FC<ContextMenuWrapperProps> = React.memo(
({
id,
global,
children,
onShow,
onHide,
hideOnSelfClick,
hideOnOutsideClick,
hideOnEscape,
hideOnScroll,
hideOnWindowResize,
}) => {
const wrapperRef = useRef() as React.MutableRefObject<HTMLInputElement>;
const [lastShowMenuEvent, setShowMenuEvent] = useState<Nullable<ContextMenuEvent>>(null);
const [placementStyle, setMenuPlacementStyle] = useState<Nullable<CSSProperties>>(null);
const [showMenu, hideMenu] = useMenuToggleMethods(lastShowMenuEvent, setShowMenuEvent, onShow, onHide);
useInternalHandlers(
wrapperRef,
lastShowMenuEvent,
showMenu,
hideMenu,
id,
global,
hideOnSelfClick,
hideOnOutsideClick,
hideOnEscape,
hideOnScroll,
hideOnWindowResize
);
useMenuPlacementStyle(wrapperRef, lastShowMenuEvent, setMenuPlacementStyle);
if (!lastShowMenuEvent) return null;
const style: CSSProperties = {
position: 'fixed',
zIndex: 999,
left: -9999,
top: -9999,
...placementStyle,
};
return (
<div ref={wrapperRef} style={style}>
<ContextMenuEventContext.Provider value={lastShowMenuEvent}>
{children}
</ContextMenuEventContext.Provider>
</div>
);
}
);
ContextMenuWrapper.defaultProps = {
id: undefined,
global: false,
onShow: undefined,
onHide: undefined,
hideOnSelfClick: true,
hideOnOutsideClick: true,
hideOnEscape: true,
hideOnScroll: true,
hideOnWindowResize: true,
};