UNPKG

ng-ytl-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

246 lines (221 loc) 7.61 kB
// tslint:disable:no-any ordered-imports import { Component, ViewChild, ElementRef, HostListener, OnInit, OnChanges, OnDestroy, SimpleChange, SimpleChanges, ChangeDetectorRef, Input, Renderer2, Optional } from '@angular/core'; import { HttpClient, HttpRequest, HttpEventType, HttpResponse, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { Subscription } from 'rxjs/Subscription'; import { ZipButtonOptions } from './interface'; @Component({ selector: '[nz-upload-btn]', template: ` <input type="file" #file (change)="onChange($event)" [attr.accept]="options.accept" [multiple]="options.multiple" style="display: none;"> <ng-content></ng-content> `, host: { '[class.ant-upload]': 'true', '[attr.tabindex]': '"0"', '[attr.role]': '"button"' }, preserveWhitespaces: false }) export class NzUploadBtnComponent implements OnInit, OnChanges, OnDestroy { private reqs: { [key: string]: Subscription } = {}; private inited = false; private destroy = false; @ViewChild('file') file: ElementRef; // region: fields @Input() classes: string[] = []; @Input() options: ZipButtonOptions; // endregion @HostListener('click') onClick(): void { if (this.options.disabled || !this.file) return; (this.file.nativeElement as HTMLInputElement).click(); } @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent): void { if (this.options.disabled) return; if (e.key === 'Enter') { this.onClick(); } } @HostListener('drop', ['$event']) @HostListener('dragover', ['$event']) onFileDrop(e: DragEvent): void { if (this.options.disabled) return; if (e.type === 'dragover') { e.preventDefault(); return; } const files = Array.prototype.slice.call(e.dataTransfer.files).filter( file => this.attrAccept(file, this.options.accept) ); this.uploadFiles(files); e.preventDefault(); } onChange(e: any): void { if (this.options.disabled) return; this.uploadFiles(e.target.files); e.target.value = ''; } private attrAccept(file: any, acceptedFiles: any): boolean { if (file && acceptedFiles) { const acceptedFilesArray = Array.isArray(acceptedFiles) ? acceptedFiles : acceptedFiles.split(','); const fileName = file.name || ''; const mimeType = file.type || ''; const baseMimeType = mimeType.replace(/\/.*$/, ''); return acceptedFilesArray.some(type => { const validType = type.trim(); if (validType.charAt(0) === '.') { return fileName.toLowerCase().indexOf(validType.toLowerCase(), fileName.toLowerCase().length - validType.toLowerCase().length) !== -1; } else if (/\/\*$/.test(validType)) { // This is something like a image/* mime type return baseMimeType === validType.replace(/\/.*$/, ''); } return mimeType === validType; }); } return true; } private uploadFiles(fileList: any[]): void { let postFiles: any[] = Array.prototype.slice.call(fileList); this.options.filters.forEach(f => postFiles = f.fn(postFiles)); postFiles.forEach((file: any) => { file.uid = Math.random().toString(36).substring(2); this.upload(file, postFiles); }); } private upload(file: any, fileList: any[]): any { if (!this.options.beforeUpload) { return this.post(file); } const before = this.options.beforeUpload(file, fileList); if (before instanceof Observable) { before.subscribe((processedFile: any) => { const processedFileType = Object.prototype.toString.call(processedFile); if (processedFileType === '[object File]' || processedFileType === '[object Blob]') { this.post(processedFile); } else { this.post(file); } }, (err) => { // tslint:disable-next-line:no-unused-expression console && console.log(err); }); } else if (before !== false) { return this.post(file); } } private post(file: any): void { if (this.destroy) return; const opt = this.options; const request = opt.customRequest || this.xhr; const { uid } = file; let { data } = opt; if (typeof data === 'function') { data = data(file); } this.reqs[uid] = (opt.customRequest || this.xhr).call(this, { action: opt.action, name: opt.name, headers: opt.headers, file, data, withCredentials: opt.withCredentials, onProgress: opt.onProgress ? e => { opt.onProgress(e, file); } : null, onSuccess: (ret, xhr) => { delete this.reqs[uid]; opt.onSuccess(ret, file, xhr); }, onError: (xhr) => { delete this.reqs[uid]; opt.onError(xhr, file); } }); opt.onStart(file); } private xhr(args: any): Subscription { const formData = new FormData(); formData.append(args.name, args.file); if (args.data) { Object.keys(args.data).map(key => { formData.append(key, args.data[key]); }); } if (!args.headers) args.headers = {}; if (args.headers['X-Requested-With'] !== null) { args.headers['X-Requested-With'] = `XMLHttpRequest`; } const req = new HttpRequest('POST', args.action, formData, { reportProgress: true, withCredentials: args.withCredentials, headers: new HttpHeaders(args.headers) }); return this.http.request(req).subscribe((event: any) => { if (event.type === HttpEventType.UploadProgress) { if (event.total > 0) { event.percent = event.loaded / event.total * 100; } args.onProgress(event); } else if (event instanceof HttpResponse) { args.onSuccess(event.body, event); } }, (err) => { this.abort(args); args.onError(err); }); } abort(file?: any): void { if (file) { let uid: any = file; if (file && file.uid) { uid = file.uid; } if (this.reqs[uid]) { this.reqs[uid].unsubscribe(); delete this.reqs[uid]; } } else { Object.keys(this.reqs).forEach((uid) => { if (this.reqs[uid]) { this.reqs[uid].unsubscribe(); } delete this.reqs[uid]; }); } } // region: styles _prefixCls = 'ant-upload'; _classList: string[] = []; _setClassMap(): void { this._classList.forEach(cls => this._renderer.removeClass(this._el.nativeElement, cls)); this._classList = [ this._prefixCls, this.options.disabled && `${this._prefixCls}-disabled`, ...this.classes ].filter(item => !!item); this._classList.forEach(cls => this._renderer.addClass(this._el.nativeElement, cls)); this.cd.detectChanges(); } // endregion constructor(@Optional() private http: HttpClient, private _el: ElementRef, private _renderer: Renderer2, private cd: ChangeDetectorRef) { if (!http) throw new Error(`Not found 'HttpClient', You can import 'HttpClientModel' in your root module.`); } ngOnInit(): void { this.inited = true; this._setClassMap(); } ngOnChanges(changes: { [P in keyof this]?: SimpleChange } & SimpleChanges): void { if (this.inited) { this._setClassMap(); } } ngOnDestroy(): void { this.destroy = true; this.abort(); } }