Зеленый слайдер при введении нового значения в текстовое поле выходит за пределы ползунка rangeSlider

Помогите пожалуйста разобраться с проблемой зеленого ползунка. Когда хочу ввести новое значение в текстовое поле, минимальное или максимальное, я сначала удаляю старое значение, перед тем, как ввести новое, но когда значение при удалении меняется, зеленая часть ползунка вылезает за пределы слайдера. Никак не могу понять, как решить эту проблему.

Пытался ввести фильтр-проверку на меньшее значение чем изначальное, но не сработало-вводится большее, а обратное занчение уже не вернуть.

Помогите решить проблему или подсказать как ее можно решить.

Для удобства публикую код в codesandbox: https://codesandbox.io/p/devbox/price-range-slider-zwfzyz?file=%2Fsrc%2Frange-slider%2FrangeSlider.scss%3A32%2C24

Фото ползунка за пределами слайдера: введите сюда описание изображения

Фото изначального состояния: введите сюда описание изображения

React code

function MultiRangeSlider({ minPrice, maxPrice, onChange }) {
  const [minPriceVal, setMinPriceVal] = useState(minPrice);
  const [maxPriceVal, setMaxPriceVal] = useState(maxPrice);
  const minValRef = useRef(null);
  const maxValRef = useRef(null);
  const range = useRef(null);

  const convertToPercent = useCallback(
    (value) => Math.round(((value - minPrice) / (maxPrice - minPrice)) * 100),
    [minPrice, maxPrice],
  );

  useEffect(() => {
    if (maxValRef.current) {
      const minPercent = convertToPercent(minPriceVal);
      const maxPercent = convertToPercent(+maxValRef.current.value);
      if (range.current) {
        range.current.style.left = `${minPercent}%`;
        range.current.style.width = `${maxPercent - minPercent}%`;
      }
    }
  }, [minPriceVal, convertToPercent]);

  useEffect(() => {
    if (minValRef.current) {
      const maxPercent = convertToPercent(maxPriceVal);
      const minPercent = convertToPercent(+minValRef.current.value);
      if (range.current) {
        range.current.style.width = `${maxPercent - minPercent}%`;
      }
    }
  }, [maxPriceVal, convertToPercent]);

  useEffect(() => {
    onChange({ min: minPriceVal, max: maxPriceVal });
  }, [minPriceVal, maxPriceVal, onChange]);

  const minInputField = (e) => {
    setMinPriceVal(e.target.value);
  };

  const maxInputField = (e) => {
    setMaxPriceVal(e.target.value);
  };

  return (
    <div className={"rangeSlider"}>
      {/* text input field MIN */}
      <input
        value={minPriceVal}
        type={"number"}
        onChange={(e) => {
          minInputField(e);
        }}
      />

      {/* text input field MAX */}
      <input
        value={maxPriceVal}
        type={"number"}
        onChange={(e) => {
          maxInputField(e);
        }}
      />

      <div className="container">
        <input
          type="range"
          min={minPrice}
          max={maxPrice}
          value={minPriceVal}
          ref={minValRef}
          onChange={(event) => {
            const value = Math.min(+event.target.value, maxPriceVal - 1);
            setMinPriceVal(value);
            event.target.value = value.toString();
          }}
          className="thumbMin"
        />

        <input
          type="range"
          min={minPrice}
          max={maxPrice}
          value={maxPriceVal}
          ref={maxValRef}
          onChange={(event) => {
            const value = Math.max(+event.target.value, minPriceVal + 1);
            setMaxPriceVal(value);
            event.target.value = value.toString();
          }}
          className="thumbMax"
        />

        <div className="slider">
          <div className="track" />
          <div ref={range} className="range" />
        </div>
      </div>
    </div>
  );
}

export default MultiRangeSlider;

style

.slider {
  position: relative;
  width: 200px;

  .track {
    border-radius: 3px;
    height: 5px;
    position: absolute;
    background-color: black;
    width: 100%;
    z-index: 1;
  }

  .range {
    display: block;
    border-radius: 3px;
    height: 5px;
    position: absolute;
    background-color: greenyellow;
    z-index: 2;
  }
}

.container {
  margin-top: 40px;

  .thumbMin {
    pointer-events: none;
    position: absolute;
    height: 0;
    width: 200px;
    outline: none;

    z-index: 3;

    &::-webkit-slider-thumb {
      -webkit-appearance: none;
      -webkit-tap-highlight-color: transparent;
    }

    &::-webkit-slider-thumb {
      background-color: #f1f5f7;
      border: none;
      border-radius: 50%;
      box-shadow: 0 0 1px 1px #ced4da;
      cursor: pointer;
      height: 18px;
      width: 18px;
      margin-top: 4px;
      pointer-events: all;
      position: relative;
    }

    &::-moz-range-thumb {
      background-color: #f1f5f7;
      border: none;
      border-radius: 50%;
      box-shadow: 0 0 1px 1px #ced4da;
      cursor: pointer;
      height: 18px;
      width: 18px;
      margin-top: 4px;
      pointer-events: all;
      position: relative;
    }
  }

  .thumbMax {
    pointer-events: none;
    position: absolute;
    height: 0;
    width: 200px;
    outline: none;
    z-index: 4;

    &::-webkit-slider-thumb {
      -webkit-appearance: none;
      -webkit-tap-highlight-color: transparent;
    }

    &::-webkit-slider-thumb {
      background-color: #f1f5f7;
      border: none;
      border-radius: 50%;
      box-shadow: 0 0 1px 1px #ced4da;
      cursor: pointer;
      height: 18px;
      width: 18px;
      margin-top: 4px;
      pointer-events: all;
      position: relative;
    }

    &::-moz-range-thumb {
      background-color: #f1f5f7;
      border: none;
      border-radius: 50%;
      box-shadow: 0 0 1px 1px #ced4da;
      cursor: pointer;
      height: 18px;
      width: 18px;
      margin-top: 4px;
      pointer-events: all;
      position: relative;
    }
  }
}

Ответы (1 шт):

Автор решения: Armen
  1. любые изменения с палзунком должны происходить если это верно (изменение стилей и тд)

    const isInRange = minPriceVal >= minPrice && maxPriceVal <= maxPrice;

  2. значения ползунков не должны привишать/быт меньше min или мах

    //палзунок

    value={isInRange ? minPriceVal : minPrice}

→ Ссылка