feat(playlist-switcher): add refresh button for active playlist with spinning icon

Entire-Checkpoint: c6e522b4276c
This commit is contained in:
4gray committed 2026-04-25 22:14:20 +02:00
1 parent ad33deb63e
commit 6eb602d332
3 files changed
+91 -1

No files matched your search

@@ -16,6 +16,26 @@
<div class="name">{{ displayTitle() }}</div>
<div class="channels-count">{{ subtitle() }}</div>
</div>
@if (canRefreshActivePlaylist()) {
<button
type="button"
mat-icon-button
class="trigger-refresh-button"
[disabled]="isRefreshingActivePlaylist()"
(click)="onRefreshClick($event)"
(mousedown)="$event.stopPropagation()"
[matTooltip]="'WORKSPACE.SHELL.REFRESH_PLAYLIST' | translate"
[attr.aria-label]="
'WORKSPACE.SHELL.REFRESH_PLAYLIST' | translate
"
>
<mat-icon
class="trigger-refresh-icon"
[class.is-spinning]="isRefreshingActivePlaylist()"
>sync</mat-icon
>
</button>
}
<mat-icon class="dropdown-icon" [class.rotated]="isMenuOpen()">
expand_more
</mat-icon>
@@ -93,13 +93,71 @@
}
}
.trigger-refresh-button {
margin-left: auto;
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
padding: 0;
line-height: 32px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--mat-sys-on-surface-variant);
transition:
color 0.15s ease,
background 0.15s ease;
&:hover:not(:disabled) {
color: var(--mat-sys-on-surface);
background: var(--mat-sys-surface-container);
}
::ng-deep .mat-mdc-button-touch-target {
width: 32px;
height: 32px;
}
::ng-deep .mat-mdc-button-persistent-ripple,
::ng-deep .mat-mdc-focus-indicator {
border-radius: 50%;
}
}
.trigger-refresh-icon {
font-size: 18px;
width: 18px;
height: 18px;
line-height: 18px;
margin: 0;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
&.is-spinning {
animation: switcher-refresh-spin 1s linear infinite;
}
}
@keyframes switcher-refresh-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.dropdown-icon {
font-size: 20px;
width: 20px;
height: 20px;
color: var(--mat-sys-on-surface-variant);
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
margin-left: auto;
margin-left: 4px;
&.rotated {
transform: rotate(180deg);
@@ -169,6 +227,7 @@
line-height: 30px;
color: var(--mat-sys-on-surface-variant);
opacity: 0.8;
padding: 0;
&:hover,
&.is-active {
@@ -20,6 +20,7 @@ import { MatIcon } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenu, MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTooltip } from '@angular/material/tooltip';
import { Store } from '@ngrx/store';
import { normalizeDateLocale } from '@iptvnator/pipes';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
@@ -61,6 +62,7 @@ const DEFAULT_PLAYLIST_TYPE_FILTERS: Record<PlaylistFilterType, boolean> = {
MatInputModule,
MatMenuModule,
MatRippleModule,
MatTooltip,
TranslatePipe,
],
})
@@ -87,10 +89,19 @@ export class PlaylistSwitcherComponent {
readonly showPlaylistInfo = input(false);
readonly showAccountInfo = input(false);
readonly showAddPlaylist = input(false);
readonly canRefreshActivePlaylist = input(false);
readonly isRefreshingActivePlaylist = input(false);
readonly playlistSelected = output<string>();
readonly playlistInfoRequested = output<void>();
readonly accountInfoRequested = output<void>();
readonly addPlaylistRequested = output<void>();
readonly refreshPlaylistRequested = output<void>();
onRefreshClick(event: Event): void {
event.stopPropagation();
event.preventDefault();
this.refreshPlaylistRequested.emit();
}
readonly menuTrigger = viewChild.required<MatMenuTrigger>('menuTrigger');
readonly playlistMenu = viewChild.required<MatMenu>('playlistMenu');