UNPKG

23.8 kBJavaScriptView Raw
1import { Injectable, Directive, ElementRef, Input, HostListener, HostBinding, EventEmitter, Component, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Output, ViewChild, NgModule } from '@angular/core';
2import { Subject, Observable, combineLatest, of, Subscription } from 'rxjs';
3import { switchMap } from 'rxjs/operators';
4import { NG_VALUE_ACCESSOR } from '@angular/forms';
5import { CommonModule } from '@angular/common';
6
7/**
8 * @fileoverview added by tsickle
9 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
10 */
11/**
12 * Service to share the upload events between draggable zones
13 * and inputs
14 */
15class UploadEventsService {
16 constructor() {
17 this._uploads$ = new Subject();
18 this.events$ = this._uploads$.pipe(switchMap((/**
19 * @param {?} obs
20 * @return {?}
21 */
22 obs => this.mergeIndividualUploads(obs))));
23 }
24 // tslint:disable-next-line:max-func-body-length
25 /**
26 * @param {?} fileList
27 * @param {?=} dataType
28 * @return {?}
29 */
30 readFiles(fileList, dataType = 3 /* None */) {
31 /** @type {?} */
32 const fileObservables = [];
33 if (fileList) {
34 // tslint:disable-next-line:prefer-for-of
35 for (let i = 0; i < fileList.length; i += 1) {
36 fileObservables.push(this.fileReaderObsFactory(fileList[i], dataType));
37 }
38 }
39 this._uploads$.next(fileObservables);
40 }
41 /**
42 * @private
43 * @param {?} obs
44 * @return {?}
45 */
46 mergeIndividualUploads(obs) {
47 return new Observable((/**
48 * @param {?} observer
49 * @return {?}
50 */
51 observer => {
52 /** @type {?} */
53 const subs = combineLatest(obs).subscribe({
54 next: (/**
55 * @param {?} events
56 * @return {?}
57 */
58 events => {
59 /** @type {?} */
60 let aborted = false;
61 /** @type {?} */
62 let completedCount = 0;
63 /** @type {?} */
64 const next = {
65 files: events,
66 status: 0 /* EMPTY */,
67 error: null,
68 };
69 eventLoop: for (const event of events) {
70 switch (event.status) {
71 case 4 /* ABORT */:
72 next.status = 4 /* ABORT */;
73 aborted = true;
74 break eventLoop;
75 case 1 /* LOADING */:
76 next.status = 1 /* LOADING */;
77 break;
78 case 2 /* DONE */:
79 completedCount += 1;
80 break;
81 default:
82 }
83 }
84 if (aborted) {
85 for (const event of events) {
86 if (event.reader) {
87 event.reader.abort();
88 }
89 }
90 }
91 else if (completedCount === events.length) {
92 next.status = 2 /* DONE */;
93 }
94 observer.next(next);
95 }),
96 complete: (/**
97 * @return {?}
98 */
99 () => observer.complete()),
100 error: (/**
101 * @param {?} err
102 * @return {?}
103 */
104 (err) => observer.error(err)),
105 });
106 return (/**
107 * @return {?}
108 */
109 () => {
110 subs.unsubscribe();
111 });
112 }));
113 }
114 // tslint:disable-next-line:max-func-body-length
115 /**
116 * @private
117 * @param {?} file
118 * @param {?} dataType
119 * @return {?}
120 */
121 fileReaderObsFactory(file, dataType) {
122 /** @type {?} */
123 const fileData = {
124 name: file.name,
125 size: file.size,
126 type: file.type,
127 data: null,
128 lastModified: file.lastModified,
129 total: file.size,
130 loaded: 0,
131 };
132 if (dataType === 3 /* None */) {
133 fileData.data = file;
134 return of(this.eventFactory(fileData, 2 /* DONE */, null));
135 }
136 // tslint:disable-next-line:max-func-body-length
137 return new Observable((/**
138 * @param {?} observer
139 * @return {?}
140 */
141 observer => {
142 /** @type {?} */
143 let completed = false;
144 /** @type {?} */
145 const reader = new FileReader();
146 reader.addEventListener('error', (/**
147 * @return {?}
148 */
149 () => {
150 observer.error(this.eventFactory(fileData, 3 /* ERROR */, reader));
151 completed = true;
152 observer.complete();
153 }), { once: true });
154 reader.addEventListener('abort', (/**
155 * @return {?}
156 */
157 () => {
158 observer.next(this.eventFactory(fileData, 4 /* ABORT */, reader));
159 completed = true;
160 observer.complete();
161 }), { once: true });
162 /** @type {?} */
163 const onprogress = (/**
164 * @param {?} e
165 * @return {?}
166 */
167 (e) => {
168 if (e.lengthComputable) {
169 fileData.total = e.total;
170 fileData.loaded = e.loaded;
171 }
172 observer.next(this.eventFactory(fileData, 1 /* LOADING */, reader));
173 });
174 reader.addEventListener('loadstart', (/**
175 * @return {?}
176 */
177 () => {
178 observer.next(this.eventFactory(fileData, 1 /* LOADING */, reader));
179 }), { once: true });
180 reader.addEventListener('load', (/**
181 * @return {?}
182 */
183 () => {
184 fileData.loaded = (/** @type {?} */ (fileData.size));
185 fileData.data = reader.result;
186 observer.next(this.eventFactory(fileData, 2 /* DONE */, reader));
187 completed = true;
188 observer.complete();
189 }));
190 switch (dataType) {
191 case 0 /* ArrayBuffer */:
192 reader.readAsArrayBuffer(file);
193 break;
194 case 1 /* DataURL */:
195 reader.readAsDataURL(file);
196 break;
197 case 2 /* Text */:
198 reader.readAsText(file);
199 break;
200 default:
201 observer.error(new TypeError('Invalid output type: Select ArrayBuffer, DataURL or Text'));
202 completed = true;
203 observer.complete();
204 return;
205 }
206 return (/**
207 * @return {?}
208 */
209 () => {
210 if (reader) {
211 if (!completed) {
212 reader.abort();
213 }
214 if (onprogress) {
215 reader.removeEventListener('progress', onprogress);
216 }
217 }
218 });
219 }));
220 }
221 /**
222 * @private
223 * @param {?} file
224 * @param {?} status
225 * @param {?=} reader
226 * @return {?}
227 */
228 eventFactory(file, status, reader = null) {
229 return {
230 status,
231 reader,
232 file: Object.assign({}, file),
233 };
234 }
235}
236UploadEventsService.decorators = [
237 { type: Injectable }
238];
239/** @nocollapse */
240UploadEventsService.ctorParameters = () => [];
241
242/**
243 * @fileoverview added by tsickle
244 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
245 */
246/**
247 * Implements the required callback and methods of file uploading
248 */
249class FileUploadDirective {
250 /**
251 * @param {?} _elementRef
252 * @param {?} _uploadEventsService
253 */
254 constructor(_elementRef, _uploadEventsService) {
255 this._elementRef = _elementRef;
256 this._uploadEventsService = _uploadEventsService;
257 }
258 /**
259 * @param {?} e
260 * @return {?}
261 */
262 onInputChange(e) {
263 e.preventDefault();
264 /** @type {?} */
265 const next = this._elementRef.nativeElement.files &&
266 this._elementRef.nativeElement.files.length
267 ? this._elementRef.nativeElement.files
268 : null;
269 this._uploadEventsService.readFiles(next, this.dataType);
270 }
271}
272FileUploadDirective.decorators = [
273 { type: Directive, args: [{
274 selector: 'input[bmFileUpload]',
275 },] }
276];
277/** @nocollapse */
278FileUploadDirective.ctorParameters = () => [
279 { type: ElementRef },
280 { type: UploadEventsService }
281];
282FileUploadDirective.propDecorators = {
283 dataType: [{ type: Input }],
284 onInputChange: [{ type: HostListener, args: ['change', ['$event'],] }]
285};
286
287/**
288 * @fileoverview added by tsickle
289 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
290 */
291/**
292 * Make an element a draggable target
293 */
294class FileUploadDraggableDirective {
295 /**
296 * @param {?} _uploadEventsService
297 */
298 constructor(_uploadEventsService) {
299 this._uploadEventsService = _uploadEventsService;
300 }
301 /**
302 * @param {?} event
303 * @return {?}
304 */
305 ondragover(event) {
306 if (!this.disabled) {
307 event.preventDefault();
308 this.dragHover = true;
309 }
310 }
311 /**
312 * @return {?}
313 */
314 ondragleave() {
315 this.dragHover = false;
316 }
317 /**
318 * @param {?} event
319 * @return {?}
320 */
321 ondrop(event) {
322 const { dataTransfer } = event;
323 if (!this.disabled && dataTransfer) {
324 dataTransfer.dropEffect = 'copy';
325 this._uploadEventsService.readFiles(dataTransfer.files, this.dataType);
326 event.preventDefault();
327 }
328 }
329}
330FileUploadDraggableDirective.decorators = [
331 { type: Directive, args: [{ selector: '[bmFileUploadDraggable]' },] }
332];
333/** @nocollapse */
334FileUploadDraggableDirective.ctorParameters = () => [
335 { type: UploadEventsService }
336];
337FileUploadDraggableDirective.propDecorators = {
338 disabled: [{ type: Input }],
339 dataType: [{ type: Input }],
340 dragHover: [{ type: HostBinding, args: ['class.drag-hover',] }],
341 ondragover: [{ type: HostListener, args: ['dragover', ['$event'],] }],
342 ondragleave: [{ type: HostListener, args: ['dragleave',] }],
343 ondrop: [{ type: HostListener, args: ['drop', ['$event'],] }]
344};
345
346/**
347 * @fileoverview added by tsickle
348 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
349 */
350class BMFileUploaderComponent {
351 /**
352 * @param {?} _uploadEventsService
353 */
354 constructor(_uploadEventsService) {
355 this._uploadEventsService = _uploadEventsService;
356 this.abort = new EventEmitter();
357 this.done = new EventEmitter();
358 this.empty = new EventEmitter();
359 this.error = new EventEmitter();
360 this.loading = new EventEmitter();
361 this.multipleInt = 0;
362 this._subs = Subscription.EMPTY;
363 }
364 /**
365 * @param {?} v
366 * @return {?}
367 */
368 set multiple(v) {
369 this.multipleInt = v ? 1 : 0;
370 }
371 /**
372 * @return {?}
373 */
374 get multiple() {
375 return this.multipleInt === 1;
376 }
377 /**
378 * @return {?}
379 */
380 ngOnInit() {
381 this._uploadEventsService.events$.subscribe({
382 // tslint:disable-next-line:cyclomatic-complexity
383 next: (/**
384 * @param {?} event
385 * @return {?}
386 */
387 event => {
388 switch (event.status) {
389 case 4 /* ABORT */:
390 this.abort.emit(event);
391 if (this._onChangeCb) {
392 this._onChangeCb([]);
393 }
394 break;
395 case 2 /* DONE */:
396 this.done.emit(event);
397 if (this._onChangeCb) {
398 this._onChangeCb(event.files.map((/**
399 * @param {?} e
400 * @return {?}
401 */
402 e => e.file)));
403 }
404 break;
405 case 0 /* EMPTY */:
406 this.empty.emit(event);
407 if (this._onChangeCb) {
408 this._onChangeCb([]);
409 }
410 break;
411 case 3 /* ERROR */:
412 this.error.emit(event);
413 if (this._onChangeCb) {
414 this._onChangeCb([]);
415 }
416 break;
417 case 1 /* LOADING */:
418 this.loading.emit(event);
419 if (this._onTouchedCb) {
420 this._onTouchedCb();
421 }
422 break;
423 default:
424 }
425 }),
426 });
427 }
428 /**
429 * @return {?}
430 */
431 ngOnDestroy() {
432 this._subs.unsubscribe();
433 }
434 /**
435 * @return {?}
436 */
437 onFocusin() {
438 if (this._onTouchedCb) {
439 this._onTouchedCb();
440 }
441 }
442 /**
443 * @param {?} val
444 * @return {?}
445 */
446 writeValue(val) {
447 if (val instanceof FileList) {
448 this._uploadEventsService.readFiles(val, this.dataType);
449 }
450 else {
451 this._uploadEventsService.readFiles(null, this.dataType);
452 }
453 }
454 /**
455 * @param {?} fn
456 * @return {?}
457 */
458 registerOnChange(fn) {
459 this._onChangeCb = fn;
460 }
461 /**
462 * @param {?} fn
463 * @return {?}
464 */
465 registerOnTouched(fn) {
466 this._onTouchedCb = fn;
467 }
468 /**
469 * @param {?} isDisabled
470 * @return {?}
471 */
472 setDisabledState(isDisabled) {
473 this.disabled = isDisabled;
474 }
475}
476BMFileUploaderComponent.decorators = [
477 { type: Component, args: [{
478 selector: 'bm-file-upload',
479 template: "<input\n [accept]=\"accept\"\n [dataType]=\"dataType\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n #inputFile\n bmFileUpload\n class=\"bm-visual-hidden\"\n tabindex=\"-1\"\n type=\"file\"\n/>\n<div\n [dataType]=\"dataType\"\n [disabled]=\"disabled\"\n bmFileUploadDraggable\n class=\"bm-file-upload-drop-area\"\n>\n <ng-content></ng-content>\n <div>\n <i class=\"fas fa-upload bm-file-upload__upload-icon\" aria-hidden=\"true\"></i>\n <div>\n <p i18n>\n Drag &amp; Drop your audio {multipleInt, plural, =0 {file} =1 {files}}\n </p>\n <p>\n <button\n (click)=\"inputFile.click()\"\n (focusin)=\"onFocusin()\"\n [disabled]=\"disabled\"\n class=\"bm-button bm-button--primary\"\n type=\"button\"\n i18n=\"Select files|Text in the button inside the draggable area of the file-uploader\"\n >\n Select {multipleInt, plural, =0 {file} =1 {files}}\n </button>\n </p>\n </div>\n </div>\n <ng-content select=\"[bmFileUploadFooter]\"></ng-content>\n</div>\n",
480 providers: [
481 {
482 provide: NG_VALUE_ACCESSOR,
483 // tslint:disable-next-line:no-forward-ref
484 useExisting: forwardRef((/**
485 * @return {?}
486 */
487 () => BMFileUploaderComponent)),
488 multi: true,
489 },
490 UploadEventsService,
491 ],
492 changeDetection: ChangeDetectionStrategy.OnPush,
493 // tslint:disable-next-line:use-component-view-encapsulation
494 encapsulation: ViewEncapsulation.None,
495 styles: [".bm-file-upload--drag-hover .bm-file-upload-drop-area{border-width:2px}.bm-file-upload__upload-icon{font-size:3em}.bm-file-upload-drop-area{box-sizing:border-box;border:1px dashed var(--header-background-color,#3e71ad);border-radius:3px;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;padding:1em}.bm-file-upload-drop-area p{font-size:.9rem;margin:.5rem 0}.bm-file-upload-drop-area *{pointer-events:none}.bm-file-upload-drop-area button{pointer-events:auto}"]
496 }] }
497];
498/** @nocollapse */
499BMFileUploaderComponent.ctorParameters = () => [
500 { type: UploadEventsService }
501];
502BMFileUploaderComponent.propDecorators = {
503 accept: [{ type: Input }],
504 dataType: [{ type: Input }],
505 disabled: [{ type: Input }],
506 multiple: [{ type: Input }],
507 abort: [{ type: Output }],
508 done: [{ type: Output }],
509 empty: [{ type: Output }],
510 error: [{ type: Output }],
511 loading: [{ type: Output }]
512};
513
514/**
515 * @fileoverview added by tsickle
516 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
517 */
518class BMClickFileUploaderComponent {
519 /**
520 * @param {?} _uploadEventsService
521 */
522 constructor(_uploadEventsService) {
523 this._uploadEventsService = _uploadEventsService;
524 this.abort = new EventEmitter();
525 this.done = new EventEmitter();
526 this.empty = new EventEmitter();
527 this.error = new EventEmitter();
528 this.loading = new EventEmitter();
529 this._subs = Subscription.EMPTY;
530 }
531 /**
532 * @return {?}
533 */
534 ngOnInit() {
535 this._uploadEventsService.events$.subscribe({
536 // tslint:disable-next-line:cyclomatic-complexity
537 next: (/**
538 * @param {?} event
539 * @return {?}
540 */
541 event => {
542 switch (event.status) {
543 case 4 /* ABORT */:
544 this.abort.emit(event);
545 if (this._onChangeCb) {
546 this._onChangeCb([]);
547 }
548 break;
549 case 2 /* DONE */:
550 this.done.emit(event);
551 if (this._onChangeCb) {
552 this._onChangeCb(event.files.map((/**
553 * @param {?} e
554 * @return {?}
555 */
556 e => e.file)));
557 }
558 break;
559 case 0 /* EMPTY */:
560 this.empty.emit(event);
561 if (this._onChangeCb) {
562 this._onChangeCb([]);
563 }
564 break;
565 case 3 /* ERROR */:
566 this.error.emit(event);
567 if (this._onChangeCb) {
568 this._onChangeCb([]);
569 }
570 break;
571 case 1 /* LOADING */:
572 this.loading.emit(event);
573 if (this._onTouchedCb) {
574 this._onTouchedCb();
575 }
576 break;
577 default:
578 }
579 }),
580 });
581 }
582 /**
583 * @return {?}
584 */
585 ngOnDestroy() {
586 this._subs.unsubscribe();
587 }
588 /**
589 * @return {?}
590 */
591 onFocusin() {
592 if (this._onTouchedCb) {
593 this._onTouchedCb();
594 }
595 }
596 /**
597 * @param {?} val
598 * @return {?}
599 */
600 writeValue(val) {
601 if (val instanceof FileList) {
602 this._uploadEventsService.readFiles(val, this.dataType);
603 }
604 else {
605 this._inputElement.nativeElement.value = '';
606 this._uploadEventsService.readFiles(null, this.dataType);
607 }
608 }
609 /**
610 * @param {?} fn
611 * @return {?}
612 */
613 registerOnChange(fn) {
614 this._onChangeCb = fn;
615 }
616 /**
617 * @param {?} fn
618 * @return {?}
619 */
620 registerOnTouched(fn) {
621 this._onTouchedCb = fn;
622 }
623 /**
624 * @param {?} isDisabled
625 * @return {?}
626 */
627 setDisabledState(isDisabled) {
628 this.disabled = isDisabled;
629 }
630}
631BMClickFileUploaderComponent.decorators = [
632 { type: Component, args: [{
633 selector: 'bm-click-file-upload',
634 template: "<input\n [accept]=\"accept\"\n [dataType]=\"dataType\"\n [disabled]=\"disabled\"\n [multiple]=\"multiple\"\n #inputFile\n bmFileUpload\n class=\"bm-visual-hidden\"\n tabindex=\"-1\"\n type=\"file\"\n/>\n<button\n (click)=\"inputFile.click()\"\n (focusin)=\"onFocusin()\"\n [dataType]=\"dataType\"\n [disabled]=\"disabled\"\n bmFileUploadDraggable\n class=\"bm-click-file-upload-drop-area\"\n>\n <ng-content></ng-content>\n</button>\n",
635 providers: [
636 {
637 provide: NG_VALUE_ACCESSOR,
638 // tslint:disable-next-line:no-forward-ref
639 useExisting: forwardRef((/**
640 * @return {?}
641 */
642 () => BMClickFileUploaderComponent)),
643 multi: true,
644 },
645 UploadEventsService,
646 ],
647 changeDetection: ChangeDetectionStrategy.OnPush,
648 // tslint:disable-next-line:use-component-view-encapsulation
649 encapsulation: ViewEncapsulation.None,
650 styles: [".bm-click-file-upload-drop-area{background:0 0;border:0;display:inline-block}"]
651 }] }
652];
653/** @nocollapse */
654BMClickFileUploaderComponent.ctorParameters = () => [
655 { type: UploadEventsService }
656];
657BMClickFileUploaderComponent.propDecorators = {
658 accept: [{ type: Input }],
659 dataType: [{ type: Input }],
660 disabled: [{ type: Input }],
661 multiple: [{ type: Input }],
662 abort: [{ type: Output }],
663 done: [{ type: Output }],
664 empty: [{ type: Output }],
665 error: [{ type: Output }],
666 loading: [{ type: Output }],
667 _inputElement: [{ type: ViewChild, args: ['inputFile', { read: ElementRef, static: true },] }]
668};
669
670/**
671 * @fileoverview added by tsickle
672 * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
673 */
674class UploadersModule {
675}
676UploadersModule.decorators = [
677 { type: NgModule, args: [{
678 imports: [CommonModule],
679 declarations: [
680 BMClickFileUploaderComponent,
681 BMFileUploaderComponent,
682 FileUploadDirective,
683 FileUploadDraggableDirective,
684 ],
685 exports: [
686 BMClickFileUploaderComponent,
687 BMFileUploaderComponent,
688 FileUploadDirective,
689 FileUploadDraggableDirective,
690 ],
691 },] }
692];
693
694export { BMClickFileUploaderComponent, BMFileUploaderComponent, FileUploadDirective, FileUploadDraggableDirective, UploadersModule, UploadEventsService as ɵa };
695//# sourceMappingURL=bmat-angular-forms-uploaders.js.map