-
Notifications
You must be signed in to change notification settings - Fork 11
/
ContextMenu.tsx
118 lines (106 loc) · 3.05 KB
/
ContextMenu.tsx
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
import { Colors } from '../constants/Colors';
import { ContextMenuEntry } from './ContextMenuEntry';
import { ContextMenuHr } from './ContextMenuHr';
import { useDispatch, useSelector } from '../states/store';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
// == styles =======================================================================================
const Container = styled.div`
position: absolute;
overflow: hidden;
padding: 0.25rem;
border-radius: 0.25rem;
background: ${ Colors.back2 };
filter: drop-shadow( 0 0 2px ${ Colors.black } );
font-size: 0.8rem;
`;
const OverlayBG = styled.div`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba( 0, 0, 0, 0 );
`;
const Root = styled.div`
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba( 0, 0, 0, 0 );
`;
// == element ======================================================================================
export interface ContextMenuProps {
className?: string;
}
const ContextMenu = ( { className }: ContextMenuProps ): JSX.Element => {
const dispatch = useDispatch();
const { position, commands } = useSelector( ( state ) => ( {
position: state.contextMenu.position,
commands: state.contextMenu.commands
} ) );
const style: React.CSSProperties = useMemo(
() => {
const width = document.documentElement.clientWidth;
const height = document.documentElement.clientHeight;
const ret: React.CSSProperties = {};
( position.x < width - 240 )
? ( ret.left = position.x )
: ( ret.right = width - position.x );
( position.y < height - 120 )
? ( ret.top = position.y )
: ( ret.bottom = height - position.y );
return ret;
},
[ position ]
);
const handleClickBG = useCallback(
() => {
dispatch( { type: 'ContextMenu/Close' } );
},
[ dispatch ]
);
const handleContextMenuBG = useCallback(
() => {
dispatch( { type: 'ContextMenu/Close' } );
},
[ dispatch ]
);
return <Root
className={ className }
>
<OverlayBG
onClick={ handleClickBG }
onContextMenu={ handleContextMenuBG }
/>
<Container
style={ style }
>
{ commands.map( ( command, iCommand ) => (
command === 'hr'
? <ContextMenuHr key={ iCommand } />
: (
<ContextMenuEntry
key={ iCommand }
name={ command.name }
description={ command.description }
onClick={ ( event ) => {
command.callback?.();
if ( command.more != null ) {
dispatch( {
type: 'ContextMenu/More',
position: { x: event.clientX, y: event.clientY },
commands: command.more,
} );
} else {
dispatch( { type: 'ContextMenu/Close' } );
}
} }
/>
)
) ) }
</Container>
</Root>;
};
export { ContextMenu };