import React, { useState, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import { FormGroup, TextInput, InputGroup, InputGroupText, Dropdown, DropdownItem, DropdownToggle, } from '@patternfly/react-core'; import { sprintf, translate as __ } from 'foremanReact/common/I18n'; import { findLargestFittingUnit } from '../../../../helper'; const UnitInputField = ({ initialValue, onChange, isDisabled, handleInputValidation, minValue, maxValue, units, labelIcon, }) => { /* flexible unit adaption variables */ const [errorText, setErrorText] = useState(''); const [validated, setValidated] = useState('default'); const [isUnitOpen, setIsUnitOpen] = useState(false); const bestFitUnit = findLargestFittingUnit(initialValue, units); const [selectedUnit, setSelectedUnit] = useState(bestFitUnit); const [inputValue, setInputValue] = useState( initialValue / bestFitUnit.factor ); let unitDropdownItems = []; /* generate unitDropdownItems depending on unit */ if (units.length > 1) { unitDropdownItems = units.map(unit => ( {unit.symbol} )); } /* text for bounds errors */ const errorTextBounds = useCallback(() => { const boundsText = 'Value must be between %d and %d.'; let errorMin = minValue; let errorMax = maxValue; if (selectedUnit) { errorMin = minValue / selectedUnit.factor; errorMax = maxValue / selectedUnit.factor; } return __(sprintf(boundsText, errorMin, errorMax)); }, [minValue, maxValue, selectedUnit]); /* text for float errors */ const errorTextNatural = useCallback( () => __('Value must be a natural number.'), [] ); /* text for float errors */ const errorTextFloating = useCallback( () => __(`No floating point for smallest unit (${units[0].symbol}).`), [units] ); /* applies the selected unit and checks the bounds */ const isValid = useCallback( val => { if (Number.isNaN(Number(val))) { setErrorText(errorTextNatural()); return false; } const baseValue = valueToBaseUnit(val); if (baseValue < minValue || baseValue > maxValue) { setErrorText(errorTextBounds()); return false; } if (baseValue !== Math.floor(baseValue)) { setErrorText(errorTextFloating()); return false; } return true; }, [ minValue, maxValue, valueToBaseUnit, errorTextNatural, errorTextBounds, errorTextFloating, ] ); /* applies the selected unit and returns the base-unit value */ const valueToBaseUnit = useCallback( val => { if (units.length > 1) { return selectedUnit.factor * val; } return Number(val); }, [units, selectedUnit] ); useEffect(() => { if (isDisabled) { handleInputValidation(true); setValidated('default'); } else if (isValid(inputValue)) { const baseValue = valueToBaseUnit(inputValue); onChange(baseValue); handleInputValidation(true); setValidated('default'); } else { handleInputValidation(false); setValidated('error'); } }, [ isDisabled, inputValue, selectedUnit, handleInputValidation, onChange, isValid, valueToBaseUnit, ]); /* set the selected unit */ const onUnitSelect = event => { const { id } = event.currentTarget; const selectedListItem = unitDropdownItems.find( item => item.props.id === id ); const unitItem = units.find( item => item.symbol === selectedListItem.props.children ); setSelectedUnit(unitItem); setIsUnitOpen(false); // FIXME: Fix input validation on unit selection }; const onUnitToggle = () => { setIsUnitOpen(!isUnitOpen); }; /* return the unit view depending on the available units * * unit = null : don't print any unit * unit = [.] : print a single Textfield * unit = [..] : print an editable Dropdown menu * */ const unitView = () => { if (units.length === 1) { return {__(units[0].symbol)}; } return ( {__(`${selectedUnit.symbol}`)} } isOpen={isUnitOpen} dropdownItems={unitDropdownItems} /> ); }; return ( {unitView()} ); }; UnitInputField.propTypes = { initialValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), onChange: PropTypes.func.isRequired, isDisabled: PropTypes.bool.isRequired, handleInputValidation: PropTypes.func.isRequired, units: PropTypes.arrayOf( PropTypes.shape({ symbol: PropTypes.string, factor: PropTypes.number, }) ).isRequired, labelIcon: PropTypes.node, minValue: PropTypes.number.isRequired, maxValue: PropTypes.number.isRequired, }; UnitInputField.defaultProps = { initialValue: 0, labelIcon: null, }; export default UnitInputField;