feat: enhance TextField functionality

This commit is contained in:
Hossein Mehrabi 2023-02-22 15:21:38 +03:30
parent 146337e8f4
commit 74a54a6575
3 changed files with 86 additions and 28 deletions

View File

@ -25,4 +25,6 @@ Root.args = {
withIcon: false,
error: false,
placeholder: 'Placeholder',
defaultValue: 'default value',
onChange: undefined,
}

View File

@ -1,5 +1,6 @@
import clsx from 'clsx'
import React, { useRef, useState } from 'react'
import React, { useRef } from 'react'
import { useInput } from '../../utils/useInput'
import { CheckIcon, CloseIcon, ErrorIcon } from '../Icons'
import { Typography } from '../Typography'
import { textFieldClasses } from './TextField.classes'
@ -7,15 +8,17 @@ import { textFieldClasses } from './TextField.classes'
export type TextFieldProps = Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange' | 'value'
> & {
size?: 'large' | 'medium'
withIcon?: boolean
error?: boolean
disabled?: boolean
supportingText?: string
value?: string
onChange?: (value: any) => void
}
> &
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
size?: 'large' | 'medium'
withIcon?: boolean
error?: boolean
disabled?: boolean
supportingText?: string
value?: string
defaultValue?: string
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
}
export const TextField: React.FC<TextFieldProps> & {
classes: typeof textFieldClasses
@ -26,26 +29,18 @@ export const TextField: React.FC<TextFieldProps> & {
error = false,
children,
value,
defaultValue,
onChange,
inputProps = {},
...props
}) => {
const ref = useRef<HTMLDivElement>(null)
const [inputValue, setInputValue] = useState<string>('')
const ref = useRef<HTMLInputElement>(null)
const input = useInput({ defaultValue, value, onChange, ref })
const handleChange = (e: React.FormEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.value
if (onChange) onChange(newValue)
setInputValue(newValue)
}
const onCancel = () => {
setInputValue('')
if (typeof onChange !== 'undefined') onChange('')
}
const onCancel = () => input.setValue('')
return (
<div
ref={ref}
className={clsx(
props.className,
textFieldClasses.root,
@ -53,26 +48,29 @@ export const TextField: React.FC<TextFieldProps> & {
props.disabled && textFieldClasses.disabled,
withIcon && textFieldClasses.withIcon,
)}
{...props}
>
<div>
<input
onChange={handleChange}
{...inputProps}
ref={ref}
value={input.value}
onChange={input.onChange}
className={clsx(
inputProps.className,
textFieldClasses.input,
error && textFieldClasses.error,
)}
value={inputValue}
{...props}
/>
{withIcon && error ? (
<span className={textFieldClasses.icon} onClick={onCancel}>
<ErrorIcon color="primary" className={textFieldClasses.icon} />
</span>
) : withIcon && !inputValue.length ? (
) : withIcon && !input.filled ? (
<span className={textFieldClasses.icon}>
<CheckIcon color="primary" />
</span>
) : withIcon && inputValue.length ? (
) : withIcon && input.filled ? (
<span className={textFieldClasses.icon} onClick={onCancel}>
<CloseIcon color="primary" />
</span>

View File

@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react'
export type InputValueType =
React.InputHTMLAttributes<HTMLInputElement>['value']
export type InputOnChangeType =
React.InputHTMLAttributes<HTMLInputElement>['onChange']
export type InputProps = {
value?: InputValueType
defaultValue?: InputValueType
onChange?: InputOnChangeType
ref?: React.RefObject<HTMLInputElement>
}
export const useInput = (props: InputProps) => {
const [value, setValue] = useState<InputValueType>(
props.value ?? props.defaultValue ?? '',
)
const uncontrolled = typeof props.value === 'undefined'
const filled =
typeof value === 'undefined'
? false
: typeof value === 'string'
? value.length > 0
: value.toString().length > 0
const onChange: InputOnChangeType = (event) => {
if (uncontrolled) return setValue(event.target.value)
props.onChange && props.onChange(event)
}
const setter = (value: InputValueType) => {
if (!props.ref?.current) return
const element = props.ref.current
const event = new Event('input', { bubbles: true })
Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value',
)?.set?.call?.(element, value)
element.dispatchEvent(event)
}
useEffect(() => {
!uncontrolled && setValue(props.value)
}, [uncontrolled, props.value])
return {
value,
filled,
onChange,
setValue: setter,
}
}