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 {