From 5a453a3f40a124ee03cf74f2808620c458511ef4 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 29 Nov 2025 19:39:52 +0100 Subject: [PATCH] style(recent-playlists): refine styles and add clipboard imports Update recent playlists styles to improve visuals, hover feedback, and theme adaptability. Key changes: - Hide horizontal overflow on playlists list to avoid scroll bleed. - Add rounded corners, subtle transitions, and hover background for mat-list-item; include light theme hover variant. - Reduce drag preview radius, replace shadow with layered shadows, increase opacity, and extend drag animation duration for smoother interactions. - Adjust responsive selector formatting. - Add transitions and hover scaling to action buttons, plus light theme variant; improve icon opacity transitions. - Improve icon container behavior and upload-type-icon visibility on hover. - Restyle status dot and state shadows, tweak unavailable color. - Restyle auto-refresh indicator with shadow, rotation on hover, and light theme background. - Minor spacing and layout tweaks across SCSS. Code imports: - Add Clipboard and ClipboardModule import and include MatIconButton and MatTooltip references in playlist-info component to support new copy/tooltip UI affordances. These changes enhance UX polish, accessibility of interactive affordances, and light/dark theme consistency. --- .../playlist-info.component.html | 9 ++ .../playlist-info/playlist-info.component.ts | 25 ++++- .../playlist-item.component.scss | 94 +++++++++++++++++-- .../recent-playlists.component.scss | 1 + 4 files changed, 120 insertions(+), 9 deletions(-) diff --git a/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.html b/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.html index 9b93d8ffa..4a8b850e3 100644 --- a/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.html +++ b/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.html @@ -58,6 +58,15 @@ 'HOME.PLAYLISTS.INFO_DIALOG.FROM_URL' | translate }} + } @if (playlist.filename) { diff --git a/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.ts b/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.ts index d95a2eb79..495e16f1d 100644 --- a/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.ts +++ b/libs/ui/components/src/lib/recent-playlists/playlist-info/playlist-info.component.ts @@ -1,3 +1,4 @@ +import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'; import { DatePipe } from '@angular/common'; import { Component, inject } from '@angular/core'; import { @@ -7,12 +8,13 @@ import { UntypedFormGroup, Validators, } from '@angular/forms'; -import { MatButton } from '@angular/material/button'; +import { MatButton, MatIconButton } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; +import { MatTooltip } from '@angular/material/tooltip'; import { Store } from '@ngrx/store'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import * as PlaylistActions from 'm3u-state'; @@ -32,16 +34,20 @@ import { Playlist, PlaylistMeta } from 'shared-interfaces'; ], providers: [DatePipe], imports: [ + ClipboardModule, MatButton, MatCheckboxModule, MatDialogModule, MatIcon, + MatIconButton, MatInputModule, + MatTooltip, ReactiveFormsModule, TranslatePipe, ], }) export class PlaylistInfoComponent { + private clipboard = inject(Clipboard); private datePipe = inject(DatePipe); private formBuilder = inject(UntypedFormBuilder); private playlistsService = inject(PlaylistsService); @@ -215,4 +221,21 @@ export class PlaylistInfoComponent { document.body.removeChild(element); } } + + /** + * Copy URL to clipboard + */ + copyUrl(): void { + const url = this.playlistDetails.get('url')?.value; + if (url) { + const success = this.clipboard.copy(url); + if (success) { + this.snackBar.open( + this.translate.instant('HOME.PLAYLISTS.INFO_DIALOG.URL_COPIED'), + this.translate.instant('CLOSE'), + { duration: 2000 } + ); + } + } + } } diff --git a/libs/ui/components/src/lib/recent-playlists/playlist-item/playlist-item.component.scss b/libs/ui/components/src/lib/recent-playlists/playlist-item/playlist-item.component.scss index d3695f333..d6277ee91 100644 --- a/libs/ui/components/src/lib/recent-playlists/playlist-item/playlist-item.component.scss +++ b/libs/ui/components/src/lib/recent-playlists/playlist-item/playlist-item.component.scss @@ -1,5 +1,19 @@ mat-list-item { cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + border-radius: 12px; + margin: 2px 4px; + + &:hover { + background-color: rgba(255, 255, 255, 0.03) !important; + } +} + +// Light theme hover +@media (prefers-color-scheme: light) { + mat-list-item:hover { + background-color: rgba(0, 0, 0, 0.04) !important; + } } .mat-list-base { @@ -12,10 +26,21 @@ mat-list-item { margin-top: 2px !important; } +// Light theme +@media (prefers-color-scheme: light) { + .meta { + color: rgba(0, 0, 0, 0.6); + } +} + .cdk-drag-preview { box-sizing: border-box; - border-radius: 30px; - box-shadow: 0 5px 5px -3px rgb(0 0 0 / 20%), 0 8px 10px 1px rgba(58, 58, 58, 0.14), 0 3px 14px 2px rgb(0 0 0 / 12%); + border-radius: 12px; + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.25), + 0 4px 12px rgba(0, 0, 0, 0.15), + 0 2px 6px rgba(0, 0, 0, 0.1); + opacity: 0.9; } .cdk-drag-placeholder { @@ -23,7 +48,7 @@ mat-list-item { } .cdk-drag-animating { - transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); + transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1); } .drag-icon { @@ -33,7 +58,7 @@ mat-list-item { ::ng-deep { @media only screen and (max-width: 480px) { - .main-container { + .main-container { .favorites-icon, .upload-type-icon, .refresh-btn, @@ -51,11 +76,45 @@ mat-list-item { .action-buttons { align-self: center !important; + + button { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background-color: rgba(255, 255, 255, 0.06) !important; + transform: scale(1.05); + + mat-icon { + opacity: 1; + } + } + + mat-icon { + opacity: 0.85; + transition: opacity 0.3s ease; + } + } +} + +// Light theme action buttons +@media (prefers-color-scheme: light) { + .action-buttons button:hover { + background-color: rgba(0, 0, 0, 0.04) !important; + } } .icon-container { position: relative; display: inline-flex; + + .upload-type-icon { + opacity: 0.75; + transition: opacity 0.3s ease; + } + + mat-list-item:hover & .upload-type-icon { + opacity: 0.95; + } } .status-dot { @@ -65,23 +124,28 @@ mat-list-item { border-radius: 50%; right: 0; bottom: 0; - border: 1px solid var(--background-color, #fff); + border: 2px solid var(--background-color, #131316); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease; } .status-active { background-color: #4caf50; + box-shadow: 0 0 8px rgba(76, 175, 80, 0.4); } .status-inactive { background-color: #f44336; + box-shadow: 0 0 8px rgba(244, 67, 54, 0.4); } .status-expired { background-color: #ff9800; + box-shadow: 0 0 8px rgba(255, 152, 0, 0.4); } .status-unavailable { - background-color: #444444; + background-color: #666666; } .auto-refresh-indicator { @@ -92,11 +156,25 @@ mat-list-item { width: 14px !important; height: 14px !important; color: #4caf50; - background-color: var(--background-color, #fff); + background-color: var(--background-color, #131316); border-radius: 50%; display: flex; align-items: center; justify-content: center; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease; + + mat-list-item:hover & { + color: #66bb6a; + transform: rotate(90deg); + } +} + +// Light theme auto-refresh indicator +@media (prefers-color-scheme: light) { + .auto-refresh-indicator { + background-color: #ffffff; + } } @media only screen and (max-width: 480px) { @@ -104,4 +182,4 @@ mat-list-item { .icon-container { display: none; } -} \ No newline at end of file +} diff --git a/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.scss b/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.scss index 438c9c68c..90d625557 100644 --- a/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.scss +++ b/libs/ui/components/src/lib/recent-playlists/recent-playlists.component.scss @@ -13,6 +13,7 @@ .playlists-list { padding: 0; width: 100%; + overflow-x: hidden; } .meta {