Implement 'call' button

This commit is contained in:
Ian Keane 2026-06-11 12:30:11 -04:00
parent 19cd65387d
commit 7706459daa
3 changed files with 204 additions and 1 deletions

126
app.js
View file

@ -880,7 +880,131 @@ function openRefsModal(tuneId) {
refsModal.showModal();
}
// ── Bulk Edit ─────────────────────────────────────────────
// ── Call mode ─────────────────────────────────────────────────────
const callModal = $('call-modal');
let callStep = 0;
let callInstrumentId = null;
let callTuningId = null;
function callableEntries() {
const result = [];
for (const tune of allTunes)
for (const entry of tune.instruments)
if (entry.callable) result.push({ tune, entry });
return result;
}
function callGetInstruments() {
const map = new Map();
callableEntries().forEach(({ entry }) => {
if (!map.has(entry.instrument_id)) map.set(entry.instrument_id, entry.instrument_name);
});
return [...map.entries()].map(([id, name]) => ({ id, name }))
.sort((a, b) => a.name.localeCompare(b.name));
}
function callGetTunings() {
const map = new Map();
callableEntries()
.filter(({ entry }) => callInstrumentId === null || entry.instrument_id === callInstrumentId)
.forEach(({ entry }) => {
if (entry.tuning_id != null && !map.has(entry.tuning_id))
map.set(entry.tuning_id, entry.tuning);
});
return [...map.entries()].map(([id, name]) => ({ id, name }))
.sort((a, b) => a.name.localeCompare(b.name));
}
function callGetKeys() {
const map = new Map();
callableEntries()
.filter(({ entry }) =>
(callInstrumentId === null || entry.instrument_id === callInstrumentId) &&
(callTuningId === null || entry.tuning_id === callTuningId)
)
.forEach(({ tune }) => {
if (tune.key) {
const k = tune.key + '|' + (tune.modal ? '1' : '0');
if (!map.has(k)) map.set(k, { key: tune.key, modal: !!tune.modal });
}
});
return [...map.values()]
.sort((a, b) => a.key.localeCompare(b.key) || (a.modal ? 1 : -1));
}
function openCallModal() {
callStep = 0;
callInstrumentId = null;
callTuningId = null;
renderCallStep();
callModal.showModal();
}
function renderCallStep() {
const items = callStep === 0 ? callGetInstruments()
: callStep === 1 ? callGetTunings()
: callGetKeys();
// Skip this screen if there's only one option
if (items.length === 1) {
callSelect(callStep === 2 ? items[0].key : items[0].id,
callStep === 2 ? items[0].modal : null);
return;
}
$('call-title').textContent = ['Instrument', 'Tuning', 'Key'][callStep];
$('call-back').hidden = callStep === 0;
const opts = $('call-options');
opts.innerHTML = '';
const allBtn = document.createElement('button');
allBtn.className = 'call-option-btn call-all-btn';
allBtn.textContent = 'All';
allBtn.addEventListener('click', () => callSelect(null, null));
opts.appendChild(allBtn);
items.forEach(item => {
const btn = document.createElement('button');
btn.className = 'call-option-btn';
btn.textContent = callStep === 2
? (item.modal ? item.key + ' modal' : item.key)
: item.name;
btn.addEventListener('click', () =>
callSelect(callStep === 2 ? item.key : item.id,
callStep === 2 ? item.modal : null));
opts.appendChild(btn);
});
}
function callSelect(value, modal) {
if (callStep === 0) {
callInstrumentId = value;
callStep = 1;
renderCallStep();
} else if (callStep === 1) {
callTuningId = value;
callStep = 2;
renderCallStep();
} else {
filters.callable = true;
filters.instrument = callInstrumentId !== null ? String(callInstrumentId) : null;
filters.tuning = callTuningId !== null ? String(callTuningId) : null;
filters.key = value;
filters.modal = value !== null ? modal : null;
updateFilterIndicators();
renderTable();
callModal.close();
}
}
$('btn-call').addEventListener('click', openCallModal);
$('call-close').addEventListener('click', () => callModal.close());
$('call-back').addEventListener('click', () => {
callStep--;
if (callStep === 0) callInstrumentId = null;
else if (callStep === 1) callTuningId = null;
renderCallStep();
});
// ── Bulk Edit ──────────────────────────────────────────────
btnBulkEdit.addEventListener('click', e => {
e.stopPropagation();
if (!bulkEditPopover.hidden) { bulkEditPopover.hidden = true; return; }