brf/client/shared/components/image_upload_factory.tsx

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