Загрузка данных


import { InputNumber } from 'exchange-elements/v2';
import { FormEvent, FocusEvent, memo, SyntheticEvent, useCallback, useEffect, useRef, useState } from 'react';

import styles from './index.module.scss';

interface NumberFieldProps {
  label: string;
  value?: number;
  initialValue?: number;
  min?: number;
  max?: number;
  onValueChange: (value: number) => void;
}

type HandleChange = {
  (event: FormEvent<HTMLInputElement>): void;
  (item: number, event?: SyntheticEvent): void;
};

export const NumberField = memo(({ label, value, initialValue, min, max, onValueChange }: NumberFieldProps) => {
  const initialFieldValue = initialValue ?? value;
  const [localValue, setLocalValue] = useState<number | undefined>(initialFieldValue);
  const lastValidValueRef = useRef<number | undefined>(initialFieldValue);

  useEffect(() => {
    if (value !== undefined) {
      setLocalValue(value);
      lastValidValueRef.current = value;
    }
  }, [value]);

  const handleChange = useCallback<HandleChange>(
    (valueOrEvent: number | FormEvent<HTMLInputElement>) => {
      if (typeof valueOrEvent !== 'number' || Number.isNaN(valueOrEvent)) {
        setLocalValue(undefined);
        return;
      }

      setLocalValue(valueOrEvent);
    },
    [],
  );

  const handleBlur = useCallback(
    (_event: FocusEvent<HTMLInputElement>) => {
      const fallbackValue = lastValidValueRef.current ?? initialFieldValue ?? min ?? 0;
      const normalizedValue = normalizeValue(localValue, fallbackValue, min, max);

      setLocalValue(normalizedValue);
      lastValidValueRef.current = normalizedValue;
      onValueChange(normalizedValue);
    },
    [initialFieldValue, localValue, min, max, onValueChange],
  );

  return (
    <InputNumber
      className={styles.input}
      value={localValue}
      onChange={handleChange}
      onBlur={handleBlur}
      label={label}
      labelPos="top"
      size="sm"
    />
  );
});

function normalizeValue(value: number | undefined, fallbackValue: number, min?: number, max?: number): number {
  if (value === undefined || Number.isNaN(value)) {
    return fallbackValue;
  }

  if (min !== undefined && value < min) {
    return min;
  }

  if (max !== undefined && value > max) {
    return max;
  }

  return value;
}