Compare commits
3 Commits
89ca2dfefc
...
fce007b848
| Author | SHA1 | Date | |
|---|---|---|---|
| fce007b848 | |||
| 1a1d4ccdea | |||
| 38fe753570 |
+21
-1
@@ -1,4 +1,24 @@
|
|||||||
{
|
{
|
||||||
"indexes": [],
|
"indexes": [
|
||||||
|
{
|
||||||
|
"collectionGroup": "shows",
|
||||||
|
"queryScope": "COLLECTION",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "published",
|
||||||
|
"order": "ASCENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "date",
|
||||||
|
"order": "DESCENDING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "__name__",
|
||||||
|
"order": "DESCENDING"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"density": "SPARSE_ALL"
|
||||||
|
}
|
||||||
|
],
|
||||||
"fieldOverrides": []
|
"fieldOverrides": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,9 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-checkbox formControlName="archived">Archiviert</mat-checkbox>
|
<mat-checkbox formControlName="archived">Archiviert</mat-checkbox>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <i>Anzahl der Suchergebnisse: {{ shows?.length ?? 0 }}</i>-->
|
@if (filterActive) {
|
||||||
|
<app-button (click)="resetFilter()" [fullWidth]="true" [icon]="faResetFilter">Filter zurücksetzen</app-button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,13 +2,17 @@
|
|||||||
div[formGroup] {
|
div[formGroup] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: var(--gap-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.third {
|
.third {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
margin-bottom: var(--gap-m);
|
||||||
|
}
|
||||||
|
|
||||||
:host ::ng-deep .mat-mdc-form-field {
|
:host ::ng-deep .mat-mdc-form-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {Component, DestroyRef, inject, Input} from '@angular/core';
|
|||||||
import {KeyValue} from '@angular/common';
|
import {KeyValue} from '@angular/common';
|
||||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
|
import {faFilterCircleXmark} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {FilterValues} from './filter-values';
|
import {FilterValues} from './filter-values';
|
||||||
import {Show} from '../../services/show';
|
import {Show} from '../../services/show';
|
||||||
import {ShowService} from '../../services/show.service';
|
import {ShowService} from '../../services/show.service';
|
||||||
@@ -15,14 +16,23 @@ import {MatSelect} from '@angular/material/select';
|
|||||||
import {MatOptgroup, MatOption} from '@angular/material/core';
|
import {MatOptgroup, MatOption} from '@angular/material/core';
|
||||||
import {ShowTypePipe} from '../../../../widget-modules/pipes/show-type-translater/show-type.pipe';
|
import {ShowTypePipe} from '../../../../widget-modules/pipes/show-type-translater/show-type.pipe';
|
||||||
import {MatCheckbox} from '@angular/material/checkbox';
|
import {MatCheckbox} from '@angular/material/checkbox';
|
||||||
|
import {ButtonComponent} from '../../../../widget-modules/components/button/button.component';
|
||||||
|
|
||||||
|
const DEFAULT_SHOW_FILTER: FilterValues = {
|
||||||
|
time: 1,
|
||||||
|
owner: '',
|
||||||
|
showType: '',
|
||||||
|
archived: false,
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-filter',
|
selector: 'app-filter',
|
||||||
templateUrl: './filter.component.html',
|
templateUrl: './filter.component.html',
|
||||||
styleUrls: ['./filter.component.less'],
|
styleUrls: ['./filter.component.less'],
|
||||||
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatSelect, MatOption, MatOptgroup, ShowTypePipe, MatCheckbox],
|
imports: [ReactiveFormsModule, MatFormField, MatLabel, MatSelect, MatOption, MatOptgroup, ShowTypePipe, MatCheckbox, ButtonComponent],
|
||||||
})
|
})
|
||||||
export class FilterComponent {
|
export class FilterComponent {
|
||||||
|
public faResetFilter = faFilterCircleXmark;
|
||||||
@Input() public shows: Show[] = [];
|
@Input() public shows: Show[] = [];
|
||||||
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
public showTypePublic = ShowService.SHOW_TYPE_PUBLIC;
|
||||||
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE;
|
||||||
@@ -106,6 +116,20 @@ export class FilterComponent {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public get filterActive(): boolean {
|
||||||
|
const filter = this.filterFormGroup.getRawValue();
|
||||||
|
return (
|
||||||
|
filter.time !== DEFAULT_SHOW_FILTER.time ||
|
||||||
|
!!filter.owner ||
|
||||||
|
!!filter.showType ||
|
||||||
|
filter.archived !== DEFAULT_SHOW_FILTER.archived
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetFilter(): void {
|
||||||
|
this.filterStore.resetShowFilter();
|
||||||
|
}
|
||||||
|
|
||||||
private filterValueChanged<T>(key: keyof FilterValues, value: T): void {
|
private filterValueChanged<T>(key: keyof FilterValues, value: T): void {
|
||||||
this.filterStore.updateShowFilter({[key]: value} as Partial<FilterValues>);
|
this.filterStore.updateShowFilter({[key]: value} as Partial<FilterValues>);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
@if (showSidebar$ | async) {
|
@if (showSidebar$ | async) {
|
||||||
<app-page-frame title="Veranstaltungen">
|
<app-page-frame title="Veranstaltungen" [menuBadge]="(filterActive$ | async) ?? false">
|
||||||
<div class="sidebar-content" sidebar>
|
<div class="sidebar-content" sidebar>
|
||||||
<app-filter [shows]="(publicShows$ | async) ?? []"></app-filter>
|
<app-filter [shows]="(publicShows$ | async) ?? []"></app-filter>
|
||||||
</div>
|
</div>
|
||||||
<div content>
|
<div content>
|
||||||
@if (privateShows$ | async; as privateShows) {
|
@if (privateShows$ | async; as privateShows) {
|
||||||
<app-card [padding]="false" heading="Meine Veranstaltungen">
|
<app-card [padding]="false" heading="Meine Veranstaltungen">
|
||||||
|
@if ((filterActive$ | async) ?? false) {
|
||||||
|
<div class="filter-active">
|
||||||
|
<span>Filter aktiv: {{ privateShows.length }} Veranstaltungen angezeigt.</span>
|
||||||
|
<button (click)="resetFilter()" class="filter-reset-link" type="button">Filter zurücksetzen</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@for (show of privateShows; track trackBy($index, show)) {
|
@for (show of privateShows; track trackBy($index, show)) {
|
||||||
<app-list-item
|
<app-list-item
|
||||||
[routerLink]="show.id"
|
[routerLink]="show.id"
|
||||||
@@ -20,8 +26,14 @@
|
|||||||
</app-card>
|
</app-card>
|
||||||
}
|
}
|
||||||
@if (publicShows$ | async; as shows) {
|
@if (publicShows$ | async; as shows) {
|
||||||
@if (shows.length > 0) {
|
@if (shows.length > 0 || ((filterActive$ | async) ?? false)) {
|
||||||
<app-card [padding]="false" heading="Veröffentlichte Veranstaltungen">
|
<app-card [padding]="false" heading="Veröffentlichte Veranstaltungen">
|
||||||
|
@if ((filterActive$ | async) ?? false) {
|
||||||
|
<div class="filter-active">
|
||||||
|
<span>Filter aktiv: {{ shows.length }} Veranstaltungen angezeigt.</span>
|
||||||
|
<button (click)="resetFilter()" class="filter-reset-link" type="button">Filter zurücksetzen</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@for (show of shows; track trackBy($index, show)) {
|
@for (show of shows; track trackBy($index, show)) {
|
||||||
<app-list-item [routerLink]="show.id" [show]="show"></app-list-item>
|
<app-list-item [routerLink]="show.id" [show]="show"></app-list-item>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,28 @@
|
|||||||
.list-action {
|
.list-action {
|
||||||
margin: 10px 20px;
|
margin: 10px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ import {ButtonComponent} from '../../../widget-modules/components/button/button.
|
|||||||
import {faPlus} from '@fortawesome/free-solid-svg-icons';
|
import {faPlus} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {RoleDirective} from '../../../services/user/role.directive';
|
import {RoleDirective} from '../../../services/user/role.directive';
|
||||||
|
|
||||||
|
const DEFAULT_SHOW_FILTER: FilterValues = {
|
||||||
|
time: 1,
|
||||||
|
owner: '',
|
||||||
|
showType: '',
|
||||||
|
archived: false,
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-list',
|
selector: 'app-list',
|
||||||
templateUrl: './list.component.html',
|
templateUrl: './list.component.html',
|
||||||
@@ -28,6 +35,7 @@ export class ListComponent {
|
|||||||
public faNewShow = faPlus;
|
public faNewShow = faPlus;
|
||||||
private filterStore = inject(FilterStoreService);
|
private filterStore = inject(FilterStoreService);
|
||||||
public filter$ = this.filterStore.showFilter$;
|
public filter$ = this.filterStore.showFilter$;
|
||||||
|
public filterActive$ = this.filter$.pipe(map(filter => this.isFilterActive(filter)));
|
||||||
public lastMonths$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.time || 1));
|
public lastMonths$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.time || 1));
|
||||||
public owner$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.owner));
|
public owner$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.owner));
|
||||||
public showType$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.showType));
|
public showType$ = this.filter$.pipe(map((filterValues: FilterValues) => filterValues.showType));
|
||||||
@@ -69,6 +77,10 @@ export class ListComponent {
|
|||||||
|
|
||||||
public trackBy = (index: number, show: unknown) => (show as Show).id;
|
public trackBy = (index: number, show: unknown) => (show as Show).id;
|
||||||
|
|
||||||
|
public resetFilter(): void {
|
||||||
|
this.filterStore.resetShowFilter();
|
||||||
|
}
|
||||||
|
|
||||||
private matchesTimeFilter(show: Show, lastMonths: number): boolean {
|
private matchesTimeFilter(show: Show, lastMonths: number): boolean {
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
startDate.setHours(0, 0, 0, 0);
|
startDate.setHours(0, 0, 0, 0);
|
||||||
@@ -88,4 +100,13 @@ export class ListComponent {
|
|||||||
const roles = role.split(';').map(item => item.trim());
|
const roles = role.split(';').map(item => item.trim());
|
||||||
return roles.includes('admin') || roles.includes('leader');
|
return roles.includes('admin') || roles.includes('leader');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isFilterActive(filter: FilterValues): boolean {
|
||||||
|
return (
|
||||||
|
filter.time !== DEFAULT_SHOW_FILTER.time ||
|
||||||
|
!!filter.owner ||
|
||||||
|
!!filter.showType ||
|
||||||
|
filter.archived !== DEFAULT_SHOW_FILTER.archived
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@
|
|||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@if (filterActive) {
|
||||||
|
<app-button (click)="resetFilter()" [fullWidth]="true" [icon]="faResetFilter">Filter zurücksetzen</app-button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<i>Anzahl der Suchergebnisse: {{ songs.length }}</i>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {Component, DestroyRef, Input, inject} from '@angular/core';
|
|||||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
|
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
|
||||||
|
import {faFilterCircleXmark} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {SongService} from '../../services/song.service';
|
import {SongService} from '../../services/song.service';
|
||||||
import {FilterValues} from './filter-values';
|
import {FilterValues} from './filter-values';
|
||||||
import {Song} from '../../services/song';
|
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 {LegalTypePipe} from '../../../../widget-modules/pipes/legal-type-translator/legal-type.pipe';
|
||||||
import {KeyPipe} from '../../../../widget-modules/pipes/key-translator/key.pipe';
|
import {KeyPipe} from '../../../../widget-modules/pipes/key-translator/key.pipe';
|
||||||
import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translater/song-type.pipe';
|
import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translater/song-type.pipe';
|
||||||
|
import {ButtonComponent} from '../../../../widget-modules/components/button/button.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-filter',
|
selector: 'app-filter',
|
||||||
templateUrl: './filter.component.html',
|
templateUrl: './filter.component.html',
|
||||||
styleUrls: ['./filter.component.less'],
|
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 {
|
export class FilterComponent {
|
||||||
private filterStore = inject(FilterStoreService);
|
private filterStore = inject(FilterStoreService);
|
||||||
private destroyRef = inject(DestroyRef);
|
private destroyRef = inject(DestroyRef);
|
||||||
|
public faResetFilter = faFilterCircleXmark;
|
||||||
|
|
||||||
public filterFormGroup: FormGroup<{
|
public filterFormGroup: FormGroup<{
|
||||||
q: FormControl<string>;
|
q: FormControl<string>;
|
||||||
@@ -73,6 +76,15 @@ export class FilterComponent {
|
|||||||
return flags.filter((n, i) => flags.indexOf(n) === i);
|
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 {
|
private filterValueChanged(key: keyof FilterValues, value: string): void {
|
||||||
this.filterStore.updateSongFilter({[key]: value} as Partial<FilterValues>);
|
this.filterStore.updateSongFilter({[key]: value} as Partial<FilterValues>);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
@if (songs$ | async; as songs) {
|
@if (viewModel$ | async; as viewModel) {
|
||||||
<app-page-frame title="Lieder">
|
<app-page-frame title="Lieder" [menuBadge]="viewModel.filterActive">
|
||||||
<div class="sidebar-content" sidebar>
|
<div class="sidebar-content" sidebar>
|
||||||
<app-filter [songs]="songs"></app-filter>
|
<app-filter [songs]="viewModel.songs"></app-filter>
|
||||||
</div>
|
</div>
|
||||||
<div content>
|
<div content>
|
||||||
<app-card [padding]="false">
|
<app-card [padding]="false">
|
||||||
@for (song of songs; track trackBy($index, song)) {
|
@if (viewModel.filterActive) {
|
||||||
|
<div class="filter-active">
|
||||||
|
<span>Filter aktiv: {{ viewModel.songs.length }} Lieder gefunden.</span>
|
||||||
|
<button (click)="resetFilter()" class="filter-reset-link" type="button">Filter zurücksetzen</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@for (song of viewModel.songs; track trackBy($index, song)) {
|
||||||
<div [routerLink]="song.id" class="list-item">
|
<div [routerLink]="song.id" class="list-item">
|
||||||
<div class="number">{{ song.number }}</div>
|
<div class="number">{{ song.number }}</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
|||||||
@@ -31,6 +31,31 @@
|
|||||||
text-align: right;
|
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 {
|
.title {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ interface SongListItem extends Song {
|
|||||||
hasChordValidationIssues: boolean;
|
hasChordValidationIssues: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SongListViewModel {
|
||||||
|
songs: SongListItem[];
|
||||||
|
filterActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-songs',
|
selector: 'app-songs',
|
||||||
templateUrl: './song-list.component.html',
|
templateUrl: './song-list.component.html',
|
||||||
@@ -37,22 +42,31 @@ export class SongListComponent {
|
|||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
private textRenderingService = inject(TextRenderingService);
|
private textRenderingService = inject(TextRenderingService);
|
||||||
private filterStore = inject(FilterStoreService);
|
private filterStore = inject(FilterStoreService);
|
||||||
public songs$: Observable<SongListItem[]> = combineLatest([
|
public viewModel$: Observable<SongListViewModel> = combineLatest([
|
||||||
this.filterStore.songFilter$,
|
this.filterStore.songFilter$,
|
||||||
this.route.data.pipe(map(data => (data['songs'] as Song[]).slice().sort((a, b) => a.number - b.number))),
|
this.route.data.pipe(map(data => (data['songs'] as Song[]).slice().sort((a, b) => a.number - b.number))),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([filter, songs]) => {
|
map(([filter, songs]) => {
|
||||||
return searchSongs(songs, filter.q)
|
const filteredSongs = searchSongs(songs, filter.q)
|
||||||
.filter(song => this.filter(song, filter))
|
.filter(song => this.filter(song, filter))
|
||||||
.map(song => ({
|
.map(song => ({
|
||||||
...song,
|
...song,
|
||||||
hasChordValidationIssues: this.textRenderingService.validateChordNotation(song.text ?? '').length > 0,
|
hasChordValidationIssues: this.textRenderingService.validateChordNotation(song.text ?? '').length > 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
songs: filteredSongs,
|
||||||
|
filterActive: this.isFilterActive(filter),
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
public trackBy = (index: number, show: SongListItem) => show.id;
|
public trackBy = (index: number, show: SongListItem) => show.id;
|
||||||
|
|
||||||
|
public resetFilter(): void {
|
||||||
|
this.filterStore.resetSongFilter();
|
||||||
|
}
|
||||||
|
|
||||||
private filter(song: Song, filter: FilterValues): boolean {
|
private filter(song: Song, filter: FilterValues): boolean {
|
||||||
let baseFilter = !filter.type || filter.type === song.type;
|
let baseFilter = !filter.type || filter.type === song.type;
|
||||||
baseFilter = baseFilter && (!filter.key || filter.key === song.key);
|
baseFilter = baseFilter && (!filter.key || filter.key === song.key);
|
||||||
@@ -62,6 +76,10 @@ export class SongListComponent {
|
|||||||
return baseFilter;
|
return baseFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isFilterActive(filter: FilterValues): boolean {
|
||||||
|
return !!(filter.q || filter.type || filter.key || filter.legalType || filter.flag);
|
||||||
|
}
|
||||||
|
|
||||||
private checkFlag(flag: string, flags: string) {
|
private checkFlag(flag: string, flags: string) {
|
||||||
if (!flags) {
|
if (!flags) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
class="sidebar-toggle"
|
class="sidebar-toggle"
|
||||||
mat-icon-button type="button">
|
mat-icon-button type="button">
|
||||||
<fa-icon [icon]="collapsed ? closedIcon : openIcon"></fa-icon>
|
<fa-icon [icon]="collapsed ? closedIcon : openIcon"></fa-icon>
|
||||||
|
@if (menuBadge()) {
|
||||||
|
<span aria-hidden="true" class="sidebar-toggle-badge"></span>
|
||||||
|
}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
<div class="title">{{ title() }}</div>
|
<div class="title">{{ title() }}</div>
|
||||||
|
|||||||
@@ -39,6 +39,17 @@
|
|||||||
color: inherit;
|
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 {
|
.sidebar-toggle:hover {
|
||||||
color: var(--icon-button-hover-color);
|
color: var(--icon-button-hover-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class PageFrameComponent {
|
|||||||
public closedIcon = faBars;
|
public closedIcon = faBars;
|
||||||
public title = input.required<string>();
|
public title = input.required<string>();
|
||||||
public withMenu = input<boolean>(true);
|
public withMenu = input<boolean>(true);
|
||||||
|
public menuBadge = input<boolean>(false);
|
||||||
|
|
||||||
public toggle(): void {
|
public toggle(): void {
|
||||||
this.collapsed = !this.collapsed;
|
this.collapsed = !this.collapsed;
|
||||||
|
|||||||
Reference in New Issue
Block a user