react-native-sticky-range-slider
Version:
[React Native | TypeScript] A pure TypeScript component offering a customizable range slider optimized for performance. It supports dragging functionalities for selecting a range of values, with values that smoothly follow the thumb. This component is ful
93 lines (85 loc) • 2.78 kB
text/typescript
import { useCallback, useMemo, useRef, type MutableRefObject } from 'react';
import { Animated, type LayoutChangeEvent } from 'react-native';
import { clamp } from '../utils/helpers';
export const useLowHigh = (
lowProp: number | undefined,
highProp: number | undefined,
min: number,
max: number,
step: number
) => {
const validLowProp = lowProp === undefined ? min : clamp(lowProp, min, max);
const validHighProp =
highProp === undefined ? max : clamp(highProp, min, max);
const inPropsRef = useRef({
low: validLowProp,
high: validHighProp,
step,
min: validLowProp,
max: validHighProp,
});
const { low: lowState, high: highState } = inPropsRef.current;
const inPropsRefPrev = { lowPrev: lowState, highPrev: highState };
const low = clamp(lowProp === undefined ? lowState : lowProp, min, max);
const high = clamp(highProp === undefined ? highState : highProp, min, max);
Object.assign(inPropsRef.current, { low, high, min, max });
const setLow = (value: number) => (inPropsRef.current.low = value);
const setHigh = (value: number) => (inPropsRef.current.high = value);
return { inPropsRef, inPropsRefPrev, setLow, setHigh };
};
export const useWidthLayout = (
widthRef: MutableRefObject<number>,
callback?: (width: number) => void
) => {
return useCallback(
({ nativeEvent }: LayoutChangeEvent) => {
const {
layout: { width },
} = nativeEvent;
const { current: w } = widthRef;
if (w !== width) {
widthRef.current = width;
if (callback) {
callback(width);
}
}
},
[callback, widthRef]
);
};
interface InProps {
low: number;
high: number;
min: number;
max: number;
step: number;
}
export const useSelectedRail = (
inPropsRef: MutableRefObject<InProps>,
containerWidthRef: MutableRefObject<number>,
thumbWidth: number,
disableRange: boolean
) => {
const { current: left } = useRef(new Animated.Value(0));
const { current: right } = useRef(new Animated.Value(0));
const updateSelectedRail = useCallback(() => {
const { low, high, min, max } = inPropsRef.current;
const { current: containerWidth } = containerWidthRef;
const fullScale = (max - min) / (containerWidth - thumbWidth);
const leftValue = (low - min) / fullScale;
const rightValue = (max - high) / fullScale;
left.setValue(disableRange ? 0 : leftValue);
right.setValue(
disableRange ? containerWidth - thumbWidth - leftValue : rightValue
);
}, [containerWidthRef, disableRange, inPropsRef, left, right, thumbWidth]);
const selectedRailStyle = useMemo(
() => ({
position: 'absolute',
left,
right,
}),
[left, right]
);
return { selectedRailStyle, updateSelectedRail };
};