-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
ContextMenuItem.tsx
114 lines (104 loc) · 3.68 KB
/
ContextMenuItem.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
import { Box, UnstyledButton, parseThemeColor, rgba } from '@mantine/core';
import { useMediaQuery, useTimeout } from '@mantine/hooks';
import clsx from 'clsx';
import { useContext, useRef, useState, type MouseEventHandler } from 'react';
import { ContextMenu } from './ContextMenu';
import { ContextMenuSettingsCtx } from './ContextMenuProvider';
import type { ContextMenuContent, ContextMenuItemOptions, WithRequiredProperty } from './types';
export function ContextMenuItem({
className,
style,
icon,
iconRight,
title,
color,
disabled,
onClick,
onHide,
items,
}: WithRequiredProperty<Omit<ContextMenuItemOptions, 'key'>, 'title'> & { onHide: () => void }) {
const ref = useRef<HTMLButtonElement>(null);
const { submenuDelay } = useContext(ContextMenuSettingsCtx);
const hoverAvailable = useMediaQuery(`(hover: hover)`);
const [submenuPosition, setSubmenuPosition] = useState<{ x: number; y: number } | null>(null);
const { start: startShowingSubmenu, clear: stopShowingSubmenu } = useTimeout(() => {
const { top: y, right: x } = ref.current!.getBoundingClientRect();
setSubmenuPosition({ x, y });
}, submenuDelay);
const { start: startHidingSubmenu, clear: stopHidingSubmenu } = useTimeout(() => {
setSubmenuPosition(null);
}, submenuDelay);
const showSubmenu = () => {
stopHidingSubmenu();
startShowingSubmenu();
};
const hideSubmenu = () => {
stopShowingSubmenu();
startHidingSubmenu();
};
const hasSubmenu = items && !disabled;
const showSubmenuOnHover = hasSubmenu && hoverAvailable;
const handleClick: MouseEventHandler<HTMLButtonElement> | undefined = hasSubmenu
? (e) => {
e.stopPropagation();
showSubmenu();
}
: onClick
? (e) => {
onHide();
onClick!(e);
}
: undefined;
return (
<div
onMouseEnter={showSubmenuOnHover ? showSubmenu : undefined}
onMouseLeave={showSubmenuOnHover ? hideSubmenu : undefined}
>
<UnstyledButton
ref={ref}
style={[
(theme) => {
const { colors } = theme;
const parsedColor = color ? parseThemeColor({ color, theme }).value : undefined;
return {
'--mantine-contextmenu-item-button-color': parsedColor ? parsedColor : 'var(--mantine-color-text)',
'--mantine-contextmenu-item-button-hover-bg-color-light': parsedColor
? rgba(parsedColor, 0.08)
: rgba(colors.gray[4], 0.25),
'--mantine-contextmenu-item-button-hover-bg-color-dark': parsedColor
? rgba(parsedColor, 0.15)
: rgba(colors.dark[3], 0.25),
'--mantine-contextmenu-item-button-pressed-bg-color-light': parsedColor
? rgba(parsedColor, 0.2)
: rgba(colors.gray[4], 0.5),
'--mantine-contextmenu-item-button-pressed-bg-color-dark': parsedColor
? rgba(parsedColor, 0.3)
: rgba(colors.dark[3], 0.5),
};
},
style,
]}
className={clsx('mantine-contextmenu-item-button', className)}
disabled={disabled}
onClick={handleClick}
>
{icon && (
<Box fz={0} mr="xs" mt={-2}>
{icon}
</Box>
)}
<div className="mantine-contextmenu-item-button-title">{title}</div>
{ iconRight ? (
<Box fz={0} ml="xs" mt={-2}>
{iconRight}
</Box>
) : items && (
<Box fz={10} mt={-2} ml="xs">
▶
</Box>
)}
</UnstyledButton>
{submenuPosition && <ContextMenu content={items as ContextMenuContent} onHide={onHide} {...submenuPosition} />}
</div>
);
}