Files

105 lines
3.7 KiB
TypeScript
Raw Permalink Normal View History

2026-05-23 17:17:56 -07:00
import { forwardRef, useEffect, useId, useImperativeHandle, useRef, useState, type RefObject } from 'react'
import VectorIconTop from '../../vectors/top.svg'
import './styles/File.css'
import type { BackendLimit } from '../../functions/BackendTypes'
export interface PropsForInputFile {
limits: BackendLimit['upload'] | undefined
}
export interface HandleForInputFile {
getPreview: () => HTMLVideoElement | HTMLImageElement | undefined
getValue: () => File | undefined
}
const InputFile = forwardRef<HandleForInputFile, PropsForInputFile>(({ limits }, ref) => {
const componentID = useId()
const previewRef = useRef<HTMLVideoElement | HTMLImageElement>(undefined)
const inputRef = useRef<HTMLInputElement>(null)
const [fileInstance, setFileInstance] = useState<File>()
const [fileObjectURL, setFileObjectURL] = useState<string>()
function updateInput(file?: File) {
if (fileObjectURL) {
URL.revokeObjectURL(fileObjectURL)
}
const accept = file && !!limits?.mime_types.find((t) => file.type === t)
if (accept) {
setFileObjectURL(URL.createObjectURL(file))
setFileInstance(file)
} else {
setFileObjectURL(undefined)
setFileInstance(undefined)
}
}
useImperativeHandle(ref, () => ({
getPreview: () => previewRef.current,
getValue: () => fileInstance,
}))
useEffect(() => {
return () => {
fileObjectURL && URL.revokeObjectURL(fileObjectURL)
}
}, [fileObjectURL])
return (
<div
id="input-file"
className="input-file"
tabIndex={0}
onClick={() => inputRef.current?.click()}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault()
updateInput(e.dataTransfer.files.item(0)!)
}}>
<input
id={componentID}
ref={inputRef}
type="file"
accept={limits?.mime_types ? limits.mime_types.join(',') : '*/*'}
onChange={(e) => {
e.preventDefault()
updateInput(e.target.files?.item(0) ?? undefined)
}}
/>
{!fileInstance && (
<div className="prompt">
<img className="icon animation-fade-in" src={VectorIconTop} />
<span className="header animation-scroll-in">DRAG OR CLICK TO UPLOAD A FILE</span>
{limits && (
<span className="subheader animation-scroll-in">
MAX: {limits.video_width_max} &times; {limits.video_height_max}; SIZE:{' '}
{Math.floor(limits.filesize / 1024 / 1024)}MB; DURATION:{' '}
{Math.floor(limits.duration / 10) * 10} SECS;
</span>
)}
</div>
)}
{fileInstance && (
<div className="preview">
{fileInstance.type.startsWith('video') && (
<video
ref={previewRef as RefObject<HTMLVideoElement>}
autoPlay
loop
muted
src={fileObjectURL}
/>
)}
{fileInstance.type.startsWith('image') && (
<img ref={previewRef as RefObject<HTMLImageElement>} src={fileObjectURL} />
)}
</div>
)}
</div>
)
})
export default InputFile