/* eslint-disable no-nested-ternary */ import { last } from 'lodash'; import PropTypes from 'prop-types'; import { ThemeProvider } from 'styled-components'; import React, { useState } from 'react'; import { Icon, Input, Button, Label, InputContainer, ArrowsContainer, } from './styledComponents'; import IconPlus from '../images/icon-plus-input-white.svg'; import IconMinus from '../images/icon-minus-input-white.svg'; import { getTheme } from '../utils/theme'; import { Props } from './interfaces'; import colorPalette from '../utils/colorPalette'; const KEYBOARD = { UP: 38, DOWN: 40, }; const ACTIONS = { ADD: '+', SUBSTRACT: '-', MANUAL: 'keyboard', }; const getBackgroundColor = ( isHighlighted?: boolean, isDisabled?: boolean, isButtonHovered?: boolean ) => { const backgroundColor = { default: colorPalette.WHITE, hovered: colorPalette.WHITE, }; if (isDisabled) { backgroundColor.default = colorPalette.LMGrey; backgroundColor.hovered = colorPalette.LMGrey; } else if (isHighlighted) { backgroundColor.default = colorPalette.IP_LBlue; backgroundColor.hovered = colorPalette.IP_Blue; } else if (!isButtonHovered) { backgroundColor.hovered = colorPalette.LGrey; } return backgroundColor; }; const getBorderColor = (isHighlighted?: boolean, focused?: boolean) => { const borderColor = { default: colorPalette.LMGrey, hovered: colorPalette.LMGrey, error: colorPalette.Info_Red, }; if (isHighlighted) { borderColor.default = colorPalette.IP_Blue; borderColor.hovered = colorPalette.IP_DBlue; borderColor.error = colorPalette.IP_DBlue; borderColor.error = colorPalette.Info_Red; } else if (focused) { borderColor.default = colorPalette.IP_Black; borderColor.hovered = colorPalette.IP_Black; borderColor.error = colorPalette.Info_Red; } return borderColor; }; const NUMBER_CHECK_REGEXP = new RegExp(/^(-)?\d*($|(\.|,)(\d+|$))$/); const ZERO_AFTER_COMMA = new RegExp(/^\d+(\.(0+))$/); const LAST_CHARACTER_IS_NUMBER = new RegExp(/^\d+(\.(\d))\d+$/); // Interesting link to understand the purpose of this method // https://stackoverflow.com/questions/588004/is-floating-point-math-broken const formatFloatNumber = (floatNumber: number) => { if (!floatNumber) { return floatNumber; } return +floatNumber.toFixed(10); }; const InputIncrement = (props: Props): JSX.Element => { const { label, height, width, theme, value, setValue, required, allowNull, allowDecimals, isDisabled, limitValues, placeholder, hasErrors, disableUpDownBehaviour, isHighlighted, isButtonHidden, } = props; const updatedTheme = getTheme(theme, 'inputIncrement'); const [focused, setFocused] = React.useState(false); const onFocus = () => setFocused(true); const onBlur = () => setFocused(false); const [isButtonHovered, setIsButtonHovered] = useState(false); const changeValue = (type: string, inputValue?: string) => { if (!isDisabled) { if ( !value && value !== 0 && allowNull && [ACTIONS.ADD, ACTIONS.SUBSTRACT].includes(type) ) { setValue(limitValues.min); return; } if (type === ACTIONS.ADD) { const updatedValue = !Number.isNaN(value) ? limitValues.max || limitValues.max === 0 ? Math.min( formatFloatNumber( Number.parseFloat(value as unknown as string) + 1 ), limitValues.max ) : formatFloatNumber( Number.parseFloat(value as unknown as string) + 1 ) : 0; setValue(updatedValue); return; } if (type === ACTIONS.SUBSTRACT) { const updatedValue = !Number.isNaN(value) ? limitValues.min || limitValues.min === 0 ? Math.max( limitValues.min, formatFloatNumber( Number.parseFloat(value as unknown as string) - 1 ) ) : formatFloatNumber( Number.parseFloat(value as unknown as string) - 1 ) : 0; setValue(updatedValue); return; } if (type === ACTIONS.MANUAL) { if (!inputValue) { setValue(allowNull ? null : 0); return; } const formattedValue = inputValue.replace(',', '.'); if ( !allowDecimals && (!Number.isInteger(+formattedValue) || last(formattedValue) === '.') ) { /* eslint-disable no-restricted-globals */ if (isNaN(+formattedValue)) { return; } setValue(Math.ceil(+formattedValue)); return; } if (!NUMBER_CHECK_REGEXP.test(inputValue)) { return; } if ( last(formattedValue) === '.' || ZERO_AFTER_COMMA.test(formattedValue) || LAST_CHARACTER_IS_NUMBER.test(formattedValue) ) { setValue(formattedValue); return; } const parsedValue = Number.parseFloat(formattedValue); const updatedValue = !Number.isNaN(parsedValue) ? limitValues.min || limitValues.min === 0 ? Math.max(limitValues.min, parsedValue) : !limitValues.min ? parsedValue : value : formattedValue; setValue(updatedValue); return; } } setValue(value); }; return ( {label && ( )} changeValue(ACTIONS.MANUAL, e.target.value)} value={value} onFocus={onFocus} onBlur={onBlur} onKeyDown={(event) => { if ( disableUpDownBehaviour && [KEYBOARD.UP, KEYBOARD.DOWN].includes(event.keyCode) ) { event.preventDefault(); } }} disabled={isDisabled} isDisabled={isDisabled} focused={focused} isButtonHovered={isButtonHovered} placeholder={placeholder} backgroundColor={getBackgroundColor( isHighlighted, isDisabled, isButtonHovered )} borderColor={getBorderColor(isHighlighted, focused)} isHighlighted={isHighlighted} /> {!isButtonHidden && ( )} ); }; InputIncrement.propTypes = { // eslint-disable-next-line react/forbid-prop-types theme: PropTypes.objectOf(PropTypes.any), limitValues: PropTypes.shape({ min: PropTypes.oneOfType([PropTypes.number]), max: PropTypes.oneOfType([PropTypes.number]), }), label: PropTypes.string, width: PropTypes.string, height: PropTypes.string, required: PropTypes.bool, hasErrors: PropTypes.bool, isDisabled: PropTypes.bool, placeholder: PropTypes.string, allowNull: PropTypes.bool, allowDecimals: PropTypes.bool, disableUpDownBehaviour: PropTypes.bool, value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), setValue: PropTypes.func.isRequired, isHighlighted: PropTypes.bool, isButtonHidden: PropTypes.bool, }; InputIncrement.defaultProps = { theme: null, label: null, value: '', height: '40px', width: '240px', limitValues: {}, required: false, hasErrors: false, placeholder: '0', isDisabled: false, allowNull: false, allowDecimals: true, disableUpDownBehaviour: true, isHighlighted: false, isButtonHidden: false, }; export default InputIncrement;