antoncoding/monarch

View on GitHub
src/components/Input/Input.tsx

Summary

Maintainability
A
2 hrs
Test Coverage
import { useCallback, useState } from 'react';

import { parseUnits } from 'viem';
import { formatBalance } from '@/utils/balance';

type InputProps = {
  decimals: number;
  max: bigint;
  setValue: React.Dispatch<React.SetStateAction<bigint>>;
  setError?: React.Dispatch<React.SetStateAction<string | null>>;
  exceedMaxErrMessage?: string;
  allowExceedMax?: boolean; // whether to still "setValue" when the input exceeds max
};

export default function Input({
  decimals,
  max,
  setValue,
  setError,
  exceedMaxErrMessage,
  allowExceedMax = false,
}: InputProps): JSX.Element {
  // State for the input text
  const [inputAmount, setInputAmount] = useState<string>('0');

  const onInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      // update the shown input text regardless
      const inputText = e.target.value;
      setInputAmount(inputText);

      try {
        const inputBigInt = parseUnits(inputText, decimals);

        if (inputBigInt > max) {
          if (setError) setError(exceedMaxErrMessage ?? 'Input exceeds max');
          if (allowExceedMax) {
            setValue(inputBigInt);
          }
          return;
        }

        setValue(inputBigInt);
        if (setError) setError(null);

        // eslint-disable-next-line @typescript-eslint/no-shadow
      } catch (e) {
        if (setError) setError('Invalid input');
        console.log('e', e);
      }
    },
    [decimals, setError, setInputAmount, setValue, max, exceedMaxErrMessage, allowExceedMax],
  );

  // if max is clicked, set the input to the max value
  const handleMax = useCallback(() => {
    setValue(max);
    // set readable input
    setInputAmount(formatBalance(max, decimals).toString());
  }, [max, decimals, setInputAmount, setValue]);

  return (
    <div className="relative flex-grow">
      <input
        type="number"
        value={inputAmount}
        onChange={onInputChange}
        className="bg-hovered focus:border-monarch-orange h-10 w-full rounded p-2 focus:outline-none"
      />
      <button
        type="button"
        onClick={handleMax}
        className="absolute right-2 top-1/2 -translate-y-1/2 transform rounded bg-secondary p-1 text-sm text-secondary opacity-80 duration-300 ease-in-out hover:scale-105 hover:opacity-100"
      >
        Max
      </button>
    </div>
  );
}