2023-07-02 05:08:42 +00:00
|
|
|
import { clsx } from 'clsx'
|
|
|
|
import styles from './DropArea.module.scss'
|
|
|
|
import { createSignal, JSX, Show } from 'solid-js'
|
|
|
|
import { createDropzone, createFileUploader } from '@solid-primitives/upload'
|
|
|
|
import { useLocalize } from '../../../context/localize'
|
|
|
|
import { validateFiles } from '../../../utils/validateFile'
|
|
|
|
import type { FileTypeToUpload } from '../../../pages/types'
|
|
|
|
import { handleFileUpload } from '../../../utils/handleFileUpload'
|
2023-07-14 13:06:21 +00:00
|
|
|
import { UploadedFile } from '../../../pages/types'
|
2023-10-27 18:50:13 +00:00
|
|
|
import { handleImageUpload } from '../../../utils/handleImageUpload'
|
2023-07-02 05:08:42 +00:00
|
|
|
|
|
|
|
type Props = {
|
|
|
|
class?: string
|
|
|
|
placeholder: string
|
|
|
|
isMultiply: boolean
|
2023-07-14 13:06:21 +00:00
|
|
|
fileType: FileTypeToUpload
|
|
|
|
onUpload: (value: UploadedFile[]) => void
|
|
|
|
description?: string | JSX.Element
|
|
|
|
isSquare?: boolean
|
2023-07-02 05:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export const DropArea = (props: Props) => {
|
|
|
|
const { t } = useLocalize()
|
|
|
|
const [dragActive, setDragActive] = createSignal(false)
|
|
|
|
const [dropAreaError, setDropAreaError] = createSignal<string>()
|
|
|
|
const [loading, setLoading] = createSignal(false)
|
|
|
|
|
|
|
|
const runUpload = async (files) => {
|
|
|
|
try {
|
|
|
|
setLoading(true)
|
|
|
|
|
2023-07-14 13:06:21 +00:00
|
|
|
const results: UploadedFile[] = []
|
2023-07-02 05:08:42 +00:00
|
|
|
for (const file of files) {
|
2023-10-27 18:50:13 +00:00
|
|
|
const handler = props.fileType === 'image' ? handleImageUpload : handleFileUpload
|
|
|
|
const result = await handler(file)
|
2023-07-14 13:06:21 +00:00
|
|
|
results.push(result)
|
2023-07-02 05:08:42 +00:00
|
|
|
}
|
|
|
|
props.onUpload(results)
|
|
|
|
setLoading(false)
|
|
|
|
} catch (error) {
|
2023-07-14 13:06:21 +00:00
|
|
|
setLoading(false)
|
|
|
|
setDropAreaError(t('Upload error'))
|
2023-07-02 05:08:42 +00:00
|
|
|
console.error('[runUpload]', error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const initUpload = async (selectedFiles) => {
|
|
|
|
if (!props.isMultiply && files.length > 1) {
|
|
|
|
setDropAreaError(t('Many files, choose only one'))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const isValid = validateFiles(props.fileType, selectedFiles)
|
|
|
|
if (isValid) {
|
|
|
|
await runUpload(selectedFiles)
|
|
|
|
} else {
|
|
|
|
setDropAreaError(t('Invalid file type'))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const { files, selectFiles } = createFileUploader({
|
|
|
|
multiple: true,
|
|
|
|
accept: `${props.fileType}/*`
|
|
|
|
})
|
|
|
|
|
|
|
|
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
|
|
|
|
onDrop: async () => {
|
|
|
|
setDragActive(false)
|
|
|
|
await initUpload(droppedFiles())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
const handleDrag = (event) => {
|
|
|
|
if (event.type === 'dragenter' || event.type === 'dragover') {
|
|
|
|
setDragActive(true)
|
|
|
|
} else if (event.type === 'dragleave') {
|
|
|
|
setDragActive(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const handleDropFieldClick = async () => {
|
|
|
|
selectFiles((selectedFiles) => {
|
|
|
|
const filesArray = selectedFiles.map((file) => {
|
|
|
|
return file
|
|
|
|
})
|
|
|
|
initUpload(filesArray)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2023-07-14 13:06:21 +00:00
|
|
|
<div class={clsx(styles.DropArea, props.class, props.isSquare && styles['square'])}>
|
2023-07-02 05:08:42 +00:00
|
|
|
<div
|
|
|
|
class={clsx(styles.field, { [styles.active]: dragActive() })}
|
|
|
|
onDragEnter={handleDrag}
|
|
|
|
onDragLeave={handleDrag}
|
|
|
|
onDragOver={handleDrag}
|
|
|
|
ref={dropzoneRef}
|
|
|
|
onClick={handleDropFieldClick}
|
|
|
|
>
|
2023-11-06 07:05:01 +00:00
|
|
|
<div class={styles.text}>{loading() ? t('Loading') : props.placeholder}</div>
|
2023-07-14 13:06:21 +00:00
|
|
|
<Show when={!loading() && props.isSquare && props.description}>
|
|
|
|
<div class={styles.description}>{props.description}</div>
|
|
|
|
</Show>
|
2023-07-02 05:08:42 +00:00
|
|
|
</div>
|
|
|
|
<Show when={dropAreaError()}>
|
|
|
|
<div class={styles.error}>{dropAreaError()}</div>
|
|
|
|
</Show>
|
2023-07-14 13:06:21 +00:00
|
|
|
<Show when={!dropAreaError() && props.description && !props.isSquare}>
|
2023-07-02 05:08:42 +00:00
|
|
|
<div class={styles.description}>{props.description}</div>
|
|
|
|
</Show>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|