|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
import ReactConfetti from 'react-confetti';
|
|
|
|
|
|
|
|
type ConfettiPosition = {
|
|
|
|
x: number;
|
|
|
|
y: number;
|
|
|
|
w: number;
|
|
|
|
h: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
type ConfettiProps = {
|
|
|
|
pieces?: number;
|
|
|
|
element?: HTMLElement | null;
|
|
|
|
onDone?: () => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function Confetti(props: ConfettiProps) {
|
|
|
|
const { element = document.body, onDone = () => null, pieces = 40 } = props;
|
|
|
|
|
|
|
|
const [confettiPos, setConfettiPos] = useState<
|
|
|
|
undefined | ConfettiPosition
|
|
|
|
>();
|
|
|
|
|
|
|
|
function populateConfettiPosition(element: HTMLElement) {
|
|
|
|
const elRect = element.getBoundingClientRect();
|
|
|
|
|
|
|
|
// set confetti position, keeping in mind the scroll values
|
|
|
|
setConfettiPos({
|
|
|
|
x: elRect?.x || 0,
|
|
|
|
y: (elRect?.y || 0) + window.scrollY,
|
|
|
|
w: elRect?.width || 0,
|
|
|
|
h: elRect?.height || 0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!element) {
|
|
|
|
setConfettiPos(undefined);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
populateConfettiPosition(element);
|
|
|
|
}, [element]);
|
|
|
|
|
|
|
|
if (!confettiPos) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ReactConfetti
|
|
|
|
height={document.body.scrollHeight}
|
|
|
|
numberOfPieces={pieces}
|
|
|
|
recycle={false}
|
|
|
|
onConfettiComplete={(confettiInstance) => {
|
|
|
|
setConfettiPos(undefined);
|
|
|
|
onDone();
|
|
|
|
}}
|
|
|
|
initialVelocityX={4}
|
|
|
|
initialVelocityY={8}
|
|
|
|
tweenDuration={10}
|
|
|
|
confettiSource={{
|
|
|
|
x: confettiPos.x,
|
|
|
|
y: confettiPos.y,
|
|
|
|
w: confettiPos.w,
|
|
|
|
h: confettiPos.h,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|