Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 19x 19x 19x 19x 19x 12x 19x 19x 19x 7x 19x 19x 7x 19x 19x | import { CSSProperties, HTMLAttributes, ReactElement, useCallback, useEffect, useMemo, useState, } from "react"; import { LottieOptions, useLottie } from "lottie-react"; import dislikeAnimation from "animations/dislike.json"; import likeAnimation from "animations/like.json"; import { wait } from "utils/wait"; export default function Heart({ active, handleLike, handleDislike, options, style, ...props }: { active: boolean | string; handleLike?: () => Promise<true | null>; handleDislike?: () => Promise<true | null>; options?: LottieOptions; style?: CSSProperties; } & HTMLAttributes<HTMLButtonElement>): ReactElement { const [defaultActiveValue, setDefaultActiveValue] = useState(active); const [isPlaying, setIsPlaying] = useState(false); const data = useMemo( () => ({ animationData: defaultActiveValue ? dislikeAnimation : likeAnimation, loop: false, autoplay: false, ...options, }), [defaultActiveValue, options] ); const { View, getDuration, play, animationLoaded } = useLottie(data, { width: 32, height: 32, ...style, }); useEffect(() => { setDefaultActiveValue(active); }, [active]); const playAnimationAndSetValue = useCallback( (value: boolean) => { play(); const duration = getDuration(); Iif (!duration) return setIsPlaying(false); wait(duration * 1000 - 100).then(() => { setDefaultActiveValue(value); setIsPlaying(false); }); }, [getDuration, play] ); if (handleLike && handleDislike) { return ( <button aria-label={defaultActiveValue ? "Dislike" : "Like"} disabled={isPlaying || !animationLoaded} onClick={(e) => { e.preventDefault(); e.stopPropagation(); setIsPlaying(true); if (defaultActiveValue) { handleDislike().then((res) => { Iif (!res) return setIsPlaying(false); playAnimationAndSetValue(false); }); } else { handleLike().then((res) => { Iif (!res) return setIsPlaying(false); playAnimationAndSetValue(true); }); } }} {...props} > {View} <style jsx>{` button { background-color: transparent; border: none; } button:hover { transform: scale(1.06); } button :global(svg > g > g:nth-child(5) path), button :global(svg > g > g:nth-child(7) path), button :global(svg > g > g:nth-child(8) path) { fill: #ffffffb3; stroke: #ffffffb3; stroke-width: 20px; } button:hover :global(svg > g > g:nth-child(5) path), button:hover :global(svg > g > g:nth-child(7) path), button:hover :global(svg > g > g:nth-child(8) path) { fill: #fff; stroke: #fff; } button:active { transform: scale(1); } `}</style> </button> ); } return ( <div> {View} <style jsx>{` div :global(svg path) { fill: ${typeof props.color === "string" ? props.color : "#ffffffb3"}; } `}</style> </div> ); } |