Files

95 lines
7.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export function initCockpit({ storage, toast }) {
const $ = s => document.querySelector(s), audio = $('#audio');
const dedicated = new URLSearchParams(location.search).get('display') === 'drive';
let collapsed = dedicated || storage.get('railCollapsed') === 'true';
function rail() {
document.body.classList.toggle('rail-collapsed', collapsed);
$('#rail-toggle').setAttribute('aria-expanded', String(!collapsed));
$('#rail-toggle').setAttribute('aria-label', collapsed ? 'Afficher les favoris' : 'Masquer les favoris');
document.dispatchEvent(new Event('caria-layout'));
}
$('#rail-toggle').onclick = () => { collapsed = !collapsed; storage.set('railCollapsed', String(collapsed)); rail(); };
rail();
if (dedicated && !location.hash) location.hash = 'drive';
const time = seconds => Number.isFinite(seconds) ? `${Math.floor(seconds / 60)}:${String(Math.floor(seconds % 60)).padStart(2,'0')}` : '0:00';
let previousPaused, previousSilent;
function updatePlayer() {
const duration = Number.isFinite(audio.duration) ? audio.duration : 0, progress = duration ? audio.currentTime / duration : 0;
$('#track-arc').style.strokeDashoffset = String(100 * (1 - progress));
$('#immersive-current').textContent = time(audio.currentTime); $('#immersive-duration').textContent = time(duration);
$('#immersive-title').textContent = $('#track-title').textContent;
$('#immersive-subtitle').textContent = $('#track-subtitle').textContent;
if (previousPaused !== audio.paused) $('#immersive-play').innerHTML = audio.paused ? '<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m9 5 11 7-11 7Z"/></svg>' : '<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5v14M16 5v14"/></svg>';
$('#immersive-play').setAttribute('aria-label', audio.paused ? 'Lire la musique' : 'Mettre en pause');
$('#now-playing').classList.toggle('playing', !audio.paused);
$('#immersive-volume').value = audio.muted ? '0' : String(audio.volume);
const silent = audio.muted || audio.volume === 0;
if (previousSilent !== silent) $('#immersive-mute').innerHTML = silent ? '<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4 6 8H3v8h3l5 4Zm5 5 6 6m0-6-6 6"/></svg>' : '<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4 6 8H3v8h3l5 4Zm4 4a6 6 0 0 1 0 8m3-11a10 10 0 0 1 0 14"/></svg>';
$('#immersive-mute').setAttribute('aria-label', silent ? 'Activer le son' : 'Couper le son');
$('#immersive-mute').title = silent ? 'Activer le son' : 'Couper le son';
$('#immersive-mute').setAttribute('aria-pressed', String(silent));
$('#immersive-volume-value').textContent = Math.round((silent ? 0 : audio.volume) * 100) + ' %';
previousPaused = audio.paused; previousSilent = silent;
document.querySelectorAll('[data-audio-control]').forEach(button => button.disabled = !audio.getAttribute('src'));
}
$('#immersive-play').onclick = () => $('#play').click();
$('#immersive-previous').onclick = () => $('#previous').click();
$('#immersive-next').onclick = () => $('#next').click();
$('#immersive-volume').oninput = e => { audio.volume = Number(e.target.value); audio.muted = false; };
$('#immersive-mute').onclick = () => { if (audio.volume === 0) { audio.volume = .5; audio.muted = false; } else audio.muted = !audio.muted; };
$('#immersive-repeat').onclick = () => { audio.loop = !audio.loop; $('#immersive-repeat').setAttribute('aria-pressed', String(audio.loop)); toast(audio.loop ? 'Répétition du morceau activée' : 'Répétition désactivée'); };
for (const event of ['timeupdate','loadedmetadata','durationchange','play','pause','volumechange','emptied','ended']) audio.addEventListener(event,updatePlayer);
new MutationObserver(updatePlayer).observe($('#track-title'),{childList:true,subtree:true});
audio.addEventListener('error', () => $('#immersive-subtitle').textContent = 'Lecture impossible. Choisissez un autre morceau dans la bibliothèque.');
updatePlayer();
// Keep lengthy setup panels reachable with large page buttons.
for (const panel of document.querySelectorAll('.journey-panel')) {
const content = document.createElement('div'), nav = document.createElement('nav');
content.className = 'panel-content'; nav.className = 'collection-pages panel-pages';
content.append(...panel.childNodes); panel.append(content,nav);
const back = document.createElement('button'), next = document.createElement('button'), label = document.createElement('span');
back.type = next.type = 'button'; back.textContent = '‹'; next.textContent = '›';
back.setAttribute('aria-label','Page précédente du panneau'); next.setAttribute('aria-label','Page suivante du panneau');
nav.append(back,label,next);
function refresh() {
if (!panel.getClientRects().length) return;
const max = content.scrollHeight - content.clientHeight;
nav.hidden = max <= 2;
back.disabled = content.scrollTop < 2; next.disabled = content.scrollTop >= max - 2;
label.textContent = max <= 2 ? '' : `${Math.floor(content.scrollTop / Math.max(1,content.clientHeight - 60)) + 1} / ${Math.ceil(max / Math.max(1,content.clientHeight - 60)) + 1}`;
}
back.onclick = () => { content.scrollTop -= Math.max(44,content.clientHeight - 60); refresh(); };
next.onclick = () => { content.scrollTop += Math.max(44,content.clientHeight - 60); refresh(); };
new ResizeObserver(refresh).observe(content);
new MutationObserver(refresh).observe(content,{childList:true,subtree:true,attributes:true,attributeFilter:['open','hidden']});
new MutationObserver(refresh).observe(panel,{attributes:true,attributeFilter:['open']});
content.addEventListener('scroll',refresh); document.addEventListener('caria-view',refresh);
}
// Paginate collections rather than shrinking touch targets or scrolling the screen.
for (const selector of ['#playlist', '.detail-grid', '.home-grid']) {
const container = $(selector), pager = document.createElement('nav');
pager.className = 'collection-pages'; pager.setAttribute('aria-label', 'Pages de contenu');
const back = document.createElement('button'), next = document.createElement('button'), label = document.createElement('span');
back.textContent = '‹'; next.textContent = '›';
back.setAttribute('aria-label', 'Page précédente'); next.setAttribute('aria-label', 'Page suivante');
pager.append(back,label,next); container.after(pager);
let page = 0;
function layout() {
if (!container.getClientRects().length) return;
const items = [...container.children], small = innerWidth < 760;
let count = items.length || 1;
if (selector === '#playlist') count = Math.max(1, Math.floor(container.clientHeight / 58));
if (selector === '.detail-grid') count = small || innerHeight < 750 ? 4 : 8;
if (selector === '.home-grid') count = small ? 2 : 4;
const pages = Math.max(1,Math.ceil(items.length / count)); page = Math.min(page,pages - 1);
items.forEach((item,i) => item.hidden = i < page * count || i >= (page + 1) * count);
pager.hidden = pages === 1; label.textContent = `${page + 1} / ${pages}`;
back.disabled = page === 0; next.disabled = page === pages - 1;
}
back.onclick = () => { page--; layout(); }; next.onclick = () => { page++; layout(); };
new ResizeObserver(layout).observe(container);
new MutationObserver(() => { page = 0; layout(); }).observe(container,{childList:true});
document.addEventListener('caria-view',layout); layout();
}
}