/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ /* eslint-disable @typescript-eslint/consistent-type-assertions */ import { Button, SpaceBetween, TokenGroup } from '@cloudscape-design/components'; import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { commonTableLabels, fileOperationsLabels, fileUploadLabels } from '../../common/labels'; import { FileWithPath, formatFileSize, removeFileNameFromPath, toFileWithPath, } from '../../helpers/fileHelper'; import styles from '../../styles/FileUpload.module.scss'; export interface FileUploadProps { onChange: (files: FileWithPath[]) => void; value: ReadonlyArray; disabled?: boolean; } function FileUpload(props: FileUploadProps) { const { onChange, value, disabled } = props; const [isDropzoneHovered, setDropzoneHovered] = useState(false); const uploadFilesInputRef = useRef(null); const uploadFolderInputRef = useRef(null); // Set HTMLInputElement: webkitdirectory property useEffect(() => { if (uploadFolderInputRef.current !== null) { uploadFolderInputRef.current.setAttribute('directory', ''); uploadFolderInputRef.current.setAttribute('webkitdirectory', ''); } }, [uploadFolderInputRef]); const onUploadInputChange = ({ target }: ChangeEvent) => { onChange( target.files ? Array.from(target.files).map((file) => toFileWithPath(file, removeFileNameFromPath(file.webkitRelativePath)) ) : [] ); }; const onFileRemove = (removeFileIndex: number) => { const newValue = value.filter((_, fileIndex) => fileIndex !== removeFileIndex); onChange(Array.from(newValue)); }; const onDragOver = (event: React.DragEvent) => { event.preventDefault(); setDropzoneHovered(true); if (event.dataTransfer) { event.dataTransfer.dropEffect = 'copy'; } }; const onDragLeave = (event: React.DragEvent) => { event.preventDefault(); setDropzoneHovered(false); if (event.dataTransfer) { event.dataTransfer.dropEffect = 'none'; } }; const onDrop = async (event: React.DragEvent) => { event.preventDefault(); setDropzoneHovered(false); const files = await getFilesDataTransferItems(event.dataTransfer.items); onChange(Array.from(files)); }; const traverseFileTreePromise = (item: FileSystemEntry, path: string, files: FileWithPath[]) => { return new Promise((resolve) => { if (item.isFile) { (item as FileSystemFileEntry).file((file: File) => { const relativePath = path + '/'; files.push(toFileWithPath(file, relativePath)); resolve(file); }); } else if (item.isDirectory) { const dirReader = (item as FileSystemDirectoryEntry).createReader(); dirReader.readEntries((entries) => { const entriesPromises = []; for (const entry of entries) { entriesPromises.push(traverseFileTreePromise(entry, path + '/' + item.name, files)); } resolve(Promise.all(entriesPromises)); }); } }); }; const getFilesDataTransferItems = async ( dataTransferItems: DataTransferItemList ): Promise => { const files: FileWithPath[] = []; return new Promise((resolve, reject) => { const entriesPromises = []; for (const item of dataTransferItems) { const fileSystemEntry = item.webkitGetAsEntry(); if (!fileSystemEntry) { reject(`${item} is not a File`); } else { entriesPromises.push(traverseFileTreePromise(fileSystemEntry, '', files)); } } Promise.all(entriesPromises) .then(() => { resolve(files); }) .catch((e) => reject(e)); }); }; return (
{fileUploadLabels.dragAndDropFolderLabel}
onFileRemove(itemIndex)} items={value.map((file) => ({ label: file.relativePath + file.name, tags: [formatFileSize(file.size)], }))} alignment="vertical" limit={3} /> {fileOperationsLabels.selectFileSubtext}
); } export default FileUpload;