import React, { BaseSyntheticEvent, ReactNode, useCallback, useEffect, useState } from 'react'; import { debounce as _debounce } from 'lodash'; interface FabInputProps { id: string, onChange?: (value: string, validity?: ValidityState) => void, defaultValue: any, icon?: ReactNode, addOn?: ReactNode, addOnClassName?: string, className?: string, disabled?: boolean, required?: boolean, debounce?: number, readOnly?: boolean, maxLength?: number, pattern?: string, placeholder?: string, error?: string, type?: 'text' | 'date' | 'password' | 'url' | 'time' | 'tel' | 'search' | 'number' | 'month' | 'email' | 'datetime-local' | 'week', step?: number | 'any', min?: number, max?: number, } /** * This component is a template for an input component that wraps the application style */ export const FabInput: React.FC = ({ id, onChange, defaultValue, icon, className, disabled, type, required, debounce, addOn, addOnClassName, readOnly, maxLength, pattern, placeholder, error, step, min, max }) => { const [inputValue, setInputValue] = useState(defaultValue); /** * When the component is mounted, initialize the default value for the input. * If the default value changes, update the value of the input until there's no content in it. */ useEffect(() => { if (!inputValue) { setInputValue(defaultValue); if (typeof onChange === 'function') { onChange(defaultValue); } } }, [defaultValue]); /** * Check if the current component was provided an icon to display */ const hasIcon = (): boolean => { return !!icon; } /** * Check if the current component was provided an add-on element to display, at the end of the input */ const hasAddOn = (): boolean => { return !!addOn; } /** * Check if the current component was provided an error string to display, on the input */ const hasError = (): boolean => { return !!error; } /** * Debounced (ie. temporised) version of the 'on change' callback. */ const debouncedOnChange = debounce ? useCallback(_debounce(onChange, debounce), [onChange, debounce]) : null; /** * Handle the change of content in the input field, and trigger the parent callback, if any */ const handleChange = (e: BaseSyntheticEvent): void => { const { value, validity } = e.target; setInputValue(value); if (typeof onChange === 'function') { if (debounce) { debouncedOnChange(value, validity); } else { onChange(value, validity); } } } return (
{hasIcon() && {icon}} {hasAddOn() && {addOn}}
{hasError() && {error} }
); } FabInput.defaultProps = { type: 'text', debounce: 0 };