Add search box to track pages

This commit is contained in:
2025-06-22 05:22:54 -04:00
parent 8b66682f8d
commit 3cbd2a7c6a

View File

@ -18,52 +18,120 @@
.card { .card {
background-color: #222; background-color: #222;
border: 1px solid #444; border: 1px solid #444;
border-radius: 8px;
overflow: hidden;
}
.card-body {
padding: 1.5rem;
} }
.btn-primary { .btn-primary {
background-color: #ff5500; background-color: #ff5500;
border-color: #ff5500; border-color: #ff5500;
transition: background-color 0.3s ease;
} }
.btn-primary:hover { .btn-primary:hover {
background-color: #ff3300; background-color: #ff3300;
} }
/* Custom slim dark mode scrollbar for webkit browsers (Chrome, Safari, Edge) */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
/* Slim width for the scrollbar */
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: #1e1e1e; background: #1e1e1e;
/* Dark background for the scrollbar track */
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background-color: #4a4a4a; background-color: #4a4a4a;
/* Slightly lighter thumb color */
border-radius: 10px; border-radius: 10px;
/* Rounded corners for the scrollbar */
border: 2px solid #1e1e1e; border: 2px solid #1e1e1e;
/* Matches the track background to create a gap effect */
} }
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background-color: #555555; background-color: #555555;
/* Lighter on hover */
} }
/* Scrollbar styling for Firefox */
* { * {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: #4a4a4a #1e1e1e; scrollbar-color: #4a4a4a #1e1e1e;
} }
/* Search box styling */
.search-container {
position: relative;
width: 100%;
max-width: 500px;
margin: 0 auto 20px;
}
.search-input {
background-color: #222;
border: 1px solid #444;
color: white;
border-radius: 4px;
padding: 8px 12px;
width: 100%;
transition: border-color 0.3s ease;
}
.search-input:focus {
border-color: #ff5500;
outline: none;
box-shadow: 0 0 5px rgba(255, 85, 0, 0.3);
}
.search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: #222;
border: 1px solid #444;
border-radius: 4px;
max-height: 300px;
overflow-y: auto;
z-index: 1000;
display: none;
margin-top: 5px;
}
.search-results.show {
display: block;
}
.search-result-item {
padding: 10px;
color: white;
cursor: pointer;
transition: background-color 0.2s ease;
}
.search-result-item:hover {
background-color: #ff5500;
}
.search-result-item small {
color: #aaa;
display: block;
}
/* Adjust container padding */
.container {
padding-top: 30px;
padding-bottom: 30px;
}
</style> </style>
</head> </head>
<body> <body>
<div class="container mt-5"> <div class="container mt-5">
<div class="search-container">
<input type="text" class="search-input" placeholder="Search tracks or lyrics..." aria-label="Search tracks">
<div class="search-results" id="search-results"></div>
</div>
<div class="card text-center"> <div class="card text-center">
<div class="card-body"> <div class="card-body">
<h1 class="card-title"> <h1 class="card-title">
@ -75,12 +143,107 @@
<p class="card-text"> <p class="card-text">
<%- (track.description && track.description.trim()) ? track.description.replace(/\n/g, '<br>' ) <%- (track.description && track.description.trim()) ? track.description.replace(/\n/g, '<br>' )
: 'No description available for this track.' %> : 'No description available for this track.' %>
<BR> <br>
<a href="/" class="btn btn-primary mt-3">Back</a> <a href="/" class="btn btn-primary mt-3">Back</a>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
// Search functionality
const searchInput = document.querySelector('.search-input');
const searchResults = document.getElementById('search-results');
const searchContainer = document.querySelector('.search-container');
let allTracks = [];
const genres = ['metal', 'altrock', 'rap', 'lofi', 'edm', 'cuts'];
// Fetch all tracks from all genres
async function fetchAllTracks() {
try {
const fetchPromises = genres.map(genre =>
fetch(`/json/${genre}`)
.then(res => res.json())
.then(tracks => tracks.map(track => ({ ...track, genre })))
);
const tracksArrays = await Promise.all(fetchPromises);
allTracks = tracksArrays.flat();
console.log(`Fetched ${allTracks.length} tracks across all genres`);
} catch (err) {
console.error('Error fetching tracks:', err);
}
}
// Initialize tracks on page load
fetchAllTracks();
// Debounce function to limit search frequency
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Handle search
const performSearch = debounce(() => {
const query = searchInput.value.trim().toLowerCase();
searchResults.innerHTML = '';
if (query.length < 2) {
searchResults.classList.remove('show');
return;
}
const filteredTracks = allTracks.filter(track =>
track.title.toLowerCase().includes(query) ||
(track.description && track.description.toLowerCase().includes(query))
);
if (filteredTracks.length === 0) {
searchResults.innerHTML = '<div class="search-result-item">No results found</div>';
} else {
filteredTracks.forEach(track => {
const resultItem = document.createElement('div');
resultItem.className = 'search-result-item';
resultItem.innerHTML = `
${track.title}
<small>${track.genre.charAt(0).toUpperCase() + track.genre.slice(1)}</small>
`;
resultItem.addEventListener('click', () => {
window.location.href = `/${track.genre}/track/${track.slug}`;
});
searchResults.appendChild(resultItem);
});
}
searchResults.classList.add('show');
}, 300);
// Search input event listener
searchInput.addEventListener('input', performSearch);
// Hide search results when clicking outside
document.addEventListener('click', (e) => {
if (!searchContainer.contains(e.target)) {
searchResults.classList.remove('show');
}
});
// Show search results when clicking input
searchInput.addEventListener('focus', () => {
if (searchInput.value.trim().length >= 2) {
performSearch();
}
});
</script>
</body> </body>
</html> </html>