Added color reducer. Performed state management refactor to prevent circular behavior.
This commit is contained in:
+86
-39
@@ -1,12 +1,15 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
import type { CartesianSpace } from "@/types";
|
||||
import type { CartesianSpace, Range, Setter } from "@/types";
|
||||
import { Direction } from "@/types";
|
||||
import {
|
||||
chooseValueByDirection,
|
||||
extractEventCoordinates,
|
||||
isLeftMouseButton,
|
||||
isTouchEvent,
|
||||
minmax,
|
||||
positionToValue,
|
||||
valueToPosition,
|
||||
} from "@/util";
|
||||
|
||||
import { useScroll } from "./scroll";
|
||||
@@ -16,19 +19,6 @@ if (typeof TouchEvent === "undefined") {
|
||||
window.TouchEvent = window.MouseEvent;
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
HORIZONTAL = "horizontal",
|
||||
VERTICAL = "vertical",
|
||||
}
|
||||
|
||||
function chooseValueByDirection(
|
||||
direction: Direction,
|
||||
xValue: number,
|
||||
yValue: number,
|
||||
) {
|
||||
return direction === Direction.HORIZONTAL ? xValue : yValue;
|
||||
}
|
||||
|
||||
function extractEventCoordinateByDirection(
|
||||
event: MouseEvent | TouchEvent,
|
||||
direction: Direction,
|
||||
@@ -41,43 +31,74 @@ export function useSlider({
|
||||
direction,
|
||||
origin,
|
||||
dimensions,
|
||||
setPosition,
|
||||
valueRange,
|
||||
value,
|
||||
setValue,
|
||||
}: {
|
||||
direction: Direction;
|
||||
origin: CartesianSpace;
|
||||
dimensions: CartesianSpace;
|
||||
setPosition: Dispatch<SetStateAction<number>>;
|
||||
valueRange: Range;
|
||||
value: number;
|
||||
setValue: Setter<number>;
|
||||
}) {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Slider UI refs
|
||||
const directionRef = useRef(direction);
|
||||
const originRef = useRef(origin);
|
||||
const dimensionsRef = useRef(dimensions);
|
||||
|
||||
// Slider value refs
|
||||
const setValueRef = useRef(setValue);
|
||||
const valueRangeRef = useRef(valueRange);
|
||||
const maxPosition = useRef(0);
|
||||
|
||||
// Internal position management
|
||||
const [position, setPosition] = useState(0);
|
||||
const positionRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
directionRef.current = direction;
|
||||
originRef.current = origin;
|
||||
dimensionsRef.current = dimensions;
|
||||
}, [direction, origin, dimensions]);
|
||||
maxPosition.current = chooseValueByDirection(
|
||||
direction,
|
||||
dimensions.x,
|
||||
dimensions.y,
|
||||
);
|
||||
valueRangeRef.current = valueRange;
|
||||
}, [direction, origin, dimensions, valueRangeRef]);
|
||||
|
||||
useEffect(() => {
|
||||
setValueRef.current = setValue;
|
||||
}, [setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
positionRef.current = position;
|
||||
}, [position]);
|
||||
|
||||
// Setup drag handlers
|
||||
const calculatePosition = useCallback(
|
||||
(event: MouseEvent | TouchEvent) => {
|
||||
const dir = directionRef.current;
|
||||
const orig = originRef.current;
|
||||
const dims = dimensionsRef.current;
|
||||
const calculatePosition = useCallback((event: MouseEvent | TouchEvent) => {
|
||||
const dir = directionRef.current;
|
||||
const orig = originRef.current;
|
||||
const dims = dimensionsRef.current;
|
||||
|
||||
const clientCoord = extractEventCoordinateByDirection(event, dir);
|
||||
const positionValue = minmax(
|
||||
clientCoord - chooseValueByDirection(dir, orig.x, orig.y),
|
||||
0,
|
||||
chooseValueByDirection(dir, dims.x, dims.y),
|
||||
);
|
||||
setPosition(positionValue);
|
||||
},
|
||||
[setPosition],
|
||||
);
|
||||
const clientCoord = extractEventCoordinateByDirection(event, dir);
|
||||
const newPosition = minmax(
|
||||
clientCoord - chooseValueByDirection(dir, orig.x, orig.y),
|
||||
0,
|
||||
chooseValueByDirection(dir, dims.x, dims.y),
|
||||
);
|
||||
const newValue = positionToValue(
|
||||
newPosition,
|
||||
maxPosition.current,
|
||||
valueRangeRef.current,
|
||||
);
|
||||
|
||||
setValueRef.current(newValue);
|
||||
}, []);
|
||||
|
||||
const handleMove = useCallback(
|
||||
(event: MouseEvent | TouchEvent) => {
|
||||
@@ -120,20 +141,37 @@ export function useSlider({
|
||||
const dims = dimensionsRef.current;
|
||||
const inc = chooseValueByDirection(dir, 1, -1);
|
||||
|
||||
setPosition((prev: number) =>
|
||||
minmax(prev + inc, 0, chooseValueByDirection(dir, dims.x, dims.y)),
|
||||
const newPosition = minmax(
|
||||
positionRef.current + inc,
|
||||
0,
|
||||
chooseValueByDirection(dir, dims.x, dims.y),
|
||||
);
|
||||
}, [setPosition]);
|
||||
const newValue = positionToValue(
|
||||
newPosition,
|
||||
maxPosition.current,
|
||||
valueRangeRef.current,
|
||||
);
|
||||
setValueRef.current(newValue);
|
||||
}, []);
|
||||
|
||||
const handleScrollDown = useCallback(() => {
|
||||
const dir = directionRef.current;
|
||||
const dims = dimensionsRef.current;
|
||||
const inc = chooseValueByDirection(dir, -1, 1);
|
||||
|
||||
setPosition((prev: number) =>
|
||||
minmax(prev + inc, 0, chooseValueByDirection(dir, dims.x, dims.y)),
|
||||
const newPosition = minmax(
|
||||
positionRef.current + inc,
|
||||
0,
|
||||
chooseValueByDirection(dir, dims.x, dims.y),
|
||||
);
|
||||
}, [setPosition]);
|
||||
const newValue = positionToValue(
|
||||
newPosition,
|
||||
maxPosition.current,
|
||||
valueRangeRef.current,
|
||||
);
|
||||
|
||||
setValueRef.current(newValue);
|
||||
}, []);
|
||||
|
||||
const { addScrollListener, removeScrollListener } = useScroll({
|
||||
targetRef: sliderRef,
|
||||
@@ -141,6 +179,15 @@ export function useSlider({
|
||||
onScrollDown: handleScrollDown,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const newPosition = valueToPosition(
|
||||
value,
|
||||
maxPosition.current,
|
||||
valueRangeRef.current,
|
||||
);
|
||||
setPosition(newPosition);
|
||||
}, [value, setPosition]);
|
||||
|
||||
// Set up entry listeners
|
||||
useEffect(() => {
|
||||
const currentRef = sliderRef.current;
|
||||
|
||||
Reference in New Issue
Block a user