Refactor quality badges to use QualityBadge component with compact mode
- Add compact prop to QualityBadge for inline usage in track lists - Compact mode shows simple text (24bit/96kHz or CD Quality) - Use golden gradient for hires badges in compact mode - Update ArtistDetailView to use QualityBadge compact - Update FavoritesView to use QualityBadge compact - Update SearchView to use QualityBadge compact - Remove duplicate CSS and getQualityLabel functions - LocalLibrary views keep custom format showing codec (FLAC 24/96)
This commit is contained in:
1 parent
af74ee8cb8
commit
c0e9a94f98
4 files changed
+97
-91
No files matched your search
@@ -4,13 +4,15 @@
|
|||||||
bitDepth?: number;
|
bitDepth?: number;
|
||||||
samplingRate?: number;
|
samplingRate?: number;
|
||||||
format?: string;
|
format?: string;
|
||||||
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
quality = '',
|
quality = '',
|
||||||
bitDepth,
|
bitDepth,
|
||||||
samplingRate,
|
samplingRate,
|
||||||
format
|
format,
|
||||||
|
compact = false
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// Determine quality tier
|
// Determine quality tier
|
||||||
@@ -53,7 +55,7 @@
|
|||||||
return 'cd'; // Default to CD instead of unknown
|
return 'cd'; // Default to CD instead of unknown
|
||||||
});
|
});
|
||||||
|
|
||||||
// Format the display text
|
// Format the display text for full mode
|
||||||
const displayText = $derived.by(() => {
|
const displayText = $derived.by(() => {
|
||||||
if (tier === 'max') {
|
if (tier === 'max') {
|
||||||
const depth = bitDepth || 24;
|
const depth = bitDepth || 24;
|
||||||
@@ -77,6 +79,19 @@
|
|||||||
return '16-bit / 44.1 kHz';
|
return '16-bit / 44.1 kHz';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Format the compact display text
|
||||||
|
const compactText = $derived.by(() => {
|
||||||
|
if (tier === 'max' || tier === 'hires') {
|
||||||
|
const depth = bitDepth || 24;
|
||||||
|
const rate = samplingRate || (tier === 'max' ? 192 : 96);
|
||||||
|
return `${depth}bit/${rate}kHz`;
|
||||||
|
}
|
||||||
|
if (tier === 'mp3') {
|
||||||
|
return 'MP3';
|
||||||
|
}
|
||||||
|
return 'CD Quality';
|
||||||
|
});
|
||||||
|
|
||||||
const tierLabel = $derived.by(() => {
|
const tierLabel = $derived.by(() => {
|
||||||
if (tier === 'max') return 'Hi-Res';
|
if (tier === 'max') return 'Hi-Res';
|
||||||
if (tier === 'hires') return 'Hi-Res';
|
if (tier === 'hires') return 'Hi-Res';
|
||||||
@@ -96,23 +111,52 @@
|
|||||||
const isHiRes = $derived(tier === 'max' || tier === 'hires');
|
const isHiRes = $derived(tier === 'max' || tier === 'hires');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="quality-badge" title="{tierLabel}: {displayText}">
|
{#if compact}
|
||||||
<!-- Icon -->
|
<span class="quality-badge-compact" class:hires={isHiRes} title="{tierLabel}: {displayText}">
|
||||||
<img
|
{compactText}
|
||||||
src={iconPath}
|
</span>
|
||||||
alt={tierLabel}
|
{:else}
|
||||||
class="badge-icon"
|
<div class="quality-badge" title="{tierLabel}: {displayText}">
|
||||||
class:hires={isHiRes}
|
<!-- Icon -->
|
||||||
/>
|
<img
|
||||||
|
src={iconPath}
|
||||||
|
alt={tierLabel}
|
||||||
|
class="badge-icon"
|
||||||
|
class:hires={isHiRes}
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
<div class="badge-text">
|
<div class="badge-text">
|
||||||
<span class="tier-label">{tierLabel}</span>
|
<span class="tier-label">{tierLabel}</span>
|
||||||
<span class="quality-info">{displayText}</span>
|
<span class="quality-info">{displayText}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Compact mode styles */
|
||||||
|
.quality-badge-compact {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--alpha-10);
|
||||||
|
color: var(--alpha-85);
|
||||||
|
border: 1px solid var(--alpha-15);
|
||||||
|
min-width: 90px;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-badge-compact.hires {
|
||||||
|
background: linear-gradient(135deg, #fbbf24 0%, #b8860b 100%);
|
||||||
|
color: #1a1a1a;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full mode styles */
|
||||||
.quality-badge {
|
.quality-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -125,21 +169,21 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] .quality-badge {
|
[data-theme="light"] .quality-badge {
|
||||||
background: rgba(40, 42, 54, 0.85);
|
background: rgba(40, 42, 54, 0.85);
|
||||||
border: 1px solid rgba(40, 42, 54, 0.95);
|
border: 1px solid rgba(40, 42, 54, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] .tier-label,
|
[data-theme="light"] .tier-label,
|
||||||
[data-theme="light"] .quality-info {
|
[data-theme="light"] .quality-info {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] .badge-icon {
|
[data-theme="light"] .badge-icon {
|
||||||
filter: invert(1) brightness(1.5);
|
filter: invert(1) brightness(1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] .badge-icon.hires {
|
[data-theme="light"] .badge-icon.hires {
|
||||||
filter: brightness(1.2);
|
filter: brightness(1.2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import type { ArtistDetail, QobuzArtist } from '$lib/types';
|
import type { ArtistDetail, QobuzArtist } from '$lib/types';
|
||||||
import AlbumCard from '../AlbumCard.svelte';
|
import AlbumCard from '../AlbumCard.svelte';
|
||||||
import TrackMenu from '../TrackMenu.svelte';
|
import TrackMenu from '../TrackMenu.svelte';
|
||||||
|
import QualityBadge from '../QualityBadge.svelte';
|
||||||
import { consumeContextTrackFocus, setPlaybackContext, getPlaybackContext } from '$lib/stores/playbackContextStore';
|
import { consumeContextTrackFocus, setPlaybackContext, getPlaybackContext } from '$lib/stores/playbackContextStore';
|
||||||
import { togglePlay } from '$lib/stores/playerStore';
|
import { togglePlay } from '$lib/stores/playerStore';
|
||||||
import { getQueue, syncQueueState, playQueueIndex } from '$lib/stores/queueStore';
|
import { getQueue, syncQueueState, playQueueIndex } from '$lib/stores/queueStore';
|
||||||
@@ -643,13 +644,6 @@
|
|||||||
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQualityLabel(track: Track): string {
|
|
||||||
if (track.hires_streamable && track.maximum_bit_depth && track.maximum_sampling_rate) {
|
|
||||||
return `${track.maximum_bit_depth}bit/${track.maximum_sampling_rate}kHz`;
|
|
||||||
}
|
|
||||||
return 'CD Quality';
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePausePlayback(event: MouseEvent) {
|
function handlePausePlayback(event: MouseEvent) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
void togglePlay();
|
void togglePlay();
|
||||||
@@ -1426,9 +1420,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="track-quality">
|
<div class="track-quality">
|
||||||
<span class="quality-badge" class:hires={track.hires_streamable}>
|
<QualityBadge
|
||||||
{getQualityLabel(track)}
|
bitDepth={track.maximum_bit_depth}
|
||||||
</span>
|
samplingRate={track.maximum_sampling_rate}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="track-duration">{formatDuration(track.duration)}</div>
|
<div class="track-duration">{formatDuration(track.duration)}</div>
|
||||||
<div class="track-actions">
|
<div class="track-actions">
|
||||||
@@ -3435,25 +3431,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-quality .quality-badge {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
padding: 3px 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: var(--alpha-10);
|
|
||||||
color: var(--alpha-85);
|
|
||||||
border: 1px solid var(--alpha-15);
|
|
||||||
white-space: nowrap;
|
|
||||||
min-width: 90px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-quality .quality-badge.hires {
|
|
||||||
background: linear-gradient(135deg, #fbbf24 0%, #b8860b 100%);
|
|
||||||
color: #1a1a1a;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-duration {
|
.track-duration {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { Heart, Play, Disc3, Mic2, Music, Search, X, LayoutGrid, List, ChevronDown, ListMusic, Edit3, Star, Folder, Library } from 'lucide-svelte';
|
import { Heart, Play, Disc3, Mic2, Music, Search, X, LayoutGrid, List, ChevronDown, ListMusic, Edit3, Star, Folder, Library } from 'lucide-svelte';
|
||||||
import AlbumCard from '../AlbumCard.svelte';
|
import AlbumCard from '../AlbumCard.svelte';
|
||||||
import TrackRow from '../TrackRow.svelte';
|
import TrackRow from '../TrackRow.svelte';
|
||||||
|
import QualityBadge from '../QualityBadge.svelte';
|
||||||
import VirtualizedTrackList from '../VirtualizedTrackList.svelte';
|
import VirtualizedTrackList from '../VirtualizedTrackList.svelte';
|
||||||
import PlaylistCollage from '../PlaylistCollage.svelte';
|
import PlaylistCollage from '../PlaylistCollage.svelte';
|
||||||
import FavoritesEditModal from '../FavoritesEditModal.svelte';
|
import FavoritesEditModal from '../FavoritesEditModal.svelte';
|
||||||
@@ -607,13 +608,6 @@
|
|||||||
return album.release_date_original.slice(0, 4);
|
return album.release_date_original.slice(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlbumQualityLabel(album: FavoriteAlbum): string {
|
|
||||||
if (album.hires && album.maximum_bit_depth && album.maximum_sampling_rate) {
|
|
||||||
return `${album.maximum_bit_depth}bit/${album.maximum_sampling_rate}kHz`;
|
|
||||||
}
|
|
||||||
return album.hires ? 'Hi-Res' : 'CD Quality';
|
|
||||||
}
|
|
||||||
|
|
||||||
function groupAlbums(items: FavoriteAlbum[], mode: AlbumGroupMode) {
|
function groupAlbums(items: FavoriteAlbum[], mode: AlbumGroupMode) {
|
||||||
const prefix = `album-${mode}`;
|
const prefix = `album-${mode}`;
|
||||||
const sorted = [...items].sort((a, b) => {
|
const sorted = [...items].sort((a, b) => {
|
||||||
@@ -1342,9 +1336,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="album-row-quality">
|
<div class="album-row-quality">
|
||||||
<span class="quality-badge" class:hires={album.hires}>
|
<QualityBadge
|
||||||
{getAlbumQualityLabel(album)}
|
bitDepth={album.maximum_bit_depth}
|
||||||
</span>
|
samplingRate={album.maximum_sampling_rate}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -1416,9 +1412,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="album-row-quality">
|
<div class="album-row-quality">
|
||||||
<span class="quality-badge" class:hires={album.hires}>
|
<QualityBadge
|
||||||
{getAlbumQualityLabel(album)}
|
bitDepth={album.maximum_bit_depth}
|
||||||
</span>
|
samplingRate={album.maximum_sampling_rate}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -2160,25 +2158,6 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-row-quality .quality-badge {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--alpha-85);
|
|
||||||
background: var(--alpha-10);
|
|
||||||
border: 1px solid var(--alpha-15);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 3px 8px;
|
|
||||||
min-width: 90px;
|
|
||||||
text-align: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-row-quality .quality-badge.hires {
|
|
||||||
background: linear-gradient(135deg, #fbbf24 0%, #b8860b 100%);
|
|
||||||
color: #1a1a1a;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.artist-grid {
|
.artist-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { Search, Disc3, Music, Mic2, User, X, ChevronLeft, ChevronRight, Crown } from 'lucide-svelte';
|
import { Search, Disc3, Music, Mic2, User, X, ChevronLeft, ChevronRight, Crown } from 'lucide-svelte';
|
||||||
import AlbumCard from '../AlbumCard.svelte';
|
import AlbumCard from '../AlbumCard.svelte';
|
||||||
import TrackMenu from '../TrackMenu.svelte';
|
import TrackMenu from '../TrackMenu.svelte';
|
||||||
|
import QualityBadge from '../QualityBadge.svelte';
|
||||||
import { getSearchState, setSearchState, subscribeSearchFocus, type SearchResults, type SearchAllResults, type SearchTab, type SearchFilterType } from '$lib/stores/searchState';
|
import { getSearchState, setSearchState, subscribeSearchFocus, type SearchResults, type SearchAllResults, type SearchTab, type SearchFilterType } from '$lib/stores/searchState';
|
||||||
import { setPlaybackContext } from '$lib/stores/playbackContextStore';
|
import { setPlaybackContext } from '$lib/stores/playbackContextStore';
|
||||||
import { togglePlay } from '$lib/stores/playerStore';
|
import { togglePlay } from '$lib/stores/playerStore';
|
||||||
@@ -988,7 +989,13 @@
|
|||||||
<div class="track-artist">{track.performer?.name || 'Unknown Artist'}</div>
|
<div class="track-artist">{track.performer?.name || 'Unknown Artist'}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="track-quality">{getQualityLabel(track)}</div>
|
<div class="track-quality">
|
||||||
|
<QualityBadge
|
||||||
|
bitDepth={track.maximum_bit_depth}
|
||||||
|
samplingRate={track.maximum_sampling_rate}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="track-duration">{formatDuration(track.duration)}</div>
|
<div class="track-duration">{formatDuration(track.duration)}</div>
|
||||||
<div class="track-actions">
|
<div class="track-actions">
|
||||||
<TrackMenu
|
<TrackMenu
|
||||||
@@ -1126,7 +1133,13 @@
|
|||||||
<div class="track-artist">{track.performer?.name || 'Unknown Artist'}</div>
|
<div class="track-artist">{track.performer?.name || 'Unknown Artist'}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="track-quality">{getQualityLabel(track)}</div>
|
<div class="track-quality">
|
||||||
|
<QualityBadge
|
||||||
|
bitDepth={track.maximum_bit_depth}
|
||||||
|
samplingRate={track.maximum_sampling_rate}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="track-duration">{formatDuration(track.duration)}</div>
|
<div class="track-duration">{formatDuration(track.duration)}</div>
|
||||||
<div class="track-actions">
|
<div class="track-actions">
|
||||||
<TrackMenu
|
<TrackMenu
|
||||||
@@ -1574,15 +1587,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.track-quality {
|
.track-quality {
|
||||||
font-size: 11px;
|
display: flex;
|
||||||
font-weight: 600;
|
align-items: center;
|
||||||
color: var(--alpha-85);
|
|
||||||
background: var(--alpha-10);
|
|
||||||
border: 1px solid var(--alpha-15);
|
|
||||||
padding: 3px 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
min-width: 90px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-duration {
|
.track-duration {
|
||||||
|
|||||||
Reference in new issue
Block a user