feat(ui): inline expansion for cards view at mobile

Same accordion pattern as stream: card-row gets entity-exp markup,
selectEntity/expandInline/dismissPeek/Escape all handle .card-row.
Fullscreen expand works for both views.
This commit is contained in:
2026-05-16 22:46:01 -04:00
parent ad44d35d9b
commit fa960ec204
2 changed files with 38 additions and 23 deletions
+12 -5
View File
@@ -636,6 +636,7 @@
const affHtml = affs.map(a => `<span class="aff clickable ${AFF_CLASSES[a]}">${AFF_LABELS[a]}</span>`).join('');
return `<div class="card-row${selected}${pinCls}${flashCls}" data-index="${idx}" data-id="${e.id}">
<div class="card-head">
<span class="card-row-title">${escHtml(title)}</span>
<span class="card-row-dash">—</span>
<span class="card-row-preview">${preview}</span>
@@ -645,6 +646,12 @@
${e.pinned ? '<span class="card-row-pin">★</span>' : ''}
${e.use_count > 0 ? `<span class="card-row-use">${e.use_count}×</span>` : ''}
</div>
</div>
<div class="entity-exp">
<div class="entity-exp-clip">
${renderInlineDetail(e)}
</div>
</div>
</div>`;
}
@@ -1138,9 +1145,9 @@
} else {
state.selectedIndex = idx;
}
$$('.entity-item.selected').forEach(el => el.classList.remove('selected'));
$$('.entity-item.selected, .card-row.selected').forEach(el => el.classList.remove('selected'));
if (state.selectedIndex >= 0) {
const target = $(`.entity-item[data-index="${state.selectedIndex}"]`);
const target = $(`.entity-item[data-index="${state.selectedIndex}"], .card-row[data-index="${state.selectedIndex}"]`);
if (target) target.classList.add('selected');
}
return;
@@ -1459,7 +1466,7 @@
},
expandInline() {
const sel = $(`.entity-item.selected`);
const sel = $(`.entity-item.selected, .card-row.selected`);
if (!sel) return;
sel.classList.toggle('exp-full');
const btn = sel.querySelector('.exp-toolbar .peek-mobile-btn');
@@ -1468,7 +1475,7 @@
dismissPeek() {
if (isMobileBreakpoint()) {
const sel = $(`.entity-item.selected`);
const sel = $(`.entity-item.selected, .card-row.selected`);
if (sel) sel.classList.remove('selected', 'exp-full');
state.selectedIndex = -1;
return;
@@ -1531,7 +1538,7 @@
if (ev.key === 'Escape') {
if (isMobileBreakpoint()) {
if (state.selectedIndex >= 0) {
const sel = $(`.entity-item.selected`);
const sel = $(`.entity-item.selected, .card-row.selected`);
if (sel) sel.classList.remove('selected', 'exp-full');
state.selectedIndex = -1;
return;
+18 -10
View File
@@ -600,20 +600,23 @@ main.focus-peek .resize-handle { visibility: hidden; }
/* ── CARD ROWS ──────────────────────────────────────── */
.card-row {
display: flex;
align-items: center;
gap: 7px;
padding: 9px 12px 9px 14px;
margin: 2px 10px;
background: var(--surf);
border: 1px solid var(--border);
border-radius: var(--r2);
cursor: pointer;
min-height: 40px;
position: relative;
transition: border-color var(--t-fast), background var(--t-fast);
}
.card-head {
display: flex;
align-items: center;
gap: 7px;
padding: 9px 12px 9px 14px;
min-height: 40px;
}
.card-row:hover { border-color: var(--muted); }
.card-row.selected { border-color: var(--accent); background: var(--a-bg); }
.card-row.pinned { border-left: 2px solid var(--accent); }
@@ -1531,10 +1534,13 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius:
grid-template-rows: 0fr;
transition: grid-template-rows .2s ease;
}
.entity-item.selected .entity-exp { grid-template-rows: 1fr; }
.entity-item.selected .exp-toolbar { display: flex; }
.entity-item.selected .entity-exp,
.card-row.selected .entity-exp { grid-template-rows: 1fr; }
.entity-item.selected .exp-toolbar,
.card-row.selected .exp-toolbar { display: flex; }
.entity-item.exp-full,
.entity-item.is-card.exp-full {
.entity-item.is-card.exp-full,
.card-row.exp-full {
position: fixed;
inset: 0;
z-index: 60;
@@ -1544,7 +1550,9 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius:
margin: 0;
border-radius: 0;
}
.entity-item.exp-full .entity-exp { grid-template-rows: 1fr; }
.entity-item.exp-full .exp-inner { padding-top: 1rem; padding-bottom: 2rem; }
.entity-item.exp-full .entity-exp,
.card-row.exp-full .entity-exp { grid-template-rows: 1fr; }
.entity-item.exp-full .exp-inner,
.card-row.exp-full .exp-inner { padding-top: 1rem; padding-bottom: 2rem; }
main.focus-peek #entity-panel { display: block; overflow: auto; min-width: 0; }
}