UNPKG

@react-native-assets/slider

Version:

Lightweight slider for React-Native and React-Native-Web. A Range slider is included

87 lines (73 loc) 3.7 kB
import React from 'react' import { useEvent } from './useEvent' import useThumb from './useThumb' type Props = { step: number; range: [number, number]; minimumRange: number; minimumValue: number; maximumValue: number; slideOnTap?: boolean; crossingAllowed?: boolean; onValueChange?: (range: [number, number]) => boolean | void; } /** Handle the state of a range slider */ const useRange = ({ step, range: propValue, minimumRange, minimumValue, maximumValue, slideOnTap, onValueChange, crossingAllowed }: Props) => { const [minProp, maxProp] = propValue const minRef = React.useRef<number>(minProp) const maxRef = React.useRef<number>(maxProp) // When updating the props, we immediately apply the change to the refs in order to have the correct values in useThumb minRef.current = React.useMemo(() => Math.max(minimumValue, minProp), [minProp, minimumValue]) maxRef.current = React.useMemo(() => Math.min(maximumValue, maxProp), [maxProp, maximumValue]) const onMinChange = useEvent((min: number) => { minRef.current = min onValueChange?.([min, maxRef.current].sort((a, b) => a - b) as [number, number]) }) const onMaxChange = useEvent((max: number) => { maxRef.current = max onValueChange?.([minRef.current, max].sort((a, b) => a - b) as [number, number]) }) // Min value thumb const { updateValue: updateMinValue, canMove: canMoveMin, value: minValue } = useThumb({ minimumValue, maximumValue: Math.max(minimumValue, maxRef.current - minimumRange), value: minProp, step, slideOnTap, onValueChange: onMinChange }) // Max value thumb const { updateValue: updateMaxValue, canMove: canMoveMax, value: maxValue } = useThumb({ minimumValue: Math.min(maximumValue, minRef.current + minimumRange), maximumValue, value: maxProp, step, slideOnTap, onValueChange: onMaxChange }) const range = React.useMemo(() => [minValue, maxValue].sort((a, b) => a - b) as [number, number], [maxValue, minValue]) const currentThumb = React.useRef<'min' | 'max'>() // Method to update the lower or higher bound according to which one is the closest const updateClosestValue = useEvent((value: number, state: 'press' | 'release' | 'drag') => { let isMinClosest = false // When moving a thumb, we don't want to let it cross the other thumb if (currentThumb.current && !crossingAllowed) isMinClosest = currentThumb.current === 'min' else if (!currentThumb.current) isMinClosest = Math.abs(value - minValue) < Math.abs(value - maxValue) || (minValue === maxValue && value < minValue) // if the current thumb is the min, we keep it as long as it's below the max else if (currentThumb.current === 'min') isMinClosest = value <= maxValue // Otherwise, if we hold the max thumb, we switch only if the value is below the min else isMinClosest = value < minValue // We update the state accordingly isMinClosest ? updateMinValue(value) : updateMaxValue(value) const newThumb = isMinClosest ? 'min' : 'max' // We set the thumb being currently moved // When the 2 thumbs cross, we set the other thumb to the max possible value if (state === 'drag' && newThumb !== currentThumb.current) isMinClosest ? updateMaxValue(minValue) : updateMinValue(maxValue) currentThumb.current = state === 'release' ? undefined : newThumb // We release the thumb, or keep maintaining it return isMinClosest ? [value, maxValue] : [minValue, value] }) const canMove = useEvent((value: number) => { return canMoveMax(value) || canMoveMin(value) }) return { updateMinValue, updateMaxValue, updateClosestValue, canMove, range } } export default useRange