import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' import useTheme from '../use-theme' import InputLabel from './input-label' import InputBlockLabel from './input-block-label' import InputIcon from './input-icon' import InputClearIcon from './input-icon-clear' import { getColors } from './styles' import { Props, defaultProps } from './input-props' import useScaleable, { withScaleable } from '../use-scaleable' type NativeAttrs = Omit, keyof Props> export type InputProps = Props & NativeAttrs const simulateChangeEvent = ( el: HTMLInputElement, event: React.MouseEvent, ): React.ChangeEvent => { return { ...event, target: el, currentTarget: el, } } const InputComponent = React.forwardRef< HTMLInputElement, React.PropsWithChildren >( ( { label, labelRight, type, htmlType, icon, iconRight, iconClickable, onIconClick, initialValue, onChange, readOnly, value, onClearClick, clearable, className, onBlur, onFocus, autoComplete, placeholder, children, disabled, ...props }: React.PropsWithChildren & typeof defaultProps, ref: React.Ref, ) => { const theme = useTheme() const { SCALES } = useScaleable() const inputRef = useRef(null) useImperativeHandle(ref, () => inputRef.current) const [selfValue, setSelfValue] = useState(initialValue) const [hover, setHover] = useState(false) const isControlledComponent = useMemo(() => value !== undefined, [value]) const labelClasses = useMemo( () => (labelRight ? 'right-label' : label ? 'left-label' : ''), [label, labelRight], ) const iconClasses = useMemo( () => (iconRight ? 'right-icon' : icon ? 'left-icon' : ''), [icon, iconRight], ) const { color, borderColor, hoverBorder } = useMemo( () => getColors(theme.palette, type), [theme.palette, type], ) const changeHandler = (event: React.ChangeEvent) => { if (disabled || readOnly) return setSelfValue(event.target.value) onChange && onChange(event) } const clearHandler = (event: React.MouseEvent) => { setSelfValue('') onClearClick && onClearClick(event) /* istanbul ignore next */ if (!inputRef.current) return const changeEvent = simulateChangeEvent(inputRef.current, event) changeEvent.target.value = '' onChange && onChange(changeEvent) inputRef.current.focus() } const focusHandler = (e: React.FocusEvent) => { setHover(true) onFocus && onFocus(e) } const blurHandler = (e: React.FocusEvent) => { setHover(false) onBlur && onBlur(e) } const iconClickHandler = (e: React.MouseEvent) => { if (disabled) return onIconClick && onIconClick(e) } const iconProps = useMemo( () => ({ clickable: iconClickable, onClick: iconClickHandler, }), [iconClickable, iconClickHandler], ) useEffect(() => { if (isControlledComponent) { setSelfValue(value as string) } }) const controlledValue = isControlledComponent ? { value: selfValue } : { defaultValue: initialValue } const inputProps = { ...props, ...controlledValue, } return (
{children && {children}}
{label && {label}}
{icon && } {clearable && ( )} {iconRight && }
{labelRight && {labelRight}}
) }, ) InputComponent.defaultProps = defaultProps InputComponent.displayName = 'GeistInput' const Input = withScaleable(InputComponent) export default Input