mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-18 23:46:03 +01:00
refactor(web/progress): circle progress to Mantine
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CircularProgress, CircularProgressLabel, Flex, Text } from '@chakra-ui/react';
|
import { RingProgress, Text, useMantineTheme, keyframes, Stack, createStyles } from '@mantine/core';
|
||||||
import { useNuiEvent } from '../../hooks/useNuiEvent';
|
import { useNuiEvent } from '../../hooks/useNuiEvent';
|
||||||
import { fetchNui } from '../../utils/fetchNui';
|
import { fetchNui } from '../../utils/fetchNui';
|
||||||
|
|
||||||
@@ -10,13 +10,49 @@ export interface CircleProgressbarProps {
|
|||||||
percent?: boolean;
|
percent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 33.5 is the r of the circle
|
||||||
|
const progressCircle = keyframes({
|
||||||
|
'0%': { strokeDasharray: `0, ${33.5 * 2 * Math.PI}` },
|
||||||
|
'100%': { strokeDasharray: `${33.5 * 2 * Math.PI}, 0` },
|
||||||
|
});
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme, params: { position: 'middle' | 'bottom'; duration: number }) => ({
|
||||||
|
container: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: params.position === 'middle' ? '50%' : undefined,
|
||||||
|
bottom: params.position === 'bottom' ? '20%' : undefined,
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
// Scuffed way of grabbing the first section and animating it
|
||||||
|
'> svg > circle:nth-child(2)': {
|
||||||
|
transition: 'none',
|
||||||
|
animation: `${progressCircle} linear forwards`,
|
||||||
|
animationDuration: `${params.duration}ms`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontFamily: 'roboto-mono',
|
||||||
|
textShadow: theme.shadows.sm,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
textAlign: 'center',
|
||||||
|
textShadow: theme.shadows.sm,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const CircleProgressbar: React.FC = () => {
|
const CircleProgressbar: React.FC = () => {
|
||||||
const [visible, setVisible] = React.useState(false);
|
const [visible, setVisible] = React.useState(false);
|
||||||
const [progressDuration, setProgressDuration] = React.useState(0);
|
const [progressDuration, setProgressDuration] = React.useState(0);
|
||||||
const [position, setPosition] = React.useState<'middle' | 'bottom'>('middle');
|
const [position, setPosition] = React.useState<'middle' | 'bottom'>('middle');
|
||||||
const [value, setValue] = React.useState(0);
|
const [value, setValue] = React.useState(0);
|
||||||
const [label, setLabel] = React.useState('');
|
const [label, setLabel] = React.useState('');
|
||||||
const [cancelled, setCancelled] = React.useState(false);
|
const theme = useMantineTheme();
|
||||||
|
const { classes } = useStyles({ position, duration: progressDuration });
|
||||||
|
|
||||||
const progressComplete = () => {
|
const progressComplete = () => {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
@@ -24,7 +60,6 @@ const CircleProgressbar: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const progressCancel = () => {
|
const progressCancel = () => {
|
||||||
setCancelled(true);
|
|
||||||
setValue(99); // Sets the final value to 100% kek
|
setValue(99); // Sets the final value to 100% kek
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
};
|
};
|
||||||
@@ -33,7 +68,6 @@ const CircleProgressbar: React.FC = () => {
|
|||||||
|
|
||||||
useNuiEvent<CircleProgressbarProps>('circleProgress', (data) => {
|
useNuiEvent<CircleProgressbarProps>('circleProgress', (data) => {
|
||||||
if (visible) return;
|
if (visible) return;
|
||||||
setCancelled(false);
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
setValue(0);
|
setValue(0);
|
||||||
setLabel(data.label || '');
|
setLabel(data.label || '');
|
||||||
@@ -50,50 +84,21 @@ const CircleProgressbar: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<>
|
||||||
h={position === 'middle' ? '100%' : '20%'}
|
|
||||||
w="100%"
|
|
||||||
position="absolute"
|
|
||||||
bottom="0"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
{visible && (
|
{visible && (
|
||||||
<Flex alignItems="center" flexDirection="column">
|
<Stack spacing={0} className={classes.container} bottom="0">
|
||||||
<CircularProgress
|
<RingProgress
|
||||||
value={value}
|
size={90}
|
||||||
size="5rem"
|
thickness={7}
|
||||||
trackColor="rgba(0, 0, 0, 0.6)"
|
sections={[{ value: 0, color: theme.primaryColor }]}
|
||||||
onAnimationEnd={progressComplete}
|
onAnimationEnd={progressComplete}
|
||||||
thickness={6}
|
className={classes.progress}
|
||||||
color={cancelled ? 'rgb(198, 40, 40)' : 'white'}
|
label={<Text className={classes.value}>{value}%</Text>}
|
||||||
sx={
|
/>
|
||||||
!cancelled
|
{label && <Text className={classes.label}>{label}</Text>}
|
||||||
? {
|
</Stack>
|
||||||
'.chakra-progress__indicator': {
|
|
||||||
transition: 'none !important',
|
|
||||||
animation: 'progress linear forwards !important',
|
|
||||||
animationDuration: `${progressDuration}ms !important`,
|
|
||||||
opacity: '1 !important',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
// Currently unused
|
|
||||||
'.chakra-progress__indicator': {
|
|
||||||
transition: 'none !important',
|
|
||||||
strokeDasharray: '264, 0 !important', // sets circle to full
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<CircularProgressLabel fontFamily="Fira Mono">{value}%</CircularProgressLabel>
|
|
||||||
</CircularProgress>
|
|
||||||
<Text fontFamily="Inter" fontSize={18} fontWeight="light">
|
|
||||||
{label}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap');
|
||||||
@import url("https://use.typekit.net/wxh5ury.css");
|
@import url("https://use.typekit.net/wxh5ury.css");
|
||||||
|
@import url("https://use.typekit.net/qgr5ebd.css");
|
||||||
|
|
||||||
html {
|
html {
|
||||||
color-scheme: normal !important;
|
color-scheme: normal !important;
|
||||||
@@ -29,15 +30,6 @@ code {
|
|||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes progress {
|
|
||||||
0% {
|
|
||||||
stroke-dasharray: 0, 264;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
stroke-dasharray: 264, 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes progress-bar {
|
@keyframes progress-bar {
|
||||||
from {
|
from {
|
||||||
width: 0%;
|
width: 0%;
|
||||||
|
|||||||
@@ -28,7 +28,11 @@ const root = document.getElementById('root');
|
|||||||
ReactDOM.createRoot(root!).render(
|
ReactDOM.createRoot(root!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<LocaleProvider>
|
<LocaleProvider>
|
||||||
<MantineProvider withNormalizeCSS withGlobalStyles theme={{ colorScheme: 'dark', fontFamily: 'Roboto' }}>
|
<MantineProvider
|
||||||
|
withNormalizeCSS
|
||||||
|
withGlobalStyles
|
||||||
|
theme={{ colorScheme: 'dark', fontFamily: 'Roboto', shadows: { sm: '1px 1px 3px rgba(0, 0, 0, 0.5)' } }}
|
||||||
|
>
|
||||||
<ChakraProvider theme={theme}>
|
<ChakraProvider theme={theme}>
|
||||||
<App />
|
<App />
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user