refactor: independent per-day heading + table, no nested mega-table
Each day is now a div.day-section with a heading bar and its own independent <table>, rather than rows inside a single monolithic table. Toggle shows/hides the per-day table element directly. Highlights panel is a div between the heading and table, not a table row. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -677,18 +677,21 @@ button.cut:hover:not(:disabled){background:#1e3a8a}
|
||||
.filter-bar input[type=date]{background:var(--bg);border:1px solid var(--brd);
|
||||
color:var(--txt);padding:3px 6px;border-radius:4px;font-size:13px;color-scheme:dark}
|
||||
.filter-bar input:focus{outline:2px solid var(--accent);outline-offset:1px}
|
||||
/* day groups */
|
||||
tr.day-head td{background:var(--surf);padding:7px 10px;border-bottom:2px solid var(--brd);border-top:2px solid var(--brd)}
|
||||
/* day sections */
|
||||
.day-section{margin-bottom:18px}
|
||||
.day-heading-bar{background:var(--surf);padding:7px 10px;border:1px solid var(--brd);
|
||||
border-radius:6px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
||||
.day-heading-bar.open{border-radius:6px 6px 0 0}
|
||||
.day-toggle{background:none;border:none;color:var(--txt);font-size:13px;font-weight:600;
|
||||
cursor:pointer;padding:2px 0;display:inline-flex;align-items:center;gap:8px}
|
||||
cursor:pointer;padding:2px 0;display:inline-flex;align-items:center;gap:8px;flex:1 1 auto}
|
||||
.day-toggle:hover{color:var(--accent)}
|
||||
.day-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
|
||||
.day-meta{color:var(--muted);font-size:12px;font-weight:400}
|
||||
button.day-hl{color:var(--green);border-color:#166534;background:#052e16;font-size:11px;
|
||||
margin-left:14px;vertical-align:middle}
|
||||
button.day-hl{color:var(--green);border-color:#166534;background:#052e16;font-size:11px}
|
||||
button.day-hl:hover:not(:disabled){background:#0a3d1f}
|
||||
button.day-hl:disabled{opacity:.5;cursor:default}
|
||||
tr.day-hl-row td{background:var(--bg);padding:8px 12px 12px}
|
||||
.day-hl-container{background:var(--bg);border:1px solid var(--brd);border-top:none;padding:8px 12px 12px}
|
||||
table.day-table{width:100%;border-collapse:collapse;border:1px solid var(--brd);border-top:none}
|
||||
svg.day-timeline{display:block;width:100%;height:22px}
|
||||
.day-tl-labels{display:flex;justify-content:space-between;font-size:10px;
|
||||
color:var(--muted);font-family:ui-monospace,monospace;margin-top:2px}
|
||||
@@ -727,19 +730,7 @@ svg.day-timeline{display:block;width:100%;height:22px}
|
||||
<button id="filter-clear" aria-label="Clear all filters">✕ Clear</button>
|
||||
</div>
|
||||
<div class="wrap" id="main">
|
||||
<table aria-label="Recordings archive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">File</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Duration</th>
|
||||
<th scope="col">Size</th>
|
||||
<th scope="col">Loudness</th>
|
||||
<th scope="col"><span class="sr">Actions</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbody"></tbody>
|
||||
</table>
|
||||
<div id="tbody" role="region" aria-label="Recordings archive"></div>
|
||||
<div id="empty" class="empty" style="display:none" role="status">No recordings found.</div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -1084,8 +1075,8 @@ function _attachFileRowHandlers(f, isRec, expanded, dayId) {
|
||||
}
|
||||
|
||||
function renderFiles(files) {
|
||||
const tbody = document.getElementById('tbody');
|
||||
tbody.innerHTML = '';
|
||||
const container = document.getElementById('tbody');
|
||||
container.innerHTML = '';
|
||||
recMap.clear();
|
||||
sectionMap.clear();
|
||||
|
||||
@@ -1111,31 +1102,52 @@ function renderFiles(files) {
|
||||
const sizeStr = ' · ' + fmtSize(totalSize);
|
||||
const fileStr = `${dayFiles.length} file${dayFiles.length !== 1 ? 's' : ''}`;
|
||||
|
||||
// Day heading row
|
||||
const headRow = document.createElement('tr');
|
||||
headRow.className = 'day-head';
|
||||
headRow.id = 'dayhead-' + dayId;
|
||||
headRow.innerHTML = `<td colspan="6">
|
||||
// Day section wrapper
|
||||
const section = document.createElement('div');
|
||||
section.className = 'day-section';
|
||||
section.id = 'daysec-' + dayId;
|
||||
|
||||
// Heading bar
|
||||
const headBar = document.createElement('div');
|
||||
headBar.className = 'day-heading-bar' + (expanded ? ' open' : '');
|
||||
headBar.id = 'dayhead-' + dayId;
|
||||
headBar.innerHTML = `
|
||||
<button class="day-toggle" id="daytgl-${dayId}"
|
||||
aria-expanded="${expanded}"
|
||||
aria-controls="${dayId}-body"
|
||||
aria-controls="daytbl-${dayId}"
|
||||
aria-label="${expanded ? 'Collapse' : 'Expand'} ${esc(day)}">
|
||||
<span class="day-arrow" aria-hidden="true">${expanded ? '▼' : '▶'}</span>
|
||||
<strong>${esc(day)}</strong>
|
||||
<span class="day-meta">${fileStr}${durStr}${sizeStr}</span>
|
||||
</button>
|
||||
${canHl ? `<button class="day-hl" id="dayhln-${dayId}"
|
||||
aria-label="Show day highlights for ${esc(day)}">★ Highlights</button>` : ''}
|
||||
</td>`;
|
||||
tbody.appendChild(headRow);
|
||||
aria-label="Show day highlights for ${esc(day)}">★ Highlights</button>` : ''}`;
|
||||
section.appendChild(headBar);
|
||||
|
||||
// Highlights row (hidden until button clicked)
|
||||
const hlRow = document.createElement('tr');
|
||||
hlRow.className = 'day-hl-row';
|
||||
hlRow.id = 'dayhl-' + dayId;
|
||||
hlRow.hidden = true;
|
||||
hlRow.innerHTML = `<td colspan="6"><div id="dayhlc-${dayId}"></div></td>`;
|
||||
tbody.appendChild(hlRow);
|
||||
// Highlights panel (hidden until button clicked)
|
||||
const hlDiv = document.createElement('div');
|
||||
hlDiv.className = 'day-hl-container';
|
||||
hlDiv.id = 'dayhl-' + dayId;
|
||||
hlDiv.hidden = true;
|
||||
hlDiv.innerHTML = `<div id="dayhlc-${dayId}"></div>`;
|
||||
section.appendChild(hlDiv);
|
||||
|
||||
// Per-day table
|
||||
const table = document.createElement('table');
|
||||
table.className = 'day-table';
|
||||
table.id = 'daytbl-' + dayId;
|
||||
table.setAttribute('aria-label', `Recordings for ${day}`);
|
||||
if (!expanded) table.hidden = true;
|
||||
table.innerHTML = `<thead><tr>
|
||||
<th scope="col">File</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Duration</th>
|
||||
<th scope="col">Size</th>
|
||||
<th scope="col">Loudness</th>
|
||||
<th scope="col"><span class="sr">Actions</span></th>
|
||||
</tr></thead>`;
|
||||
const dayTbody = document.createElement('tbody');
|
||||
table.appendChild(dayTbody);
|
||||
|
||||
// File rows
|
||||
dayFiles.forEach(f => {
|
||||
@@ -1147,8 +1159,6 @@ function renderFiles(files) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.className = 'data-row';
|
||||
tr.id = 'row-'+i;
|
||||
tr.setAttribute('data-day', dayId);
|
||||
if (!expanded) tr.hidden = true;
|
||||
|
||||
const recBadge = `<span id="rec-${i}" class="badge-rec"${isRec?'':' hidden'}
|
||||
aria-label="Currently recording" aria-hidden="${isRec?'false':'true'}">
|
||||
@@ -1179,13 +1189,12 @@ function renderFiles(files) {
|
||||
${isRec ? 'disabled title="Cannot delete while recording"' : ''}>✕ Delete</button>
|
||||
</div>
|
||||
</td>`;
|
||||
tbody.appendChild(tr);
|
||||
dayTbody.appendChild(tr);
|
||||
|
||||
const prow = document.createElement('tr');
|
||||
prow.className = 'player-row';
|
||||
prow.id = 'prow-'+i;
|
||||
prow.hidden = true;
|
||||
prow.setAttribute('data-day', dayId);
|
||||
const durLabel = f.duration != null
|
||||
? `<div class="muted" style="font-size:11px;margin-top:3px">Duration: ${fmtDur(f.duration)}</div>`
|
||||
: '';
|
||||
@@ -1205,11 +1214,14 @@ function renderFiles(files) {
|
||||
aria-label="Download cut of ${esc(f.name)}">↓ Download cut</button>
|
||||
</div>
|
||||
</td>`;
|
||||
tbody.appendChild(prow);
|
||||
dayTbody.appendChild(prow);
|
||||
|
||||
_attachFileRowHandlers(f, isRec, expanded, dayId);
|
||||
});
|
||||
|
||||
section.appendChild(table);
|
||||
container.appendChild(section);
|
||||
|
||||
// Day toggle handler
|
||||
document.getElementById('daytgl-' + dayId).addEventListener('click', () => {
|
||||
const nowExp = !dayExpanded.get(dayId);
|
||||
@@ -1218,11 +1230,9 @@ function renderFiles(files) {
|
||||
tgl.setAttribute('aria-expanded', nowExp);
|
||||
tgl.setAttribute('aria-label', (nowExp ? 'Collapse' : 'Expand') + ' ' + day);
|
||||
tgl.querySelector('.day-arrow').textContent = nowExp ? '▼' : '▶';
|
||||
document.querySelectorAll(`tr[data-day="${dayId}"].data-row`).forEach(r => {
|
||||
r.hidden = !nowExp;
|
||||
});
|
||||
headBar.classList.toggle('open', nowExp);
|
||||
document.getElementById('daytbl-' + dayId).hidden = !nowExp;
|
||||
if (!nowExp) {
|
||||
// Collapse: pause any open players, hide player rows and highlights
|
||||
dayFiles.forEach(f => {
|
||||
const prow = document.getElementById('prow-' + f._idx);
|
||||
if (prow && !prow.hidden) {
|
||||
|
||||
Reference in New Issue
Block a user