Add card-style mobile layout
This commit is contained in:
parent
067272b797
commit
19cd65387d
3 changed files with 155 additions and 12 deletions
63
app.js
63
app.js
|
|
@ -409,10 +409,32 @@ function renderTable() {
|
|||
|
||||
rows.forEach(({ tune, entry }) => {
|
||||
const hasNotes = (tune.notes?.length > 0) || (entry?.notes?.length > 0);
|
||||
|
||||
// ── Mobile 2-line card data ──────────────────────────────────
|
||||
const nameRaw = tune.name ?? '';
|
||||
const sourceRaw = tune.source?.name ?? '';
|
||||
const instRaw = entry?.instrument_name ?? '';
|
||||
const tuningRaw = entry?.tuning ?? '';
|
||||
const instStr = instRaw ? (tuningRaw ? `${instRaw} (${tuningRaw})` : instRaw) : '';
|
||||
const keyStr = tune.key
|
||||
? (tune.modal ? `${tune.key} modal` : tune.key)
|
||||
: (tune.modal ? 'modal' : '');
|
||||
const metaRaw = [instStr, keyStr].filter(Boolean).join(' · ');
|
||||
const mobPills = entry ? [
|
||||
`<span class="pill mob-pill ${entry.callable ? 'pill-yes' : 'pill-dim'}">Call</span>`,
|
||||
`<span class="pill mob-pill ${entry.review ? 'pill-warn' : 'pill-dim'}">Rev</span>`,
|
||||
`<span class="pill mob-pill ${entry.to_learn ? 'pill-warn' : 'pill-dim'}">Lrn</span>`,
|
||||
].join('') : '';
|
||||
const mobActions = [
|
||||
hasNotes ? `<button class="action-btn btn-notes" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}">📝</button>` : '',
|
||||
tune.references?.length > 0 ? `<button class="action-btn btn-show-refs" data-tune-id="${tune.id}">🔗</button>` : '',
|
||||
`<button class="action-btn btn-edit-row" data-tune-id="${tune.id}" data-entry-id="${entry?.id ?? ''}" ${unlocked ? '' : 'hidden'}>✏️</button>`,
|
||||
].join('');
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>${esc(tune.name ?? '')}</td>
|
||||
<td>${esc(tune.source?.name ?? '')}</td>
|
||||
<td><span class="cell-trunc cell-name-content mob-truncatable" data-full="${esc(nameRaw)}" title="${esc(nameRaw)}">${esc(nameRaw)}</span></td>
|
||||
<td><span class="cell-trunc cell-source-content mob-truncatable" data-full="${esc(sourceRaw)}" title="${esc(sourceRaw)}">${esc(sourceRaw)}</span></td>
|
||||
<td>${esc(tune.key ?? '')}</td>
|
||||
<td>${tune.modal ? '<span class="pill pill-yes">Yes</span>' : '<span class="pill" style="background:#222">—</span>'}</td>
|
||||
<td>${esc(entry?.instrument_name ?? '')}</td>
|
||||
|
|
@ -430,7 +452,20 @@ function renderTable() {
|
|||
data-tune-id="${tune.id}"
|
||||
data-entry-id="${entry?.id ?? ''}"
|
||||
title="Edit"
|
||||
${unlocked ? '' : 'hidden'}>✏️</button></td>`;
|
||||
${unlocked ? '' : 'hidden'}>✏️</button></td>
|
||||
<td class="mobile-row" colspan="99">
|
||||
<div class="mob-line1">
|
||||
<div class="mob-name-wrap">
|
||||
<span class="mob-name mob-truncatable" data-full="${esc(nameRaw)}" title="${esc(nameRaw)}">${esc(nameRaw)}</span>
|
||||
${sourceRaw ? `<span class="mob-source mob-truncatable" data-full="${esc(sourceRaw)}" title="${esc(sourceRaw)}">(${esc(sourceRaw)})</span>` : ''}
|
||||
</div>
|
||||
<span class="mob-status">${mobPills}</span>
|
||||
</div>
|
||||
<div class="mob-line2">
|
||||
<span class="mob-meta mob-truncatable" data-full="${esc(metaRaw)}" title="${esc(metaRaw)}">${esc(metaRaw)}</span>
|
||||
<span class="mob-actions">${mobActions}</span>
|
||||
</div>
|
||||
</td>`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
|
|
@ -1046,6 +1081,28 @@ function esc(str) {
|
|||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
// ── Mobile truncation tooltip ───────────────────────────────────
|
||||
{
|
||||
const tip = $('mob-tooltip');
|
||||
let timer = null;
|
||||
|
||||
tbody.addEventListener('click', e => {
|
||||
const el = e.target.closest('.mob-truncatable');
|
||||
if (!el) return;
|
||||
if (el.scrollWidth <= el.offsetWidth) return; // not truncated
|
||||
clearTimeout(timer);
|
||||
tip.textContent = el.dataset.full;
|
||||
const rect = el.getBoundingClientRect();
|
||||
tip.style.top = (rect.bottom + 4) + 'px';
|
||||
tip.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - 220)) + 'px';
|
||||
tip.hidden = false;
|
||||
timer = setTimeout(() => { tip.hidden = true; }, 2500);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
document.addEventListener('click', () => { clearTimeout(timer); tip.hidden = true; });
|
||||
}
|
||||
|
||||
// Pressing Tab in a datalist-backed input fills the top matching suggestion.
|
||||
function attachTabAutocomplete(input, datalist) {
|
||||
input.addEventListener('keydown', e => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue