| 215 | */ |
| 216 | // oxlint-disable-next-line react/react-compiler |
| 217 | export function useSliderState<T extends number | number[]>( |
| 218 | props: SliderStateOptions<T> |
| 219 | ): SliderState { |
| 220 | const { |
| 221 | isDisabled = false, |
| 222 | minValue = DEFAULT_MIN_VALUE, |
| 223 | maxValue = DEFAULT_MAX_VALUE, |
| 224 | numberFormatter: formatter, |
| 225 | step = DEFAULT_STEP_VALUE, |
| 226 | orientation = 'horizontal' |
| 227 | } = props; |
| 228 | |
| 229 | // Page step should be at least equal to step and always a multiple of the step. |
| 230 | let pageSize = useMemo(() => { |
| 231 | let calcPageSize = (maxValue - minValue) / 10; |
| 232 | calcPageSize = snapValueToStep(calcPageSize, 0, calcPageSize + step, step); |
| 233 | return Math.max(calcPageSize, step); |
| 234 | }, [step, maxValue, minValue]); |
| 235 | |
| 236 | let restrictValues = useCallback( |
| 237 | (values: number[] | undefined) => |
| 238 | values?.map((val, idx) => { |
| 239 | let min = idx === 0 ? minValue : values[idx - 1]; |
| 240 | let max = idx === values.length - 1 ? maxValue : values[idx + 1]; |
| 241 | return snapValueToStep(val, min, max, step); |
| 242 | }), |
| 243 | [minValue, maxValue, step] |
| 244 | ); |
| 245 | |
| 246 | let value = useMemo( |
| 247 | () => restrictValues(convertValue(props.value)), |
| 248 | [props.value, restrictValues] |
| 249 | ); |
| 250 | let defaultValue = useMemo( |
| 251 | () => restrictValues(convertValue(props.defaultValue) ?? [minValue])!, |
| 252 | [props.defaultValue, minValue, restrictValues] |
| 253 | ); |
| 254 | let onChange = createOnChange(props.value, props.defaultValue, props.onChange); |
| 255 | let onChangeEnd = createOnChange(props.value, props.defaultValue, props.onChangeEnd); |
| 256 | |
| 257 | const [values, setValuesState] = useControlledState<number[]>(value, defaultValue, onChange); |
| 258 | let [initialValues] = useState(values); |
| 259 | const [isDraggings, setDraggingsState] = useState<boolean[]>( |
| 260 | new Array(values.length).fill(false) |
| 261 | ); |
| 262 | const isEditablesRef = useRef<boolean[]>(new Array(values.length).fill(true)); |
| 263 | const [focusedIndex, setFocusedIndex] = useState<number | undefined>(undefined); |
| 264 | |
| 265 | const valuesRef = useRef<number[]>(values); |
| 266 | const isDraggingsRef = useRef<boolean[]>(isDraggings); |
| 267 | |
| 268 | let setValues = (values: number[]) => { |
| 269 | valuesRef.current = values; |
| 270 | setValuesState(values); |
| 271 | }; |
| 272 | |
| 273 | let setDraggings = (draggings: boolean[]) => { |
| 274 | isDraggingsRef.current = draggings; |