|
|
|
|
@ -47,10 +47,6 @@ const FileUploader = ({
|
|
|
|
|
const dropRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
const dragRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
const fileUploader = useRef<HTMLInputElement>(null)
|
|
|
|
|
|
|
|
|
|
// For upload folder
|
|
|
|
|
const dirUploader = useRef<HTMLInputElement>(null)
|
|
|
|
|
|
|
|
|
|
const hideUpload = notSupportBatchUpload && fileList.length > 0
|
|
|
|
|
|
|
|
|
|
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
|
|
|
|
@ -200,30 +196,73 @@ const FileUploader = ({
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
e.target === dragRef.current && setDragging(false)
|
|
|
|
|
}
|
|
|
|
|
type FileWithPath = {
|
|
|
|
|
relativePath?: string
|
|
|
|
|
} & File
|
|
|
|
|
const traverseFileEntry = useCallback(
|
|
|
|
|
(entry: any, prefix = ''): Promise<FileWithPath[]> => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
if (entry.isFile) {
|
|
|
|
|
entry.file((file: FileWithPath) => {
|
|
|
|
|
file.relativePath = `${prefix}${file.name}` // 保留相对路径
|
|
|
|
|
resolve([file])
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
else if (entry.isDirectory) {
|
|
|
|
|
const reader = entry.createReader()
|
|
|
|
|
const entries: any[] = []
|
|
|
|
|
const read = () => {
|
|
|
|
|
reader.readEntries(async (results: FileSystemEntry[]) => {
|
|
|
|
|
if (!results.length) {
|
|
|
|
|
const files = await Promise.all(
|
|
|
|
|
entries.map(ent =>
|
|
|
|
|
traverseFileEntry(ent, `${prefix}${entry.name}/`),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
resolve(files.flat())
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
entries.push(...results)
|
|
|
|
|
read()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
read()
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
resolve([])
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
[], // ← 依赖为空,引用稳定
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleDrop = useCallback((e: DragEvent) => {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
setDragging(false)
|
|
|
|
|
if (!e.dataTransfer)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
const files = [...e.dataTransfer.files] as File[]
|
|
|
|
|
const validFiles = files.filter(isValid)
|
|
|
|
|
initialUpload(validFiles)
|
|
|
|
|
}, [initialUpload, isValid])
|
|
|
|
|
|
|
|
|
|
const handleDrop = useCallback(
|
|
|
|
|
async (e: DragEvent) => {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
setDragging(false)
|
|
|
|
|
if (!e.dataTransfer) return
|
|
|
|
|
const nested = await Promise.all(
|
|
|
|
|
Array.from(e.dataTransfer.items).map((it) => {
|
|
|
|
|
const entry = (it as any).webkitGetAsEntry?.()
|
|
|
|
|
if (entry) return traverseFileEntry(entry)
|
|
|
|
|
const f = it.getAsFile?.()
|
|
|
|
|
return f ? Promise.resolve([f]) : Promise.resolve([])
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
let files = nested.flat()
|
|
|
|
|
if (notSupportBatchUpload) files = files.slice(0, 1)
|
|
|
|
|
const valid = files.filter(isValid)
|
|
|
|
|
initialUpload(valid)
|
|
|
|
|
},
|
|
|
|
|
[initialUpload, isValid, notSupportBatchUpload, traverseFileEntry],
|
|
|
|
|
)
|
|
|
|
|
const selectHandle = () => {
|
|
|
|
|
if (fileUploader.current)
|
|
|
|
|
fileUploader.current.click()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// click on button, use folder upload
|
|
|
|
|
const selectFolderHandle = () => {
|
|
|
|
|
if (dirUploader.current)
|
|
|
|
|
dirUploader.current.click()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const removeFile = (fileID: string) => {
|
|
|
|
|
if (fileUploader.current)
|
|
|
|
|
fileUploader.current.value = ''
|
|
|
|
|
@ -233,15 +272,7 @@ const FileUploader = ({
|
|
|
|
|
}
|
|
|
|
|
const fileChangeHandle = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const files = [...(e.target.files ?? [])] as File[]
|
|
|
|
|
const validFiles = files.filter(isValid)
|
|
|
|
|
initialUpload(validFiles)
|
|
|
|
|
}, [isValid, initialUpload])
|
|
|
|
|
|
|
|
|
|
// Callback for folder upload
|
|
|
|
|
const folderChangeHandle = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const files = [...(e.target.files ?? [])] as File[]
|
|
|
|
|
const validFiles = files.filter(isValid)
|
|
|
|
|
initialUpload(validFiles)
|
|
|
|
|
initialUpload(files.filter(isValid))
|
|
|
|
|
}, [isValid, initialUpload])
|
|
|
|
|
|
|
|
|
|
const { theme } = useTheme()
|
|
|
|
|
@ -263,36 +294,15 @@ const FileUploader = ({
|
|
|
|
|
return (
|
|
|
|
|
<div className="mb-5 w-[640px]">
|
|
|
|
|
{!hideUpload && (
|
|
|
|
|
<>
|
|
|
|
|
<input
|
|
|
|
|
ref={fileUploader}
|
|
|
|
|
id="fileUploader"
|
|
|
|
|
className="hidden"
|
|
|
|
|
type="file"
|
|
|
|
|
multiple={!notSupportBatchUpload}
|
|
|
|
|
accept={ACCEPTS.join(',')}
|
|
|
|
|
onChange={fileChangeHandle}
|
|
|
|
|
/>
|
|
|
|
|
{/* folder uploader */}
|
|
|
|
|
<input
|
|
|
|
|
ref={dirUploader}
|
|
|
|
|
className="hidden"
|
|
|
|
|
type="file"
|
|
|
|
|
webkitdirectory="true"
|
|
|
|
|
directory=""
|
|
|
|
|
onChange={folderChangeHandle}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* give user a “Upload Folder” button */}
|
|
|
|
|
{!hideUpload && (
|
|
|
|
|
<button
|
|
|
|
|
onClick={selectFolderHandle}
|
|
|
|
|
className="mb-2 rounded bg-secondary px-4 py-2 text-black"
|
|
|
|
|
>
|
|
|
|
|
Upload Folder
|
|
|
|
|
</button>
|
|
|
|
|
<input
|
|
|
|
|
ref={fileUploader}
|
|
|
|
|
id="fileUploader"
|
|
|
|
|
className="hidden"
|
|
|
|
|
type="file"
|
|
|
|
|
multiple={!notSupportBatchUpload}
|
|
|
|
|
accept={ACCEPTS.join(',')}
|
|
|
|
|
onChange={fileChangeHandle}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className={cn('mb-1 text-sm font-semibold leading-6 text-text-secondary', titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
|
|
|
|
|
|