forked from pterodactyl/panel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDropdownMenu.tsx
More file actions
106 lines (86 loc) · 3.11 KB
/
DropdownMenu.tsx
File metadata and controls
106 lines (86 loc) · 3.11 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
import React, { createRef } from 'react';
import styled from 'styled-components/macro';
import tw from 'twin.macro';
import Fade from '@/components/elements/Fade';
interface Props {
children: React.ReactNode;
renderToggle: (onClick: (e: React.MouseEvent<any, MouseEvent>) => void) => React.ReactChild;
}
export const DropdownButtonRow = styled.button<{ danger?: boolean }>`
${tw`p-2 flex items-center rounded w-full text-neutral-500`};
transition: 150ms all ease;
&:hover {
${props => props.danger ? tw`text-red-700 bg-red-100` : tw`text-neutral-700 bg-neutral-100`};
}
`;
interface State {
posX: number;
visible: boolean;
}
class DropdownMenu extends React.PureComponent<Props, State> {
menu = createRef<HTMLDivElement>();
state: State = {
posX: 0,
visible: false,
};
componentWillUnmount () {
this.removeListeners();
}
componentDidUpdate (prevProps: Readonly<Props>, prevState: Readonly<State>) {
const menu = this.menu.current;
if (this.state.visible && !prevState.visible && menu) {
document.addEventListener('click', this.windowListener);
document.addEventListener('contextmenu', this.contextMenuListener);
menu.style.left = `${Math.round(this.state.posX - menu.clientWidth)}px`;
}
if (!this.state.visible && prevState.visible) {
this.removeListeners();
}
}
removeListeners = () => {
document.removeEventListener('click', this.windowListener);
document.removeEventListener('contextmenu', this.contextMenuListener);
};
onClickHandler = (e: React.MouseEvent<any, MouseEvent>) => {
e.preventDefault();
this.triggerMenu(e.clientX);
};
contextMenuListener = () => this.setState({ visible: false });
windowListener = (e: MouseEvent) => {
const menu = this.menu.current;
if (e.button === 2 || !this.state.visible || !menu) {
return;
}
if (e.target === menu || menu.contains(e.target as Node)) {
return;
}
if (e.target !== menu && !menu.contains(e.target as Node)) {
this.setState({ visible: false });
}
};
triggerMenu = (posX: number) => this.setState(s => ({
posX: !s.visible ? posX : s.posX,
visible: !s.visible,
}));
render () {
return (
<div>
{this.props.renderToggle(this.onClickHandler)}
<Fade timeout={150} in={this.state.visible} unmountOnExit>
<div
ref={this.menu}
onClick={e => {
e.stopPropagation();
this.setState({ visible: false });
}}
style={{ width: '12rem' }}
css={tw`absolute bg-white p-2 rounded border border-neutral-700 shadow-lg text-neutral-500 z-50`}
>
{this.props.children}
</div>
</Fade>
</div>
);
}
}
export default DropdownMenu;