95 lines
7.8 KiB
JavaScript
95 lines
7.8 KiB
JavaScript
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();
|
||
}
|
||
}
|