From 393b9cf4ab17006cefe1259d8a5c641d89fb1f22 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 25 Oct 2020 11:27:42 +0100 Subject: [PATCH] Drag and drop of folders. --- .../angular/forms/file-drop.directive.ts | 109 +++++++++++++----- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/frontend/app/framework/angular/forms/file-drop.directive.ts b/frontend/app/framework/angular/forms/file-drop.directive.ts index ad112f5ff..bcae3172e 100644 --- a/frontend/app/framework/angular/forms/file-drop.directive.ts +++ b/frontend/app/framework/angular/forms/file-drop.directive.ts @@ -6,6 +6,7 @@ */ // tslint:disable: prefer-for-of +// tslint:disable: readonly-array import { Directive, ElementRef, EventEmitter, HostListener, Input, Output, Renderer2 } from '@angular/core'; import { Types } from '@app/framework/internal'; @@ -45,18 +46,16 @@ export class FileDropDirective { } @HostListener('paste', ['$event']) - public onPaste(event: ClipboardEvent) { - if (this.noPaste) { - return; - } + public async onPaste(event: ClipboardEvent) { + if (!this.noPaste) { + this.stopEvent(event); - const files = this.getAllowedFiles(event.clipboardData); + const files = await this.getAllowedFiles(event.clipboardData); - if (files && !this.disabled) { - this.drop.emit(files); + if (files && !this.disabled) { + this.drop.emit(files); + } } - - this.stopEvent(event); } @HostListener('dragend', ['$event']) @@ -88,16 +87,15 @@ export class FileDropDirective { } @HostListener('drop', ['$event']) - public onDrop(event: DragDropEvent) { + public async onDrop(event: DragDropEvent) { if (hasFiles(event.dataTransfer)) { - const files = this.getAllowedFiles(event.dataTransfer); + this.stopDrag(event); + + const files = await this.getAllowedFiles(event.dataTransfer); if (files && !this.disabled) { this.drop.emit(files); } - - this.dragEnd(0); - this.stopEvent(event); } } @@ -106,6 +104,11 @@ export class FileDropDirective { event.stopPropagation(); } + private stopDrag(event: DragDropEvent) { + this.dragEnd(0); + this.stopEvent(event); + } + private dragStart() { this.dragCounter++; @@ -122,30 +125,20 @@ export class FileDropDirective { } } - private getAllowedFiles(dataTransfer: DataTransfer | null) { + private async getAllowedFiles(dataTransfer: DataTransfer | null) { if (!dataTransfer || !hasFiles(dataTransfer)) { return null; } - const files: File[] = []; + let files: File[] = []; - for (let i = 0; i < dataTransfer.files.length; i++) { - const file = dataTransfer.files.item(i); + for (let i = 0; i < dataTransfer.items.length; i++) { + const item = dataTransfer.items[i]; - if (file && this.isAllowedFile(file)) { - files.push(file); - } + await transferFileTree(item, files); } - if (files.length === 0) { - for (let i = 0; i < dataTransfer.items.length; i++) { - const file = dataTransfer.items[i].getAsFile(); - - if (file && this.isAllowedFile(file)) { - files.push(file); - } - } - } + files = files.filter(f => this.isAllowedFile(f)); return files.length > 0 ? files : null; } @@ -203,6 +196,62 @@ function hasFiles(dataTransfer: DataTransfer): boolean { } } +async function transferWebkitTree(item: any, files: File[]) { + if (item.isFile) { + const file = await getFilePromise(item); + + files.push(file); + } else if (item.isDirectory) { + const entries = await getFilesPromise(item); + + for (const entry of entries) { + await transferWebkitTree(entry, files); + } + } +} + +async function transferFileTree(item: DataTransferItem, files: File[]) { + if (Types.isFunction(item['webkitGetAsEntry'])) { + const webkitEntry = item.webkitGetAsEntry(); + + if (webkitEntry) { + await transferWebkitTree(webkitEntry, files); + + return; + } + } + + if (Types.isFunction(item['getAsFile'])) { + const fileItem = item.getAsFile(); + + if (fileItem) { + files.push(fileItem); + } + } +} + +function getFilesPromise(item: any): Promise> { + return new Promise((resolve, reject) => { + try { + const reader = item.createReader(); + + reader.readEntries(resolve); + } catch (ex) { + reject(ex); + } + }); +} + +function getFilePromise(item: any): Promise { + return new Promise((resolve, reject) => { + try { + item.file(resolve); + } catch (ex) { + reject(ex); + } + }); +} + interface DragDropEvent extends MouseEvent { readonly dataTransfer: DataTransfer; } \ No newline at end of file