improved uploading of files (#1730)

* improved uploading of files

- new core/UploadDirective to allow dropping files to any html element
- enhanced file dropping for DataTable rows (disabled by default)
- enhanced file dropping for DocumentList rows (disabled by default)
- upload drop area now handles file uploads for child elements (i.e.
rows in the document list)

* fix unit tests

* unit tests and code cleanup

* #1732, fix upload of folders
This commit is contained in:
Denys Vuika
2017-03-16 16:28:18 +00:00
committed by Mario Romano
parent f3de023ab3
commit 3fee3b5002
19 changed files with 497 additions and 97 deletions

View File

@@ -128,7 +128,7 @@ export class UploadButtonComponent {
let directoryName = this.getDirectoryName(directoryPath);
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
this.uploadService.createFolder(absolutePath, directoryName)
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
.subscribe(
res => {
let relativeDir = this.currentFolderPath + '/' + directoryPath;

View File

@@ -4,8 +4,7 @@
text-align: center;
}
.input-focus {
.file-draggable__input-focus {
color: #2196F3;
margin-left: 3px;
border: 3px dashed #2196F3;
border: 1px dashed #2196F3;
}

View File

@@ -2,6 +2,7 @@
(onFilesDropped)="onFilesDropped($event)"
(onFilesEntityDropped)="onFilesEntityDropped($event)"
(onFolderEntityDropped)="onFolderEntityDropped($event)"
(upload-files)="onUploadFiles($event)"
dropzone="" webkitdropzone="*" #droparea>
<ng-content></ng-content>
</div>

View File

@@ -72,16 +72,35 @@ export class UploadDragAreaComponent {
this.uploadService.setOptions(formFields, this.versioning);
}
/**
* Handles 'upload-files' events raised by child components.
* @param e DOM event
*/
onUploadFiles(e: CustomEvent) {
e.stopPropagation();
e.preventDefault();
let files = e.detail.files;
if (files && files.length > 0) {
if (e.detail.data.obj.entry.isFolder) {
let id = e.detail.data.obj.entry.id;
this.onFilesDropped(files, id, '/');
} else {
this.onFilesDropped(files);
}
}
}
/**
* Method called when files are dropped in the drag area.
*
* @param {File[]} files - files dropped in the drag area.
*/
onFilesDropped(files: File[]): void {
onFilesDropped(files: File[], rootId?: string, directory?: string): void {
if (files.length) {
if (this.checkValidity(files)) {
this.uploadService.addToQueue(files);
this.uploadService.uploadFilesInTheQueue(this.rootFolderId, this.currentFolderPath, this.onSuccess);
this.uploadService.uploadFilesInTheQueue(rootId || this.rootFolderId, directory || this.currentFolderPath, this.onSuccess);
let latestFilesAdded = this.uploadService.getQueue();
if (this.showNotificationBar) {
this.showUndoNotificationBar(latestFilesAdded);
@@ -132,7 +151,7 @@ export class UploadDragAreaComponent {
let relativePath = folder.fullPath.replace(folder.name, '');
relativePath = this.currentFolderPath + relativePath;
this.uploadService.createFolder(relativePath, folder.name)
this.uploadService.createFolder(relativePath, folder.name, this.rootFolderId)
.subscribe(
message => {
this.onSuccess.emit({

View File

@@ -19,7 +19,7 @@ import { FileDraggableDirective } from '../directives/file-draggable.directive';
describe('FileDraggableDirective', () => {
let component;
let component: FileDraggableDirective;
beforeEach( () => {
component = new FileDraggableDirective();
@@ -51,7 +51,7 @@ describe('FileDraggableDirective', () => {
done();
});
component._onDropFiles(fakeEvent);
component.onDropFiles(fakeEvent);
});
it('should emit onFilesDropped event when a file is dragged not with Chrome' , (done) => {
@@ -70,7 +70,7 @@ describe('FileDraggableDirective', () => {
done();
});
component._onDropFiles(fakeEvent);
component.onDropFiles(fakeEvent);
});
it('should emit onFilesDropped event when a file is dragged with Chrome', (done) => {
@@ -90,7 +90,7 @@ describe('FileDraggableDirective', () => {
done();
});
component._onDropFiles(fakeEvent);
component.onDropFiles(fakeEvent);
});
it('should take the focus when the drag enter is called', () => {
@@ -98,7 +98,7 @@ describe('FileDraggableDirective', () => {
spyOn(mockEvent, 'preventDefault');
expect(component.getInputFocus()).toBe(false);
component._onDragEnter(mockEvent);
component.onDragEnter(mockEvent);
expect(component.getInputFocus()).toBe(true);
});
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Directive, EventEmitter, Output } from '@angular/core';
import { Directive, HostListener, HostBinding, EventEmitter, Output } from '@angular/core';
/**
* [file-draggable]
@@ -29,17 +29,12 @@ import { Directive, EventEmitter, Output } from '@angular/core';
* @returns {FileDraggableDirective} .
*/
@Directive({
selector: '[file-draggable]',
host: {
'(drop)': '_onDropFiles($event)',
'(dragenter)': '_onDragEnter($event)',
'(dragleave)': '_onDragLeave($event)',
'(dragover)': '_onDragOver($event)',
'[class.input-focus]': '_inputFocusClass'
}
selector: '[file-draggable]'
})
export class FileDraggableDirective {
files: File [];
@Output()
onFilesDropped: EventEmitter<any> = new EventEmitter();
@@ -49,41 +44,39 @@ export class FileDraggableDirective {
@Output()
onFolderEntityDropped: EventEmitter<any> = new EventEmitter();
files: File [];
private _inputFocusClass: boolean = false;
constructor() {
}
@HostBinding('class.file-draggable__input-focus')
inputFocusClass: boolean = false;
/**
* Method called when files is dropped in the drag and drop area.
*
* @param {$event} $event - DOM $event.
* @param event DOM event.
*/
_onDropFiles($event: any): void {
this._preventDefault($event);
@HostListener('drop', ['$event'])
onDropFiles(event: any): void {
if (!event.defaultPrevented) {
this.preventDefault(event);
let items = $event.dataTransfer.items;
if (items) {
for (let i = 0; i < items.length; i++) {
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
let item = items[i].webkitGetAsEntry();
if (item) {
this._traverseFileTree(item);
let items = event.dataTransfer.items;
if (items) {
for (let i = 0; i < items.length; i++) {
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
let item = items[i].webkitGetAsEntry();
if (item) {
this.traverseFileTree(item);
}
} else {
let files = event.dataTransfer.files;
this.onFilesDropped.emit(files);
}
} else {
let files = $event.dataTransfer.files;
this.onFilesDropped.emit(files);
}
} else {
// safari or FF
let files = event.dataTransfer.files;
this.onFilesDropped.emit(files);
}
} else {
// safari or FF
let files = $event.dataTransfer.files;
this.onFilesDropped.emit(files);
}
this._inputFocusClass = false;
this.inputFocusClass = false;
}
}
/**
@@ -91,7 +84,7 @@ export class FileDraggableDirective {
*
* @param {Object} item - can contains files or folders.
*/
private _traverseFileTree(item: any): void {
private traverseFileTree(item: any): void {
if (item.isFile) {
let self = this;
self.onFilesEntityDropped.emit(item);
@@ -105,44 +98,50 @@ export class FileDraggableDirective {
/**
* Change the style of the drag area when a file drag in.
*
* @param {$event} $event - DOM $event.
* @param {event} event - DOM event.
*/
_onDragEnter($event: Event): void {
this._preventDefault($event);
this._inputFocusClass = true;
@HostListener('dragenter', ['$event'])
onDragEnter(event: Event): void {
if (!event.defaultPrevented) {
this.preventDefault(event);
this.inputFocusClass = true;
}
}
/**
* Change the style of the drag area when a file drag out.
*
* @param {$event} $event - DOM $event.
* @param {event} event - DOM event.
*/
_onDragLeave($event: Event): void {
this._preventDefault($event);
this._inputFocusClass = false;
@HostListener('dragleave', ['$event'])
onDragLeave(event: Event): void {
if (!event.defaultPrevented) {
this.preventDefault(event);
this.inputFocusClass = false;
}
}
/**
* Change the style of the drag area when a file is over the drag area.
*
* @param $event
* @private
* @param event
*/
_onDragOver($event: Event): void {
this._preventDefault($event);
this._inputFocusClass = true;
@HostListener('dragover', ['$event'])
onDragOver(event: Event): void {
if (!event.defaultPrevented) {
this.preventDefault(event);
this.inputFocusClass = true;
}
}
/**
* Prevent default and stop propagation of the DOM event.
*
* @param {$event} $event - DOM $event.
* @param {event} $event - DOM event.
*/
_preventDefault($event: Event): void {
$event.stopPropagation();
$event.preventDefault();
preventDefault(event: Event): void {
event.stopPropagation();
event.preventDefault();
}
/**
@@ -150,6 +149,6 @@ export class FileDraggableDirective {
* @returns {boolean}
*/
getInputFocus () {
return this._inputFocusClass;
return this.inputFocusClass;
}
}

View File

@@ -162,17 +162,14 @@ export class UploadService {
* Create a folder
* @param name - the folder name
*/
createFolder(relativePath: string, name: string) {
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name))
.map(res => {
return res;
})
createFolder(relativePath: string, name: string, parentId?: string) {
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name, parentId))
.do(data => this.logService.info('Node data', data)) // eyeball results in the console
.catch(err => this.handleError(err));
}
callApiCreateFolder(relativePath: string, name: string): Promise<MinimalNodeEntity> {
return this.apiService.getInstance().nodes.createFolder(name, relativePath);
callApiCreateFolder(relativePath: string, name: string, parentId?: string): Promise<MinimalNodeEntity> {
return this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId);
}
/**