feat: drag'n'drop with directory
This commit is contained in:
1 parent
845a7aa5b5
commit
8faf1a233e
2 files changed
+61
-6
No files matched your search
@@ -2,6 +2,7 @@ import { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useConsistCallback } from '../utils/useConsistCallback';
|
||||
import { isEqual } from '../utils/isEqual';
|
||||
import { createThumbnail } from '../utils/createThumbnail';
|
||||
import { getFilesFromDataTransferItem } from '../utils/fileEntry';
|
||||
|
||||
interface Props {
|
||||
text?: string;
|
||||
@@ -105,10 +106,13 @@ export const ContentInput = memo<Props>((props) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const droppedFiles = Array.from(e.dataTransfer!.files);
|
||||
if (droppedFiles.length) {
|
||||
handleFilesChange((prev) => [...prev, ...droppedFiles]);
|
||||
}
|
||||
const promises = Array.from(e.dataTransfer?.items || [], getFilesFromDataTransferItem)
|
||||
Promise.all(promises).then((files) => {
|
||||
const droppedFiles = ([] as File[]).concat(...files);
|
||||
if (droppedFiles.length) {
|
||||
handleFilesChange((prev) => [...prev, ...droppedFiles]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('drop', handleDrop, true);
|
||||
@@ -184,7 +188,7 @@ export const ContentInput = memo<Props>((props) => {
|
||||
Add file
|
||||
</button>
|
||||
|
||||
{files.map((file, index) => <UploadedFileItem file={file} index={index} removeFile={removeFile} key={index} />)}
|
||||
{files.map((file, index) => <AttachedFileItem file={file} index={index} removeFile={removeFile} key={index} />)}
|
||||
|
||||
<button onClick={doClear} className='btn-gray' key='clearBtn'>
|
||||
Clear
|
||||
@@ -209,7 +213,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
function UploadedFileItem({ index, file, removeFile }: { index: number; file: File; removeFile: (index: number) => void; }) {
|
||||
function AttachedFileItem({ index, file, removeFile }: { index: number; file: File; removeFile: (index: number) => void; }) {
|
||||
const [url, setUrl] = useState<string>();
|
||||
useEffect(() => {
|
||||
const url = URL.createObjectURL(file);
|
||||
@@ -250,3 +254,4 @@ export async function addThumbnail(file: File) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
export async function getFilesFromDataTransferItem(item: DataTransferItem) {
|
||||
const entry = item.webkitGetAsEntry?.();
|
||||
const allFiles: File[] = [];
|
||||
if (entry?.isDirectory) {
|
||||
const dir = entry as FileSystemDirectoryEntry;
|
||||
await readDirectory(dir, allFiles, `${dir.name}/`);
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
if (entry?.isFile) {
|
||||
const file = await getFile(entry as FileSystemFileEntry);
|
||||
allFiles.push(file);
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function readDirectory(directory: FileSystemDirectoryEntry, allFiles: File[], pathPrefix: string): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const reader = directory.createReader();
|
||||
reader.readEntries(async (entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory) {
|
||||
await readDirectory(entry as FileSystemDirectoryEntry, allFiles, `${pathPrefix}${entry.name}/`);
|
||||
} else if (entry.isFile) {
|
||||
const originalFile = await getFile(entry as FileSystemFileEntry);
|
||||
const file = new File(
|
||||
[originalFile],
|
||||
`${pathPrefix}${entry.name}`,
|
||||
{
|
||||
type: originalFile.type,
|
||||
lastModified: originalFile.lastModified,
|
||||
}
|
||||
);
|
||||
allFiles.push(file);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getFile(fileEntry: FileSystemFileEntry): Promise<File> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fileEntry.file((file) => {
|
||||
resolve(file);
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user