diff --git a/ng2-components/ng2-alfresco-core/README.md b/ng2-components/ng2-alfresco-core/README.md index 9866d8648a..88522d275c 100644 --- a/ng2-components/ng2-alfresco-core/README.md +++ b/ng2-components/ng2-alfresco-core/README.md @@ -89,6 +89,56 @@ It is possible controlling when upload behaviour is enabled/disabled by binding
...
``` +You can decorate any element including buttons, for example: + +```html + +``` + +### Modes + +Directive supports several modes: + +- **drop** mode, where decorated element acts like a drop zone for files (**default** mode) +- **click** mode, where decorated element invokes File Dialog to select files or folders. + +It is also possible combining modes together. + +```html +
...
+
...
+
...
+``` + +#### Click mode + +For the click mode you can provide additional attributes for the File Dialog: + +- **directory**, enables directory selection +- **multiple**, enables multiple file/folder selection +- **accept**, filters the content accepted + +```html +
+
+ +
+
+``` + +#### Drop mode + +For the moment upload directive supports only Files (single or multiple). +Support for Folders and `accept` filters is subject to implement. + ### Events Once a single or multiple files are dropped on the decorated element the `upload-files` [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) is raised. diff --git a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts index 166c9afd52..d45181bb5a 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts +++ b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.spec.ts @@ -27,7 +27,7 @@ describe('UploadDirective', () => { nativeElement = { dispatchEvent: () => {} }; - directive = new UploadDirective(new ElementRef(nativeElement)); + directive = new UploadDirective(new ElementRef(nativeElement), null); }); it('should be enabled by default', () => { diff --git a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts index b3fb37d393..e41e8ecae0 100644 --- a/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts +++ b/ng2-components/ng2-alfresco-core/src/directives/upload.directive.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { Directive, Input, HostBinding, HostListener, ElementRef } from '@angular/core'; +import { Directive, Input, HostBinding, HostListener, ElementRef, Renderer, OnInit } from '@angular/core'; @Directive({ selector: '[adf-upload]' }) -export class UploadDirective { +export class UploadDirective implements OnInit { @Input('adf-upload') enabled: boolean = true; @@ -28,25 +28,68 @@ export class UploadDirective { @Input('adf-upload-data') data: any; + @Input() + mode: string[] = ['drop']; // click|drop + + @Input() + multiple: boolean; + + @Input() + accept: string; + + @Input() + directory: boolean; + @Input() debug: boolean = false; @HostBinding('class.adf-upload__dragging') isDragging: boolean; - constructor(private el: ElementRef) { + private upload: HTMLInputElement; + + constructor(private el: ElementRef, private renderer: Renderer) { + } + + ngOnInit() { + if (this.isClickMode() && this.renderer) { + this.upload = this.renderer.createElement(this.el.nativeElement.parentNode, 'input') as HTMLInputElement; + this.upload.type = 'file'; + this.upload.style.display = 'none'; + this.upload.addEventListener('change', e => this.onSelectFiles(e)); + + if (this.multiple) { + this.upload.setAttribute('multiple', ''); + } + + if (this.accept) { + this.upload.setAttribute('accept', this.accept); + } + + if (this.directory) { + this.upload.setAttribute('webkitdirectory', ''); + } + } + } + + @HostListener('click', ['$event']) + onClick(event: Event) { + if (this.isClickMode() && this.upload) { + event.preventDefault(); + this.upload.click(); + } } @HostListener('dragenter') onDragEnter() { - if (this.enabled) { + if (this.isDropMode()) { this.isDragging = true; } } @HostListener('dragover', ['$event']) onDragOver(event: Event) { - if (this.enabled) { + if (this.isDropMode()) { if (event) { event.preventDefault(); } @@ -56,58 +99,94 @@ export class UploadDirective { @HostListener('dragleave') onDragLeave() { - if (this.enabled) { + if (this.isDropMode()) { this.isDragging = false; } } @HostListener('drop', ['$event']) onDrop(event: DragEvent) { - if (this.enabled) { + if (this.isDropMode()) { event.preventDefault(); event.stopPropagation(); this.isDragging = false; - let files = this.getFilesDropped(event.dataTransfer); - if (files.length > 0) { - let e = new CustomEvent('upload-files', { - detail: { - sender: this, - data: this.data, - files: files - }, - bubbles: true - }); - - this.el.nativeElement.dispatchEvent(e); - } + const files = this.getFilesDropped(event.dataTransfer); + this.onUploadFiles(files); } } + onUploadFiles(files: File[]) { + if (this.enabled && files.length > 0) { + let e = new CustomEvent('upload-files', { + detail: { + sender: this, + data: this.data, + files: files + }, + bubbles: true + }); + + this.el.nativeElement.dispatchEvent(e); + } + } + + protected hasMode(mode: string): boolean { + return this.enabled && mode && this.mode && this.mode.indexOf(mode) > -1; + } + + protected isDropMode(): boolean { + return this.hasMode('drop'); + } + + protected isClickMode(): boolean { + return this.hasMode('click'); + } + /** * Extract files from the DataTransfer object used to hold the data that is being dragged during a drag and drop operation. * @param dataTransfer DataTransfer object */ protected getFilesDropped(dataTransfer: DataTransfer): File[] { - let result: File[] = []; + const result: File[] = []; if (dataTransfer) { - let items: DataTransferItemList = dataTransfer.items; + const items: FileList = dataTransfer.files; if (items && items.length > 0) { for (let i = 0; i < items.length; i++) { - let item: DataTransferItem = items[i]; - if (item.type) { - let file = item.getAsFile(); - if (file) { - result.push(file); - } - } + result.push(items[i]); } } } return result; } + + /** + * Extract files from the FileList object used to hold files that user selected by means of File Dialog. + * @param fileList List of selected files + */ + protected getFilesSelected(fileList: FileList) { + let result: File[] = []; + if (fileList && fileList.length > 0) { + for (let i = 0; i < fileList.length; i++) { + result.push(fileList[i]); + } + } + return result; + } + + /** + * Invoked when user selects files or folders by means of File Dialog + * @param e DOM event + */ + protected onSelectFiles(e: Event) { + if (this.isClickMode()) { + const input = (e.currentTarget); + const files = this.getFilesSelected(input.files); + this.onUploadFiles(files); + } + } }