169 lines
4.4 KiB
TypeScript
169 lines
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;
|