diff --git a/.changeset/twelve-insects-scream.md b/.changeset/twelve-insects-scream.md new file mode 100644 index 0000000..1238aa5 --- /dev/null +++ b/.changeset/twelve-insects-scream.md @@ -0,0 +1,5 @@ +--- +"@fileverse/heartbit-react": minor +--- + +Fixed bug where events where not called on fill complete diff --git a/packages/heartbit-react/src/components/HeartBitUI/index.tsx b/packages/heartbit-react/src/components/HeartBitUI/index.tsx index 13e7b3c..85ad0dc 100644 --- a/packages/heartbit-react/src/components/HeartBitUI/index.tsx +++ b/packages/heartbit-react/src/components/HeartBitUI/index.tsx @@ -12,7 +12,12 @@ import type { InternalHandlerRef, TotalFillRange, } from "./types"; -import { HEART_CV_HEIGHT, HEART_CV_WIDTH, makeHeartCanvas } from "../../utils"; +import { + HEART_CV_HEIGHT, + HEART_CV_WIDTH, + disableUpEvents, + makeHeartCanvas, +} from "../../utils"; import clx from "classnames"; const HeartBitUI = forwardRef( @@ -26,10 +31,15 @@ const HeartBitUI = forwardRef( isDisabled = false, disableBeatingAnimation = false, fillInterval = 750, + startFilling = false, } = props; const intervalRef = useRef | null>(null); + const autFillRef = useRef | null>(null); + + const canvasRef = useRef(null); + const [currentFillIdx, setCurrentFillIdx] = useState(0); useEffect(() => { @@ -53,12 +63,53 @@ const HeartBitUI = forwardRef( fillPosition: startFillPos || defaultFillPos, scale, }); - + canvasRef.current = node; setCanvasContext(context); }, [defaultFillPos, scale, startFillPos] ); + const autoFillHeart = useCallback(() => { + if (!canvasContext || currentFillIdx >= 10 || !startFilling) return; + + let idx = currentFillIdx; + + autFillRef.current = setInterval(() => { + if (idx >= 10) { + if (onMouseUp && typeof onMouseUp === "function") { + if (canvasRef.current) disableUpEvents(canvasRef.current); + onMouseUp(); + } + clearInterval(autFillRef.current as ReturnType); + return; + } + canvasContext.reset(); + makeHeartCanvas({ + canvasContext, + fillPosition: idx + 1, + scale, + }); + if (idx < 11) idx = idx + 1; + + setCurrentFillIdx(idx); + }, fillInterval); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [canvasContext, fillInterval, scale, startFilling]); + + useEffect(() => { + const cleanup = () => { + if (autFillRef.current) + clearInterval(autFillRef.current as ReturnType); + }; + + if (startFilling) autoFillHeart(); + else cleanup(); + + return () => { + cleanup(); + }; + }, [autoFillHeart, startFilling]); + const onMousedown = ( e: | React.MouseEvent @@ -71,6 +122,10 @@ const HeartBitUI = forwardRef( let idx = currentFillIdx; intervalRef.current = setInterval(() => { if (idx >= 10) { + if (onMouseUp && typeof onMouseUp === "function") { + if (canvasRef.current) disableUpEvents(canvasRef.current); + onMouseUp(); + } clearInterval(intervalRef.current as ReturnType); return; } @@ -81,7 +136,7 @@ const HeartBitUI = forwardRef( scale, }); - if (idx < 10) idx += 1; + if (idx < 11) idx += 1; setCurrentFillIdx(idx); }, fillInterval); @@ -97,8 +152,6 @@ const HeartBitUI = forwardRef( scale, }); - setCanvasContext(canvasContext); - setCurrentFillIdx(startFillPos); }, [canvasContext, defaultFillPos, scale, startFillPos]); @@ -138,6 +191,7 @@ const HeartBitUI = forwardRef( onMouseUp={onMouseup} onTouchStart={onMousedown} onTouchEnd={onMouseup} + onContextMenu={(e) => e.preventDefault()} className={clx(styles.heart, { [styles.disabled]: isDisabled || disableBeatingAnimation, })} diff --git a/packages/heartbit-react/src/components/HeartBitUI/types.ts b/packages/heartbit-react/src/components/HeartBitUI/types.ts index a8e2a97..5021612 100644 --- a/packages/heartbit-react/src/components/HeartBitUI/types.ts +++ b/packages/heartbit-react/src/components/HeartBitUI/types.ts @@ -2,12 +2,12 @@ export type TotalFillRange = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; type DefaultFillRange = 1 | 2 | 3 | 4 | 5; export interface HeartBitUIProps { onMouseUp?: ( - event: + event?: | React.MouseEvent | React.TouchEvent ) => void; onMouseDown?: ( - event: + event?: | React.MouseEvent | React.TouchEvent ) => void; @@ -17,6 +17,7 @@ export interface HeartBitUIProps { isDisabled?: boolean; disableBeatingAnimation?: boolean; fillInterval?: number; + startFilling?: boolean; } export interface InternalHandlerRef { diff --git a/packages/heartbit-react/src/utils/index.ts b/packages/heartbit-react/src/utils/index.ts index 7f4b0c0..082554a 100644 --- a/packages/heartbit-react/src/utils/index.ts +++ b/packages/heartbit-react/src/utils/index.ts @@ -53,3 +53,26 @@ export const getStartFillPosition = (totalMintsByUser: number) => { if (totalMintsByUser < 60) return 0; return 5; }; + +export const addSelfDestructingEventListener = ( + element: HTMLElement, + event: string, + callback: (event: Event) => void +) => { + const handler = (e: Event) => { + callback(e); + element.removeEventListener(event, handler, true); + }; + element.addEventListener(event, handler, true); +}; + +export const disableUpEvents = (element: HTMLElement) => { + addSelfDestructingEventListener(element, "mouseup", (e) => { + e.stopImmediatePropagation(); + e.stopPropagation(); + }); + addSelfDestructingEventListener(element, "touchend", (e) => { + e.stopImmediatePropagation(); + e.stopPropagation(); + }); +};