Refactored files.

This commit is contained in:
Jay
2025-08-01 20:24:19 -04:00
parent fa641c186c
commit c6118c8b0d
5 changed files with 125 additions and 140 deletions
+36 -51
View File
@@ -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 { Dispatch, SetStateAction } from "react";
import type { CartesianSpace } from "../types"; import type { CartesianSpace } from "../types";
import { minmax } from "../util"; import {
extractEventCoordinates,
isLeftMouseButton,
isTouchEvent,
minmax,
} from "../util";
if (typeof TouchEvent === "undefined") { if (typeof TouchEvent === "undefined") {
// @ts-ignore - intentionally creating global // @ts-ignore - intentionally creating global
window.TouchEvent = window.MouseEvent; 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({ export function useCrosshair({
origin, origin,
dimensions, dimensions,
@@ -65,7 +51,7 @@ export function useCrosshair({
[setXPosition, setYPosition], [setXPosition, setYPosition],
); );
const processCrosshairInteraction = useCallback( const handleMove = useCallback(
(event: MouseEvent | TouchEvent) => { (event: MouseEvent | TouchEvent) => {
event.preventDefault(); event.preventDefault();
calculatePositions(event); calculatePositions(event);
@@ -73,69 +59,68 @@ export function useCrosshair({
[calculatePositions], [calculatePositions],
); );
const endCrosshairInteraction = useCallback( const handleEnd = useCallback(
(event: MouseEvent | TouchEvent) => { (event: MouseEvent | TouchEvent) => {
setIsDragging(false); setIsDragging(false);
if (!isTouchEvent(event)) { if (!isTouchEvent(event)) {
document.removeEventListener("mousemove", processCrosshairInteraction); document.removeEventListener("mousemove", handleMove);
document.removeEventListener("mouseup", endCrosshairInteraction); document.removeEventListener("mouseup", handleEnd);
} else { } else {
document.removeEventListener("touchmove", processCrosshairInteraction); document.removeEventListener("touchmove", handleMove);
document.removeEventListener("touchend", endCrosshairInteraction); document.removeEventListener("touchend", handleEnd);
document.removeEventListener("touchcancel", endCrosshairInteraction); document.removeEventListener("touchcancel", handleEnd);
} }
}, },
[processCrosshairInteraction], [handleMove],
); );
const startCrosshairInteraction = useCallback( const handleStart = useCallback(
(event: MouseEvent | TouchEvent) => { (event: MouseEvent | TouchEvent) => {
if (!isTouchEvent(event) && !isLeftMouseButton(event.buttons)) {
return;
}
event.preventDefault(); event.preventDefault();
calculatePositions(event); calculatePositions(event);
setIsDragging(true); setIsDragging(true);
if (!isTouchEvent(event)) { if (!isTouchEvent(event)) {
document.addEventListener("mousemove", processCrosshairInteraction); document.addEventListener("mousemove", handleMove);
document.addEventListener("mouseup", endCrosshairInteraction, { document.addEventListener("mouseup", handleEnd, {
passive: true, passive: true,
}); });
} else { } else {
document.addEventListener("touchmove", processCrosshairInteraction); document.addEventListener("touchmove", handleMove);
document.addEventListener("touchend", endCrosshairInteraction, { document.addEventListener("touchend", handleEnd, {
passive: true, passive: true,
}); });
document.addEventListener("touchcancel", endCrosshairInteraction, { document.addEventListener("touchcancel", handleEnd, {
passive: true, passive: true,
}); });
} }
}, },
[calculatePositions, processCrosshairInteraction, endCrosshairInteraction], [calculatePositions, handleMove, handleEnd],
); );
useEffect(() => { useEffect(() => {
const currentRef = crosshairRef.current; const currentRef = crosshairRef.current;
if (currentRef) { if (currentRef) {
currentRef.addEventListener("mousedown", startCrosshairInteraction); currentRef.addEventListener("mousedown", handleStart);
currentRef.addEventListener("touchstart", startCrosshairInteraction); currentRef.addEventListener("touchstart", handleStart);
} }
return () => { return () => {
if (currentRef) { if (currentRef) {
currentRef.removeEventListener("mousedown", startCrosshairInteraction); currentRef.removeEventListener("mousedown", handleStart);
currentRef.removeEventListener("touchstart", startCrosshairInteraction); currentRef.removeEventListener("touchstart", handleStart);
} }
document.removeEventListener("mousemove", processCrosshairInteraction); document.removeEventListener("mousemove", handleMove);
document.removeEventListener("mouseup", endCrosshairInteraction); document.removeEventListener("mouseup", handleEnd);
document.removeEventListener("touchmove", processCrosshairInteraction); document.removeEventListener("touchmove", handleMove);
document.removeEventListener("touchend", endCrosshairInteraction); document.removeEventListener("touchend", handleEnd);
document.removeEventListener("touchcancel", endCrosshairInteraction); document.removeEventListener("touchcancel", handleEnd);
}; };
}, [ }, [handleStart, handleMove, handleEnd]);
startCrosshairInteraction,
processCrosshairInteraction,
endCrosshairInteraction,
]);
return { crosshairRef, isDragging }; return { crosshairRef, isDragging };
} }
+34 -53
View File
@@ -15,10 +15,6 @@ if (typeof TouchEvent === "undefined") {
window.TouchEvent = window.MouseEvent; window.TouchEvent = window.MouseEvent;
} }
function isTouchEvent(event: Event): event is TouchEvent {
return "touches" in event;
}
export enum Direction { export enum Direction {
HORIZONTAL = "horizontal", HORIZONTAL = "horizontal",
VERTICAL = "vertical", VERTICAL = "vertical",
@@ -32,18 +28,12 @@ function chooseValueByDirection(
return direction === Direction.HORIZONTAL ? xValue : yValue; return direction === Direction.HORIZONTAL ? xValue : yValue;
} }
function extractEventCoordinate( function extractEventCoordinateByDirection(
event: MouseEvent | TouchEvent, event: MouseEvent | TouchEvent,
direction: Direction, direction: Direction,
): number { ): number {
if (isTouchEvent(event)) { const { clientX, clientY } = extractEventCoordinates(event);
return chooseValueByDirection( return chooseValueByDirection(direction, clientX, clientY);
direction,
event.touches[0].clientX,
event.touches[0].clientY,
);
}
return chooseValueByDirection(direction, event.clientX, event.clientY);
} }
export function useSlider({ export function useSlider({
@@ -77,7 +67,7 @@ export function useSlider({
const orig = originRef.current; const orig = originRef.current;
const dims = dimensionsRef.current; const dims = dimensionsRef.current;
const clientCoord = extractEventCoordinate(event, dir); const clientCoord = extractEventCoordinateByDirection(event, dir);
const positionValue = minmax( const positionValue = minmax(
clientCoord - chooseValueByDirection(dir, orig.x, orig.y), clientCoord - chooseValueByDirection(dir, orig.x, orig.y),
0, 0,
@@ -88,7 +78,7 @@ export function useSlider({
[setPosition], [setPosition],
); );
const processSliderInteraction = useCallback( const handleMove = useCallback(
(event: MouseEvent | TouchEvent) => { (event: MouseEvent | TouchEvent) => {
event.preventDefault(); event.preventDefault();
calculatePosition(event); calculatePosition(event);
@@ -96,43 +86,34 @@ export function useSlider({
[calculatePosition], [calculatePosition],
); );
const endSliderInteraction = useCallback( const handleEnd = useCallback(
(event: MouseEvent | TouchEvent) => { (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); 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) => { (event: MouseEvent | TouchEvent) => {
if (!isTouchEvent(event) && !isLeftMouseButton(event.buttons)) {
return;
}
event.preventDefault(); event.preventDefault();
calculatePosition(event); calculatePosition(event);
setIsDragging(true); setIsDragging(true);
if (!isTouchEvent(event)) { document.addEventListener("mousemove", handleMove);
document.addEventListener("mousemove", processSliderInteraction); document.addEventListener("mouseup", handleEnd, { passive: true });
document.addEventListener("mouseup", endSliderInteraction, { document.addEventListener("touchmove", handleMove);
passive: true, document.addEventListener("touchend", handleEnd, { passive: true });
}); document.addEventListener("touchcancel", handleEnd, { passive: true });
} else {
document.addEventListener("touchmove", processSliderInteraction);
document.addEventListener("touchend", endSliderInteraction, {
passive: true,
});
document.addEventListener("touchcancel", endSliderInteraction, {
passive: true,
});
}
}, },
[calculatePosition, processSliderInteraction, endSliderInteraction], [calculatePosition, handleMove, handleEnd],
); );
// Setup scroll handlers // Setup scroll handlers
@@ -165,29 +146,29 @@ export function useSlider({
const currentRef = sliderRef.current; const currentRef = sliderRef.current;
if (currentRef) { if (currentRef) {
addScrollListener(); addScrollListener();
currentRef.addEventListener("mousedown", startSliderInteraction); currentRef.addEventListener("mousedown", handleStart);
currentRef.addEventListener("touchstart", startSliderInteraction); currentRef.addEventListener("touchstart", handleStart);
} }
return () => { return () => {
if (currentRef) { if (currentRef) {
removeScrollListener(); removeScrollListener();
currentRef.removeEventListener("mousedown", startSliderInteraction); currentRef.removeEventListener("mousedown", handleStart);
currentRef.removeEventListener("touchstart", startSliderInteraction); currentRef.removeEventListener("touchstart", handleStart);
} }
document.removeEventListener("mousemove", processSliderInteraction); document.removeEventListener("mousemove", handleMove);
document.removeEventListener("mouseup", endSliderInteraction); document.removeEventListener("mouseup", handleEnd);
document.removeEventListener("touchmove", processSliderInteraction); document.removeEventListener("touchmove", handleMove);
document.removeEventListener("touchend", endSliderInteraction); document.removeEventListener("touchend", handleEnd);
document.removeEventListener("touchcancel", endSliderInteraction); document.removeEventListener("touchcancel", handleEnd);
}; };
}, [ }, [
addScrollListener, addScrollListener,
removeScrollListener, removeScrollListener,
startSliderInteraction, handleStart,
processSliderInteraction, handleMove,
endSliderInteraction, handleEnd,
]); ]);
return { sliderRef, isDragging }; return { sliderRef, isDragging };
+1
View File
@@ -118,6 +118,7 @@ const triggerMouseEvent = (eventType: string, x: number, y: number) => {
cy.dataCy("crosshair-container").trigger(eventType, { cy.dataCy("crosshair-container").trigger(eventType, {
clientX: x, clientX: x,
clientY: y, clientY: y,
buttons: 1,
eventConstructor: "MouseEvent", eventConstructor: "MouseEvent",
}); });
}; };
+30 -36
View File
@@ -1,6 +1,7 @@
import { useState, useRef, useEffect } from "react"; import { useEffect, useRef, useState } from "react";
import { useSlider, Direction } from "../slider";
import type { CartesianSpace } from "../../types"; import type { CartesianSpace } from "../../types";
import { Direction, useSlider } from "../slider";
// Test Fixtures // Test Fixtures
@@ -111,6 +112,7 @@ function createTestUtils(isHorizontal = true) {
cy.dataCy("slider-container").trigger(eventType, { cy.dataCy("slider-container").trigger(eventType, {
clientX: x, clientX: x,
clientY: y, clientY: y,
buttons: 1,
eventConstructor: "MouseEvent", eventConstructor: "MouseEvent",
}); });
}; };
@@ -176,28 +178,24 @@ describe("horizontal slider hook tests", () => {
assertPosition(164); assertPosition(164);
}); });
if (isTouchSupported()) { it("moves the slider with touch events.", () => {
it("moves the slider with touch events.", () => { assertPosition(0);
assertPosition(0);
triggerTouchEvent("touchstart", 86, 53); triggerTouchEvent("touchstart", 86, 53);
assertPosition(0); assertPosition(0);
triggerTouchEvent("touchmove", 150, 150); triggerTouchEvent("touchmove", 150, 150);
assertPosition(64); assertPosition(64);
triggerTouchEvent("touchmove", 500, 500); triggerTouchEvent("touchmove", 500, 500);
assertPosition(250); assertPosition(250);
triggerTouchEvent("touchmove", 250, 250); triggerTouchEvent("touchmove", 250, 250);
assertPosition(164); assertPosition(164);
triggerTouchEvent("touchend", 250, 250); triggerTouchEvent("touchend", 250, 250);
assertPosition(164); assertPosition(164);
}); });
} else {
console.log("Skipping Unsupported Touch Event Tests");
}
it("moves the slider with mouse wheel scrolling", () => { it("moves the slider with mouse wheel scrolling", () => {
assertPosition(0); assertPosition(0);
@@ -256,28 +254,24 @@ describe("vertical slider hook tests", () => {
assertPosition(172); assertPosition(172);
}); });
if (isTouchSupported()) { it("moves the slider with touch events.", () => {
it("moves the slider with touch events.", () => { assertPosition(0);
assertPosition(0);
triggerTouchEvent("touchstart", 86, 53); triggerTouchEvent("touchstart", 86, 53);
assertPosition(0); assertPosition(0);
triggerTouchEvent("touchmove", 150, 150); triggerTouchEvent("touchmove", 150, 150);
assertPosition(72); assertPosition(72);
triggerTouchEvent("touchmove", 500, 500); triggerTouchEvent("touchmove", 500, 500);
assertPosition(250); assertPosition(250);
triggerTouchEvent("touchmove", 250, 250); triggerTouchEvent("touchmove", 250, 250);
assertPosition(172); assertPosition(172);
triggerTouchEvent("touchend", 250, 250); triggerTouchEvent("touchend", 250, 250);
assertPosition(172); assertPosition(172);
}); });
} else {
console.log("Skipping Unsupported Touch Event Tests");
}
it("moves the slider with mouse wheel scrolling", () => { it("moves the slider with mouse wheel scrolling", () => {
assertPosition(0); assertPosition(0);
+24
View File
@@ -1,3 +1,27 @@
export function minmax(number: number, min: number, max: number) { export function minmax(number: number, min: number, max: number) {
return Math.min(max, Math.max(min, 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,
};
}