diff --git a/src/app/modules/songs/song-list/filter/filter.component.html b/src/app/modules/songs/song-list/filter/filter.component.html index fec3258..9fa66a2 100644 --- a/src/app/modules/songs/song-list/filter/filter.component.html +++ b/src/app/modules/songs/song-list/filter/filter.component.html @@ -44,7 +44,8 @@ } + @if (filterActive) { + Filter zurücksetzen + } - - Anzahl der Suchergebnisse: {{ songs.length }} diff --git a/src/app/modules/songs/song-list/filter/filter.component.ts b/src/app/modules/songs/song-list/filter/filter.component.ts index a630e5b..0a22a6d 100644 --- a/src/app/modules/songs/song-list/filter/filter.component.ts +++ b/src/app/modules/songs/song-list/filter/filter.component.ts @@ -2,6 +2,7 @@ import {Component, DestroyRef, Input, inject} from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {debounceTime, distinctUntilChanged} from 'rxjs/operators'; +import {faFilterCircleXmark} from '@fortawesome/free-solid-svg-icons'; import {SongService} from '../../services/song.service'; import {FilterValues} from './filter-values'; import {Song} from '../../services/song'; @@ -15,16 +16,18 @@ import {MatOption} from '@angular/material/core'; import {LegalTypePipe} from '../../../../widget-modules/pipes/legal-type-translator/legal-type.pipe'; import {KeyPipe} from '../../../../widget-modules/pipes/key-translator/key.pipe'; import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translater/song-type.pipe'; +import {ButtonComponent} from '../../../../widget-modules/components/button/button.component'; @Component({ selector: 'app-filter', templateUrl: './filter.component.html', styleUrls: ['./filter.component.less'], - imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatSelect, MatOption, LegalTypePipe, KeyPipe, SongTypePipe], + imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatSelect, MatOption, LegalTypePipe, KeyPipe, SongTypePipe, ButtonComponent], }) export class FilterComponent { private filterStore = inject(FilterStoreService); private destroyRef = inject(DestroyRef); + public faResetFilter = faFilterCircleXmark; public filterFormGroup: FormGroup<{ q: FormControl; @@ -73,6 +76,15 @@ export class FilterComponent { return flags.filter((n, i) => flags.indexOf(n) === i); } + public get filterActive(): boolean { + const filter = this.filterFormGroup.getRawValue(); + return !!(filter.q || filter.type || filter.key || filter.legalType || filter.flag); + } + + public resetFilter(): void { + this.filterStore.resetSongFilter(); + } + private filterValueChanged(key: keyof FilterValues, value: string): void { this.filterStore.updateSongFilter({[key]: value} as Partial); } 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 243d72b..052b87a 100644 --- a/src/app/modules/songs/song-list/song-list.component.html +++ b/src/app/modules/songs/song-list/song-list.component.html @@ -1,11 +1,17 @@ -@if (songs$ | async; as songs) { - +@if (viewModel$ | async; as viewModel) { +
- @for (song of songs; track trackBy($index, song)) { + @if (viewModel.filterActive) { +
+ Filter aktiv: {{ viewModel.songs.length }} Lieder gefunden. + +
+ } + @for (song of viewModel.songs; track trackBy($index, song)) {
{{ song.number }}
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 c4c0b3d..ceffc40 100644 --- a/src/app/modules/songs/song-list/song-list.component.less +++ b/src/app/modules/songs/song-list/song-list.component.less @@ -31,6 +31,31 @@ text-align: right; } +.filter-active { + padding: 10px 20px; + display: flex; + align-items: baseline; + gap: 10px; + flex-wrap: wrap; + color: var(--danger); + font-weight: bold; +} + +.filter-reset-link { + padding: 0; + border: 0; + background: transparent; + color: var(--link-color); + cursor: pointer; + font: inherit; + font-weight: normal; + text-decoration: underline; +} + +.filter-reset-link:hover { + color: var(--primary-active); +} + .title { gap: 6px; } 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 bf32da6..cff2c03 100644 --- a/src/app/modules/songs/song-list/song-list.component.ts +++ b/src/app/modules/songs/song-list/song-list.component.ts @@ -21,6 +21,11 @@ interface SongListItem extends Song { hasChordValidationIssues: boolean; } +interface SongListViewModel { + songs: SongListItem[]; + filterActive: boolean; +} + @Component({ selector: 'app-songs', templateUrl: './song-list.component.html', @@ -37,22 +42,31 @@ export class SongListComponent { private route = inject(ActivatedRoute); private textRenderingService = inject(TextRenderingService); private filterStore = inject(FilterStoreService); - public songs$: Observable = combineLatest([ + public viewModel$: Observable = combineLatest([ this.filterStore.songFilter$, this.route.data.pipe(map(data => (data['songs'] as Song[]).slice().sort((a, b) => a.number - b.number))), ]).pipe( map(([filter, songs]) => { - return searchSongs(songs, filter.q) + const filteredSongs = searchSongs(songs, filter.q) .filter(song => this.filter(song, filter)) .map(song => ({ ...song, hasChordValidationIssues: this.textRenderingService.validateChordNotation(song.text ?? '').length > 0, })); + + return { + songs: filteredSongs, + filterActive: this.isFilterActive(filter), + }; }), ); public trackBy = (index: number, show: SongListItem) => show.id; + public resetFilter(): void { + this.filterStore.resetSongFilter(); + } + private filter(song: Song, filter: FilterValues): boolean { let baseFilter = !filter.type || filter.type === song.type; baseFilter = baseFilter && (!filter.key || filter.key === song.key); @@ -62,6 +76,10 @@ export class SongListComponent { return baseFilter; } + private isFilterActive(filter: FilterValues): boolean { + return !!(filter.q || filter.type || filter.key || filter.legalType || filter.flag); + } + private checkFlag(flag: string, flags: string) { if (!flags) { return false; diff --git a/src/app/widget-modules/components/sidebar/page-frame.component.html b/src/app/widget-modules/components/sidebar/page-frame.component.html index 9d4e1f8..d30d910 100644 --- a/src/app/widget-modules/components/sidebar/page-frame.component.html +++ b/src/app/widget-modules/components/sidebar/page-frame.component.html @@ -7,6 +7,9 @@ class="sidebar-toggle" mat-icon-button type="button"> + @if (menuBadge()) { + + } }
{{ title() }}
diff --git a/src/app/widget-modules/components/sidebar/page-frame.component.less b/src/app/widget-modules/components/sidebar/page-frame.component.less index e908d47..6bd31ca 100644 --- a/src/app/widget-modules/components/sidebar/page-frame.component.less +++ b/src/app/widget-modules/components/sidebar/page-frame.component.less @@ -39,6 +39,17 @@ color: inherit; } +.sidebar-toggle-badge { + position: absolute; + right: 2px; + bottom: 3px; + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--danger); + box-shadow: 0 0 0 1px var(--surface-persist); +} + .sidebar-toggle:hover { color: var(--icon-button-hover-color); } diff --git a/src/app/widget-modules/components/sidebar/page-frame.component.ts b/src/app/widget-modules/components/sidebar/page-frame.component.ts index 0046738..b662595 100644 --- a/src/app/widget-modules/components/sidebar/page-frame.component.ts +++ b/src/app/widget-modules/components/sidebar/page-frame.component.ts @@ -18,6 +18,7 @@ export class PageFrameComponent { public closedIcon = faBars; public title = input.required(); public withMenu = input(true); + public menuBadge = input(false); public toggle(): void { this.collapsed = !this.collapsed;