Enhance album cards with improved layout, Wikipedia links, and mobile optimization
- Redesigned album card layout with grid-based header (rank + details) - Added Wikipedia links for all albums with accurate URL mappings - Improved description formatting with proper paragraphs and typography - Made stats cards clickable filters that maintain static numbers - Significantly increased album art size on mobile devices (280px/240px vs 120px) - Enhanced visual feedback for interactive elements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
88a6434132
commit
ed8ad3c02a
3 changed files with 724 additions and 29 deletions
148
script.js
148
script.js
|
|
@ -4,6 +4,7 @@ let filteredData = [];
|
|||
let currentPage = 1;
|
||||
const itemsPerPage = 50;
|
||||
let isReversed = false;
|
||||
let wikipediaUrlMappings = {};
|
||||
|
||||
// DOM elements
|
||||
const albumsGrid = document.getElementById('albumsGrid');
|
||||
|
|
@ -19,11 +20,24 @@ const stats = document.getElementById('stats');
|
|||
|
||||
// Initialize the application
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadWikipediaMapping();
|
||||
loadAlbumsData();
|
||||
setupEventListeners();
|
||||
handleUrlParams();
|
||||
});
|
||||
|
||||
// Load Wikipedia URL mappings
|
||||
async function loadWikipediaMapping() {
|
||||
try {
|
||||
const response = await fetch('wikipedia_urls_mapping.json');
|
||||
if (response.ok) {
|
||||
wikipediaUrlMappings = await response.json();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Could not load Wikipedia URL mappings:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
function setupEventListeners() {
|
||||
searchInput.addEventListener('input', debounce(handleSearch, 300));
|
||||
|
|
@ -35,6 +49,53 @@ function setupEventListeners() {
|
|||
|
||||
// Add scroll listener for infinite scroll
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
// Add click listeners to stats cards
|
||||
setupStatsClickHandlers();
|
||||
}
|
||||
|
||||
// Setup click handlers for stats cards
|
||||
function setupStatsClickHandlers() {
|
||||
const totalAlbumsCard = document.querySelector('.stat-item:has(#totalAlbums)') ||
|
||||
document.querySelector('#totalAlbums').closest('.stat-item');
|
||||
const newAlbumsCard = document.querySelector('.stat-item:has(#newAlbums)') ||
|
||||
document.querySelector('#newAlbums').closest('.stat-item');
|
||||
const improvedAlbumsCard = document.querySelector('.stat-item:has(#improvedAlbums)') ||
|
||||
document.querySelector('#improvedAlbums').closest('.stat-item');
|
||||
const droppedAlbumsCard = document.querySelector('.stat-item:has(#droppedAlbums)') ||
|
||||
document.querySelector('#droppedAlbums').closest('.stat-item');
|
||||
|
||||
if (totalAlbumsCard) {
|
||||
totalAlbumsCard.addEventListener('click', () => handleStatsCardClick(''));
|
||||
totalAlbumsCard.style.cursor = 'pointer';
|
||||
}
|
||||
|
||||
if (newAlbumsCard) {
|
||||
newAlbumsCard.addEventListener('click', () => handleStatsCardClick('New in 2023'));
|
||||
newAlbumsCard.style.cursor = 'pointer';
|
||||
}
|
||||
|
||||
if (improvedAlbumsCard) {
|
||||
improvedAlbumsCard.addEventListener('click', () => handleStatsCardClick('improved'));
|
||||
improvedAlbumsCard.style.cursor = 'pointer';
|
||||
}
|
||||
|
||||
if (droppedAlbumsCard) {
|
||||
droppedAlbumsCard.addEventListener('click', () => handleStatsCardClick('dropped'));
|
||||
droppedAlbumsCard.style.cursor = 'pointer';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle stats card clicks
|
||||
function handleStatsCardClick(filterValue) {
|
||||
// Update the status filter dropdown
|
||||
statusFilter.value = filterValue;
|
||||
|
||||
// Trigger the filter change
|
||||
statusFilter.dispatchEvent(new Event('change'));
|
||||
|
||||
// Scroll to the albums grid
|
||||
albumsGrid.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// Load albums data from CSV
|
||||
|
|
@ -51,7 +112,7 @@ async function loadAlbumsData() {
|
|||
|
||||
hideLoading();
|
||||
renderAlbums();
|
||||
updateStats();
|
||||
initializeStats();
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error loading data:', err);
|
||||
|
|
@ -155,13 +216,13 @@ function createAlbumCard(album) {
|
|||
const coverImagePath = getCoverImagePath(album);
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="album-rank">#${album.Rank}</div>
|
||||
<div class="album-content">
|
||||
<div class="album-title">${escapeHtml(album.Album)}</div>
|
||||
<div class="album-artist">${escapeHtml(album.Artist)}</div>
|
||||
${album.Info ? `<div class="album-info">${escapeHtml(album.Info)}</div>` : ''}
|
||||
<div class="album-status ${statusClass}">${statusText}</div>
|
||||
${album.Description ? `<div class="album-description">${escapeHtml(album.Description)}</div>` : ''}
|
||||
<div class="album-header-grid">
|
||||
<div class="album-rank">#${album.Rank}</div>
|
||||
<div class="album-details">
|
||||
<div class="album-title">${escapeHtml(album.Album)}</div>
|
||||
<div class="album-artist">${escapeHtml(album.Artist)}</div>
|
||||
${album.Info ? `<div class="album-info">${escapeHtml(album.Info)}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="album-cover">
|
||||
${coverImagePath ?
|
||||
|
|
@ -170,6 +231,13 @@ function createAlbumCard(album) {
|
|||
`<div class="album-cover-icon">🎵</div>`
|
||||
}
|
||||
</div>
|
||||
<div class="album-status ${statusClass}">${statusText}</div>
|
||||
${album.Description ? `<div class="album-description">${formatDescription(album.Description)}</div>` : ''}
|
||||
<div class="album-links">
|
||||
<a href="${generateWikipediaUrl(album.Album, album.Artist)}" target="_blank" rel="noopener noreferrer" class="wikipedia-link">
|
||||
View on Wikipedia →
|
||||
</a>
|
||||
</div>
|
||||
<button class="album-share" title="Share this album" data-rank="${album.Rank}">🔗</button>
|
||||
`;
|
||||
|
||||
|
|
@ -315,11 +383,12 @@ function handleScroll() {
|
|||
}
|
||||
|
||||
// Update statistics
|
||||
function updateStats() {
|
||||
const total = filteredData.length;
|
||||
const newAlbums = filteredData.filter(album => album.Status === 'New in 2023').length;
|
||||
const improved = filteredData.filter(album => album.Status.startsWith('+')).length;
|
||||
const dropped = filteredData.filter(album => album.Status.startsWith('-') || album.Status.startsWith('Dropped')).length;
|
||||
// Initialize stats with static values from the full dataset (called only once)
|
||||
function initializeStats() {
|
||||
const total = albumsData.length;
|
||||
const newAlbums = albumsData.filter(album => album.Status === 'New in 2023').length;
|
||||
const improved = albumsData.filter(album => album.Status.startsWith('+')).length;
|
||||
const dropped = albumsData.filter(album => album.Status.startsWith('-') || album.Status.startsWith('Dropped')).length;
|
||||
|
||||
document.getElementById('totalAlbums').textContent = total;
|
||||
document.getElementById('newAlbums').textContent = newAlbums;
|
||||
|
|
@ -327,6 +396,11 @@ function updateStats() {
|
|||
document.getElementById('droppedAlbums').textContent = dropped;
|
||||
}
|
||||
|
||||
// Update stats function (now empty but kept for compatibility)
|
||||
function updateStats() {
|
||||
// Stats remain static and don't update when filtering
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
|
|
@ -347,6 +421,54 @@ function escapeHtml(text) {
|
|||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatDescription(text) {
|
||||
if (!text) return '';
|
||||
|
||||
// Split by double line breaks or periods followed by capital letters
|
||||
const paragraphs = text
|
||||
.split(/\n\n|\. (?=[A-Z])/)
|
||||
.map(p => p.trim())
|
||||
.filter(p => p.length > 0)
|
||||
.map(p => p.endsWith('.') ? p : p + '.');
|
||||
|
||||
// If we only have one paragraph, try to split by sentences for better formatting
|
||||
if (paragraphs.length === 1 && text.length > 200) {
|
||||
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
|
||||
if (sentences.length > 3) {
|
||||
// Group sentences into 2-3 paragraphs
|
||||
const third = Math.ceil(sentences.length / 3);
|
||||
return [
|
||||
sentences.slice(0, third).join(' '),
|
||||
sentences.slice(third, third * 2).join(' '),
|
||||
sentences.slice(third * 2).join(' ')
|
||||
].filter(p => p.trim()).map(p => `<p>${escapeHtml(p.trim())}</p>`).join('');
|
||||
}
|
||||
}
|
||||
|
||||
return paragraphs.map(p => `<p>${escapeHtml(p)}</p>`).join('');
|
||||
}
|
||||
|
||||
function generateWikipediaUrl(album, artist) {
|
||||
// Clean up album and artist names
|
||||
const albumName = album.trim();
|
||||
const artistName = artist.trim();
|
||||
|
||||
// Check if we have the exact Wikipedia URL from our mapping
|
||||
if (wikipediaUrlMappings[albumName]) {
|
||||
return `https://en.wikipedia.org/wiki/${wikipediaUrlMappings[albumName]}`;
|
||||
}
|
||||
|
||||
// Remove "The" from the beginning of artist names for URL formatting
|
||||
const artistForUrl = artistName.replace(/^The /, '');
|
||||
|
||||
// Format for Wikipedia URL - replace spaces with underscores
|
||||
const albumUrl = albumName.replace(/ /g, '_');
|
||||
const artistUrl = artistForUrl.replace(/ /g, '_');
|
||||
|
||||
// Default pattern: Album_Name_(Artist_Name_album)
|
||||
return `https://en.wikipedia.org/wiki/${encodeURIComponent(albumUrl)}_(${encodeURIComponent(artistUrl)}_album)`;
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
loading.style.display = 'none';
|
||||
albumsGrid.style.display = 'grid';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue