houdini/app/javascript/components/common/progress/AnimatedCheckmark.tsx
2021-03-10 15:24:00 -06:00

169 lines
No EOL
4.4 KiB
TypeScript

// License: LGPL-3.0-or-later
// from: https://github.com/davidwilson3/react-typescript-checkmark/blob/1de3e0362965602d4345868f1f876aa54a96d5b6/src/checkmark.tsx
import React from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
interface StyledProps {
animationDuration: number;
backgroundColor: string;
checkColor: string;
checkThickness: number;
explosion: number;
height: number;
width: number;
}
const useStyles = (makeStyles((theme:Theme) =>
createStyles({
root: {
display: "block",
marginLeft: "auto",
marginRight: "auto",
borderRadius: "50%",
width: (props:StyledProps) => props.width,
height: (props:StyledProps) => props.height,
stroke: (props:StyledProps) => props.checkColor,
strokeWidth: (props:StyledProps) => props.checkThickness,
strokeMiterlimit: 10,
animation: (props:StyledProps) => `$fill ${props.animationDuration * 0.66}s ease-in-out 0.4s
forwards,
$scale 0.3s ease-in-out 0.9s both`,
},
circle: {
strokeDasharray: 166,
strokeDashoffset: 166,
strokeWidth: (props:StyledProps) => props.checkThickness,
strokeMiterlimit: 10,
stroke: (props:StyledProps) => props.backgroundColor || theme.palette.success.main,
fill: "none",
animation: (props:StyledProps) => `$stroke-keyframe ${props.animationDuration}s
cubic-bezier(0.65, 0, 0.45, 1) forwards`,
},
checkmark: {
transformOrigin: "50% 50%",
strokeDasharray: 48,
strokeDashoffset: 48,
animation: (props:StyledProps) => `$stroke-keyframe ${props.animationDuration * 0.5}s
cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards`,
},
"@keyframes scale": {
"0%": {},
"100%": {
transform: "none",
},
"50%": {
transform: (props:StyledProps) => `scale3d(${props.explosion}, ${props.explosion}, 1)`,
},
},
"@keyframes fill": {
"100%": {
boxShadow: (props:StyledProps) => `inset 0 0 0 100vh ${props.backgroundColor || theme.palette.success.main}`,
},
},
"@keyframes stroke-keyframe": {
"100%": {
/* this is needed because makeStyles function has bugs (https://github.com/mui-org/material-ui/issues/15511) */
strokeDashoffset:() => 0,
},
},
})
));
/**
* The different preset checkmark sizes. Measured in pixels
*/
export const sizes = {
xs: 12,
sm: 16,
md: 24,
lg: 52,
xl: 72,
xxl: 96,
};
export type Sizes = keyof typeof sizes;
export interface AnimatedCheckmarkProps {
/**
* Duration of the checkmark animation in milliseconds
*/
animationDuration: number;
/** A string for describing what the component means in screen readers*/
ariaLabel: string;
/** Color in hex of the circle and background of component
* Defaults to `theme.palette.success.main`
*/
backgroundColor?: string;
/**
* Color in hex of checkmark in the middle of the component
* Defaults to white (#000)
*/
checkColor?: string;
/**
* The stroke width of the checkmark. Defaults to 5.
*/
checkThickness: number;
/**
* How much the circle temporarily expands to on success. 1 means no expansion, 1.1 means 10% expansion, etc.
* Defaults to 1.1
*/
explosion: number;
/**
* The role for accessibility purposes.
* Defaults to 'alert'.
*/
role?: string;
/**
* The height and width in pixels of the component. Accepts a size string (listed in `sizes`) or a number
*
* Defaults to 'lg' which is 52 pixels
*/
size: Sizes | number;
/**
* Whether the component should be visible in the document.
*/
visible: boolean;
}
function AnimatedCheckmark(props: AnimatedCheckmarkProps): JSX.Element {
const selectedSize = typeof props.size === 'number' ? props.size : sizes[props.size];
const classes = useStyles({
backgroundColor: props.backgroundColor,
checkColor: props.checkColor,
checkThickness: props.checkThickness,
animationDuration: props.animationDuration,
explosion: props.explosion,
width: selectedSize,
height: selectedSize,
});
if (!props.visible) return <></>;
return (
<svg
data-testid="CheckmarkTest"
className={classes.root}
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 52 52' role={props.role} aria-label={props.ariaLabel}
>
<circle className={classes.circle} cx='26' cy='26' r='25' fill='none' />
<path className={classes.checkmark} fill='none' d='M14.1 27.2l7.1 7.2 16.7-16.8' />
</svg>
);
}
AnimatedCheckmark.defaultProps = {
size: 'lg',
visible: true,
checkColor: '#FFF',
checkThickness: 5,
animationDuration: 0.6,
explosion: 1.1,
role: 'alert',
};
export default AnimatedCheckmark;