feat: snack-bar component (#626)
* feat: SnackBarProvider and queuing * feat: use snack bar on onboarding errors * feat: workspace copy use snackBar * fix: remove magic number
This commit is contained in:
101
front/src/modules/ui/components/progress-bar/ProgressBar.tsx
Normal file
101
front/src/modules/ui/components/progress-bar/ProgressBar.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimationControls, motion, useAnimation } from 'framer-motion';
|
||||
|
||||
const Bar = styled.div<Pick<ProgressBarProps, 'barHeight'>>`
|
||||
height: ${({ barHeight }) => barHeight}px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const BarFilling = styled(motion.div)`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export type ProgressBarProps = {
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
easing?: string;
|
||||
barHeight?: number;
|
||||
barColor?: string;
|
||||
autoStart?: boolean;
|
||||
};
|
||||
|
||||
export type ProgressBarControls = AnimationControls & {
|
||||
start: () => Promise<any>;
|
||||
pause: () => Promise<any>;
|
||||
};
|
||||
|
||||
export const ProgressBar = forwardRef<ProgressBarControls, ProgressBarProps>(
|
||||
(
|
||||
{
|
||||
duration = 3,
|
||||
delay = 0,
|
||||
easing = 'easeInOut',
|
||||
barHeight = 24,
|
||||
barColor,
|
||||
autoStart = true,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const controls = useAnimation();
|
||||
const startTimestamp = useRef<number>(0);
|
||||
const remainingTime = useRef<number>(duration);
|
||||
|
||||
const start = useCallback(async () => {
|
||||
startTimestamp.current = Date.now();
|
||||
return controls.start({
|
||||
scaleX: 0,
|
||||
transition: {
|
||||
duration: remainingTime.current / 1000, // convert ms to s for framer-motion
|
||||
delay: delay / 1000, // likewise
|
||||
ease: easing,
|
||||
},
|
||||
});
|
||||
}, [controls, delay, easing]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
...controls,
|
||||
start: async () => {
|
||||
return start();
|
||||
},
|
||||
pause: async () => {
|
||||
const elapsed = Date.now() - startTimestamp.current;
|
||||
|
||||
remainingTime.current = remainingTime.current - elapsed;
|
||||
return controls.stop();
|
||||
},
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (autoStart) {
|
||||
start();
|
||||
}
|
||||
}, [controls, delay, duration, easing, autoStart, start]);
|
||||
|
||||
return (
|
||||
<Bar barHeight={barHeight}>
|
||||
<BarFilling
|
||||
style={{
|
||||
originX: 0,
|
||||
// Seems like custom props are not well handled by react when used with framer-motion and emotion styled
|
||||
backgroundColor: barColor ?? theme.color.gray80,
|
||||
}}
|
||||
initial={{ scaleX: 1 }}
|
||||
animate={controls}
|
||||
exit={{ scaleX: 0 }}
|
||||
/>
|
||||
</Bar>
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -38,3 +38,5 @@ export { IconTimelineEvent } from '@tabler/icons-react';
|
||||
export { IconAlertCircle } from '@tabler/icons-react';
|
||||
export { IconEye } from '@tabler/icons-react';
|
||||
export { IconEyeOff } from '@tabler/icons-react';
|
||||
export { IconAlertTriangle } from '@tabler/icons-react';
|
||||
export { IconCopy } from '@tabler/icons-react';
|
||||
|
||||
@ -13,6 +13,20 @@ const common = {
|
||||
icon: icon,
|
||||
text: text,
|
||||
blur: blur,
|
||||
snackBar: {
|
||||
success: {
|
||||
background: '#16A26B',
|
||||
color: '#D0F8E9',
|
||||
},
|
||||
error: {
|
||||
background: '#B43232',
|
||||
color: '#FED8D8',
|
||||
},
|
||||
info: {
|
||||
background: color.gray80,
|
||||
color: color.gray0,
|
||||
},
|
||||
},
|
||||
spacing: (multiplicator: number) => `${multiplicator * 4}px`,
|
||||
table: {
|
||||
horizontalCellMargin: '8px',
|
||||
|
||||
Reference in New Issue
Block a user