refactor(immersive): clean up controls and improve split view layout
- Remove track info (artwork, title, artist) from ImmersiveControls bottom bar - Add track title and artist below album artwork in split view mode - Change Lyrics Focus icon from MessageSquareText to Mic (microphone) - Remove unused compact mode from ImmersiveControls - Simplify props and cleanup CSS
This commit is contained in:
1 parent
0fc0723804
commit
4d5a78ff24
3 files changed
+63
-103
No files matched your search
@@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
// Track info
|
|
||||||
artwork: string;
|
|
||||||
trackTitle: string;
|
|
||||||
artist: string;
|
|
||||||
// Playback state
|
// Playback state
|
||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
@@ -32,15 +28,10 @@
|
|||||||
onToggleRepeat: () => void;
|
onToggleRepeat: () => void;
|
||||||
onToggleFavorite: () => void;
|
onToggleFavorite: () => void;
|
||||||
onVolumeChange: (volume: number) => void;
|
onVolumeChange: (volume: number) => void;
|
||||||
// Compact mode (hide some elements)
|
|
||||||
compact?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
visible = true,
|
visible = true,
|
||||||
artwork,
|
|
||||||
trackTitle,
|
|
||||||
artist,
|
|
||||||
isPlaying,
|
isPlaying,
|
||||||
currentTime,
|
currentTime,
|
||||||
duration,
|
duration,
|
||||||
@@ -58,23 +49,11 @@
|
|||||||
onToggleShuffle,
|
onToggleShuffle,
|
||||||
onToggleRepeat,
|
onToggleRepeat,
|
||||||
onToggleFavorite,
|
onToggleFavorite,
|
||||||
onVolumeChange,
|
onVolumeChange
|
||||||
compact = false
|
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="immersive-controls" class:visible class:compact>
|
<div class="immersive-controls" class:visible>
|
||||||
<!-- Left: Track Info -->
|
|
||||||
{#if !compact}
|
|
||||||
<div class="track-info">
|
|
||||||
<img src={artwork} alt="" class="mini-artwork" />
|
|
||||||
<div class="track-meta">
|
|
||||||
<div class="track-title">{trackTitle}</div>
|
|
||||||
<div class="track-artist">{artist}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- Center: Playback Controls + Progress -->
|
<!-- Center: Playback Controls + Progress -->
|
||||||
<div class="playback-section">
|
<div class="playback-section">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
@@ -136,31 +115,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right: Volume + Actions -->
|
<!-- Right: Volume + Actions -->
|
||||||
{#if !compact}
|
<div class="right-section">
|
||||||
<div class="right-section">
|
<button
|
||||||
<button
|
class="action-btn"
|
||||||
class="action-btn"
|
class:active={isFavorite}
|
||||||
class:active={isFavorite}
|
onclick={onToggleFavorite}
|
||||||
onclick={onToggleFavorite}
|
title={isFavorite ? 'Remove from Favorites' : 'Add to Favorites'}
|
||||||
title={isFavorite ? 'Remove from Favorites' : 'Add to Favorites'}
|
>
|
||||||
>
|
<Heart
|
||||||
<Heart
|
size={18}
|
||||||
size={18}
|
fill={isFavorite ? 'currentColor' : 'none'}
|
||||||
fill={isFavorite ? 'currentColor' : 'none'}
|
/>
|
||||||
/>
|
</button>
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="volume-wrapper">
|
<div class="volume-wrapper">
|
||||||
<VolumeSlider {volume} {onVolumeChange} />
|
<VolumeSlider {volume} {onVolumeChange} />
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if quality}
|
|
||||||
<div class="quality-wrapper">
|
|
||||||
<QualityBadge {quality} {bitDepth} {samplingRate} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
|
{#if quality}
|
||||||
|
<div class="quality-wrapper">
|
||||||
|
<QualityBadge {quality} {bitDepth} {samplingRate} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -193,46 +170,6 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Track Info (Left) */
|
|
||||||
.track-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 280px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-artwork {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border-radius: 6px;
|
|
||||||
object-fit: cover;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-meta {
|
|
||||||
min-width: 0;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-primary, white);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-artist {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--alpha-60, rgba(255, 255, 255, 0.6));
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Playback Section (Center) */
|
/* Playback Section (Center) */
|
||||||
.playback-section {
|
.playback-section {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -351,14 +288,8 @@
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compact mode */
|
|
||||||
.immersive-controls.compact .playback-section {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.track-info,
|
|
||||||
.right-section {
|
.right-section {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { X, Disc3, LayoutGrid, MessageSquareText, ListMusic, Music2, Info, Radio } from 'lucide-svelte';
|
import { X, Disc3, LayoutGrid, Mic, ListMusic, Music2, Info, Radio } from 'lucide-svelte';
|
||||||
import { t } from '$lib/i18n';
|
import { t } from '$lib/i18n';
|
||||||
|
|
||||||
export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'queue';
|
export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'queue';
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
const displayModes: { id: DisplayMode; icon: typeof Disc3; title: string }[] = [
|
const displayModes: { id: DisplayMode; icon: typeof Disc3; title: string }[] = [
|
||||||
{ id: 'coverflow', icon: Disc3, title: 'Coverflow (1)' },
|
{ id: 'coverflow', icon: Disc3, title: 'Coverflow (1)' },
|
||||||
{ id: 'split', icon: LayoutGrid, title: 'Split View (2)' },
|
{ id: 'split', icon: LayoutGrid, title: 'Split View (2)' },
|
||||||
{ id: 'lyrics-focus', icon: MessageSquareText, title: 'Lyrics Focus (3)' },
|
{ id: 'lyrics-focus', icon: Mic, title: 'Lyrics Focus (3)' },
|
||||||
{ id: 'queue-focus', icon: ListMusic, title: 'Queue Focus (4)' },
|
{ id: 'queue-focus', icon: ListMusic, title: 'Queue Focus (4)' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -271,10 +271,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Split: Artwork + Panel side by side -->
|
<!-- Split: Artwork + Panel side by side -->
|
||||||
<div class="main-content">
|
<div class="immersive-main">
|
||||||
<!-- Left: Artwork -->
|
<!-- Left: Artwork + Track Info -->
|
||||||
<div class="artwork-section">
|
<div class="artwork-section">
|
||||||
<ImmersiveArtwork {artwork} {trackTitle} variant="floating" />
|
<ImmersiveArtwork {artwork} {trackTitle} variant="floating" />
|
||||||
|
<div class="split-track-info">
|
||||||
|
<h2 class="split-track-title">{trackTitle}</h2>
|
||||||
|
<p class="split-track-artist">{artist}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right: Active Panel -->
|
<!-- Right: Active Panel -->
|
||||||
@@ -307,9 +311,6 @@
|
|||||||
<!-- Bottom Controls -->
|
<!-- Bottom Controls -->
|
||||||
<ImmersiveControls
|
<ImmersiveControls
|
||||||
visible={showUI}
|
visible={showUI}
|
||||||
{artwork}
|
|
||||||
{trackTitle}
|
|
||||||
{artist}
|
|
||||||
{isPlaying}
|
{isPlaying}
|
||||||
{currentTime}
|
{currentTime}
|
||||||
{duration}
|
{duration}
|
||||||
@@ -354,7 +355,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Split mode layout */
|
/* Split mode layout */
|
||||||
.main-content {
|
.immersive-main {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -373,8 +374,36 @@
|
|||||||
.artwork-section {
|
.artwork-section {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-track-info {
|
||||||
|
text-align: center;
|
||||||
|
max-width: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-track-title {
|
||||||
|
font-size: clamp(18px, 2.5vw, 24px);
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary, white);
|
||||||
|
margin: 0 0 6px 0;
|
||||||
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
/* Truncate long titles */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-track-artist {
|
||||||
|
font-size: clamp(14px, 1.8vw, 16px);
|
||||||
|
color: var(--alpha-70, rgba(255, 255, 255, 0.7));
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-section {
|
.panel-section {
|
||||||
@@ -412,7 +441,7 @@
|
|||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.main-content {
|
.immersive-main {
|
||||||
padding: 70px 32px 130px;
|
padding: 70px 32px 130px;
|
||||||
gap: 40px;
|
gap: 40px;
|
||||||
}
|
}
|
||||||
@@ -427,7 +456,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.main-content {
|
.immersive-main {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 70px 24px 140px;
|
padding: 70px 24px 140px;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
@@ -454,7 +483,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.main-content {
|
.immersive-main {
|
||||||
padding: 60px 16px 130px;
|
padding: 60px 16px 130px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user