From 75b5fe1d8523f75195bdd07cb22647bb5fc75456 Mon Sep 17 00:00:00 2001 From: merlosy Date: Mon, 29 Jul 2019 18:09:55 +0200 Subject: [PATCH] demo menu + appearance --- apps/demo/src/app/app.component.css | 9 +- apps/demo/src/app/app.component.html | 122 +++--------------- apps/demo/src/app/app.component.ts | 115 +---------------- apps/demo/src/app/app.module.ts | 17 ++- .../app/appearance/appearance.component.css | 3 + .../app/appearance/appearance.component.html | 64 +++++++++ .../appearance/appearance.component.spec.ts | 25 ++++ .../app/appearance/appearance.component.ts | 63 +++++++++ .../app/code-sample/code-sample.component.ts | 2 +- apps/demo/src/app/menu/menu.component.css | 0 apps/demo/src/app/menu/menu.component.html | 18 +++ apps/demo/src/app/menu/menu.component.spec.ts | 25 ++++ apps/demo/src/app/menu/menu.component.ts | 15 +++ apps/demo/src/app/usage/usage.component.css | 3 + apps/demo/src/app/usage/usage.component.html | 101 +++++++++++++++ .../src/app/usage/usage.component.spec.ts | 25 ++++ apps/demo/src/app/usage/usage.component.ts | 122 ++++++++++++++++++ .../app/utils/example-error-state-matcher.ts | 11 ++ 18 files changed, 521 insertions(+), 219 deletions(-) create mode 100644 apps/demo/src/app/appearance/appearance.component.css create mode 100644 apps/demo/src/app/appearance/appearance.component.html create mode 100644 apps/demo/src/app/appearance/appearance.component.spec.ts create mode 100644 apps/demo/src/app/appearance/appearance.component.ts create mode 100644 apps/demo/src/app/menu/menu.component.css create mode 100644 apps/demo/src/app/menu/menu.component.html create mode 100644 apps/demo/src/app/menu/menu.component.spec.ts create mode 100644 apps/demo/src/app/menu/menu.component.ts create mode 100644 apps/demo/src/app/usage/usage.component.css create mode 100644 apps/demo/src/app/usage/usage.component.html create mode 100644 apps/demo/src/app/usage/usage.component.spec.ts create mode 100644 apps/demo/src/app/usage/usage.component.ts create mode 100644 apps/demo/src/app/utils/example-error-state-matcher.ts diff --git a/apps/demo/src/app/app.component.css b/apps/demo/src/app/app.component.css index 7d49522..2765eda 100644 --- a/apps/demo/src/app/app.component.css +++ b/apps/demo/src/app/app.component.css @@ -1,5 +1,10 @@ -mat-form-field { - width: 100%; +.toolbar-title { + margin-left: 16px; +} + +.sidenav-content { + height: calc(100vh - 64px); + overflow: auto; } .main { diff --git a/apps/demo/src/app/app.component.html b/apps/demo/src/app/app.component.html index d0d490c..03169ba 100644 --- a/apps/demo/src/app/app.component.html +++ b/apps/demo/src/app/app.component.html @@ -1,101 +1,21 @@ - Angular Material - File Input - -
- -

Code samples

- -
- -

Simple input

- - - - - - folder - - -

Input with clear button

- -

- This is a workaround for an issue with Firefox that doesn't triggers change event when the user cancels the upload. With other browsers, it results in removing files from the input. -

- - - -

Add a file to reveal the clear button.

- - - - - - -

Input with validation: required and maxSize

- - - - - - - folder - Please select a file - - The total size must not exceed {{ formDoc.get('requiredfile')?.getError('maxContentSize').maxSize | byteFormat }} - ({{ formDoc.get('requiredfile')?.getError('maxContentSize').actualSize | byteFormat }}). - - -
{{ formDoc.get('requiredfile')?.errors | json }}
- -

Disabled input

- - - - - - folder - - -

Multiple input

- - - - - - folder - - -

Input with file type constraint (accept)

- - - - - - folder - - -

Input with custom ErrorStateMatcher

-

An ErrorStateMatcher defines, when a control displays the error state. A custom ErrorStateMatcher can for example be used to display validations on untouched controls. - ErrorStateMatchers can either be defined globally or for single controls (seen in the following example). -

- - - - - - - - -

ByteFormat pipe

- - - -

A file size of {{ maxSize }} gives a human readable size of {{ maxSize | byteFormat }}

-
-
+ + + Angular Material - File Input + + + + + + + +
+
+ +
+ +
+
+ +
diff --git a/apps/demo/src/app/app.component.ts b/apps/demo/src/app/app.component.ts index 6e15ee5..ed4a4b7 100644 --- a/apps/demo/src/app/app.component.ts +++ b/apps/demo/src/app/app.component.ts @@ -3,127 +3,16 @@ import { FormGroup, FormBuilder, Validators, FormControl, NgForm, FormGroupDirec import { FileValidator } from 'ngx-material-file-input'; import { ErrorStateMatcher } from '@angular/material'; -/** -* Shows error state on the file-input if a pdf-file is selected. -*/ -class ExampleErrorStateMatcher implements ErrorStateMatcher { - public isErrorState(control: FormControl, _: NgForm | FormGroupDirective): boolean { - return (control && control.value && control.value._fileNames && control.value._fileNames.endsWith('pdf')); - } -} - @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { - errorStateMatcher = new ExampleErrorStateMatcher(); - formDoc: FormGroup; - - // 100 MB - readonly maxSize = 104857600; - - constructor(private _fb: FormBuilder) {} - - ngOnInit() { - this.formDoc = this._fb.group({ - basicfile: [], - removablefile: [], - acceptfile: [], - requiredfile: [{ value: undefined, disabled: false }, [Validators.required, FileValidator.maxContentSize(this.maxSize)]], - disabledfile: [{ value: undefined, disabled: true }], - multiplefile: [{ value: undefined, disabled: false }], - errorStateFile: [] - }); - } - onSubmit(form: FormGroup) {} + menuOpened = true; - get simple() { - return ` - - folder - `; + ngOnInit(): void { } - get advancedTs() { - return `constructor(private _fb: FormBuilder) {} - - ngOnInit() { - this.formDoc = this._fb.group({ - requiredfile: [ - undefined, - [Validators.required, FileValidator.maxContentSize(this.maxSize)] - ] - }); - }`; - } - - get advanced() { - return ` - - folder - - Please select a file - - - The total size must not exceed {{formDoc.get('requiredfile')?.getError('maxContentSize').maxSize | byteFormat}} ({{formDoc.get('requiredfile')?.getError('maxContentSize').actualSize - | byteFormat}}). - - `; - } - - get disabledTs() { - return `constructor(private _fb: FormBuilder) {} - - ngOnInit() { - this.formDoc = this._fb.group({ - disabledfile: [{ value: undefined, disabled: true }] - }); - }`; - } - - get accept() { - return ` - - folder - `; - } - - get multiple() { - return ` - - folder - `; - } - - get removable() { - return ` - - - `; - } - - get bytePipe() { - return `

A file size of {{ maxSize }} gives a human readable size of {{ maxSize | byteFormat }}

`; - } - - get errorStateTs() { - return `class ExampleErrorStateMatcher implements ErrorStateMatcher { - public isErrorState(control: FormControl, _: NgForm | FormGroupDirective): boolean { - return - (control && control.value && control.value._fileNames && control.value._fileNames.endsWith('pdf')); - } - }`; - } - get errorState() { - return ` - - - `; - } } diff --git a/apps/demo/src/app/app.module.ts b/apps/demo/src/app/app.module.ts index 68bb3dc..0baa91f 100644 --- a/apps/demo/src/app/app.module.ts +++ b/apps/demo/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import { NgModule, Injector } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { NxModule } from '@nrwl/nx'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -12,6 +12,11 @@ import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { MaterialFileInputModule } from 'ngx-material-file-input'; import { CodeSampleComponent } from './code-sample/code-sample.component'; +import { MenuComponent } from './menu/menu.component'; +import { UsageComponent } from './usage/usage.component'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatListModule } from '@angular/material/list'; +import { AppearanceComponent } from './appearance/appearance.component'; @NgModule({ imports: [ @@ -24,11 +29,19 @@ import { CodeSampleComponent } from './code-sample/code-sample.component'; MatFormFieldModule, MatIconModule, MatInputModule, + MatListModule, + MatSidenavModule, MatToolbarModule, // Lib Module MaterialFileInputModule ], - declarations: [AppComponent, CodeSampleComponent], + declarations: [ + AppComponent, + AppearanceComponent, + CodeSampleComponent, + MenuComponent, + UsageComponent + ], bootstrap: [AppComponent] }) export class AppModule {} diff --git a/apps/demo/src/app/appearance/appearance.component.css b/apps/demo/src/app/appearance/appearance.component.css new file mode 100644 index 0000000..c7acb4b --- /dev/null +++ b/apps/demo/src/app/appearance/appearance.component.css @@ -0,0 +1,3 @@ +mat-form-field { + width: 100%; +} diff --git a/apps/demo/src/app/appearance/appearance.component.html b/apps/demo/src/app/appearance/appearance.component.html new file mode 100644 index 0000000..1f3547f --- /dev/null +++ b/apps/demo/src/app/appearance/appearance.component.html @@ -0,0 +1,64 @@ +

Form field appearance variants

+

+ File input appearance is fully compatible with Angular Material form field appearance. +

+

+ There are significant differences between the legacy variant and the 3 newer ones (see link above). Especially, + "standard, fill, and outline appearances do not promote placeholders to labels." This means you'll need to add the + "mat-label" element if you want to show some text. +

+ +
+ +

Legacy (default)

+ + + + + + folder + + +

Legacy (default) with mat-label

+ + + + + Basic legacy input + + folder + + +

Standard

+ + + + + Basic standard input + + folder + + +

Fill

+ + + + + Basic fill input + + folder + + +

Outline

+ + + + + Basic outline input + + folder + + +
diff --git a/apps/demo/src/app/appearance/appearance.component.spec.ts b/apps/demo/src/app/appearance/appearance.component.spec.ts new file mode 100644 index 0000000..9d73abd --- /dev/null +++ b/apps/demo/src/app/appearance/appearance.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AppearanceComponent } from './appearance.component'; + +describe('AppearanceComponent', () => { + let component: AppearanceComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AppearanceComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AppearanceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/demo/src/app/appearance/appearance.component.ts b/apps/demo/src/app/appearance/appearance.component.ts new file mode 100644 index 0000000..255f51c --- /dev/null +++ b/apps/demo/src/app/appearance/appearance.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-appearance', + templateUrl: './appearance.component.html', + styleUrls: ['./appearance.component.css'] +}) +export class AppearanceComponent implements OnInit { + formDoc: FormGroup; + + constructor(private _fb: FormBuilder) {} + + ngOnInit() { + this.formDoc = this._fb.group({ + legacyNoLabel: [], + legacy: [], + standard: [], + fill: [], + outline: [] + }); + } + + get legacyNoLabel() { + return ` + + folder + `; + } + + get legacy() { + return ` + Basic legacy input + + folder + `; + } + + get standard() { + return ` + Basic standard input + + folder + `; + } + + get fill() { + return ` + Basic fill input + + folder + `; + } + + get outline() { + return ` + Basic outline input + + folder + `; + } + +} diff --git a/apps/demo/src/app/code-sample/code-sample.component.ts b/apps/demo/src/app/code-sample/code-sample.component.ts index 151c928..da53be9 100644 --- a/apps/demo/src/app/code-sample/code-sample.component.ts +++ b/apps/demo/src/app/code-sample/code-sample.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core'; +import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; @Component({ selector: 'app-code-sample', diff --git a/apps/demo/src/app/menu/menu.component.css b/apps/demo/src/app/menu/menu.component.css new file mode 100644 index 0000000..e69de29 diff --git a/apps/demo/src/app/menu/menu.component.html b/apps/demo/src/app/menu/menu.component.html new file mode 100644 index 0000000..b2ea128 --- /dev/null +++ b/apps/demo/src/app/menu/menu.component.html @@ -0,0 +1,18 @@ + + Input examples + + Simple input + With clear button + With form validation + With disabled state + With multiple files + With file type constraint (accept) + With custom ErrorStateMatcher + + + + Other examples + + ByteFormat pipe + Appearance variants + diff --git a/apps/demo/src/app/menu/menu.component.spec.ts b/apps/demo/src/app/menu/menu.component.spec.ts new file mode 100644 index 0000000..beb2d9b --- /dev/null +++ b/apps/demo/src/app/menu/menu.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MenuComponent } from './menu.component'; + +describe('MenuComponent', () => { + let component: MenuComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MenuComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/demo/src/app/menu/menu.component.ts b/apps/demo/src/app/menu/menu.component.ts new file mode 100644 index 0000000..af2aede --- /dev/null +++ b/apps/demo/src/app/menu/menu.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-menu', + templateUrl: './menu.component.html', + styleUrls: ['./menu.component.css'] +}) +export class MenuComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/apps/demo/src/app/usage/usage.component.css b/apps/demo/src/app/usage/usage.component.css new file mode 100644 index 0000000..c7acb4b --- /dev/null +++ b/apps/demo/src/app/usage/usage.component.css @@ -0,0 +1,3 @@ +mat-form-field { + width: 100%; +} diff --git a/apps/demo/src/app/usage/usage.component.html b/apps/demo/src/app/usage/usage.component.html new file mode 100644 index 0000000..eb587a4 --- /dev/null +++ b/apps/demo/src/app/usage/usage.component.html @@ -0,0 +1,101 @@ +

Code samples

+ +
+ +

Simple input

+ + + + + + folder + + +

Input with clear button

+ +

+ This is a workaround for an issue with Firefox that doesn't triggers change event when the user cancels the upload. + With other browsers, it results in removing files from the input. +

+ + + +

Add a file to reveal the clear button.

+ + + + + + + +

Input with validation: required and maxSize

+ + + + + + + folder + Please select a file + + The total size must not exceed {{ formDoc.get('requiredfile')?.getError('maxContentSize').maxSize | byteFormat }} + ({{ formDoc.get('requiredfile')?.getError('maxContentSize').actualSize | byteFormat }}). + + +
{{ formDoc.get('requiredfile')?.errors | json }}
+ +

Disabled input

+ + + + + + folder + + +

Multiple input

+ + + + + + folder + + +

Input with file type constraint (accept)

+ + + + + + folder + + +

Input with custom ErrorStateMatcher

+ +

An ErrorStateMatcher defines when a control displays the error message. A custom ErrorStateMatcher can for example + be used to display validations on untouched controls. + ErrorStateMatchers can either be defined globally or for single controls (seen in the following example). +

+

Learn more about custom ErrorStateMatcher +

+ + + + + + + + +

ByteFormat pipe

+ + + +

A file size of {{ maxSize }} gives a human readable size of {{ maxSize | byteFormat }}

+
diff --git a/apps/demo/src/app/usage/usage.component.spec.ts b/apps/demo/src/app/usage/usage.component.spec.ts new file mode 100644 index 0000000..40b7df0 --- /dev/null +++ b/apps/demo/src/app/usage/usage.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UsageComponent } from './usage.component'; + +describe('UsageComponent', () => { + let component: UsageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UsageComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UsageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/demo/src/app/usage/usage.component.ts b/apps/demo/src/app/usage/usage.component.ts new file mode 100644 index 0000000..1453f77 --- /dev/null +++ b/apps/demo/src/app/usage/usage.component.ts @@ -0,0 +1,122 @@ +import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { ExampleErrorStateMatcher } from '../utils/example-error-state-matcher'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { FileValidator } from 'ngx-material-file-input'; + +@Component({ + selector: 'app-usage', + templateUrl: './usage.component.html', + styleUrls: ['./usage.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class UsageComponent implements OnInit { + errorStateMatcher = new ExampleErrorStateMatcher(); + formDoc: FormGroup; + + // 100 MB + readonly maxSize = 104857600; + + constructor(private _fb: FormBuilder) {} + + ngOnInit() { + this.formDoc = this._fb.group({ + basicfile: [], + removablefile: [], + acceptfile: [], + requiredfile: [{ value: undefined, disabled: false }, [Validators.required, FileValidator.maxContentSize(this.maxSize)]], + disabledfile: [{ value: undefined, disabled: true }], + multiplefile: [{ value: undefined, disabled: false }], + errorStateFile: [] + }); + } + + onSubmit(form: FormGroup) {} + + get simple() { + return ` + + folder + `; + } + + get advancedTs() { + return `constructor(private _fb: FormBuilder) {} + + ngOnInit() { + this.formDoc = this._fb.group({ + requiredfile: [ + undefined, + [Validators.required, FileValidator.maxContentSize(this.maxSize)] + ] + }); + }`; + } + + get advanced() { + return ` + + folder + + Please select a file + + + The total size must not exceed {{formDoc.get('requiredfile')?.getError('maxContentSize').maxSize | byteFormat}} ({{formDoc.get('requiredfile')?.getError('maxContentSize').actualSize + | byteFormat}}). + + `; + } + + get disabledTs() { + return `constructor(private _fb: FormBuilder) {} + + ngOnInit() { + this.formDoc = this._fb.group({ + disabledfile: [{ value: undefined, disabled: true }] + }); + }`; + } + + get accept() { + return ` + + folder + `; + } + + get multiple() { + return ` + + folder + `; + } + + get removable() { + return ` + + + `; + } + + get bytePipe() { + return `

A file size of {{ maxSize }} gives a human readable size of {{ maxSize | byteFormat }}

`; + } + + get errorStateTs() { + return `class ExampleErrorStateMatcher implements ErrorStateMatcher { + public isErrorState(control: FormControl, _: NgForm | FormGroupDirective): boolean { + return + (control && control.value && control.value._fileNames && control.value._fileNames.endsWith('pdf')); + } + }`; + } + get errorState() { + return ` + + + `; + } + +} diff --git a/apps/demo/src/app/utils/example-error-state-matcher.ts b/apps/demo/src/app/utils/example-error-state-matcher.ts new file mode 100644 index 0000000..c975984 --- /dev/null +++ b/apps/demo/src/app/utils/example-error-state-matcher.ts @@ -0,0 +1,11 @@ +import { ErrorStateMatcher } from '@angular/material'; +import { FormControl, NgForm, FormGroupDirective } from '@angular/forms'; + +/** +* Shows error state on the file-input if a pdf-file is selected. +*/ +export class ExampleErrorStateMatcher implements ErrorStateMatcher { + public isErrorState(control: FormControl, _: NgForm | FormGroupDirective): boolean { + return (control && control.value && control.value._fileNames && control.value._fileNames.endsWith('pdf')); + } +}