All files / app/src/shared/ui/numeric-input numeric-input.tsx

31.25% Statements 15/48
100% Branches 3/3
25% Functions 1/4
31.25% Lines 15/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 711x                         1x 25x         25x                 25x 25x 25x 25x 25x 25x 25x 25x       25x                                                       25x   25x  
import { FC } from 'react'
import { NumericFormat } from 'react-number-format'
import { InputProps } from '@/shared/ui'
import { Input } from '@tmk/ui-kit'
 
export interface NumericInputProps extends Omit<InputProps, 'value' | 'type'> {
  decimalScale?: number
  thousandSeparator?: string
  value: string | number | null
  allowNegative?: boolean
  onlyInteger?: boolean
}
 
export const NumericInput: FC<NumericInputProps> = ({ name, label, onlyInteger = false, ...rest }) => {
  const isNumberCodeKey = (key: string) => {
    const keyValue = parseInt(key, 10)
    return !isNaN(keyValue) && keyValue >= 0 && keyValue <= 9
  }
 
  const isLetterCodeKey = (key: string) => {
    const keyValue = key.charCodeAt(0)
    return (
      (keyValue >= 65 && keyValue <= 90) ||
      (keyValue >= 97 && keyValue <= 122) ||
      (keyValue >= 1040 && keyValue <= 1103)
    )
  }
 
  return (
    <NumericFormat
      {...rest}
      id={name}
      name={name}
      customInput={Input}
      value={rest.value === null ? '' : rest.value}
      label={label}
      // @ts-ignore -  у NumericFormat неправильно указан тип в интерфейсе, поэтому приходится использовать ignore
      // #50710 - убрал number, как так уже не нужно но возможно понадобится в будущем
      //type='number'
      onKeyDown={e => {
        const controlKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Backspace', 'Delete', 'Tab']
        const isNotNumberKey = isLetterCodeKey(e.key)
        const isControllingKey = controlKeys.includes(e.key)
 
        if (onlyInteger && !isNumberCodeKey(e.key) && !isControllingKey) {
          e.preventDefault()
        }
 
        if (isNotNumberKey && !isControllingKey) {
          e.preventDefault()
        }
        const selectionStart = e.currentTarget.selectionStart || 0
        if (!rest.allowNegative) {
          if (e.key === '-') {
            e.preventDefault()
          }
        }
        // Кастомная валидация для decimalScale, которая в библиотеке почему-то не срабатывает
        if (rest.decimalScale) {
          const value = e.target.value
          const dotIndex = value.indexOf('.')
          const isAfterDot = selectionStart > dotIndex
          if (dotIndex !== -1 && value.length - dotIndex > rest.decimalScale && isNumberCodeKey(e.key) && isAfterDot) {
            e.preventDefault()
          }
        }
      }}
    />
  )
}