refactor: simplify and standardize modal system
This commit is contained in:
parent
7516df9278
commit
13d5f07b6c
4 changed files with 303 additions and 264 deletions
131
index.html
131
index.html
|
|
@ -68,38 +68,115 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="playlist-modal" class="modal" style="display: none;">
|
||||
<div class="modal-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000;">
|
||||
<div class="modal-content" style="background: var(--card); padding: 2rem; border-radius: var(--radius); max-width: 400px; width: 90%;">
|
||||
<h3 id="playlist-modal-title">Create Playlist</h3>
|
||||
<input type="text" id="playlist-name-input" placeholder="Playlist name" style="width: 100%; margin: 1rem 0; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--background); color: var(--foreground);">
|
||||
<div id="csv-import-section" style="display: none; margin: 1rem 0; padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--background-secondary);">
|
||||
<p style="margin-bottom: 0.5rem; font-size: 0.9rem;">Import from CSV</p>
|
||||
<p style="font-size: 0.8rem; margin: 0;">Only Spotify Is Supported for now. please use <a href="https://exportify.app/" style="text-decoration: underline;">Exportify</a> to export your playlist into a csv. Also Make sure its headers are in english.</p>
|
||||
<br>
|
||||
<input type="file" id="csv-file-input" class="btn-secondary" accept=".csv" style="width: 100%; margin-bottom: 0.5rem;">
|
||||
<p style="font-size: 0.8rem; margin: 0;">This Feature Isnt Perfect And is Prone To Errors! Please check Your Playlist After To Remove Weird Songs That Were Added By The System.</p>
|
||||
</div>
|
||||
<div id="playlist-modal" class="modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content">
|
||||
<h3 id="playlist-modal-title">Create Playlist</h3>
|
||||
<input type="text" id="playlist-name-input" class="template-input" placeholder="Playlist name" style="margin: 1rem 0;">
|
||||
<div id="csv-import-section" style="display: none; margin: 1rem 0; padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--background-secondary);">
|
||||
<p style="margin-bottom: 0.5rem; font-size: 0.9rem;">Import from CSV</p>
|
||||
<p style="font-size: 0.8rem; margin: 0;">Only Spotify Is Supported for now. please use <a href="https://exportify.app/" style="text-decoration: underline;">Exportify</a> to export your playlist into a csv. Also Make sure its headers are in english.</p>
|
||||
<br>
|
||||
<input type="file" id="csv-file-input" class="btn-secondary" accept=".csv" style="width: 100%; margin-bottom: 0.5rem;">
|
||||
<p style="font-size: 0.8rem; margin: 0;">This Feature Isnt Perfect And is Prone To Errors! Please check Your Playlist After To Remove Weird Songs That Were Added By The System.</p>
|
||||
</div>
|
||||
|
||||
<div class="setting-item" style="margin-bottom: 1rem; padding: 0; border: none; background: transparent;">
|
||||
<div class="info">
|
||||
<span class="label">Public Playlist</span>
|
||||
<span class="description">Visible to anyone with the link</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="playlist-public-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<div class="setting-item" style="margin-bottom: 1rem; padding: 0; border: none; background: transparent;">
|
||||
<div class="info">
|
||||
<span class="label">Public Playlist</span>
|
||||
<span class="description">Visible to anyone with the link</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="playlist-public-toggle">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions" style="display: flex; gap: 0.5rem; justify-content: space-between; align-items: center;">
|
||||
<button id="playlist-share-btn" class="btn-secondary" style="display: none;">Share</button>
|
||||
<div style="display: flex; gap: 0.5rem; margin-left: auto;">
|
||||
<button id="playlist-modal-cancel" class="btn-secondary">Cancel</button>
|
||||
<button id="playlist-modal-save" class="btn-primary">Save</button>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button id="playlist-share-btn" class="btn-secondary" style="display: none;">Share</button>
|
||||
<button id="playlist-modal-cancel" class="btn-secondary">Cancel</button>
|
||||
<button id="playlist-modal-save" class="btn-primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="playlist-select-modal" class="modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content">
|
||||
<h3>Add to Playlist</h3>
|
||||
<div id="playlist-select-list" class="modal-list">
|
||||
<!-- Options will be injected here -->
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button id="playlist-select-cancel" class="btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shortcuts-modal" class="modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content medium">
|
||||
<div class="shortcuts-header">
|
||||
<h3>Keyboard Shortcuts</h3>
|
||||
<button class="close-shortcuts">×</button>
|
||||
</div>
|
||||
<div class="shortcuts-content">
|
||||
<div class="shortcut-item"><kbd>Space</kbd><span>Play / Pause</span></div>
|
||||
<div class="shortcut-item"><kbd>→</kbd><span>Seek forward 10s</span></div>
|
||||
<div class="shortcut-item"><kbd>←</kbd><span>Seek backward 10s</span></div>
|
||||
<div class="shortcut-item"><kbd>Shift</kbd> + <kbd>→</kbd><span>Next track</span></div>
|
||||
<div class="shortcut-item"><kbd>Shift</kbd> + <kbd>←</kbd><span>Previous track</span></div>
|
||||
<div class="shortcut-item"><kbd>↑</kbd><span>Volume up</span></div>
|
||||
<div class="shortcut-item"><kbd>↓</kbd><span>Volume down</span></div>
|
||||
<div class="shortcut-item"><kbd>M</kbd><span>Mute / Unmute</span></div>
|
||||
<div class="shortcut-item"><kbd>S</kbd><span>Toggle shuffle</span></div>
|
||||
<div class="shortcut-item"><kbd>R</kbd><span>Toggle repeat</span></div>
|
||||
<div class="shortcut-item"><kbd>Q</kbd><span>Open queue</span></div>
|
||||
<div class="shortcut-item"><kbd>L</kbd><span>Toggle lyrics</span></div>
|
||||
<div class="shortcut-item"><kbd>/</kbd><span>Focus search</span></div>
|
||||
<div class="shortcut-item"><kbd>Esc</kbd><span>Close modals</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="missing-tracks-modal" class="modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content wide">
|
||||
<div class="missing-tracks-header">
|
||||
<h3>Note</h3>
|
||||
<button class="close-missing-tracks">×</button>
|
||||
</div>
|
||||
<div class="missing-tracks-content">
|
||||
<p>Unfortunately some songs weren't able to be added. This could be an issue with our import system, try searching for the song and adding it. But it could also be due to Monochrome not having it sadly :(</p>
|
||||
<div class="missing-tracks-list">
|
||||
<h4>Missing Tracks:</h4>
|
||||
<ul id="missing-tracks-list-ul"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="missing-tracks-actions">
|
||||
<button class="btn-secondary" id="close-missing-tracks-btn">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sleep-timer-modal" class="modal">
|
||||
<div class="modal-overlay"></div>
|
||||
<div class="modal-content" style="max-width: 300px;">
|
||||
<h3 style="text-align: center; margin-bottom: 1.5rem;">Sleep Timer</h3>
|
||||
<div class="timer-options">
|
||||
<button class="timer-option btn-secondary" data-minutes="5">5 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="15">15 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="30">30 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="60">1 hour</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="120">2 hours</button>
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
<input type="number" id="custom-minutes" class="template-input" placeholder="Custom" min="1" max="480">
|
||||
<button class="timer-option btn-primary" id="custom-timer-btn" style="padding: 0.5rem 1rem;">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions" style="justify-content: center;">
|
||||
<button id="cancel-sleep-timer" class="btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
157
js/app.js
157
js/app.js
|
|
@ -420,7 +420,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
if (publicToggle) publicToggle.checked = false;
|
||||
if (shareBtn) shareBtn.style.display = 'none';
|
||||
|
||||
modal.style.display = 'flex';
|
||||
modal.classList.add('active');
|
||||
document.getElementById('playlist-name-input').focus();
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +464,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
if (window.location.hash === `#userplaylist/${editingId}`) {
|
||||
ui.renderPlaylistPage(editingId, 'user');
|
||||
}
|
||||
modal.style.display = 'none';
|
||||
modal.classList.remove('active');
|
||||
delete modal.dataset.editingId;
|
||||
}
|
||||
});
|
||||
|
|
@ -536,14 +536,14 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
await db.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
||||
syncManager.syncUserPlaylist(playlist, 'create');
|
||||
ui.renderLibraryPage();
|
||||
modal.style.display = 'none';
|
||||
modal.classList.remove('active');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.target.closest('#playlist-modal-cancel')) {
|
||||
document.getElementById('playlist-modal').style.display = 'none';
|
||||
document.getElementById('playlist-modal').classList.remove('active');
|
||||
}
|
||||
|
||||
if (e.target.closest('.edit-playlist-btn')) {
|
||||
|
|
@ -573,7 +573,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
|
||||
modal.dataset.editingId = playlistId;
|
||||
document.getElementById('csv-import-section').style.display = 'none';
|
||||
modal.style.display = 'flex';
|
||||
modal.classList.add('active');
|
||||
document.getElementById('playlist-name-input').focus();
|
||||
}
|
||||
});
|
||||
|
|
@ -612,7 +612,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
|
||||
modal.dataset.editingId = playlistId;
|
||||
document.getElementById('csv-import-section').style.display = 'none';
|
||||
modal.style.display = 'flex';
|
||||
modal.classList.add('active');
|
||||
document.getElementById('playlist-name-input').focus();
|
||||
}
|
||||
});
|
||||
|
|
@ -969,37 +969,24 @@ function showInstallPrompt(deferredPrompt) {
|
|||
}
|
||||
|
||||
function showMissingTracksNotification(missingTracks) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'missing-tracks-modal-overlay';
|
||||
modal.innerHTML = `
|
||||
<div class="missing-tracks-modal">
|
||||
<div class="missing-tracks-header">
|
||||
<h3>Note</h3>
|
||||
<button class="close-missing-tracks">×</button>
|
||||
</div>
|
||||
<div class="missing-tracks-content">
|
||||
<p>Unfortunately some songs weren't able to be added. This could be an issue with our import system, try searching for the song and adding it. But it could also be due to Monochrome not having it sadly :(</p>
|
||||
<div class="missing-tracks-list">
|
||||
<h4>Missing Tracks:</h4>
|
||||
<ul>
|
||||
${missingTracks.map(track => `<li>${track}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="missing-tracks-actions">
|
||||
<button class="btn-secondary" id="close-missing-tracks-btn">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
const modal = document.getElementById('missing-tracks-modal');
|
||||
const listUl = document.getElementById('missing-tracks-list-ul');
|
||||
|
||||
listUl.innerHTML = missingTracks.map(track => `<li>${track}</li>`).join('');
|
||||
|
||||
const closeModal = () => modal.classList.remove('active');
|
||||
|
||||
const closeModal = () => modal.remove();
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal || e.target.classList.contains('close-missing-tracks') || e.target.id === 'close-missing-tracks-btn') {
|
||||
// Remove old listeners if any (though usually these functions are called once per instance,
|
||||
// but since we reuse the same modal element we should be careful or use a one-time listener)
|
||||
const handleClose = (e) => {
|
||||
if (e.target === modal || e.target.closest('.close-missing-tracks') || e.target.id === 'close-missing-tracks-btn' || e.target.classList.contains('modal-overlay')) {
|
||||
closeModal();
|
||||
modal.removeEventListener('click', handleClose);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
modal.addEventListener('click', handleClose);
|
||||
modal.classList.add('active');
|
||||
}
|
||||
|
||||
async function parseCSV(csvText, api, onProgress) {
|
||||
|
|
@ -1112,80 +1099,36 @@ async function parseCSV(csvText, api, onProgress) {
|
|||
}
|
||||
|
||||
function showKeyboardShortcuts() {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'shortcuts-modal-overlay';
|
||||
modal.innerHTML = `
|
||||
<div class="shortcuts-modal">
|
||||
<div class="shortcuts-header">
|
||||
<h3>Keyboard Shortcuts</h3>
|
||||
<button class="close-shortcuts">×</button>
|
||||
</div>
|
||||
<div class="shortcuts-content">
|
||||
<div class="shortcut-item">
|
||||
<kbd>Space</kbd>
|
||||
<span>Play / Pause</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>→</kbd>
|
||||
<span>Seek forward 10s</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>←</kbd>
|
||||
<span>Seek backward 10s</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>Shift</kbd> + <kbd>→</kbd>
|
||||
<span>Next track</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>Shift</kbd> + <kbd>←</kbd>
|
||||
<span>Previous track</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>↑</kbd>
|
||||
<span>Volume up</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>↓</kbd>
|
||||
<span>Volume down</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>M</kbd>
|
||||
<span>Mute / Unmute</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>S</kbd>
|
||||
<span>Toggle shuffle</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>R</kbd>
|
||||
<span>Toggle repeat</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>Q</kbd>
|
||||
<span>Open queue</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>L</kbd>
|
||||
<span>Toggle lyrics</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>/</kbd>
|
||||
<span>Focus search</span>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<kbd>Esc</kbd>
|
||||
<span>Close modals</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal || e.target.classList.contains('close-shortcuts')) {
|
||||
modal.remove();
|
||||
const modal = document.getElementById('shortcuts-modal');
|
||||
|
||||
|
||||
|
||||
const closeModal = () => {
|
||||
|
||||
modal.classList.remove('active');
|
||||
|
||||
modal.removeEventListener('click', handleClose);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleClose = (e) => {
|
||||
|
||||
if (e.target === modal || e.target.classList.contains('close-shortcuts') || e.target.classList.contains('modal-overlay')) {
|
||||
|
||||
closeModal();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
modal.addEventListener('click', handleClose);
|
||||
|
||||
modal.classList.add('active');
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
131
js/events.js
131
js/events.js
|
|
@ -615,39 +615,43 @@ export async function handleTrackAction(action, item, player, api, lyricsManager
|
|||
return;
|
||||
}
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'playlist-select-modal';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; display: flex; align-items: center; justify-content: center;">
|
||||
<div class="modal-content" style="background: var(--card); padding: 2rem; border-radius: var(--radius); max-width: 400px; width: 90%;">
|
||||
<h3>Add to Playlist</h3>
|
||||
<div id="playlist-list" style="margin: 1rem 0; max-height: 200px; overflow-y: auto;">
|
||||
${playlists.map(p => `<div class="playlist-option" data-id="${p.id}" style="padding: 0.5rem; cursor: pointer; border-bottom: 1px solid var(--border);">${p.name}</div>`).join('')}
|
||||
</div>
|
||||
<div class="modal-actions" style="display: flex; gap: 0.5rem; justify-content: flex-end;">
|
||||
<button id="cancel-add-playlist" class="btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
const modal = document.getElementById('playlist-select-modal');
|
||||
const list = document.getElementById('playlist-select-list');
|
||||
const cancelBtn = document.getElementById('playlist-select-cancel');
|
||||
const overlay = modal.querySelector('.modal-overlay');
|
||||
|
||||
modal.addEventListener('click', async (e) => {
|
||||
if (e.target.id === 'cancel-add-playlist') {
|
||||
modal.remove();
|
||||
return;
|
||||
}
|
||||
list.innerHTML = playlists.map(p => `
|
||||
<div class="modal-option" data-id="${p.id}">${p.name}</div>
|
||||
`).join('');
|
||||
|
||||
const option = e.target.closest('.playlist-option');
|
||||
const closeModal = () => {
|
||||
modal.classList.remove('active');
|
||||
cleanup();
|
||||
};
|
||||
|
||||
const handleOptionClick = async (e) => {
|
||||
const option = e.target.closest('.modal-option');
|
||||
if (option) {
|
||||
const playlistId = option.dataset.id;
|
||||
await db.addTrackToPlaylist(playlistId, item);
|
||||
const updatedPlaylist = await db.getPlaylist(playlistId);
|
||||
syncManager.syncUserPlaylist(updatedPlaylist, 'update');
|
||||
showNotification(`Added to playlist: ${option.textContent}`);
|
||||
modal.remove();
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
cancelBtn.removeEventListener('click', closeModal);
|
||||
overlay.removeEventListener('click', closeModal);
|
||||
list.removeEventListener('click', handleOptionClick);
|
||||
};
|
||||
|
||||
cancelBtn.addEventListener('click', closeModal);
|
||||
overlay.addEventListener('click', closeModal);
|
||||
list.addEventListener('click', handleOptionClick);
|
||||
|
||||
modal.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -900,61 +904,52 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen
|
|||
}
|
||||
|
||||
function showSleepTimerModal(player) {
|
||||
if (document.querySelector('.sleep-timer-modal')) return;
|
||||
const modal = document.getElementById('sleep-timer-modal');
|
||||
if (!modal) return;
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'sleep-timer-modal';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; display: flex; align-items: center; justify-content: center;">
|
||||
<div class="modal-content" style="background: var(--card); padding: 2rem; border-radius: var(--radius); max-width: 300px; width: 90%;">
|
||||
<h3 style="text-align: center; margin-bottom: 1.5rem;">Sleep Timer</h3>
|
||||
<div class="timer-options" style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<button class="timer-option btn-secondary" data-minutes="5">5 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="15">15 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="30">30 minutes</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="60">1 hour</button>
|
||||
<button class="timer-option btn-secondary" data-minutes="120">2 hours</button>
|
||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
|
||||
<input type="number" id="custom-minutes" placeholder="Custom" min="1" max="480" style="flex: 1; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--background); color: var(--foreground);">
|
||||
<button class="timer-option btn-primary" id="custom-timer-btn" style="padding: 0.5rem 1rem;">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions" style="display: flex; gap: 0.5rem; justify-content: center; margin-top: 1.5rem;">
|
||||
<button id="cancel-sleep-timer" class="btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target.id === 'cancel-sleep-timer' || e.target.classList.contains('modal-overlay')) {
|
||||
modal.remove();
|
||||
return;
|
||||
}
|
||||
const closeModal = () => {
|
||||
modal.classList.remove('active');
|
||||
cleanup();
|
||||
};
|
||||
|
||||
const handleOptionClick = (e) => {
|
||||
const timerOption = e.target.closest('.timer-option');
|
||||
if (timerOption) {
|
||||
const minutes = parseInt(timerOption.dataset.minutes);
|
||||
let minutes;
|
||||
if (timerOption.id === 'custom-timer-btn') {
|
||||
const customInput = document.getElementById('custom-minutes');
|
||||
minutes = parseInt(customInput.value);
|
||||
if (!minutes || minutes < 1) {
|
||||
showNotification('Please enter a valid number of minutes');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
minutes = parseInt(timerOption.dataset.minutes);
|
||||
}
|
||||
|
||||
if (minutes) {
|
||||
player.setSleepTimer(minutes);
|
||||
showNotification(`Sleep timer set for ${minutes} minute${minutes === 1 ? '' : 's'}`);
|
||||
modal.remove();
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (e.target.id === 'custom-timer-btn') {
|
||||
const customInput = document.getElementById('custom-minutes');
|
||||
const minutes = parseInt(customInput.value);
|
||||
if (minutes && minutes > 0 && minutes <= 480) {
|
||||
player.setSleepTimer(minutes);
|
||||
showNotification(`Sleep timer set for ${minutes} minute${minutes === 1 ? '' : 's'}`);
|
||||
modal.remove();
|
||||
} else {
|
||||
showNotification('Please enter a valid number of minutes (1-480)');
|
||||
}
|
||||
const handleCancel = (e) => {
|
||||
if (e.target.id === 'cancel-sleep-timer' || e.target.classList.contains('modal-overlay')) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
modal.removeEventListener('click', handleOptionClick);
|
||||
modal.removeEventListener('click', handleCancel);
|
||||
};
|
||||
|
||||
modal.addEventListener('click', handleOptionClick);
|
||||
modal.addEventListener('click', handleCancel);
|
||||
|
||||
modal.classList.add('active');
|
||||
}
|
||||
|
||||
function positionMenu(menu, x, y, anchorRect = null) {
|
||||
|
|
|
|||
148
styles.css
148
styles.css
|
|
@ -2408,41 +2408,6 @@ input:checked + .slider::before {
|
|||
align-items: stretch;
|
||||
}
|
||||
|
||||
.shortcuts-modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
.shortcuts-modal {
|
||||
background: var(--card);
|
||||
border-radius: var(--radius);
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--shadow-xl);
|
||||
animation: scaleIn 0.2s ease;
|
||||
}
|
||||
|
||||
.shortcuts-header {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shortcuts-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.close-shortcuts {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
|
@ -2943,6 +2908,92 @@ img:not([src]), img[src=''] {
|
|||
background: var(--primary);
|
||||
}
|
||||
|
||||
/* Modals */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(4px);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--card);
|
||||
padding: 2rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
box-shadow: var(--shadow-xl);
|
||||
animation: scaleIn 0.2s ease;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-content.wide {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.modal-content.medium {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.modal-list {
|
||||
margin: 1rem 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-option {
|
||||
padding: 0.75rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-option:hover {
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
.modal-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.timer-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.timer-option {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
#playlist-modal {
|
||||
opacity: 1;
|
||||
animation-name: fadeInOpacity;
|
||||
|
|
@ -3058,33 +3109,6 @@ img:not([src]), img[src=''] {
|
|||
|
||||
|
||||
|
||||
/* OH NO SOME SONGS WERENT FOUND FUCK ME FUCK ME */
|
||||
.missing-tracks-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
.missing-tracks-modal {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--shadow-xl);
|
||||
animation: scaleIn 0.2s ease;
|
||||
}
|
||||
|
||||
.missing-tracks-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
|||
Loading…
Reference in a new issue