// 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 ( ); } AnimatedCheckmark.defaultProps = { size: 'lg', visible: true, checkColor: '#FFF', checkThickness: 5, animationDuration: 0.6, explosion: 1.1, role: 'alert', }; export default AnimatedCheckmark;