67 lines
3.6 KiB
JavaScript
67 lines
3.6 KiB
JavaScript
// ── Rehearser cast view controls ────────────────────────────────────────────
|
|
// The cast renderer remains in rehearser.js; these handlers only resolve state
|
|
// when the cast panel is rendered, making them safe to load as a leaf module.
|
|
|
|
function applyCastView() {
|
|
const list = $('reh-cast-list'); if (!list) return;
|
|
const view = rehState.castView === 'list' ? 'list' : 'card';
|
|
list.classList.toggle('reh-cast-view-card', view === 'card');
|
|
list.classList.toggle('reh-cast-view-list', view === 'list');
|
|
document.querySelectorAll('#reh-cast-view-toggle .reh-view-btn').forEach(b => b.classList.toggle('active', b.dataset.view === view));
|
|
}
|
|
|
|
function _wireCastControls() {
|
|
const toggle = $('reh-cast-view-toggle');
|
|
if (!toggle || toggle._wired) return;
|
|
toggle._wired = true;
|
|
toggle.querySelectorAll('.reh-view-btn').forEach(b => b.addEventListener('click', () => {
|
|
rehState.castView = b.dataset.view;
|
|
try { localStorage.setItem('reh-cast-view', b.dataset.view); } catch (_) {}
|
|
applyCastView();
|
|
}));
|
|
const search = $('reh-cast-search'), fg = $('reh-cast-filter-gender'), fl = $('reh-cast-filter-lang');
|
|
const sortSel = $('reh-cast-sort'), dirBtn = $('reh-cast-sort-dir');
|
|
const setDirIcon = () => { if (dirBtn) { dirBtn.dataset.dir = rehState.castSort.dir; dirBtn.querySelector('.mdi').className = 'mdi mdi-sort-' + (rehState.castSort.dir === 'desc' ? 'descending' : 'ascending'); } };
|
|
const persist = () => { try { localStorage.setItem('reh-cast-sort', JSON.stringify(rehState.castSort)); } catch (_) {} };
|
|
if (sortSel) sortSel.value = rehState.castSort.by;
|
|
setDirIcon();
|
|
search?.addEventListener('input', () => { rehState.castFilter.search = search.value.trim(); renderCastList(); search.focus(); });
|
|
fg?.addEventListener('change', () => { rehState.castFilter.gender = fg.value; renderCastList(); });
|
|
fl?.addEventListener('change', () => { rehState.castFilter.lang = fl.value; renderCastList(); });
|
|
sortSel?.addEventListener('change', () => {
|
|
rehState.castSort.by = sortSel.value;
|
|
rehState.castSort.dir = sortSel.value === 'lines' ? 'desc' : 'asc';
|
|
setDirIcon(); persist(); renderCastList();
|
|
});
|
|
dirBtn?.addEventListener('click', () => {
|
|
rehState.castSort.dir = rehState.castSort.dir === 'desc' ? 'asc' : 'desc';
|
|
setDirIcon(); persist(); renderCastList();
|
|
});
|
|
}
|
|
|
|
function _castSortFilter(speakers, lineCount) {
|
|
const f = rehState.castFilter || {}, s = rehState.castSort || { by: 'name', dir: 'asc' };
|
|
const out = speakers.filter(sp => {
|
|
const c = rehState.cast[sp] || {};
|
|
if (f.search) { const q = f.search.toLowerCase(); if (!sp.toLowerCase().includes(q) && !(c.tags || '').toLowerCase().includes(q)) return false; }
|
|
if (f.gender && (c.gender || '') !== f.gender) return false;
|
|
if (f.lang && (c.lang || '') !== f.lang) return false;
|
|
return true;
|
|
});
|
|
const byName = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' });
|
|
const cmp = {
|
|
name: byName,
|
|
gender: (a, b) => (rehState.cast[a].gender || '~').localeCompare(rehState.cast[b].gender || '~') || byName(a, b),
|
|
lang: (a, b) => (rehState.cast[a].lang || '~').localeCompare(rehState.cast[b].lang || '~') || byName(a, b),
|
|
lines: (a, b) => (lineCount(a) - lineCount(b)) || byName(a, b),
|
|
tag: (a, b) => (rehState.cast[a].tags || '~').localeCompare(rehState.cast[b].tags || '~') || byName(a, b),
|
|
}[s.by] || byName;
|
|
out.sort(cmp);
|
|
if (s.dir === 'desc') out.reverse();
|
|
return out;
|
|
}
|
|
|
|
window.TTVC = window.TTVC || {};
|
|
window.TTVC.rehearser = window.TTVC.rehearser || {};
|
|
window.TTVC.rehearser.cast = { applyCastView, filterAndSort: _castSortFilter };
|