You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
3.2 KiB
JavaScript

import React, {useState, useRef} from 'react'
import useMeasure from 'use-measure'
import styles from './styles.module.css'
function truncateFixed(num, fixed) {
var re = new RegExp('^-?\\d+(?:\.\\d{0,' + (fixed || -1) + '})?');
return num.toString().match(re)[0];
}
export default function Input({label, prefix, numDecimals, error, hint, name, value, onChange, onBlur, isValid}){
const [currentDecimals, setCurrentDecimals] = useState((numDecimals === undefined || value === undefined || value === '') ? -1 : numDecimals)
const currentValue = value === undefined ? 0 : typeof value === 'number' ? value : typeof value === 'string' ? parseInt(value || '0', 10) : 0
const spanRef = useRef();
const {width} = useMeasure(spanRef);
const updateParent = value => onChange({target: {value}})
const onKeyDown = (ev) => {
const {key} = ev
switch(key) {
case 'Backspace':
setCurrentDecimals(dec => Math.max(dec - 1, -1))
if(currentDecimals > 0) {
updateParent(parseFloat(truncateFixed(currentValue, currentDecimals - 1)))
} else {
updateParent(Math.floor(currentValue / 10))
}
break;
case '.':
setCurrentDecimals(0)
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
const digit = parseInt(key, 10)
// If we're still in the whole numbers
if(currentDecimals < 0) {
updateParent(currentValue * 10 + digit)
} else if (currentDecimals === 0) {
// Add decimal
setCurrentDecimals(dec => dec + 1)
updateParent(parseFloat(currentValue.toFixed(0) + '.' + digit))
} else if (currentDecimals < numDecimals) {
// Add to existing decimals
setCurrentDecimals(dec => dec + 1)
updateParent(parseFloat(currentValue.toFixed(currentDecimals) + digit))
} else {
// Can we play a tick noise of some sort?
}
break;
default:
return;
}
ev.preventDefault();
}
const filledText = ((prefix + ' ') || '')
+ (currentValue !== 0 ? Math.floor(currentValue) : '')
+ (currentDecimals >= 0 ? '.' : '')
+ (currentDecimals > 0 ? currentValue.toFixed(currentDecimals).split('.')[1] : '')
const remainingText =
(currentValue === 0 ? '0' : '')
+ (currentDecimals < 0 ? '.' : '')
+ (numDecimals !== undefined && currentDecimals < numDecimals ? '0'.repeat(numDecimals - Math.max(currentDecimals, 0)) : '')
return (
<div className={styles.formElementContainer}>
<label htmlFor={name}>{label}:</label>
<div className={styles.complexInput + ((isValid && !error)?'':' ' + styles.invalid)}>
<input style={{textIndent: width}} type="text" name={name} value="" onChange={()=>{}} onKeyDown={onKeyDown} onBlur={onBlur} />
<div className={styles.inputDecorators}>
<span ref={spanRef} className={styles.numFilled}>{filledText}</span>
<span className={styles.numRemaining}>{remainingText}</span>
</div>
</div>
{(hint || error) && <span className={styles.hint}>{error || (isValid ? '' : hint)}</span>}
</div>
)
}