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>
|
<option value="inline">Inline Buttons</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//js/settings
|
//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 { db } from './db.js';
|
||||||
import { authManager } from './firebase/auth.js';
|
import { authManager } from './firebase/auth.js';
|
||||||
import { syncManager } from './firebase/sync.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
|
// Download Lyrics Toggle
|
||||||
const downloadLyricsToggle = document.getElementById('download-lyrics-toggle');
|
const downloadLyricsToggle = document.getElementById('download-lyrics-toggle');
|
||||||
if (downloadLyricsToggle) {
|
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 = {
|
export const queueManager = {
|
||||||
STORAGE_KEY: 'monochrome-queue',
|
STORAGE_KEY: 'monochrome-queue',
|
||||||
|
|
||||||
|
|
|
||||||
45
js/ui.js
45
js/ui.js
|
|
@ -1,7 +1,7 @@
|
||||||
//js/ui.js
|
//js/ui.js
|
||||||
import { SVG_PLAY, SVG_DOWNLOAD, SVG_MENU, SVG_HEART, formatTime, createPlaceholder, trackDataStore, hasExplicitContent, getTrackArtists, getTrackTitle, calculateTotalDuration, formatDuration } from './utils.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 { 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 { db } from './db.js';
|
||||||
import { getVibrantColorFromImage } from './vibrant-color.js';
|
import { getVibrantColorFromImage } from './vibrant-color.js';
|
||||||
|
|
||||||
|
|
@ -190,8 +190,9 @@ export class UIRenderer {
|
||||||
|
|
||||||
createPlaylistCardHTML(playlist) {
|
createPlaylistCardHTML(playlist) {
|
||||||
const imageId = playlist.squareImage || playlist.image || playlist.uuid; // Fallback or use a specific cover getter if needed
|
const imageId = playlist.squareImage || playlist.image || playlist.uuid; // Fallback or use a specific cover getter if needed
|
||||||
|
const isCompact = cardSettings.isCompactAlbum();
|
||||||
return `
|
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">
|
<div class="card-image-wrapper">
|
||||||
<img src="${this.api.getCoverUrl(imageId)}" alt="${playlist.title}" class="card-image" loading="lazy">
|
<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">
|
<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}
|
${SVG_PLAY}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">${playlist.title}</h4>
|
<div class="card-info">
|
||||||
<p class="card-subtitle">${playlist.numberOfTracks || 0} tracks</p>
|
<h4 class="card-title">${playlist.title}</h4>
|
||||||
|
<p class="card-subtitle">${playlist.numberOfTracks || 0} tracks</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
@ -210,9 +213,10 @@ export class UIRenderer {
|
||||||
createMixCardHTML(mix) {
|
createMixCardHTML(mix) {
|
||||||
const imageSrc = mix.cover || 'assets/appicon.png';
|
const imageSrc = mix.cover || 'assets/appicon.png';
|
||||||
const description = mix.subTitle || mix.description || '';
|
const description = mix.subTitle || mix.description || '';
|
||||||
|
const isCompact = cardSettings.isCompactAlbum();
|
||||||
|
|
||||||
return `
|
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">
|
<div class="card-image-wrapper">
|
||||||
<img src="${imageSrc}" alt="${mix.title}" class="card-image" loading="lazy">
|
<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">
|
<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}
|
${SVG_PLAY}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">${mix.title}</h4>
|
<div class="card-info">
|
||||||
<p class="card-subtitle">${description}</p>
|
<h4 class="card-title">${mix.title}</h4>
|
||||||
|
<p class="card-subtitle">${description}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
@ -266,8 +272,10 @@ export class UIRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isCompact = cardSettings.isCompactAlbum();
|
||||||
|
|
||||||
return `
|
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">
|
<div class="card-image-wrapper">
|
||||||
${imageHTML}
|
${imageHTML}
|
||||||
<button class="edit-playlist-btn" data-action="edit-playlist" title="Edit Playlist">
|
<button class="edit-playlist-btn" data-action="edit-playlist" title="Edit Playlist">
|
||||||
|
|
@ -289,8 +297,10 @@ export class UIRenderer {
|
||||||
${SVG_PLAY}
|
${SVG_PLAY}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">${playlist.name}</h4>
|
<div class="card-info">
|
||||||
<p class="card-subtitle">${playlist.tracks ? playlist.tracks.length : (playlist.numberOfTracks || 0)} tracks</p>
|
<h4 class="card-title">${playlist.name}</h4>
|
||||||
|
<p class="card-subtitle">${playlist.tracks ? playlist.tracks.length : (playlist.numberOfTracks || 0)} tracks</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
@ -312,8 +322,10 @@ export class UIRenderer {
|
||||||
typeLabel = ' • Single';
|
typeLabel = ' • Single';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isCompact = cardSettings.isCompactAlbum();
|
||||||
|
|
||||||
return `
|
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">
|
<div class="card-image-wrapper">
|
||||||
<img src="${this.api.getCoverUrl(album.cover)}" alt="${album.title}" class="card-image" loading="lazy">
|
<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">
|
<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}
|
${SVG_PLAY}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">${album.title} ${explicitBadge}</h4>
|
<div class="card-info">
|
||||||
<p class="card-subtitle">${album.artist?.name ?? ''}</p>
|
<h4 class="card-title">${album.title} ${explicitBadge}</h4>
|
||||||
<p class="card-subtitle">${yearDisplay}${typeLabel}</p>
|
<p class="card-subtitle">${album.artist?.name ?? ''}</p>
|
||||||
|
<p class="card-subtitle">${yearDisplay}${typeLabel}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
createArtistCardHTML(artist) {
|
createArtistCardHTML(artist) {
|
||||||
|
const isCompact = cardSettings.isCompactArtist();
|
||||||
return `
|
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">
|
<div class="card-image-wrapper">
|
||||||
<img src="${this.api.getArtistPictureUrl(artist.picture)}" alt="${artist.name}" class="card-image" loading="lazy">
|
<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">
|
<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 {
|
.card-grid {
|
||||||
display: 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);
|
gap: var(--spacing-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2512,7 +2512,7 @@ input:checked + .slider::before {
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-grid {
|
.card-grid {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2784,7 +2784,7 @@ input:checked + .slider::before {
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.card-grid {
|
.card-grid {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3130,6 +3130,62 @@ input:checked + .slider::before {
|
||||||
color: var(--muted-foreground);
|
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 */
|
/* Mobile adjustments */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.side-panel {
|
.side-panel {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue