diff --git a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-max-file-number/file-uploader-max-file-number-example.html b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-max-file-number/file-uploader-max-file-number-example.html index eb9b67521..058e8c94d 100644 --- a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-max-file-number/file-uploader-max-file-number-example.html +++ b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-max-file-number/file-uploader-max-file-number-example.html @@ -25,17 +25,22 @@ Add File - Required! - - There were - {{testForm.controls['documents'].getError('NxFileUploadMaxFileNumber').actual - | json}} files added, but the maximum is - {{testForm.controls['documents'].getError('NxFileUploadMaxFileNumber').max - | json}} + +
Error
+
diff --git a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-strict-type-validation/file-uploader-strict-type-validation-example.html b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-strict-type-validation/file-uploader-strict-type-validation-example.html index 3cf7343eb..884a10abb 100644 --- a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-strict-type-validation/file-uploader-strict-type-validation-example.html +++ b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-strict-type-validation/file-uploader-strict-type-validation-example.html @@ -31,15 +31,21 @@ - Required! - - File - {{testForm.controls['documents'].getError('NxFileUploadFileTypeNotAccepted').fileName - | json}} can not be uploaded. This file type is not supported! + +
Error
+
    +
  • + Required! +
  • +
  • + File + {{testForm.controls['documents'].getError('NxFileUploadFileTypeNotAccepted').fileName + | json}} can not be uploaded. This file type is not + supported! +
  • +
diff --git a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-type-validation/file-uploader-type-validation-example.html b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-type-validation/file-uploader-type-validation-example.html index cfc947914..04a97e18a 100644 --- a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-type-validation/file-uploader-type-validation-example.html +++ b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-type-validation/file-uploader-type-validation-example.html @@ -22,15 +22,21 @@ - Required! - - File - {{testForm.controls['documents'].getError('NxFileUploadFileTypeNotAccepted').fileName - | json}} can not be uploaded. This file type is not supported! + +
Error
+
    +
  • + Required! +
  • +
  • + File + {{testForm.controls['documents'].getError('NxFileUploadFileTypeNotAccepted').fileName + | json}} can not be uploaded. This file type is not + supported! +
  • +
diff --git a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-validation/file-uploader-validation-example.html b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-validation/file-uploader-validation-example.html index b82112191..30e012489 100644 --- a/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-validation/file-uploader-validation-example.html +++ b/projects/ng-aquila/documentation/examples/file-uploader/file-uploader-validation/file-uploader-validation-example.html @@ -25,18 +25,25 @@ Add File - Required! - An error occured while uploading. - - File - {{testForm.controls['documents'].getError('NxFileUploadMaxFileSize').fileName - | json}} can not be uploaded. File size exceeds size limit! + +
Error
+
    +
  • + Required! +
  • +
  • + An error occured while uploading. +
  • +
  • + File + {{testForm.controls['documents'].getError('NxFileUploadMaxFileSize').fileName + | json}} can not be uploaded. File size exceeds size limit! +
  • +
