2021-02-02 05:28:25 +01:00
|
|
|
import { h } from 'preact';
|
|
|
|
import { useCallback, useEffect, useState } from 'preact/hooks';
|
|
|
|
|
|
|
|
export default function TextField({
|
|
|
|
helpText,
|
|
|
|
keyboardType = 'text',
|
|
|
|
inputRef,
|
|
|
|
label,
|
|
|
|
leadingIcon: LeadingIcon,
|
|
|
|
onBlur,
|
|
|
|
onChangeText,
|
|
|
|
onFocus,
|
|
|
|
readonly,
|
|
|
|
trailingIcon: TrailingIcon,
|
|
|
|
value: propValue = '',
|
|
|
|
...props
|
|
|
|
}) {
|
|
|
|
const [isFocused, setFocused] = useState(false);
|
|
|
|
const [value, setValue] = useState(propValue);
|
|
|
|
|
|
|
|
const handleFocus = useCallback(
|
|
|
|
(event) => {
|
|
|
|
setFocused(true);
|
|
|
|
onFocus && onFocus(event);
|
|
|
|
},
|
|
|
|
[onFocus]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleBlur = useCallback(
|
|
|
|
(event) => {
|
|
|
|
setFocused(false);
|
|
|
|
onBlur && onBlur(event);
|
|
|
|
},
|
|
|
|
[onBlur]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleChange = useCallback(
|
|
|
|
(event) => {
|
|
|
|
const { value } = event.target;
|
|
|
|
setValue(value);
|
|
|
|
onChangeText && onChangeText(value);
|
|
|
|
},
|
|
|
|
[onChangeText, setValue]
|
|
|
|
);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (propValue !== value) {
|
|
|
|
setValue(propValue);
|
|
|
|
}
|
2021-02-09 20:35:33 +01:00
|
|
|
// DO NOT include `value`
|
|
|
|
}, [propValue, setValue]); // eslint-disable-line react-hooks/exhaustive-deps
|
2021-02-02 05:28:25 +01:00
|
|
|
|
|
|
|
const labelMoved = isFocused || value !== '';
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="w-full">
|
|
|
|
<div
|
|
|
|
className={`bg-gray-100 dark:bg-gray-700 rounded rounded-b-none border-gray-400 border-b p-1 pl-4 pr-3 ${
|
|
|
|
isFocused ? 'border-blue-500 dark:border-blue-500' : ''
|
|
|
|
}`}
|
|
|
|
ref={inputRef}
|
|
|
|
>
|
2021-02-14 16:56:10 +01:00
|
|
|
<label
|
|
|
|
className="flex space-x-2 items-center"
|
|
|
|
data-testid={`label-${label.toLowerCase().replace(/[^\w]+/g, '_')}`}
|
|
|
|
>
|
2021-02-05 00:19:47 +01:00
|
|
|
{LeadingIcon ? (
|
2021-02-09 20:35:33 +01:00
|
|
|
<div className="w-10 h-full">
|
2021-02-05 00:19:47 +01:00
|
|
|
<LeadingIcon />
|
|
|
|
</div>
|
|
|
|
) : null}
|
2021-02-02 05:28:25 +01:00
|
|
|
<div className="relative w-full">
|
|
|
|
<input
|
|
|
|
className="h-6 mt-6 w-full bg-transparent focus:outline-none focus:ring-0"
|
|
|
|
onBlur={handleBlur}
|
|
|
|
onFocus={handleFocus}
|
|
|
|
onInput={handleChange}
|
2021-02-09 20:35:33 +01:00
|
|
|
readOnly={readonly}
|
|
|
|
tabIndex="0"
|
2021-02-02 05:28:25 +01:00
|
|
|
type={keyboardType}
|
|
|
|
value={value}
|
|
|
|
{...props}
|
|
|
|
/>
|
|
|
|
<div
|
|
|
|
className={`absolute top-3 transition transform text-gray-600 dark:text-gray-400 ${
|
|
|
|
labelMoved ? 'text-xs -translate-y-2' : ''
|
|
|
|
} ${isFocused ? 'text-blue-500 dark:text-blue-500' : ''}`}
|
|
|
|
>
|
|
|
|
{label}
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-02-05 00:19:47 +01:00
|
|
|
{TrailingIcon ? (
|
2021-02-09 20:35:33 +01:00
|
|
|
<div className="w-10 h-10">
|
2021-02-05 00:19:47 +01:00
|
|
|
<TrailingIcon />
|
|
|
|
</div>
|
|
|
|
) : null}
|
2021-02-02 05:28:25 +01:00
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
{helpText ? <div className="text-xs pl-3 pt-1">{helpText}</div> : null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|