2023-01-30 19:23:14 +01:00
|
|
|
import { Box, createStyles } from '@mantine/core';
|
2023-01-16 15:07:17 +01:00
|
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
|
|
|
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
2023-01-20 12:08:46 +01:00
|
|
|
import { useState } from 'react';
|
|
|
|
|
import { useNuiEvent } from '../../../hooks/useNuiEvent';
|
|
|
|
|
import { fetchNui } from '../../../utils/fetchNui';
|
2023-01-29 13:06:57 +01:00
|
|
|
import ScaleFade from '../../../transitions/ScaleFade';
|
2023-01-16 15:07:17 +01:00
|
|
|
|
2023-01-29 13:06:57 +01:00
|
|
|
export interface MenuItem {
|
2023-01-20 12:08:46 +01:00
|
|
|
icon: IconProp;
|
|
|
|
|
label: string;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-20 12:26:50 +01:00
|
|
|
const useStyles = createStyles((theme) => ({
|
|
|
|
|
wrapper: {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: '50%',
|
|
|
|
|
left: '50%',
|
|
|
|
|
transform: 'translate(-50%, -50%)',
|
|
|
|
|
},
|
2023-01-21 15:06:25 +01:00
|
|
|
sector: {
|
|
|
|
|
fill: theme.colors.dark[6],
|
|
|
|
|
color: theme.colors.dark[0],
|
2023-01-22 14:00:44 +01:00
|
|
|
|
2023-01-21 15:06:25 +01:00
|
|
|
'&:hover': {
|
|
|
|
|
fill: theme.fn.primaryColor(),
|
2023-01-25 21:47:04 +01:00
|
|
|
cursor: 'pointer',
|
2023-01-22 14:00:44 +01:00
|
|
|
'> g > text, > g > svg > path': {
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
'> g > text': {
|
|
|
|
|
fill: theme.colors.dark[0],
|
2023-01-21 15:06:25 +01:00
|
|
|
},
|
|
|
|
|
},
|
2023-01-25 21:47:04 +01:00
|
|
|
backgroundCircle: {
|
|
|
|
|
fill: theme.colors.dark[6],
|
|
|
|
|
},
|
|
|
|
|
centerCircle: {
|
|
|
|
|
fill: theme.fn.primaryColor(),
|
|
|
|
|
color: '#fff',
|
|
|
|
|
'&:hover': {
|
|
|
|
|
fill: theme.colors[theme.primaryColor][theme.fn.primaryShade() - 1],
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
centerIcon: {
|
|
|
|
|
color: '#fff',
|
|
|
|
|
pointerEvents: 'none',
|
|
|
|
|
},
|
2023-01-20 12:26:50 +01:00
|
|
|
}));
|
|
|
|
|
|
2023-01-21 15:06:25 +01:00
|
|
|
const degToRad = (deg: number) => deg * (Math.PI / 180);
|
|
|
|
|
|
2023-01-27 22:02:16 +01:00
|
|
|
const RadialMenu: React.FC = () => {
|
2023-01-20 12:26:50 +01:00
|
|
|
const { classes } = useStyles();
|
2023-01-20 12:08:46 +01:00
|
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
|
const [menu, setMenu] = useState<{ items: MenuItem[]; sub?: boolean }>({
|
|
|
|
|
items: [],
|
|
|
|
|
sub: false,
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-04 22:13:12 +01:00
|
|
|
useNuiEvent('openRadialMenu', async (data: { items: MenuItem[]; sub?: boolean } | false) => {
|
2023-01-31 14:07:52 +01:00
|
|
|
if (!data) return setVisible(false);
|
2023-02-04 22:13:12 +01:00
|
|
|
if (visible) {
|
|
|
|
|
setVisible(false);
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
|
|
|
}
|
2023-01-20 12:08:46 +01:00
|
|
|
setMenu(data);
|
|
|
|
|
setVisible(true);
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-02 11:28:59 +01:00
|
|
|
useNuiEvent('refreshItems', (data: MenuItem[]) => {
|
|
|
|
|
setMenu({ ...menu, items: data });
|
|
|
|
|
});
|
|
|
|
|
|
2023-01-16 15:07:17 +01:00
|
|
|
return (
|
2023-01-20 12:08:46 +01:00
|
|
|
<>
|
2023-01-20 12:26:50 +01:00
|
|
|
<Box className={classes.wrapper}>
|
2023-01-29 13:06:57 +01:00
|
|
|
<ScaleFade visible={visible}>
|
|
|
|
|
<svg width="350px" height="350px" transform="rotate(90)">
|
|
|
|
|
{/*Fixed issues with background circle extending the circle when there's less than 3 items*/}
|
2023-01-30 19:38:20 +01:00
|
|
|
<g transform="translate(175, 175)">
|
|
|
|
|
<circle r={175} className={classes.backgroundCircle} />
|
|
|
|
|
</g>
|
2023-01-29 13:06:57 +01:00
|
|
|
{menu.items.map((item, index) => {
|
2023-01-30 19:38:20 +01:00
|
|
|
// Always draw full circle to avoid elipse circles with 2 or less items
|
|
|
|
|
const pieAngle = 360 / (menu.items.length < 3 ? 3 : menu.items.length);
|
2023-01-29 13:06:57 +01:00
|
|
|
const angle = degToRad(pieAngle / 2 + 90);
|
|
|
|
|
const radius = 175 * 0.65;
|
|
|
|
|
const iconX = 175 + Math.sin(angle) * radius;
|
|
|
|
|
const iconY = 175 + Math.cos(angle) * radius;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
2023-01-30 19:22:45 +01:00
|
|
|
<g
|
|
|
|
|
transform={`rotate(-${index * pieAngle} 175 175)`}
|
|
|
|
|
className={classes.sector}
|
2023-02-07 13:46:13 +01:00
|
|
|
onClick={() => fetchNui('radialClick', index)}
|
2023-01-30 19:22:45 +01:00
|
|
|
>
|
2023-01-29 13:06:57 +01:00
|
|
|
<path
|
|
|
|
|
d={`M175.01,175.01 l175,0 A175.01,175.01 0 0,0 ${175 + 175 * Math.cos(-degToRad(pieAngle))}, ${
|
|
|
|
|
175 + 175 * Math.sin(-degToRad(pieAngle))
|
|
|
|
|
} z`}
|
2023-01-25 21:47:04 +01:00
|
|
|
/>
|
2023-01-29 13:06:57 +01:00
|
|
|
<g transform={`rotate(${index * pieAngle - 90} ${iconX} ${iconY})`} pointerEvents="none">
|
|
|
|
|
<FontAwesomeIcon
|
|
|
|
|
x={iconX - 12.5}
|
|
|
|
|
y={iconY - 17.5}
|
|
|
|
|
icon={item.icon}
|
|
|
|
|
width={25}
|
|
|
|
|
height={25}
|
|
|
|
|
fixedWidth
|
|
|
|
|
/>
|
|
|
|
|
<text x={iconX} y={iconY + 25} fill="#fff" textAnchor="middle" pointerEvents="none">
|
|
|
|
|
{item.label}
|
|
|
|
|
</text>
|
|
|
|
|
</g>
|
2023-01-22 14:00:44 +01:00
|
|
|
</g>
|
2023-01-29 13:06:57 +01:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
})}
|
2023-02-04 22:13:12 +01:00
|
|
|
{/* TODO: rotate go back icon */}
|
|
|
|
|
<g
|
|
|
|
|
transform={`translate(175, 175)`}
|
|
|
|
|
onClick={() => {
|
2023-02-07 13:46:13 +01:00
|
|
|
menu.sub ? fetchNui('radialBack') : setVisible(false);
|
2023-02-04 22:13:12 +01:00
|
|
|
}}
|
|
|
|
|
>
|
2023-01-29 13:06:57 +01:00
|
|
|
<circle r={30} className={classes.centerCircle} />
|
|
|
|
|
</g>
|
|
|
|
|
<FontAwesomeIcon
|
2023-02-04 22:13:12 +01:00
|
|
|
icon={!menu.sub ? 'xmark' : 'arrow-rotate-left'}
|
2023-01-29 13:06:57 +01:00
|
|
|
className={classes.centerIcon}
|
|
|
|
|
color="#fff"
|
|
|
|
|
width={28}
|
|
|
|
|
height={28}
|
|
|
|
|
x={175 - 28 / 2}
|
|
|
|
|
y={175 - 28 / 2}
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
|
|
|
|
</ScaleFade>
|
2023-01-16 15:07:17 +01:00
|
|
|
</Box>
|
2023-01-20 12:08:46 +01:00
|
|
|
</>
|
2023-01-16 15:07:17 +01:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2023-01-27 22:02:16 +01:00
|
|
|
export default RadialMenu;
|