From 888d1e74b1848916fd77d34c6d6c36d40ea0e9d7 Mon Sep 17 00:00:00 2001 From: smuddyx Date: Fri, 24 Apr 2020 15:07:35 +0200 Subject: [PATCH] song list filter --- .../songs/song-list/filter/filter-values.ts | 6 ++ .../song-list/filter/filter.component.html | 35 +++++++++++ .../song-list/filter/filter.component.less | 5 ++ .../song-list/filter/filter.component.spec.ts | 25 ++++++++ .../song-list/filter/filter.component.ts | 63 +++++++++++++++++++ .../list-item/list-item.component.html | 6 +- .../list-item/list-item.component.less | 10 ++- .../list-item/list-item.component.ts | 2 + .../songs/song-list/song-list.component.html | 8 ++- .../songs/song-list/song-list.component.less | 2 - .../songs/song-list/song-list.component.ts | 34 +++++++++- .../songs/song-list/song-list.module.ts | 17 ++++- .../edit/edit-song/edit-song.component.html | 2 +- .../navigation/filter/filter.component.ts | 5 +- .../list-header/list-header.component.html | 9 ++- .../list-header/list-header.component.less | 5 ++ .../list-header/list-header.component.ts | 14 +++-- .../list-header/list-header.module.ts | 4 +- 18 files changed, 229 insertions(+), 23 deletions(-) create mode 100644 src/app/modules/songs/song-list/filter/filter-values.ts create mode 100644 src/app/modules/songs/song-list/filter/filter.component.html create mode 100644 src/app/modules/songs/song-list/filter/filter.component.less create mode 100644 src/app/modules/songs/song-list/filter/filter.component.spec.ts create mode 100644 src/app/modules/songs/song-list/filter/filter.component.ts diff --git a/src/app/modules/songs/song-list/filter/filter-values.ts b/src/app/modules/songs/song-list/filter/filter-values.ts new file mode 100644 index 0000000..9ed17bd --- /dev/null +++ b/src/app/modules/songs/song-list/filter/filter-values.ts @@ -0,0 +1,6 @@ +export interface FilterValues { + q: string; + type: string; + legalType: string; + flag: string; +} diff --git a/src/app/modules/songs/song-list/filter/filter.component.html b/src/app/modules/songs/song-list/filter/filter.component.html new file mode 100644 index 0000000..db8a3b0 --- /dev/null +++ b/src/app/modules/songs/song-list/filter/filter.component.html @@ -0,0 +1,35 @@ +
+ + + Titel oder Text + + + +
+ + Typ + + - kein Filter - + {{type | songType}} + + + + + Rechtlicher Status + + - kein Filter - + {{key|legalType}} + + + + + Attribute + + - kein Filter - + {{flag}} + + +
+ + Anzahl der Suchergebnisse: {{songs.length}} +
diff --git a/src/app/modules/songs/song-list/filter/filter.component.less b/src/app/modules/songs/song-list/filter/filter.component.less new file mode 100644 index 0000000..b172b2e --- /dev/null +++ b/src/app/modules/songs/song-list/filter/filter.component.less @@ -0,0 +1,5 @@ +.third { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + column-gap: 20px; +} diff --git a/src/app/modules/songs/song-list/filter/filter.component.spec.ts b/src/app/modules/songs/song-list/filter/filter.component.spec.ts new file mode 100644 index 0000000..4550630 --- /dev/null +++ b/src/app/modules/songs/song-list/filter/filter.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {FilterComponent} from './filter.component'; + +describe('FilterComponent', () => { + let component: FilterComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [FilterComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/songs/song-list/filter/filter.component.ts b/src/app/modules/songs/song-list/filter/filter.component.ts new file mode 100644 index 0000000..fe127de --- /dev/null +++ b/src/app/modules/songs/song-list/filter/filter.component.ts @@ -0,0 +1,63 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {FormBuilder, FormGroup} from '@angular/forms'; +import {SongService} from '../../services/song.service'; +import {FilterValues} from './filter-values'; +import {Song} from '../../services/song'; + +@Component({ + selector: 'app-filter', + templateUrl: './filter.component.html', + styleUrls: ['./filter.component.less'] +}) +export class FilterComponent implements OnInit { + + public filterFormGroup: FormGroup; + @Input() route: string; + @Input() songs: Song[]; + public types = SongService.TYPES; + public legalType = SongService.LEGAL_TYPE; + + constructor(private router: Router, activatedRoute: ActivatedRoute, fb: FormBuilder) { + this.filterFormGroup = fb.group({ + q: '', + type: '', + legalType: '', + flag: '', + }); + + activatedRoute.queryParams.subscribe((filterValues: FilterValues) => { + if (filterValues.q) this.filterFormGroup.controls.q.setValue(filterValues.q); + if (filterValues.type) this.filterFormGroup.controls.type.setValue(filterValues.type); + if (filterValues.legalType) this.filterFormGroup.controls.legalType.setValue(filterValues.legalType); + if (filterValues.flag) this.filterFormGroup.controls.flag.setValue(filterValues.flag); + }) + + this.filterFormGroup.controls.q.valueChanges.subscribe(_ => this.filerValueChanged('q', _)); + this.filterFormGroup.controls.type.valueChanges.subscribe(_ => this.filerValueChanged('type', _)); + this.filterFormGroup.controls.legalType.valueChanges.subscribe(_ => this.filerValueChanged('legalType', _)); + this.filterFormGroup.controls.flag.valueChanges.subscribe(_ => this.filerValueChanged('flag', _)); + + } + + ngOnInit(): void { + } + + public getFlags(): string[] { + const flags = this.songs + .map(_ => _.flags) + .filter(_ => !!_) + .map(_ => _.split(';')) + .reduce((pn, u) => [...pn, ...u], []) + .filter(_ => !!_); + + const uqFlags = flags.filter((n, i) => flags.indexOf(n) === i); + + return uqFlags; + } + + private async filerValueChanged(key: string, value: string): Promise { + const route = this.router.createUrlTree([this.route], {queryParams: {[key]: value}, queryParamsHandling: 'merge'}); + await this.router.navigateByUrl(route); + } +} diff --git a/src/app/modules/songs/song-list/list-item/list-item.component.html b/src/app/modules/songs/song-list/list-item/list-item.component.html index a74ca53..e9c5ef0 100644 --- a/src/app/modules/songs/song-list/list-item/list-item.component.html +++ b/src/app/modules/songs/song-list/list-item/list-item.component.html @@ -1,6 +1,8 @@
{{song.number}}
-
{{song.title}}
+
+   + {{song.title}}
{{song.key}}
-
{{song.legalType | legalType}}
+
diff --git a/src/app/modules/songs/song-list/list-item/list-item.component.less b/src/app/modules/songs/song-list/list-item/list-item.component.less index d7167c1..351df89 100644 --- a/src/app/modules/songs/song-list/list-item/list-item.component.less +++ b/src/app/modules/songs/song-list/list-item/list-item.component.less @@ -3,7 +3,7 @@ .list-item { padding: 5px 20px; display: grid; - grid-template-columns: 50px auto 30px 100px; + grid-template-columns: 50px auto 30px; & > div { display: flex; @@ -15,6 +15,10 @@ &:hover { background: @primary-color; color: #fff; + + .warning { + color: #fff; + } } } @@ -23,3 +27,7 @@ font-weight: bold; text-align: right; } + +.warning { + color: #ba3500; +} diff --git a/src/app/modules/songs/song-list/list-item/list-item.component.ts b/src/app/modules/songs/song-list/list-item/list-item.component.ts index c0dc434..a587f55 100644 --- a/src/app/modules/songs/song-list/list-item/list-item.component.ts +++ b/src/app/modules/songs/song-list/list-item/list-item.component.ts @@ -1,5 +1,6 @@ import {Component, Input, OnInit} from '@angular/core'; import {Song} from '../../services/song'; +import {faBalanceScaleRight} from '@fortawesome/free-solid-svg-icons/faBalanceScaleRight'; @Component({ selector: 'app-list-item', @@ -8,6 +9,7 @@ import {Song} from '../../services/song'; }) export class ListItemComponent implements OnInit { @Input() public song: Song; + public faLegal = faBalanceScaleRight; constructor() { } diff --git a/src/app/modules/songs/song-list/song-list.component.html b/src/app/modules/songs/song-list/song-list.component.html index b402bae..b0308d4 100644 --- a/src/app/modules/songs/song-list/song-list.component.html +++ b/src/app/modules/songs/song-list/song-list.component.html @@ -1,7 +1,9 @@ -
- +
+ + + - +
diff --git a/src/app/modules/songs/song-list/song-list.component.less b/src/app/modules/songs/song-list/song-list.component.less index 11c7349..e69de29 100644 --- a/src/app/modules/songs/song-list/song-list.component.less +++ b/src/app/modules/songs/song-list/song-list.component.less @@ -1,2 +0,0 @@ -.song-list { -} diff --git a/src/app/modules/songs/song-list/song-list.component.ts b/src/app/modules/songs/song-list/song-list.component.ts index f83b504..e01fa02 100644 --- a/src/app/modules/songs/song-list/song-list.component.ts +++ b/src/app/modules/songs/song-list/song-list.component.ts @@ -6,6 +6,7 @@ import {combineLatest, Observable} from 'rxjs'; import {fade} from '../../../animations'; import {ActivatedRoute} from '@angular/router'; import {filterSong} from '../../../services/filter.helper'; +import {FilterValues} from './filter/filter-values'; @Component({ selector: 'app-songs', @@ -16,6 +17,7 @@ import {filterSong} from '../../../services/filter.helper'; export class SongListComponent implements OnInit { public songs$: Observable; + public anyFilterActive = false; constructor(private songService: SongService, private activatedRoute: ActivatedRoute) { } @@ -23,7 +25,7 @@ export class SongListComponent implements OnInit { ngOnInit() { const filter$ = this.activatedRoute.queryParams.pipe( debounceTime(300), - map(_ => _.q) + map(_ => _ as FilterValues) ); const songs$ = this.songService.list$().pipe( @@ -31,7 +33,35 @@ export class SongListComponent implements OnInit { ); this.songs$ = combineLatest([filter$, songs$]).pipe( - map(_ => _[1].filter(song => filterSong(song, _[0]))) + map(_ => { + let songs = _[1]; + let filter = _[0]; + this.anyFilterActive = this.checkIfFilterActive(filter); + return songs.filter(song => this.filter(song, filter)); + }) ); + + } + + private filter(song: Song, filter: FilterValues): boolean { + let baseFilter = filterSong(song, filter.q); + baseFilter = baseFilter && (!filter.type || filter.type === song.type); + baseFilter = baseFilter && (!filter.legalType || filter.legalType === song.legalType); + baseFilter = baseFilter && (!filter.flag || this.checkFlag(filter.flag, song.flags)); + + return baseFilter; + } + + private checkIfFilterActive(filter: FilterValues): boolean { + return !!filter.q || !!filter.type || !!filter.legalType || !!filter.flag; + } + + private checkFlag(flag: string, flags: string) { + if (!flags) return false; + + const flagStrings = flags.split(';'); + if (flagStrings.length === 0) return false; + + return flagStrings.indexOf(flag) !== -1; } } diff --git a/src/app/modules/songs/song-list/song-list.module.ts b/src/app/modules/songs/song-list/song-list.module.ts index c8d59e2..ce903aa 100644 --- a/src/app/modules/songs/song-list/song-list.module.ts +++ b/src/app/modules/songs/song-list/song-list.module.ts @@ -6,10 +6,17 @@ import {CardModule} from '../../../widget-modules/components/card/card.module'; import {RouterModule} from '@angular/router'; import {LegalTypeTranslatorModule} from '../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module'; import {ListHeaderModule} from '../../../widget-modules/components/list-header/list-header.module'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; +import {FilterComponent} from './filter/filter.component'; +import {ReactiveFormsModule} from '@angular/forms'; +import {MatSelectModule} from '@angular/material/select'; +import {SongTypeTranslaterModule} from '../../../widget-modules/pipes/song-type-translater/song-type-translater.module'; +import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; @NgModule({ - declarations: [SongListComponent, ListItemComponent], + declarations: [SongListComponent, ListItemComponent, FilterComponent], exports: [SongListComponent], imports: [ CommonModule, @@ -17,7 +24,13 @@ import {ListHeaderModule} from '../../../widget-modules/components/list-header/l CardModule, LegalTypeTranslatorModule, - ListHeaderModule + ListHeaderModule, + MatFormFieldModule, + MatInputModule, + ReactiveFormsModule, + MatSelectModule, + SongTypeTranslaterModule, + FontAwesomeModule ] }) export class SongListModule { diff --git a/src/app/modules/songs/song/edit/edit-song/edit-song.component.html b/src/app/modules/songs/song/edit/edit-song/edit-song.component.html index ff0d850..fb7d91a 100644 --- a/src/app/modules/songs/song/edit/edit-song/edit-song.component.html +++ b/src/app/modules/songs/song/edit/edit-song/edit-song.component.html @@ -36,7 +36,7 @@ - + {{flag}}  diff --git a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts index 523f8a3..a0d40d2 100644 --- a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts +++ b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts @@ -16,10 +16,7 @@ export class FilterComponent { } public async valueChange(text: string): Promise { - const route = text - ? this.router.createUrlTree(['songs'], {queryParams: {q: text}}) - : this.router.createUrlTree(['songs']); - + const route = this.router.createUrlTree(['songs'], {queryParams: {q: text}, queryParamsHandling: 'merge'}); await this.router.navigateByUrl(route); } } diff --git a/src/app/widget-modules/components/list-header/list-header.component.html b/src/app/widget-modules/components/list-header/list-header.component.html index b282b90..beab8bf 100644 --- a/src/app/widget-modules/components/list-header/list-header.component.html +++ b/src/app/widget-modules/components/list-header/list-header.component.html @@ -1,8 +1,15 @@
-
+ +
+ + + +
diff --git a/src/app/widget-modules/components/list-header/list-header.component.less b/src/app/widget-modules/components/list-header/list-header.component.less index 1cfcd70..4f5fa99 100644 --- a/src/app/widget-modules/components/list-header/list-header.component.less +++ b/src/app/widget-modules/components/list-header/list-header.component.less @@ -13,3 +13,8 @@ color: #A6C4F5; } + +.filter-active { + color: #a21; + cursor: not-allowed; +} diff --git a/src/app/widget-modules/components/list-header/list-header.component.ts b/src/app/widget-modules/components/list-header/list-header.component.ts index b983a8a..23228e7 100644 --- a/src/app/widget-modules/components/list-header/list-header.component.ts +++ b/src/app/widget-modules/components/list-header/list-header.component.ts @@ -1,18 +1,21 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter'; -import {faBars} from '@fortawesome/free-solid-svg-icons/faBars'; import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus'; +import {fade} from '../../../animations'; @Component({ selector: 'app-list-header', templateUrl: './list-header.component.html', - styleUrls: ['./list-header.component.less'] + styleUrls: ['./list-header.component.less'], + animations: [fade] }) export class ListHeaderComponent implements OnInit { public faNew = faPlus; public faFilter = faFilter; - public faMenu = faBars; + public filterVisible = false; + @Output() filterVisibleChanged = new EventEmitter(); + @Input() anyFilterActive = false; constructor() { } @@ -20,4 +23,7 @@ export class ListHeaderComponent implements OnInit { ngOnInit() { } + public onFilterClick(): void { + this.filterVisible = !this.filterVisible || this.anyFilterActive; + } } diff --git a/src/app/widget-modules/components/list-header/list-header.module.ts b/src/app/widget-modules/components/list-header/list-header.module.ts index fc0ebe3..78b6b28 100644 --- a/src/app/widget-modules/components/list-header/list-header.module.ts +++ b/src/app/widget-modules/components/list-header/list-header.module.ts @@ -4,6 +4,7 @@ import {ListHeaderComponent} from './list-header.component'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {MatButtonModule} from '@angular/material/button'; import {RouterModule} from '@angular/router'; +import {CardModule} from '../card/card.module'; @NgModule({ declarations: [ListHeaderComponent], @@ -14,7 +15,8 @@ import {RouterModule} from '@angular/router'; CommonModule, FontAwesomeModule, MatButtonModule, - RouterModule + RouterModule, + CardModule, ] }) export class ListHeaderModule {