mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
upload: new 'click' mode with file/folder dialog support (#1738)
* new 'click' mode with file/folder dialog support * fix uploading files to folders via drop on Safari
This commit is contained in:
committed by
Mario Romano
parent
88abad743f
commit
4df72af86d
@@ -89,6 +89,56 @@ It is possible controlling when upload behaviour is enabled/disabled by binding
|
|||||||
<div [adf-upload]="isUploadEnabled()">...</div>
|
<div [adf-upload]="isUploadEnabled()">...</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can decorate any element including buttons, for example:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button [adf-upload]="true" [multiple]="true" [accept]="'image/*'">
|
||||||
|
Upload photos
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
<div [adf-upload]="true" mode="['click']">...</div>
|
||||||
|
<div [adf-upload]="true" mode="['drop']">...</div>
|
||||||
|
<div [adf-upload]="true" mode="['click', 'drop']">...</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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
|
||||||
|
<div style="width: 50px; height: 50px; background-color: brown"
|
||||||
|
[adf-upload]="true"
|
||||||
|
[multiple]="true"
|
||||||
|
[accept]="'image/*'">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="width: 50px; height: 50px; background-color: blueviolet"
|
||||||
|
[adf-upload]="true"
|
||||||
|
[multiple]="true"
|
||||||
|
[directory]="true">
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Drop mode
|
||||||
|
|
||||||
|
For the moment upload directive supports only Files (single or multiple).
|
||||||
|
Support for Folders and `accept` filters is subject to implement.
|
||||||
|
|
||||||
### Events
|
### 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.
|
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.
|
||||||
|
@@ -27,7 +27,7 @@ describe('UploadDirective', () => {
|
|||||||
nativeElement = {
|
nativeElement = {
|
||||||
dispatchEvent: () => {}
|
dispatchEvent: () => {}
|
||||||
};
|
};
|
||||||
directive = new UploadDirective(new ElementRef(nativeElement));
|
directive = new UploadDirective(new ElementRef(nativeElement), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be enabled by default', () => {
|
it('should be enabled by default', () => {
|
||||||
|
@@ -15,12 +15,12 @@
|
|||||||
* limitations under the License.
|
* 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({
|
@Directive({
|
||||||
selector: '[adf-upload]'
|
selector: '[adf-upload]'
|
||||||
})
|
})
|
||||||
export class UploadDirective {
|
export class UploadDirective implements OnInit {
|
||||||
|
|
||||||
@Input('adf-upload')
|
@Input('adf-upload')
|
||||||
enabled: boolean = true;
|
enabled: boolean = true;
|
||||||
@@ -28,25 +28,68 @@ export class UploadDirective {
|
|||||||
@Input('adf-upload-data')
|
@Input('adf-upload-data')
|
||||||
data: any;
|
data: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
mode: string[] = ['drop']; // click|drop
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
multiple: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
accept: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
directory: boolean;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
debug: boolean = false;
|
debug: boolean = false;
|
||||||
|
|
||||||
@HostBinding('class.adf-upload__dragging')
|
@HostBinding('class.adf-upload__dragging')
|
||||||
isDragging: boolean;
|
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')
|
@HostListener('dragenter')
|
||||||
onDragEnter() {
|
onDragEnter() {
|
||||||
if (this.enabled) {
|
if (this.isDropMode()) {
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('dragover', ['$event'])
|
@HostListener('dragover', ['$event'])
|
||||||
onDragOver(event: Event) {
|
onDragOver(event: Event) {
|
||||||
if (this.enabled) {
|
if (this.isDropMode()) {
|
||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -56,58 +99,94 @@ export class UploadDirective {
|
|||||||
|
|
||||||
@HostListener('dragleave')
|
@HostListener('dragleave')
|
||||||
onDragLeave() {
|
onDragLeave() {
|
||||||
if (this.enabled) {
|
if (this.isDropMode()) {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('drop', ['$event'])
|
@HostListener('drop', ['$event'])
|
||||||
onDrop(event: DragEvent) {
|
onDrop(event: DragEvent) {
|
||||||
if (this.enabled) {
|
if (this.isDropMode()) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
|
|
||||||
let files = this.getFilesDropped(event.dataTransfer);
|
const files = this.getFilesDropped(event.dataTransfer);
|
||||||
if (files.length > 0) {
|
this.onUploadFiles(files);
|
||||||
let e = new CustomEvent('upload-files', {
|
|
||||||
detail: {
|
|
||||||
sender: this,
|
|
||||||
data: this.data,
|
|
||||||
files: files
|
|
||||||
},
|
|
||||||
bubbles: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this.el.nativeElement.dispatchEvent(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* 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
|
* @param dataTransfer DataTransfer object
|
||||||
*/
|
*/
|
||||||
protected getFilesDropped(dataTransfer: DataTransfer): File[] {
|
protected getFilesDropped(dataTransfer: DataTransfer): File[] {
|
||||||
let result: File[] = [];
|
const result: File[] = [];
|
||||||
|
|
||||||
if (dataTransfer) {
|
if (dataTransfer) {
|
||||||
let items: DataTransferItemList = dataTransfer.items;
|
const items: FileList = dataTransfer.files;
|
||||||
|
|
||||||
if (items && items.length > 0) {
|
if (items && items.length > 0) {
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
let item: DataTransferItem = items[i];
|
result.push(items[i]);
|
||||||
if (item.type) {
|
|
||||||
let file = item.getAsFile();
|
|
||||||
if (file) {
|
|
||||||
result.push(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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 = (<HTMLInputElement>e.currentTarget);
|
||||||
|
const files = this.getFilesSelected(input.files);
|
||||||
|
this.onUploadFiles(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user