diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6e40470..940a3f5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,40 +1,61 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserModule } from '@angular/platform-browser'; -import { EffectsModule } from '@ngrx/effects'; import { NgModule } from '@angular/core'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; + +import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatCardModule } from '@angular/material/card'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatInputModule } from '@angular/material/input'; import { AppComponent } from './app.component'; import { reducers, metaReducers } from './reducers'; import { CommentStoreModule } from './store'; +import { PipesModule } from './pipes/pipes.module'; +import { DirectivesModule } from './directives/directives.module'; + import { CommentCardComponent } from './comment-card/comment-card.component'; import { CommentsListComponent } from './comments-list/comments-list.component'; -import { PipesModule } from './pipes/pipes.module'; +import { CommentCardEditComponent } from './comment-card-edit/comment-card-edit.component'; + +const materialModules = [ + MatAutocompleteModule, + MatButtonModule, + MatCardModule, + MatChipsModule, + MatFormFieldModule, + MatIconModule, + MatToolbarModule, + MatInputModule +]; @NgModule({ declarations: [ AppComponent, CommentCardComponent, - CommentsListComponent + CommentsListComponent, + CommentCardEditComponent ], imports: [ BrowserModule, BrowserAnimationsModule, + ReactiveFormsModule, + FormsModule, StoreModule.forRoot(reducers, { metaReducers }), EffectsModule.forRoot([]), CommentStoreModule, - MatToolbarModule, - MatCardModule, - MatButtonModule, - MatIconModule, - PipesModule + ...materialModules, + PipesModule, + DirectivesModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/comment-card-edit/comment-card-edit.component.html b/src/app/comment-card-edit/comment-card-edit.component.html new file mode 100644 index 0000000..e0cb72b --- /dev/null +++ b/src/app/comment-card-edit/comment-card-edit.component.html @@ -0,0 +1,36 @@ + +
+ + Comment Title + + + + + + {{ tag }} + cancel + + + + + + {{ tag }} + + + + + Comment Body + + + + + + +
+
\ No newline at end of file diff --git a/src/app/comment-card-edit/comment-card-edit.component.scss b/src/app/comment-card-edit/comment-card-edit.component.scss new file mode 100644 index 0000000..1c15ddd --- /dev/null +++ b/src/app/comment-card-edit/comment-card-edit.component.scss @@ -0,0 +1,11 @@ +:host { + display: block; +} + +.comment-card { + max-width: 420px; +} + +.full-width-field { + width: 100%; +} \ No newline at end of file diff --git a/src/app/comment-card-edit/comment-card-edit.component.spec.ts b/src/app/comment-card-edit/comment-card-edit.component.spec.ts new file mode 100644 index 0000000..2a0cd5f --- /dev/null +++ b/src/app/comment-card-edit/comment-card-edit.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CommentCardEditComponent } from './comment-card-edit.component'; + +describe('CommentCardEditComponent', () => { + let component: CommentCardEditComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CommentCardEditComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommentCardEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/comment-card-edit/comment-card-edit.component.ts b/src/app/comment-card-edit/comment-card-edit.component.ts new file mode 100644 index 0000000..98ebfe9 --- /dev/null +++ b/src/app/comment-card-edit/comment-card-edit.component.ts @@ -0,0 +1,105 @@ +import { COMMA, ENTER } from '@angular/cdk/keycodes'; +import { Component, ElementRef, ViewChild, OnInit, Input } from '@angular/core'; +import { FormBuilder, Validators, FormGroup } from '@angular/forms'; +import { MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material/autocomplete'; +import { MatChipInputEvent } from '@angular/material/chips'; + +import { Observable } from 'rxjs'; +import { map, startWith, withLatestFrom } from 'rxjs/operators'; + +import { Comment, CommentFacade } from '../store/comment' + +@Component({ + selector: 'app-comment-card-edit', + templateUrl: './comment-card-edit.component.html', + styleUrls: ['./comment-card-edit.component.scss'] +}) +export class CommentCardEditComponent implements OnInit { + @ViewChild('tagInput') tagInput: ElementRef; + @ViewChild('auto') matAutocomplete: MatAutocomplete; + @Input() data: Comment; + form: FormGroup; + tagCtrl = this.fb.control([]); + filteredTags: Observable; + separatorKeysCodes: number[] = [ENTER, COMMA]; + + constructor( + private readonly fb: FormBuilder, + private readonly commentFacade: CommentFacade + ) { } + + ngOnInit(): void { + if (!!this.data) { + this.form = this.fb.group({ + id: [this.data.id], + title: [this.data.title, [Validators.required]], + tags: [this.data.tags], + text: [this.data.text, [Validators.required]], + }); + } else { + this.form = this.fb.group({ + id: [null], + title: [null, [Validators.required]], + tags: [[]], + text: [null, [Validators.required]], + }); + } + this.filteredTags = this.tagCtrl.valueChanges.pipe( + startWith(null), + withLatestFrom(this.commentFacade.tags$), + map(([tag, allTags]) => { + if (tag === null) { + return [...allTags]; + } + const value = tag.toLowerCase() + return allTags.filter(t => t.toLowerCase().includes(value)); + }) + ); + } + + submit(event, commentForm): void { + event.preventDefault(); + if (!!this.form.value.id) { + this.commentFacade.edit(this.form.value); + } else { + this.commentFacade.add(this.form.value); + } + this.form.reset(); + commentForm.resetForm(); + } + + cancel(): void { + this.commentFacade.toggleEdit(undefined); + } + + selected(event: MatAutocompleteSelectedEvent): void { + const tags = [...this.form.value.tags]; + this.form.get('tags').setValue([...tags, event.option.viewValue]); + this.tagInput.nativeElement.value = ''; + this.tagCtrl.setValue(null); + } + + + addTag(event: MatChipInputEvent): void { + const input = event.input; + const value = event.value; + + if ((value || '').trim()) { + const tags = [...this.form.value.tags]; + this.form.get('tags').setValue([...tags, value.trim()]); + } + + if (input) { + input.value = ''; + } + + this.tagCtrl.setValue(null); + } + + removeTag(tag: string): void { + const tags = [...this.form.value.tags]; + + this.form.get('tags').setValue([...tags.filter(t => t !== tag)]); + } + +} diff --git a/src/app/comment-card/comment-card.component.html b/src/app/comment-card/comment-card.component.html index 949de2e..b095de0 100644 --- a/src/app/comment-card/comment-card.component.html +++ b/src/app/comment-card/comment-card.component.html @@ -1,13 +1,17 @@ {{ data.title }} - {{ data.tags | join }} + + + {{ tag }} + +
-