Skip to content

Commit

Permalink
Some polish
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Jan 3, 2025
1 parent 4235041 commit 2a81e76
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 99 deletions.
2 changes: 1 addition & 1 deletion packages/react/src/slider/control/SliderControl.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const testRootContext: SliderRootContext = {
disabled: false,
getFingerState: () => ({
value: 0,
percentageValue: 0,
valueRescaled: 0,
percentageValues: [0],
thumbIndex: 0,
}),
Expand Down
41 changes: 33 additions & 8 deletions packages/react/src/slider/control/useSliderControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,41 @@ import { useForkRef } from '../../utils/useForkRef';
import { useEventCallback } from '../../utils/useEventCallback';
import {
focusThumb,
trackFinger,
validateMinimumDistance,
type FingerPosition,
type useSliderRoot,
} from '../root/useSliderRoot';
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation';

const INTENTIONAL_DRAG_COUNT_THRESHOLD = 2;

function trackFinger(
event: TouchEvent | PointerEvent | React.PointerEvent,
touchIdRef: React.RefObject<any>,
): FingerPosition | null {
// The event is TouchEvent
if (touchIdRef.current !== undefined && (event as TouchEvent).changedTouches) {
const touchEvent = event as TouchEvent;
for (let i = 0; i < touchEvent.changedTouches.length; i += 1) {
const touch = touchEvent.changedTouches[i];
if (touch.identifier === touchIdRef.current) {
return {
x: touch.clientX,
y: touch.clientY,
};
}
}

return null;
}

// The event is PointerEvent
return {
x: (event as PointerEvent).clientX,
y: (event as PointerEvent).clientY,
};
}

export function useSliderControl(
parameters: useSliderControl.Parameters,
): useSliderControl.ReturnValue {
Expand Down Expand Up @@ -42,12 +69,11 @@ export function useSliderControl(

// A number that uniquely identifies the current finger in the touch session.
const touchIdRef = React.useRef<number | null>(null);

const moveCountRef = React.useRef(0);

// offset distance between:
// 1. pointerDown coordinates and
// 2. the exact intersection of the center of the thumb and the track
/**
* The difference between the value at the finger origin and the value at
* the center of the thumb scaled down to fit the range [0, 1]
*/
const offsetRef = React.useRef(0);

const handleTouchMove = useEventCallback((nativeEvent: TouchEvent | PointerEvent) => {
Expand Down Expand Up @@ -209,8 +235,7 @@ export function useSliderControl(
// percentageValue difference represented by the distance between the click origin
// and the coordinates of the value on the track area
if (thumbRefs.current.includes(event.target as HTMLElement)) {
offsetRef.current =
percentageValues[finger.thumbIndex] / 100 - finger.percentageValue;
offsetRef.current = percentageValues[finger.thumbIndex] / 100 - finger.valueRescaled;
} else {
setValue(finger.value, finger.percentageValues, finger.thumbIndex, event.nativeEvent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const testRootContext: SliderRootContext = {
disabled: false,
getFingerState: () => ({
value: 0,
percentageValue: 0,
valueRescaled: 0,
percentageValues: [0],
thumbIndex: 0,
}),
Expand Down
87 changes: 27 additions & 60 deletions packages/react/src/slider/root/useSliderRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { useFieldRootContext } from '../../field/root/FieldRootContext';
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation';
import { asc } from '../utils/asc';
import { getSliderValue } from '../utils/getSliderValue';
import { percentToValue } from '../utils/percentToValue';
import { replaceArrayItemAtIndex } from '../utils/replaceArrayItemAtIndex';
import { roundValueToStep } from '../utils/roundValueToStep';
import { ThumbMetadata } from '../thumb/useSliderThumb';
Expand Down Expand Up @@ -104,33 +103,6 @@ export function validateMinimumDistance(
return Math.min(...distances) >= step * minStepsBetweenValues;
}

export function trackFinger(
event: TouchEvent | PointerEvent | React.PointerEvent,
touchIdRef: React.RefObject<any>,
): FingerPosition | null {
// The event is TouchEvent
if (touchIdRef.current !== undefined && (event as TouchEvent).changedTouches) {
const touchEvent = event as TouchEvent;
for (let i = 0; i < touchEvent.changedTouches.length; i += 1) {
const touch = touchEvent.changedTouches[i];
if (touch.identifier === touchIdRef.current) {
return {
x: touch.clientX,
y: touch.clientY,
};
}
}

return null;
}

// The event is PointerEvent
return {
x: (event as PointerEvent).clientX,
y: (event as PointerEvent).clientY,
};
}

/**
*/
export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRoot.ReturnValue {
Expand Down Expand Up @@ -211,13 +183,13 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
const range = Array.isArray(valueUnwrapped);

const values = React.useMemo(() => {
return (range ? valueUnwrapped.slice().sort(asc) : [valueUnwrapped]).map((val) =>
val == null ? min : clamp(val, min, max),
);
if (!range) {
return [clamp(valueUnwrapped as number, min, max)];
}
return valueUnwrapped.slice().sort(asc);
}, [max, min, range, valueUnwrapped]);

function initializePercentageValues() {
// console.log('initializePercentageValues');
const vals = [];
for (let i = 0; i < values.length; i += 1) {
vals.push(valueToPercent(values[i], min, max));
Expand All @@ -228,7 +200,6 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
const [percentageValues, setPercentageValues] = React.useState<readonly number[]>(
initializePercentageValues,
);
// console.log('percentageValues', percentageValues);

const setValue = useEventCallback(
(
Expand Down Expand Up @@ -263,14 +234,7 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo

const handleInputChange = useEventCallback(
(valueInput: number, index: number, event: React.KeyboardEvent | React.ChangeEvent) => {
const newValue = getSliderValue({
valueInput,
min,
max,
index,
range,
values,
});
const newValue = getSliderValue(valueInput, index, min, max, range, values);

if (range) {
focusThumb(index, sliderRef);
Expand Down Expand Up @@ -310,10 +274,11 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
*/
shouldCaptureThumbIndex: boolean = false,
/**
* The pixel distance between the finger origin and the center of the thumb.
* The difference between the value at the finger origin and the value at
* the center of the thumb scaled down to fit the range [0, 1]
*/
offset: number = 0,
) => {
): FingerState | null => {
if (fingerPosition == null) {
return null;
}
Expand All @@ -329,26 +294,26 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo

const { width, height, bottom, left } = sliderControl.getBoundingClientRect();

// percent is a value between 0 and 1, e.g. "41%" is `0.41`
const valueRescaled = isVertical
// the value at the finger origin scaled down to fit the range [0, 1]
let valueRescaled = isVertical
? (bottom - fingerPosition.y) / height + offset
: (fingerPosition.x - left) / width + offset * (isRtl ? -1 : 1);

let percentageValue = Math.min(valueRescaled, 1);
valueRescaled = Math.min(valueRescaled, 1);

if (isRtl && !isVertical) {
percentageValue = 1 - percentageValue;
valueRescaled = 1 - valueRescaled;
}

let newValue = percentToValue(percentageValue, min, max);
let newValue = (max - min) * valueRescaled + min;
newValue = roundValueToStep(newValue, step, min);
newValue = clamp(newValue, min, max);

if (!range) {
return {
value: newValue,
percentageValue,
percentageValues: [percentageValue * 100],
valueRescaled,
percentageValues: [valueRescaled * 100],
thumbIndex: 0,
};
}
Expand All @@ -368,11 +333,11 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo

return {
value: replaceArrayItemAtIndex(values, closestThumbIndex, newValue),
percentageValue,
valueRescaled,
percentageValues: replaceArrayItemAtIndex(
percentageValues,
closestThumbIndex,
percentageValue * 100,
valueRescaled * 100,
),
thumbIndex: closestThumbIndex,
};
Expand Down Expand Up @@ -467,10 +432,17 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
);
}

export type FingerPosition = {
export interface FingerPosition {
x: number;
y: number;
};
}

interface FingerState {
value: number | number[];
valueRescaled: number;
percentageValues: number[];
thumbIndex: number;
}

export namespace useSliderRoot {
export type Orientation = 'horizontal' | 'vertical';
Expand Down Expand Up @@ -589,12 +561,7 @@ export namespace useSliderRoot {
fingerPosition: FingerPosition | null,
shouldCaptureThumbIndex?: boolean,
offset?: number,
) => {
value: number | number[];
percentageValue: number;
percentageValues: number[];
thumbIndex: number;
} | null;
) => FingerState | null;
/**
* Callback to invoke change handlers after internal value state is updated.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/slider/thumb/SliderThumb.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const testRootContext: SliderRootContext = {
disabled: false,
getFingerState: () => ({
value: 0,
percentageValue: 0,
valueRescaled: 0,
percentageValues: [0],
thumbIndex: 0,
}),
Expand Down
13 changes: 2 additions & 11 deletions packages/react/src/slider/thumb/useSliderThumb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,7 @@ export function useSliderThumb(parameters: useSliderThumb.Parameters): useSlider
}
setTouched(true);
commitValidation(
getSliderValue({
valueInput: thumbValue,
min,
max,
index,
range: sliderValues.length > 1,
values: sliderValues,
}),
getSliderValue(thumbValue, index, min, max, sliderValues.length > 1, sliderValues),
);
},
onKeyDown(event: React.KeyboardEvent) {
Expand Down Expand Up @@ -215,9 +208,7 @@ export function useSliderThumb(parameters: useSliderThumb.Parameters): useSlider
}
},
ref: mergedThumbRef,
style: {
...getThumbStyle(),
},
style: getThumbStyle(),
tabIndex: externalTabIndex ?? (disabled ? undefined : 0),
});
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/slider/track/SliderTrack.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const testRootContext: SliderRootContext = {
disabled: false,
getFingerState: () => ({
value: 0,
percentageValue: 0,
valueRescaled: 0,
percentageValues: [0],
thumbIndex: 0,
}),
Expand Down
20 changes: 8 additions & 12 deletions packages/react/src/slider/utils/getSliderValue.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { clamp } from '../../utils/clamp';
import { replaceArrayItemAtIndex } from './replaceArrayItemAtIndex';

interface GetSliderValueParameters {
valueInput: number;
index: number;
min: number;
max: number;
range: boolean;
values: readonly number[];
}

export function getSliderValue(params: GetSliderValueParameters) {
const { valueInput, index, min, max, range, values } = params;

export function getSliderValue(
valueInput: number,
index: number,
min: number,
max: number,
range: boolean,
values: readonly number[],
) {
let newValue: number | number[] = valueInput;

newValue = clamp(newValue, min, max);
Expand Down
3 changes: 0 additions & 3 deletions packages/react/src/slider/utils/percentToValue.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/react/src/slider/value/SliderValue.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const testRootContext: SliderRootContext = {
disabled: false,
getFingerState: () => ({
value: 0,
percentageValue: 0,
valueRescaled: 0,
percentageValues: [0],
thumbIndex: 0,
}),
Expand Down

0 comments on commit 2a81e76

Please sign in to comment.