novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
218 lines (191 loc) • 7.69 kB
text/typescript
// NG2
import { Component, Input, ElementRef, forwardRef, OnInit, OnDestroy, OnChanges, ViewChild, ViewContainerRef, TemplateRef, SimpleChanges } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
// APP
import { NovoLabelService } from '../../../../services/novo-label-service';
import { NovoDragulaService } from '../../../../elements/dragula/DragulaService';
import { NovoFile } from './extras/file/File';
// Value accessor for the component (supports ngModel)
const FILE_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NovoFileInputElement),
multi: true
};
const LAYOUT_DEFAULTS = { order: 'default', download: true, labelStyle: 'default', draggable: false };
export class NovoFileInputElement implements ControlValueAccessor, OnInit, OnDestroy, OnChanges {
fileInput: TemplateRef<any>;
fileOutput: TemplateRef<any>;
container: ViewContainerRef;
name: string;
multiple: boolean = false;
disabled: boolean = false;
placeholder: string;
layoutOptions: { order?: string, download?: boolean, labelStyle?: string, draggable?: boolean };
value: Array<any> = [];
elements: Array<any> = [];
files: Array<any> = [];
model: any;
active: boolean = false;
commands: any;
visible: boolean;
target: any;
fileOutputBag: string;
onModelChange: Function = () => {
};
onModelTouched: Function = () => {
};
constructor(private element: ElementRef, public labels: NovoLabelService, private dragula: NovoDragulaService) {
this.commands = {
dragenter: this.dragEnterHandler.bind(this),
dragleave: this.dragLeaveHandler.bind(this),
dragover: this.dragOverHandler.bind(this),
drop: this.dropHandler.bind(this)
};
}
ngOnInit() {
['dragenter', 'dragleave', 'dragover', 'drop'].forEach(type => {
this.element.nativeElement.addEventListener(type, this.commands[type]);
});
this.updateLayout();
this.initializeDragula();
this.setInitialFileList();
}
ngOnDestroy() {
['dragenter', 'dragleave', 'dragover', 'drop'].forEach(type => {
this.element.nativeElement.removeEventListener(type, this.commands[type]);
});
let dragulaHasFileOutputBag = this.dragula.bags.length > 0 && this.dragula.bags.filter(x => x.name === this.fileOutputBag).length > 0;
if (dragulaHasFileOutputBag) {
this.dragula.destroy(this.fileOutputBag);
}
}
ngOnChanges(changes?: SimpleChanges) {
this.onModelChange(this.model);
}
updateLayout() {
this.layoutOptions = Object.assign({}, LAYOUT_DEFAULTS, this.layoutOptions);
this.insertTemplatesBasedOnLayout();
}
insertTemplatesBasedOnLayout() {
let order;
switch (this.layoutOptions['order']) {
case 'displayFilesBelow':
order = ['fileInput', 'fileOutput'];
break;
default:
order = ['fileOutput', 'fileInput'];
}
order.forEach((template) => {
this.container.createEmbeddedView(this[template], 0);
});
return order;
}
initializeDragula() {
this.fileOutputBag = `file-output-${this.dragula.bags.length}`;
this.dragula.setOptions(this.fileOutputBag, {
moves: (el, container, handle) => {
return this.layoutOptions.draggable;
}
});
}
setInitialFileList() {
if (this.value) {
this.files = this.value;
}
}
dragEnterHandler(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
this.target = event.target;
this.active = true;
}
dragLeaveHandler(event) {
event.preventDefault();
if (this.target === event.target) {
this.active = false;
}
}
dragOverHandler(event) {
event.preventDefault();
// no-op
}
dropHandler(event) {
event.preventDefault();
this.visible = false;
if (event.dataTransfer.types[0] !== 'Files') {
return;
}
let filelist = Array.from(event.dataTransfer.files);
this.process(this.multiple ? filelist : [filelist[0]]);
this.active = false;
}
writeValue(model: any): void {
this.model = model;
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
check(event) {
this.process(Array.from(event.target.files));
}
process(filelist) {
Promise.all(
filelist.map((file) => this.readFile(file))
).then((files) => {
if (this.multiple) {
this.files.push(...files);
} else {
this.files = files;
}
this.model = this.files;
this.onModelChange(this.model);
});
}
download(file) {
window.open(file.dataURL, '_blank');
}
remove(file) {
this.files.splice(this.files.findIndex(f => (f.name === file.name && f.size === file.size)), 1);
this.model = this.files;
this.onModelChange(this.model);
}
readFile(file) {
return new NovoFile(file).read();
}
}