71 lines
1.9 KiB
TypeScript
71 lines
1.9 KiB
TypeScript
import { h } from 'preact'
|
|
import { useCallback, useRef, useState } from 'preact/hooks'
|
|
import cn from 'classnames'
|
|
|
|
// TODO when form resets after update the previous image flashes quickly after setImageSrc(null) and before new defaultValue propagates
|
|
|
|
const fileUploadFactory = ({ styles }) => {
|
|
const FileUpload = ({
|
|
autoFocus,
|
|
className,
|
|
defaultValue,
|
|
disabled,
|
|
label,
|
|
name,
|
|
noMargin,
|
|
placeholder,
|
|
required,
|
|
}) => {
|
|
const [touched, setTouched] = useState(false)
|
|
const inputRef = useRef(null)
|
|
const imgRef = useRef(null)
|
|
const [imageSrc, setImageSrc] = useState(null)
|
|
|
|
const onImageChange = useCallback((e) => {
|
|
e.preventDefault()
|
|
|
|
const form = inputRef.current.form
|
|
|
|
const onReset = () => {
|
|
setTouched(false)
|
|
setImageSrc(null)
|
|
}
|
|
|
|
form.addEventListener('reset', onReset)
|
|
|
|
imgRef.current.addEventListener('load', () => URL.revokeObjectURL(imgRef.current.src), { once: true })
|
|
setImageSrc(URL.createObjectURL(e.currentTarget.files[0]))
|
|
|
|
return () => form.removeEventListerner('reset', onReset)
|
|
}, [])
|
|
|
|
return (
|
|
<div className={cn(styles.base, noMargin && styles.noMargin, touched && styles.touched, className)}>
|
|
<input
|
|
type='file'
|
|
accept='image/*'
|
|
name={name}
|
|
ref={inputRef}
|
|
className={styles.element}
|
|
onChange={onImageChange}
|
|
autoFocus={autoFocus}
|
|
disabled={disabled}
|
|
placeholder={placeholder}
|
|
required={required}
|
|
defaultValue={defaultValue}
|
|
/>
|
|
<label htmlFor={name} className={styles.label}>
|
|
{label}
|
|
</label>
|
|
<figure className={styles.image}>
|
|
<img className={styles.image} src={imageSrc || (defaultValue && `/uploads/${defaultValue}`)} ref={imgRef} />
|
|
</figure>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return FileUpload
|
|
}
|
|
|
|
export default fileUploadFactory
|