UNPKG

vcc-ui

Version:

A React library for building user interfaces at Volvo Cars

99 lines (98 loc) 2.82 kB
import { objectReduce } from 'fast-loops'; import { useState } from 'react'; function validateField(validation, value) { return objectReduce(validation, (error, validate, message) => { // early return to skip the rest once the first validation failed if (error) { return error; } if (validate instanceof RegExp) { if (typeof value === 'string' && value.match(validate) === null) { return message; } } else if (!validate(value)) { return message; } }, undefined); } export function useField(_ref) { let { name, value = '', touched = false, disabled = false, required = false, loading, showValidationOn, requiredErrorMessage = 'This field is required.', validation = {} } = _ref; const type = typeof value; // add a special validation for required fields where the browser doesn't auto catch if (required) { validation[requiredErrorMessage] = value => type === 'string' ? value.length > 0 : type === 'boolean' ? value : true; } const errorMessage = validateField(validation, value); const initial = { value, errorMessage, isLoading: loading, isDisabled: disabled, isTouched: touched, isRequired: required, isValid: !errorMessage && !loading }; const [field, setField] = useState(initial); const touch = () => setField(field => ({ ...field, isTouched: true })); const untouch = () => setField(field => ({ ...field, isTouched: false })); const update = values => setField(field => ({ ...field, ...values })); function onChange(event) { const newValue = type === 'boolean' ? event.target.checked : event.target.value; const dirty = newValue !== value; const errorMessage = validateField(validation, newValue); setField(field => ({ ...field, value: newValue, errorMessage, isTouched: showValidationOn === 'change' ? dirty : field.isTouched, isValid: !errorMessage && !field.isLoading })); } const props = { name, [type === 'boolean' ? 'checked' : 'value']: field.value, ...(type !== 'boolean' && { loading: field.isLoading }), disabled: field.isDisabled, required: field.isRequired, // only show errrorMessage and validation styles if the field is touched according to the config errorMessage: field.isTouched ? field.errorMessage : undefined, isValid: field.isTouched ? !field.errorMessage : true, onChange }; // by default, we always hide validation errors once the field is focused again props.onFocus = untouch; if (showValidationOn === 'blur') { props.onBlur = touch; } return { // we expose those values for debugging reasons ...field, initial, name, props, update, touch, untouch }; }