NEW: compact mode for cards, default true for artists
This commit is contained in:
parent
976f24ef1a
commit
27b6b98643
5 changed files with 158 additions and 19 deletions
20
index.html
20
index.html
|
|
@ -526,6 +526,26 @@
|
|||
<option value="inline">Inline Buttons</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Compact Artists</span>
|
||||
<span class="description">Show artist cards in a compact, horizontal layout</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-artist-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Compact Albums</span>
|
||||
<span class="description">Show album cards in a compact, horizontal layout</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-album-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//js/settings
|
||||
import { themeManager, lastFMStorage, nowPlayingSettings, lyricsSettings, backgroundSettings, trackListSettings } from './storage.js';
|
||||
import { themeManager, lastFMStorage, nowPlayingSettings, lyricsSettings, backgroundSettings, trackListSettings, cardSettings } from './storage.js';
|
||||
import { db } from './db.js';
|
||||
import { authManager } from './firebase/auth.js';
|
||||
import { syncManager } from './firebase/sync.js';
|
||||
|
|
@ -266,6 +266,24 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
|||
});
|
||||
}
|
||||
|
||||
// Compact Artist Toggle
|
||||
const compactArtistToggle = document.getElementById('compact-artist-toggle');
|
||||
if (compactArtistToggle) {
|
||||
compactArtistToggle.checked = cardSettings.isCompactArtist();
|
||||
compactArtistToggle.addEventListener('change', (e) => {
|
||||
cardSettings.setCompactArtist(e.target.checked);
|
||||
});
|
||||
}
|
||||
|
||||
// Compact Album Toggle
|
||||
const compactAlbumToggle = document.getElementById('compact-album-toggle');
|
||||
if (compactAlbumToggle) {
|
||||
compactAlbumToggle.checked = cardSettings.isCompactAlbum();
|
||||
compactAlbumToggle.addEventListener('change', (e) => {
|
||||
cardSettings.setCompactAlbum(e.target.checked);
|
||||
});
|
||||
}
|
||||
|
||||
// Download Lyrics Toggle
|
||||
const downloadLyricsToggle = document.getElementById('download-lyrics-toggle');
|
||||
if (downloadLyricsToggle) {
|
||||
|
|
|
|||
|
|
@ -446,6 +446,36 @@ export const trackListSettings = {
|
|||
}
|
||||
};
|
||||
|
||||
export const cardSettings = {
|
||||
COMPACT_ARTIST_KEY: 'card-compact-artist',
|
||||
COMPACT_ALBUM_KEY: 'card-compact-album',
|
||||
|
||||
isCompactArtist() {
|
||||
try {
|
||||
const val = localStorage.getItem(this.COMPACT_ARTIST_KEY);
|
||||
return val === null ? true : val === 'true';
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
setCompactArtist(enabled) {
|
||||
localStorage.setItem(this.COMPACT_ARTIST_KEY, enabled ? 'true' : 'false');
|
||||
},
|
||||
|
||||
isCompactAlbum() {
|
||||
try {
|
||||
return localStorage.getItem(this.COMPACT_ALBUM_KEY) === 'true';
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
setCompactAlbum(enabled) {
|
||||
localStorage.setItem(this.COMPACT_ALBUM_KEY, enabled ? 'true' : 'false');
|
||||
}
|
||||
};
|
||||
|
||||
export const queueManager = {
|
||||
STORAGE_KEY: 'monochrome-queue',
|
||||
|
||||
|
|
|
|||
45
js/ui.js
45
js/ui.js
|
|
@ -1,7 +1,7 @@
|
|||
//js/ui.js
|
||||
import { SVG_PLAY, SVG_DOWNLOAD, SVG_MENU, SVG_HEART, formatTime, createPlaceholder, trackDataStore, hasExplicitContent, getTrackArtists, getTrackTitle, calculateTotalDuration, formatDuration } from './utils.js';
|
||||
import { openLyricsPanel } from './lyrics.js';
|
||||
import { recentActivityManager, backgroundSettings, trackListSettings } from './storage.js';
|
||||
import { recentActivityManager, backgroundSettings, trackListSettings, cardSettings } from './storage.js';
|
||||
import { db } from './db.js';
|
||||
import { getVibrantColorFromImage } from './vibrant-color.js';
|
||||
|
||||
|
|
@ -190,8 +190,9 @@ export class UIRenderer {
|
|||
|
||||
createPlaylistCardHTML(playlist) {
|
||||
const imageId = playlist.squareImage || playlist.image || playlist.uuid; // Fallback or use a specific cover getter if needed
|
||||
const isCompact = cardSettings.isCompactAlbum();
|
||||
return `
|
||||
<div class="card" data-playlist-id="${playlist.uuid}" data-href="#playlist/${playlist.uuid}" style="cursor: pointer;">
|
||||
<div class="card ${isCompact ? 'compact' : ''}" data-playlist-id="${playlist.uuid}" data-href="#playlist/${playlist.uuid}" style="cursor: pointer;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="${this.api.getCoverUrl(imageId)}" alt="${playlist.title}" class="card-image" loading="lazy">
|
||||
<button class="like-btn card-like-btn" data-action="toggle-like" data-type="playlist" title="Add to Liked">
|
||||
|
|
@ -201,8 +202,10 @@ export class UIRenderer {
|
|||
${SVG_PLAY}
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="card-title">${playlist.title}</h4>
|
||||
<p class="card-subtitle">${playlist.numberOfTracks || 0} tracks</p>
|
||||
<div class="card-info">
|
||||
<h4 class="card-title">${playlist.title}</h4>
|
||||
<p class="card-subtitle">${playlist.numberOfTracks || 0} tracks</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -210,9 +213,10 @@ export class UIRenderer {
|
|||
createMixCardHTML(mix) {
|
||||
const imageSrc = mix.cover || 'assets/appicon.png';
|
||||
const description = mix.subTitle || mix.description || '';
|
||||
const isCompact = cardSettings.isCompactAlbum();
|
||||
|
||||
return `
|
||||
<div class="card" data-mix-id="${mix.id}" data-href="#mix/${mix.id}" style="cursor: pointer;">
|
||||
<div class="card ${isCompact ? 'compact' : ''}" data-mix-id="${mix.id}" data-href="#mix/${mix.id}" style="cursor: pointer;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="${imageSrc}" alt="${mix.title}" class="card-image" loading="lazy">
|
||||
<button class="like-btn card-like-btn" data-action="toggle-like" data-type="mix" title="Add to Liked">
|
||||
|
|
@ -222,8 +226,10 @@ export class UIRenderer {
|
|||
${SVG_PLAY}
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="card-title">${mix.title}</h4>
|
||||
<p class="card-subtitle">${description}</p>
|
||||
<div class="card-info">
|
||||
<h4 class="card-title">${mix.title}</h4>
|
||||
<p class="card-subtitle">${description}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -266,8 +272,10 @@ export class UIRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
const isCompact = cardSettings.isCompactAlbum();
|
||||
|
||||
return `
|
||||
<div class="card user-playlist" data-playlist-id="${playlist.id}" data-href="#userplaylist/${playlist.id}" style="cursor: pointer;">
|
||||
<div class="card user-playlist ${isCompact ? 'compact' : ''}" data-playlist-id="${playlist.id}" data-href="#userplaylist/${playlist.id}" style="cursor: pointer;">
|
||||
<div class="card-image-wrapper">
|
||||
${imageHTML}
|
||||
<button class="edit-playlist-btn" data-action="edit-playlist" title="Edit Playlist">
|
||||
|
|
@ -289,8 +297,10 @@ export class UIRenderer {
|
|||
${SVG_PLAY}
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="card-title">${playlist.name}</h4>
|
||||
<p class="card-subtitle">${playlist.tracks ? playlist.tracks.length : (playlist.numberOfTracks || 0)} tracks</p>
|
||||
<div class="card-info">
|
||||
<h4 class="card-title">${playlist.name}</h4>
|
||||
<p class="card-subtitle">${playlist.tracks ? playlist.tracks.length : (playlist.numberOfTracks || 0)} tracks</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -312,8 +322,10 @@ export class UIRenderer {
|
|||
typeLabel = ' • Single';
|
||||
}
|
||||
|
||||
const isCompact = cardSettings.isCompactAlbum();
|
||||
|
||||
return `
|
||||
<div class="card" data-album-id="${album.id}" data-href="#album/${album.id}" style="cursor: pointer;">
|
||||
<div class="card ${isCompact ? 'compact' : ''}" data-album-id="${album.id}" data-href="#album/${album.id}" style="cursor: pointer;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="${this.api.getCoverUrl(album.cover)}" alt="${album.title}" class="card-image" loading="lazy">
|
||||
<button class="like-btn card-like-btn" data-action="toggle-like" data-type="album" title="Add to Liked">
|
||||
|
|
@ -323,16 +335,19 @@ export class UIRenderer {
|
|||
${SVG_PLAY}
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="card-title">${album.title} ${explicitBadge}</h4>
|
||||
<p class="card-subtitle">${album.artist?.name ?? ''}</p>
|
||||
<p class="card-subtitle">${yearDisplay}${typeLabel}</p>
|
||||
<div class="card-info">
|
||||
<h4 class="card-title">${album.title} ${explicitBadge}</h4>
|
||||
<p class="card-subtitle">${album.artist?.name ?? ''}</p>
|
||||
<p class="card-subtitle">${yearDisplay}${typeLabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
createArtistCardHTML(artist) {
|
||||
const isCompact = cardSettings.isCompactArtist();
|
||||
return `
|
||||
<div class="card artist" data-artist-id="${artist.id}" data-href="#artist/${artist.id}" style="cursor: pointer;">
|
||||
<div class="card artist ${isCompact ? 'compact' : ''}" data-artist-id="${artist.id}" data-href="#artist/${artist.id}" style="cursor: pointer;">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="${this.api.getArtistPictureUrl(artist.picture)}" alt="${artist.name}" class="card-image" loading="lazy">
|
||||
<button class="like-btn card-like-btn" data-action="toggle-like" data-type="artist" title="Add to Liked">
|
||||
|
|
|
|||
62
styles.css
62
styles.css
|
|
@ -635,7 +635,7 @@ body.has-page-background .track-item:hover {
|
|||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
|
|
@ -2512,7 +2512,7 @@ input:checked + .slider::before {
|
|||
}
|
||||
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
|
|
@ -2784,7 +2784,7 @@ input:checked + .slider::before {
|
|||
|
||||
@media (max-width: 480px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
|
|
@ -3130,6 +3130,62 @@ input:checked + .slider::before {
|
|||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
/* Compact Cards (Artist and Album) */
|
||||
.card.compact {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.card.compact .card-image-wrapper {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card.artist.compact .card-image-wrapper {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.card.compact .card-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card.compact .card-title {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card.compact .card-subtitle {
|
||||
margin: 0;
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
.card.compact .card-like-btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.card.compact:hover .card-like-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.side-panel {
|
||||
|
|
|
|||
Loading…
Reference in a new issue