diff --git a/projects/ng-aquila/src/file-uploader/file-uploader-trigger.directive.ts b/projects/ng-aquila/src/file-uploader/file-uploader-trigger.directive.ts index d4b8ef659..1267f1b90 100644 --- a/projects/ng-aquila/src/file-uploader/file-uploader-trigger.directive.ts +++ b/projects/ng-aquila/src/file-uploader/file-uploader-trigger.directive.ts @@ -1,4 +1,4 @@ -import { Directive, HostListener, Input } from '@angular/core'; +import { Directive, HostBinding, HostListener, Input } from '@angular/core'; import { NxFileUploaderComponent } from './file-uploader.component'; @@ -23,4 +23,8 @@ export class NxFileUploaderTriggerDirective { _onClick() { this._fileUpload.uploadFiles(); } + + @HostBinding('style.visibility') get visibility() { + return this._fileUpload.allFilesUploaded ? 'hidden' : 'unset'; + } } diff --git a/projects/ng-aquila/src/file-uploader/file-uploader.component.html b/projects/ng-aquila/src/file-uploader/file-uploader.component.html index 6381dbc84..2dc770d9d 100644 --- a/projects/ng-aquila/src/file-uploader/file-uploader.component.html +++ b/projects/ng-aquila/src/file-uploader/file-uploader.component.html @@ -1,3 +1,5 @@ + + @@ -29,8 +31,6 @@ - - diff --git a/projects/ng-aquila/src/file-uploader/file-uploader.component.scss b/projects/ng-aquila/src/file-uploader/file-uploader.component.scss index 270f96be3..411aa1db6 100644 --- a/projects/ng-aquila/src/file-uploader/file-uploader.component.scss +++ b/projects/ng-aquila/src/file-uploader/file-uploader.component.scss @@ -5,9 +5,21 @@ ::ng-deep nx-error.nx-error--message { margin: nx-spacer(xs) 0; + + ul { + margin-left: 28px; + margin-top: 6px; + } + li { + line-height: nx-spacer(m); + } } } +.nx-file-uploader--file-list { + max-height: 400px; + overflow-y: auto; +} .nx-file-uploader--file-row { display: flex; flex-wrap: wrap; diff --git a/projects/ng-aquila/src/file-uploader/file-uploader.component.spec.ts b/projects/ng-aquila/src/file-uploader/file-uploader.component.spec.ts index 6faec2823..aa6db703a 100644 --- a/projects/ng-aquila/src/file-uploader/file-uploader.component.spec.ts +++ b/projects/ng-aquila/src/file-uploader/file-uploader.component.spec.ts @@ -22,7 +22,7 @@ abstract class FileUploaderTest { maxFileNumber!: number; accept: any; strictAcceptValidation = false; - disableCommonValidator = false; + noBlockingValidators = false; } describe('NxFileUploaderComponent', () => { @@ -406,6 +406,18 @@ describe('NxFileUploaderComponent', () => { expect(testInstance.form.controls.documents.hasError('NxFileUploadFileTypeNotAccepted')).toBeTrue(); }); + it('invalid when file number reached max then click add button', () => { + createTestComponent(ReactiveFileUpload); + testInstance.maxFileNumber = 2; + fixture.detectChanges(); + + createAndAddFile('test.png', 'some type'); + createAndAddFile('test.png', 'some type'); + createAndAddFile('test.png', 'some type'); + fixture.detectChanges(); + expect(testInstance.form.controls.documents.hasError('NxFileUploadMaxFileNumber')).toBeTrue(); + }); + describe('getFileExtension', () => { it('should return the file extension', () => { expect(getFileExtension('test.png')).toBe('.png'); @@ -538,10 +550,10 @@ describe('NxFileUploaderComponent', () => { }); }); - describe('disable validators', () => { - it('should has require validation error even if disableCommonValidator is true', () => { + describe('no blocking validators', () => { + it('should has require validation error even if noBlockingValidators is true', () => { createTestComponent(ReactiveFileUpload); - fixture.componentInstance.disableCommonValidator = true; + fixture.componentInstance.noBlockingValidators = true; const submitButton = fixture.nativeElement.querySelector('#submit-button') as HTMLButtonElement; testInstance.required = true; @@ -551,10 +563,10 @@ describe('NxFileUploaderComponent', () => { expect(testInstance.form.controls.documents.hasError('required')).toBeTrue(); }); - it('should not has max fileNumber validator error if disableCommonValidator is true', () => { + it('should not has max fileNumber validator error if noBlockingValidators is true', () => { createTestComponent(ReactiveFileUpload); testInstance.maxFileNumber = 2; - fixture.componentInstance.disableCommonValidator = true; + fixture.componentInstance.noBlockingValidators = true; fixture.detectChanges(); createAndAddFile('test.png', 'some type'); @@ -564,9 +576,9 @@ describe('NxFileUploaderComponent', () => { expect(testInstance.form.controls.documents.hasError('NxFileUploadMaxFileNumber')).toBeFalse(); }); - it('should not has file type validator error if disableCommonValidator is true', () => { + it('should not has file type validator error if noBlockingValidators is true', () => { createTestComponent(ReactiveFileUpload); - fixture.componentInstance.disableCommonValidator = true; + fixture.componentInstance.noBlockingValidators = true; testInstance.accept = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; fixture.detectChanges(); @@ -575,9 +587,9 @@ describe('NxFileUploaderComponent', () => { expect(testInstance.form.controls.documents.hasError('NxFileUploadFileTypeNotAccepted')).toBeFalse(); }); - it('should not has file size validator error if disableCommonValidator is true', () => { + it('should not has file size validator error if noBlockingValidators is true', () => { createTestComponent(ReactiveFileUpload); - fixture.componentInstance.disableCommonValidator = true; + fixture.componentInstance.noBlockingValidators = true; testInstance.maxFileSize = 1024; fixture.detectChanges(); @@ -675,7 +687,7 @@ class BasicFileUpload extends FileUploaderTest { multiple [maxFileNumber]="maxFileNumber" [accept]="accept" - [noBlockingValidators]="disableCommonValidator" + [noBlockingValidators]="noBlockingValidators" [strictAcceptValidation]="strictAcceptValidation" > Required file to upload @@ -703,7 +715,7 @@ class ReactiveFileUpload extends FileUploaderTest { maxFileSize: any; queueList: any; maxFileNumber: any; - disableCommonValidator = false; + noBlockingValidators = false; constructor() { super(); diff --git a/projects/ng-aquila/src/file-uploader/file-uploader.component.ts b/projects/ng-aquila/src/file-uploader/file-uploader.component.ts index 57db4c15c..c81957026 100644 --- a/projects/ng-aquila/src/file-uploader/file-uploader.component.ts +++ b/projects/ng-aquila/src/file-uploader/file-uploader.component.ts @@ -24,8 +24,8 @@ import { ViewChild, ViewChildren, } from '@angular/core'; -import { ControlValueAccessor, FormControl, FormGroupDirective, NgControl, NgForm, ValidatorFn } from '@angular/forms'; -import { NxErrorComponent, NxLabelComponent } from '@aposin/ng-aquila/base'; +import { AbstractControl, ControlValueAccessor, FormControl, FormGroupDirective, NgControl, NgForm, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { ERROR_DEFAULT_OPTIONS, NxErrorComponent, NxLabelComponent } from '@aposin/ng-aquila/base'; import { ErrorStateMatcher } from '@aposin/ng-aquila/utils'; import { fromEvent, Observable, Subject, Subscription } from 'rxjs'; import { filter, map, startWith, take, takeUntil } from 'rxjs/operators'; @@ -57,6 +57,14 @@ let nextId = 0; '[attr.aria-invalid]': 'errorState', '[class.has-error]': 'errorState', }, + providers: [ + { + provide: ERROR_DEFAULT_OPTIONS, + useValue: { + appearance: 'message', + }, + }, + ], }) export class NxFileUploaderComponent implements ControlValueAccessor, AfterContentInit, OnChanges, OnDestroy, DoCheck, OnInit, AfterViewInit { /** @docs-private */ @@ -404,6 +412,7 @@ export class NxFileUploaderComponent implements ControlValueAccessor, AfterConte const reachMaxFileNumber = this.maxFileNumber && (this.value?.length || 0) === this.maxFileNumber; if (reachMaxFileNumber) { this.setMaxFileNumberError(this.maxFileNumber); + this._resetValidators(true); return; } this.nativeInputFile.nativeElement.click(); @@ -645,7 +654,9 @@ export class NxFileUploaderComponent implements ControlValueAccessor, AfterConte actual: totalFilesNum, }); if (!this.noBlockingValidators && this.ngControl?.control) { - this.validatorFnArray.push(NxFileUploaderValidators.maxFileNumber(totalFilesNum, this.maxFileNumber)); + this.validatorFnArray.push((control: AbstractControl): ValidationErrors | null => ({ + NxFileUploadMaxFileNumber: { max: this.maxFileNumber, actual: totalFilesNum }, + })); } } @@ -680,4 +691,9 @@ export class NxFileUploaderComponent implements ControlValueAccessor, AfterConte reason, }); } + + /** weather all files is uplaoded */ + get allFilesUploaded(): boolean { + return this.value?.every(f => f.isUploaded) || false; + } } diff --git a/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.scss b/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.scss index bccc53b7a..9a99d63c9 100644 --- a/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.scss +++ b/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.scss @@ -3,3 +3,25 @@ :host { @include type-style(file-uploader-file-name); } +.extension { + position: relative; + top: 4px; + display: inline-flex; +} +.extension-icon { + font-size: 22px; +} +.extension-label { + position: absolute; + text-transform: uppercase; + -webkit-user-select: none; + user-select: none; + line-height: normal; + font-weight: bold; + padding: 1px 3px; + right: 4px; + bottom: 4px; + border-radius: 2px 0 0 2px; + font-size: 6px; + color: white; +} diff --git a/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.ts b/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.ts index 8e98fb968..95a9e40ff 100644 --- a/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.ts +++ b/projects/ng-aquila/src/file-uploader/item/file-uploader-name.component.ts @@ -1,12 +1,33 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; + +import { getFileExtension } from '../file-uploader.validations'; /** Shows the file name. */ @Component({ selector: 'nx-file-upload-name', styleUrls: ['./file-uploader-name.component.scss'], - template: `{{ name }}`, + template: ` + + {{ extension }} + + + {{ name }} + `, }) -export class NxFileUploaderItemName { +export class NxFileUploaderItemName implements OnInit { /** The filename.*/ @Input() name!: string; + + iconColor: { [key: string]: string } = { + xls: '#1E8927', + xlsx: '#1E8927', + pdf: '#DC3149', + png: '#ba31dc', + }; + + extension!: string; + + ngOnInit() { + this.extension = getFileExtension(this.name).substring(1); + } }