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.
This commit is contained in:
4gray committed 2025-12-06 21:44:25 +01:00
1 parent 9238fc8d86
commit 5a453a3f40
4 files changed
+120 -9

No files matched your search

@@ -58,6 +58,15 @@
'HOME.PLAYLISTS.INFO_DIALOG.FROM_URL' | translate
}}</mat-label>
<input matInput formControlName="url" />
<button
mat-icon-button
matSuffix
type="button"
(click)="copyUrl()"
[matTooltip]="'HOME.PLAYLISTS.INFO_DIALOG.COPY_URL' | translate"
>
<mat-icon>content_copy</mat-icon>
</button>
</mat-form-field>
}
@if (playlist.filename) {
@@ -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 }
);
}
}
}
}
@@ -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;
}
}
}
@@ -13,6 +13,7 @@
.playlists-list {
padding: 0;
width: 100%;
overflow-x: hidden;
}
.meta {