pstream is dead; long live pstream
taciturnaxolotl.github.io/pstream-ng/
1import React, { RefObject, useCallback, useEffect, useState } from "react";
2
3export type MouseActivity = React.MouseEvent<HTMLElement> | MouseEvent;
4
5type ActivityEvent = MouseActivity | React.TouchEvent<HTMLElement> | TouchEvent;
6
7export function makePercentageString(num: number) {
8 return `${num.toFixed(2)}%`;
9}
10
11export function makePercentage(num: number) {
12 return Number(Math.max(0, Math.min(num, 100)).toFixed(2));
13}
14
15function isClickEvent(
16 evt: ActivityEvent,
17): evt is React.MouseEvent<HTMLElement> | MouseEvent {
18 return (
19 evt.type === "mousedown" ||
20 evt.type === "mouseup" ||
21 evt.type === "mousemove"
22 );
23}
24
25const getEventX = (evt: ActivityEvent) => {
26 return isClickEvent(evt) ? evt.pageX : evt.changedTouches[0].pageX;
27};
28
29export function useProgressBar(
30 barRef: RefObject<HTMLElement>,
31 commit: (percentage: number) => void,
32 commitImmediately = false,
33) {
34 const [mouseDown, setMouseDown] = useState<boolean>(false);
35 const [progress, setProgress] = useState<number>(0);
36
37 useEffect(() => {
38 function mouseMove(ev: ActivityEvent) {
39 if (!mouseDown || !barRef.current) return;
40 const rect = barRef.current.getBoundingClientRect();
41 const pos = (getEventX(ev) - rect.left) / barRef.current.offsetWidth;
42 setProgress(pos * 100);
43 if (commitImmediately) commit(pos);
44 }
45
46 function mouseUp(ev: ActivityEvent) {
47 if (!mouseDown) return;
48 setMouseDown(false);
49 document.body.removeAttribute("data-no-select");
50
51 if (!barRef.current) return;
52 const rect = barRef.current.getBoundingClientRect();
53 const pos = (getEventX(ev) - rect.left) / barRef.current.offsetWidth;
54 commit(pos);
55 }
56
57 document.addEventListener("mousemove", mouseMove);
58 document.addEventListener("touchmove", mouseMove);
59 document.addEventListener("mouseup", mouseUp);
60 document.addEventListener("touchend", mouseUp);
61
62 return () => {
63 document.removeEventListener("mousemove", mouseMove);
64 document.removeEventListener("touchmove", mouseMove);
65 document.removeEventListener("mouseup", mouseUp);
66 document.removeEventListener("touchend", mouseUp);
67 };
68 }, [mouseDown, barRef, commit, commitImmediately]);
69
70 const dragMouseDown = useCallback(
71 (ev: ActivityEvent) => {
72 setMouseDown(true);
73 document.body.setAttribute("data-no-select", "true");
74
75 if (!barRef.current) return;
76 const rect = barRef.current.getBoundingClientRect();
77 const pos =
78 ((getEventX(ev) - rect.left) / barRef.current.offsetWidth) * 100;
79 setProgress(pos);
80 },
81 [setProgress, barRef],
82 );
83
84 return {
85 dragging: mouseDown,
86 dragPercentage: progress,
87 dragMouseDown,
88 };
89}