feat(ui): add filter and sort menu to header and clean styles

Add a new mat-menu component for filtering and sorting playlists in the
header area. The filter section iterates playlistTypes and allows toggling
type selection with visual check state. The sort section adds options for
name (asc/desc) and date added (newest/oldest) with active-state visuals
and click handlers that call setSortOptions. A close button is also
included.

Remove unused/duplicate CSS rules from header styles: drop .filter-
container and obsolete .dropdown-menu-header rules to avoid styling
conflicts and reduce dead code. Adjust surrounding selector formatting.
This commit is contained in:
4gray committed 2025-12-21 00:42:48 +01:00
1 parent c84b42284a
commit a874db0b41
6 files changed
+357 -190

No files matched your search

@@ -2,30 +2,28 @@ import {
Component,
HostBinding,
OnInit,
effect,
inject,
input,
output,
viewChild,
} from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import { DataService, SortBy, SortOrder, SortService } from 'services';
import { DataService } from 'services';
//import { shell } from 'electron';
import { AddPlaylistMenuComponent, PlaylistType } from 'components';
import { selectActiveTypeFilters, setSelectedFilters } from 'm3u-state';
import { HomeComponent } from '../../../home/home.component';
import { AboutDialogComponent } from '../about-dialog/about-dialog.component';
import { AddPlaylistDialogComponent } from '../add-playlist/add-playlist-dialog.component';
import { FilterSortMenuComponent } from '../filter-sort-menu/filter-sort-menu.component';
@Component({
selector: 'app-header',
@@ -33,9 +31,10 @@ import { AddPlaylistDialogComponent } from '../add-playlist/add-playlist-dialog.
styleUrls: ['./header.component.scss'],
imports: [
AddPlaylistMenuComponent,
FilterSortMenuComponent,
FormsModule,
MatBadgeModule,
MatButtonModule,
MatCheckboxModule,
MatDividerModule,
MatIconModule,
MatMenuModule,
@@ -53,12 +52,13 @@ export class HeaderComponent implements OnInit {
private dialog = inject(MatDialog);
private dataService = inject(DataService);
private router = inject(Router);
private store = inject(Store);
private sortService = inject(SortService);
readonly addPlaylistMenuComponent = viewChild.required(
AddPlaylistMenuComponent
);
readonly filterSortMenuComponent = viewChild.required(
FilterSortMenuComponent
);
readonly isDesktop = !!window.electron;
readonly title = input.required<string>();
readonly subtitle = input.required<string>();
@@ -66,47 +66,6 @@ export class HeaderComponent implements OnInit {
isHome = true;
playlistTypes = [
{
title: 'M3U (local, url, text)',
id: 'm3u',
checked: true,
},
{
title: 'Xtream',
id: 'xtream',
checked: true,
},
{
title: 'Stalker',
id: 'stalker',
checked: true,
},
];
private readonly selectedTypeFilters = this.store.selectSignal(
selectActiveTypeFilters
);
readonly SortBy = SortBy;
readonly SortOrder = SortOrder;
private currentSortOptions: { by: SortBy; order: SortOrder };
constructor() {
effect(() => {
if (this.selectedTypeFilters) {
this.playlistTypes = this.playlistTypes.map((type) => {
type.checked = this.selectedTypeFilters().includes(type.id);
return type;
});
}
});
this.sortService.getSortOptions().subscribe((options) => {
this.currentSortOptions = options;
});
}
ngOnInit() {
this.isHome =
this.activatedRoute.snapshot.component.name === HomeComponent.name;
@@ -154,27 +113,6 @@ export class HeaderComponent implements OnInit {
);
}
onPlaylistFilterChange() {
this.store.dispatch(
setSelectedFilters({
selectedFilters: this.playlistTypes
.filter((f) => f.checked)
.map((f) => f.id),
})
);
}
setSortOptions(by: SortBy, order: SortOrder): void {
this.sortService.setSortOptions({ by, order });
}
isSortActive(by: SortBy, order: SortOrder): boolean {
return (
this.currentSortOptions?.by === by &&
this.currentSortOptions?.order === order
);
}
onSearchQueryUpdate(query: string): void {
this.searchQuery.emit(query);
}