86 lines
2.5 KiB
JavaScript
86 lines
2.5 KiB
JavaScript
// MusicRoom - Track Component
|
|
// Pure rendering for track rows - no event handlers attached
|
|
|
|
(function() {
|
|
const M = window.MusicRoom;
|
|
|
|
/**
|
|
* Render a track row element (pure rendering, no handlers)
|
|
* @param {Object} track - Track object with id, title, filename, duration
|
|
* @param {Object} config - Configuration options
|
|
* @param {string} config.view - 'queue' | 'library' | 'playlist'
|
|
* @param {number} config.index - Index in the list
|
|
* @param {number} [config.displayIndex] - Display number (1-based)
|
|
* @param {boolean} config.isSelected - Whether track is selected
|
|
* @param {boolean} config.isCached - Whether track is cached locally
|
|
* @param {boolean} config.isActive - Whether this is the currently playing track
|
|
* @param {boolean} config.draggable - Whether element is draggable
|
|
* @returns {HTMLElement}
|
|
*/
|
|
function render(track, config) {
|
|
const {
|
|
view,
|
|
index,
|
|
displayIndex,
|
|
isSelected,
|
|
isCached,
|
|
isActive,
|
|
draggable
|
|
} = config;
|
|
|
|
const div = document.createElement("div");
|
|
const trackId = track.id || track.filename;
|
|
|
|
// Build class list
|
|
const classes = ["track"];
|
|
if (isActive) classes.push("active");
|
|
if (isCached) classes.push("cached");
|
|
else classes.push("not-cached");
|
|
if (isSelected) classes.push("selected");
|
|
div.className = classes.join(" ");
|
|
|
|
// Store data attributes
|
|
div.dataset.index = index;
|
|
div.dataset.trackId = trackId;
|
|
div.dataset.view = view;
|
|
|
|
// Build title
|
|
const title = track.title?.trim() || (track.filename || trackId || "Unknown").replace(/\.[^.]+$/, "");
|
|
div.title = title;
|
|
|
|
// Build HTML
|
|
const checkmark = isSelected ? '<span class="track-checkmark">✓</span>' : '';
|
|
const trackNum = displayIndex != null ? `<span class="track-number">${displayIndex}.</span>` : '';
|
|
|
|
div.innerHTML = `
|
|
${checkmark}
|
|
<span class="cache-indicator"></span>
|
|
${trackNum}
|
|
<span class="track-title">${escapeHtml(title)}</span>
|
|
<span class="track-actions">
|
|
<span class="duration">${M.fmt(track.duration)}</span>
|
|
</span>
|
|
`;
|
|
|
|
if (draggable) {
|
|
div.draggable = true;
|
|
}
|
|
|
|
return div;
|
|
}
|
|
|
|
// HTML escape helper
|
|
function escapeHtml(str) {
|
|
if (!str) return '';
|
|
return str.replace(/[&<>"']/g, c => ({
|
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
|
})[c]);
|
|
}
|
|
|
|
// Export
|
|
M.trackComponent = {
|
|
render,
|
|
escapeHtml
|
|
};
|
|
})();
|