From c6118c8b0d88eb40dd7c38c191fb6f3b3b56d112 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 1 Aug 2025 20:24:19 -0400 Subject: [PATCH] Refactored files. --- src/hooks/crosshair.tsx | 87 ++++++++++++---------------- src/hooks/slider.tsx | 87 +++++++++++----------------- src/hooks/tests/crosshairTest.cy.tsx | 1 + src/hooks/tests/sliderTest.cy.tsx | 66 ++++++++++----------- src/util.ts | 24 ++++++++ 5 files changed, 125 insertions(+), 140 deletions(-) diff --git a/src/hooks/crosshair.tsx b/src/hooks/crosshair.tsx index eea0f4a..e6ba5dc 100644 --- a/src/hooks/crosshair.tsx +++ b/src/hooks/crosshair.tsx @@ -1,33 +1,19 @@ -import { useEffect, useState, useRef, useCallback } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import type { Dispatch, SetStateAction } from "react"; + import type { CartesianSpace } from "../types"; -import { minmax } from "../util"; +import { + extractEventCoordinates, + isLeftMouseButton, + isTouchEvent, + minmax, +} from "../util"; if (typeof TouchEvent === "undefined") { // @ts-ignore - intentionally creating global window.TouchEvent = window.MouseEvent; } -function isTouchEvent(event: Event): event is TouchEvent { - return "touches" in event; -} - -function extractEventCoordinates(event: MouseEvent | TouchEvent): { - clientX: number; - clientY: number; -} { - if (isTouchEvent(event)) { - return { - clientX: event.touches[0].clientX, - clientY: event.touches[0].clientY, - }; - } - return { - clientX: event.clientX, - clientY: event.clientY, - }; -} - export function useCrosshair({ origin, dimensions, @@ -65,7 +51,7 @@ export function useCrosshair({ [setXPosition, setYPosition], ); - const processCrosshairInteraction = useCallback( + const handleMove = useCallback( (event: MouseEvent | TouchEvent) => { event.preventDefault(); calculatePositions(event); @@ -73,69 +59,68 @@ export function useCrosshair({ [calculatePositions], ); - const endCrosshairInteraction = useCallback( + const handleEnd = useCallback( (event: MouseEvent | TouchEvent) => { setIsDragging(false); if (!isTouchEvent(event)) { - document.removeEventListener("mousemove", processCrosshairInteraction); - document.removeEventListener("mouseup", endCrosshairInteraction); + document.removeEventListener("mousemove", handleMove); + document.removeEventListener("mouseup", handleEnd); } else { - document.removeEventListener("touchmove", processCrosshairInteraction); - document.removeEventListener("touchend", endCrosshairInteraction); - document.removeEventListener("touchcancel", endCrosshairInteraction); + document.removeEventListener("touchmove", handleMove); + document.removeEventListener("touchend", handleEnd); + document.removeEventListener("touchcancel", handleEnd); } }, - [processCrosshairInteraction], + [handleMove], ); - const startCrosshairInteraction = useCallback( + const handleStart = useCallback( (event: MouseEvent | TouchEvent) => { + if (!isTouchEvent(event) && !isLeftMouseButton(event.buttons)) { + return; + } event.preventDefault(); calculatePositions(event); setIsDragging(true); if (!isTouchEvent(event)) { - document.addEventListener("mousemove", processCrosshairInteraction); - document.addEventListener("mouseup", endCrosshairInteraction, { + document.addEventListener("mousemove", handleMove); + document.addEventListener("mouseup", handleEnd, { passive: true, }); } else { - document.addEventListener("touchmove", processCrosshairInteraction); - document.addEventListener("touchend", endCrosshairInteraction, { + document.addEventListener("touchmove", handleMove); + document.addEventListener("touchend", handleEnd, { passive: true, }); - document.addEventListener("touchcancel", endCrosshairInteraction, { + document.addEventListener("touchcancel", handleEnd, { passive: true, }); } }, - [calculatePositions, processCrosshairInteraction, endCrosshairInteraction], + [calculatePositions, handleMove, handleEnd], ); useEffect(() => { const currentRef = crosshairRef.current; if (currentRef) { - currentRef.addEventListener("mousedown", startCrosshairInteraction); - currentRef.addEventListener("touchstart", startCrosshairInteraction); + currentRef.addEventListener("mousedown", handleStart); + currentRef.addEventListener("touchstart", handleStart); } return () => { if (currentRef) { - currentRef.removeEventListener("mousedown", startCrosshairInteraction); - currentRef.removeEventListener("touchstart", startCrosshairInteraction); + currentRef.removeEventListener("mousedown", handleStart); + currentRef.removeEventListener("touchstart", handleStart); } - document.removeEventListener("mousemove", processCrosshairInteraction); - document.removeEventListener("mouseup", endCrosshairInteraction); - document.removeEventListener("touchmove", processCrosshairInteraction); - document.removeEventListener("touchend", endCrosshairInteraction); - document.removeEventListener("touchcancel", endCrosshairInteraction); + document.removeEventListener("mousemove", handleMove); + document.removeEventListener("mouseup", handleEnd); + document.removeEventListener("touchmove", handleMove); + document.removeEventListener("touchend", handleEnd); + document.removeEventListener("touchcancel", handleEnd); }; - }, [ - startCrosshairInteraction, - processCrosshairInteraction, - endCrosshairInteraction, - ]); + }, [handleStart, handleMove, handleEnd]); return { crosshairRef, isDragging }; } diff --git a/src/hooks/slider.tsx b/src/hooks/slider.tsx index a724f5a..090e147 100644 --- a/src/hooks/slider.tsx +++ b/src/hooks/slider.tsx @@ -15,10 +15,6 @@ if (typeof TouchEvent === "undefined") { window.TouchEvent = window.MouseEvent; } -function isTouchEvent(event: Event): event is TouchEvent { - return "touches" in event; -} - export enum Direction { HORIZONTAL = "horizontal", VERTICAL = "vertical", @@ -32,18 +28,12 @@ function chooseValueByDirection( return direction === Direction.HORIZONTAL ? xValue : yValue; } -function extractEventCoordinate( +function extractEventCoordinateByDirection( event: MouseEvent | TouchEvent, direction: Direction, ): number { - if (isTouchEvent(event)) { - return chooseValueByDirection( - direction, - event.touches[0].clientX, - event.touches[0].clientY, - ); - } - return chooseValueByDirection(direction, event.clientX, event.clientY); + const { clientX, clientY } = extractEventCoordinates(event); + return chooseValueByDirection(direction, clientX, clientY); } export function useSlider({ @@ -77,7 +67,7 @@ export function useSlider({ const orig = originRef.current; const dims = dimensionsRef.current; - const clientCoord = extractEventCoordinate(event, dir); + const clientCoord = extractEventCoordinateByDirection(event, dir); const positionValue = minmax( clientCoord - chooseValueByDirection(dir, orig.x, orig.y), 0, @@ -88,7 +78,7 @@ export function useSlider({ [setPosition], ); - const processSliderInteraction = useCallback( + const handleMove = useCallback( (event: MouseEvent | TouchEvent) => { event.preventDefault(); calculatePosition(event); @@ -96,43 +86,34 @@ export function useSlider({ [calculatePosition], ); - const endSliderInteraction = useCallback( + const handleEnd = useCallback( (event: MouseEvent | TouchEvent) => { + document.removeEventListener("mousemove", handleMove); + document.removeEventListener("mouseup", handleEnd); + document.removeEventListener("touchmove", handleMove); + document.removeEventListener("touchend", handleEnd); + document.removeEventListener("touchcancel", handleEnd); setIsDragging(false); - if (!isTouchEvent(event)) { - document.removeEventListener("mousemove", processSliderInteraction); - document.removeEventListener("mouseup", endSliderInteraction); - } else { - document.removeEventListener("touchmove", processSliderInteraction); - document.removeEventListener("touchend", endSliderInteraction); - document.removeEventListener("touchcancel", endSliderInteraction); - } }, - [processSliderInteraction], + [handleMove], ); - const startSliderInteraction = useCallback( + const handleStart = useCallback( (event: MouseEvent | TouchEvent) => { + if (!isTouchEvent(event) && !isLeftMouseButton(event.buttons)) { + return; + } event.preventDefault(); calculatePosition(event); setIsDragging(true); - if (!isTouchEvent(event)) { - document.addEventListener("mousemove", processSliderInteraction); - document.addEventListener("mouseup", endSliderInteraction, { - passive: true, - }); - } else { - document.addEventListener("touchmove", processSliderInteraction); - document.addEventListener("touchend", endSliderInteraction, { - passive: true, - }); - document.addEventListener("touchcancel", endSliderInteraction, { - passive: true, - }); - } + document.addEventListener("mousemove", handleMove); + document.addEventListener("mouseup", handleEnd, { passive: true }); + document.addEventListener("touchmove", handleMove); + document.addEventListener("touchend", handleEnd, { passive: true }); + document.addEventListener("touchcancel", handleEnd, { passive: true }); }, - [calculatePosition, processSliderInteraction, endSliderInteraction], + [calculatePosition, handleMove, handleEnd], ); // Setup scroll handlers @@ -165,29 +146,29 @@ export function useSlider({ const currentRef = sliderRef.current; if (currentRef) { addScrollListener(); - currentRef.addEventListener("mousedown", startSliderInteraction); - currentRef.addEventListener("touchstart", startSliderInteraction); + currentRef.addEventListener("mousedown", handleStart); + currentRef.addEventListener("touchstart", handleStart); } return () => { if (currentRef) { removeScrollListener(); - currentRef.removeEventListener("mousedown", startSliderInteraction); - currentRef.removeEventListener("touchstart", startSliderInteraction); + currentRef.removeEventListener("mousedown", handleStart); + currentRef.removeEventListener("touchstart", handleStart); } - document.removeEventListener("mousemove", processSliderInteraction); - document.removeEventListener("mouseup", endSliderInteraction); - document.removeEventListener("touchmove", processSliderInteraction); - document.removeEventListener("touchend", endSliderInteraction); - document.removeEventListener("touchcancel", endSliderInteraction); + document.removeEventListener("mousemove", handleMove); + document.removeEventListener("mouseup", handleEnd); + document.removeEventListener("touchmove", handleMove); + document.removeEventListener("touchend", handleEnd); + document.removeEventListener("touchcancel", handleEnd); }; }, [ addScrollListener, removeScrollListener, - startSliderInteraction, - processSliderInteraction, - endSliderInteraction, + handleStart, + handleMove, + handleEnd, ]); return { sliderRef, isDragging }; diff --git a/src/hooks/tests/crosshairTest.cy.tsx b/src/hooks/tests/crosshairTest.cy.tsx index 32761c8..e84db0f 100644 --- a/src/hooks/tests/crosshairTest.cy.tsx +++ b/src/hooks/tests/crosshairTest.cy.tsx @@ -118,6 +118,7 @@ const triggerMouseEvent = (eventType: string, x: number, y: number) => { cy.dataCy("crosshair-container").trigger(eventType, { clientX: x, clientY: y, + buttons: 1, eventConstructor: "MouseEvent", }); }; diff --git a/src/hooks/tests/sliderTest.cy.tsx b/src/hooks/tests/sliderTest.cy.tsx index 17b4347..8e22a9f 100644 --- a/src/hooks/tests/sliderTest.cy.tsx +++ b/src/hooks/tests/sliderTest.cy.tsx @@ -1,6 +1,7 @@ -import { useState, useRef, useEffect } from "react"; -import { useSlider, Direction } from "../slider"; +import { useEffect, useRef, useState } from "react"; + import type { CartesianSpace } from "../../types"; +import { Direction, useSlider } from "../slider"; // Test Fixtures @@ -111,6 +112,7 @@ function createTestUtils(isHorizontal = true) { cy.dataCy("slider-container").trigger(eventType, { clientX: x, clientY: y, + buttons: 1, eventConstructor: "MouseEvent", }); }; @@ -176,28 +178,24 @@ describe("horizontal slider hook tests", () => { assertPosition(164); }); - if (isTouchSupported()) { - it("moves the slider with touch events.", () => { - assertPosition(0); + it("moves the slider with touch events.", () => { + assertPosition(0); - triggerTouchEvent("touchstart", 86, 53); - assertPosition(0); + triggerTouchEvent("touchstart", 86, 53); + assertPosition(0); - triggerTouchEvent("touchmove", 150, 150); - assertPosition(64); + triggerTouchEvent("touchmove", 150, 150); + assertPosition(64); - triggerTouchEvent("touchmove", 500, 500); - assertPosition(250); + triggerTouchEvent("touchmove", 500, 500); + assertPosition(250); - triggerTouchEvent("touchmove", 250, 250); - assertPosition(164); + triggerTouchEvent("touchmove", 250, 250); + assertPosition(164); - triggerTouchEvent("touchend", 250, 250); - assertPosition(164); - }); - } else { - console.log("Skipping Unsupported Touch Event Tests"); - } + triggerTouchEvent("touchend", 250, 250); + assertPosition(164); + }); it("moves the slider with mouse wheel scrolling", () => { assertPosition(0); @@ -256,28 +254,24 @@ describe("vertical slider hook tests", () => { assertPosition(172); }); - if (isTouchSupported()) { - it("moves the slider with touch events.", () => { - assertPosition(0); + it("moves the slider with touch events.", () => { + assertPosition(0); - triggerTouchEvent("touchstart", 86, 53); - assertPosition(0); + triggerTouchEvent("touchstart", 86, 53); + assertPosition(0); - triggerTouchEvent("touchmove", 150, 150); - assertPosition(72); + triggerTouchEvent("touchmove", 150, 150); + assertPosition(72); - triggerTouchEvent("touchmove", 500, 500); - assertPosition(250); + triggerTouchEvent("touchmove", 500, 500); + assertPosition(250); - triggerTouchEvent("touchmove", 250, 250); - assertPosition(172); + triggerTouchEvent("touchmove", 250, 250); + assertPosition(172); - triggerTouchEvent("touchend", 250, 250); - assertPosition(172); - }); - } else { - console.log("Skipping Unsupported Touch Event Tests"); - } + triggerTouchEvent("touchend", 250, 250); + assertPosition(172); + }); it("moves the slider with mouse wheel scrolling", () => { assertPosition(0); diff --git a/src/util.ts b/src/util.ts index 37924e4..4348d42 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,3 +1,27 @@ export function minmax(number: number, min: number, max: number) { return Math.min(max, Math.max(min, number)); } + +export function isTouchEvent(event: Event): event is TouchEvent { + return "touches" in event; +} + +export function isLeftMouseButton(buttonsValue: number) { + return buttonsValue === 1; +} + +export function extractEventCoordinates(event: MouseEvent | TouchEvent): { + clientX: number; + clientY: number; +} { + if (isTouchEvent(event)) { + return { + clientX: event.touches[0].clientX, + clientY: event.touches[0].clientY, + }; + } + return { + clientX: event.clientX, + clientY: event.clientY, + }; +}