Spaces:
Runtime error
Runtime error
| /* | |
| * Copyright (c) Meta Platforms, Inc. and affiliates. | |
| * | |
| * This source code is licensed under the Chameleon License found in the | |
| * LICENSE file in the root directory of this source tree. | |
| */ | |
| import { ChangeEvent, useEffect, useState } from "react"; | |
| export type InputRangeProps = { | |
| value: number; | |
| step?: number; | |
| min?: number; | |
| max?: number; | |
| integerOnly?: boolean; | |
| slider?: boolean; | |
| optional?: boolean; | |
| placeholder?: string; | |
| label?: string; | |
| className?: string; | |
| onValueChange: (n: any) => void; | |
| }; | |
| export function InputRange({ | |
| value, | |
| onValueChange, | |
| step = 0.1, | |
| min = 0, | |
| max = 1, | |
| integerOnly = false, | |
| slider = true, | |
| optional = false, | |
| placeholder, | |
| label, | |
| className, | |
| }: InputRangeProps) { | |
| const [tempValue, setTempValue] = useState<string>(""); | |
| const [tempOptionalValue, setTempOptionalValue] = useState<number>(0); | |
| const [valid, setValid] = useState<boolean>(true); | |
| const [skipped, setSkipped] = useState<boolean>(optional); | |
| /* | |
| * Here we first validate the value and then run the update callback if the value is valid. | |
| */ | |
| function validate(valueString: string, updateFn: (value: any) => void) { | |
| setTempValue(valueString); | |
| const parseFn = integerOnly ? parseInt : parseFloat; | |
| const n = parseFn(valueString) || NaN; | |
| const integerCheck = integerOnly ? Math.floor(n) === n : true; | |
| if (skipped) { | |
| updateFn(null); | |
| setValid(true); | |
| } else if (n && n >= min && n <= max && integerCheck) { | |
| updateFn(n); | |
| setValid(true); | |
| } else { | |
| setValid(false); | |
| } | |
| } | |
| function handleOptional(evt: ChangeEvent<HTMLInputElement>) { | |
| // if checked, skip should be false | |
| if (evt.currentTarget.checked === skipped) { | |
| setSkipped(!skipped); | |
| } | |
| } | |
| useEffect(() => { | |
| setTempValue(`${value}`); | |
| }, [value, setTempValue]); | |
| useEffect(() => { | |
| if (skipped) { | |
| setTempOptionalValue(value); | |
| validate("", onValueChange); | |
| } else if (optional && !value) { | |
| validate(`${tempOptionalValue || min}`, onValueChange); | |
| } | |
| }, [skipped, tempOptionalValue, setTempOptionalValue]); | |
| const input = ( | |
| <div className={`flex flex-row items-center gap-4 ${className}`}> | |
| {slider && ( | |
| <input | |
| type="range" | |
| min={min} | |
| max={max} | |
| value={tempValue} | |
| step={step} | |
| disabled={skipped} | |
| className={`range flex-1`} | |
| onChange={(evt) => validate(evt.currentTarget.value, onValueChange)} | |
| /> | |
| )} | |
| <input | |
| type="text" | |
| placeholder={placeholder || ""} | |
| value={tempValue} | |
| disabled={skipped} | |
| onChange={(evt) => validate(evt.currentTarget.value, onValueChange)} | |
| className={`input ${valid ? "border-gray-200" : "border-red-300"} w-24`} | |
| /> | |
| </div> | |
| ); | |
| return label ? ( | |
| <div className="form-control mt-6 flex flex-row gap-2 items-center"> | |
| <label className="label font-semibold flex flex-1 leading-5 gap-2"> | |
| {optional && ( | |
| <input | |
| type="checkbox" | |
| checked={!skipped} | |
| className="checkbox checkbox-primary" | |
| onChange={handleOptional} | |
| /> | |
| )} | |
| <span className={`flex-1 `}> | |
| {label}{" "} | |
| {skipped && <div className="text-xs text-gray-400">(skipped)</div>} | |
| </span> | |
| </label> | |
| <div className={`flex-1 flex-grow-3 ${skipped && "hidden"}`}>{input}</div> | |
| </div> | |
| ) : ( | |
| input | |
| ); | |
| } | |