feat/resizable-panels #20

Merged
lerko merged 2 commits from feat/resizable-panels into main 2026-05-17 00:48:39 +00:00
3 changed files with 82 additions and 7 deletions
Showing only changes of commit 0316076bf8 - Show all commits
+56
View File
@@ -1550,8 +1550,64 @@
const m = $('main'); const m = $('main');
if (localStorage.getItem('nib:hide-rail')) m.classList.add('hide-rail'); if (localStorage.getItem('nib:hide-rail')) m.classList.add('hide-rail');
if (localStorage.getItem('nib:hide-peek')) m.classList.add('hide-peek'); if (localStorage.getItem('nib:hide-peek')) m.classList.add('hide-peek');
const railW = localStorage.getItem('nib:rail-w');
const peekW = localStorage.getItem('nib:peek-w');
if (railW) m.style.setProperty('--rail-w', railW + 'px');
if (peekW) m.style.setProperty('--peek-w', peekW + 'px');
})(); })();
// ========== Resize handles ==========
$$('.resize-handle').forEach(handle => {
let startX, startW, panel;
handle.addEventListener('mousedown', (ev) => {
ev.preventDefault();
panel = handle.dataset.panel;
startX = ev.clientX;
const m = $('main');
m.classList.add('resizing');
handle.classList.add('active');
if (panel === 'rail') {
startW = $('#tag-rail').offsetWidth;
} else {
startW = $('#detail-pane').offsetWidth;
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
function onMove(ev) {
const m = $('main');
const dx = ev.clientX - startX;
let newW;
if (panel === 'rail') {
newW = Math.max(120, Math.min(360, startW + dx));
m.style.setProperty('--rail-w', newW + 'px');
} else {
newW = Math.max(250, Math.min(700, startW - dx));
m.style.setProperty('--peek-w', newW + 'px');
}
}
function onUp() {
const m = $('main');
m.classList.remove('resizing');
handle.classList.remove('active');
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
if (panel === 'rail') {
localStorage.setItem('nib:rail-w', $('#tag-rail').offsetWidth);
} else {
localStorage.setItem('nib:peek-w', $('#detail-pane').offsetWidth);
}
}
});
function scrollSelectedIntoView() { function scrollSelectedIntoView() {
const el = $(`.entity-item[data-index="${state.selectedIndex}"], .card-row[data-index="${state.selectedIndex}"]`); const el = $(`.entity-item[data-index="${state.selectedIndex}"], .card-row[data-index="${state.selectedIndex}"]`);
if (el) el.scrollIntoView({ block: 'nearest' }); if (el) el.scrollIntoView({ block: 'nearest' });
+2
View File
@@ -24,11 +24,13 @@
</header> </header>
<main> <main>
<aside id="tag-rail"></aside> <aside id="tag-rail"></aside>
<div class="resize-handle" data-panel="rail"></div>
<section id="entity-panel"> <section id="entity-panel">
<div id="month-nav"></div> <div id="month-nav"></div>
<div id="entity-list"></div> <div id="entity-list"></div>
<div id="capture-bar"></div> <div id="capture-bar"></div>
</section> </section>
<div class="resize-handle" data-panel="peek"></div>
<aside id="detail-pane"> <aside id="detail-pane">
<div class="detail-empty">select an entity</div> <div class="detail-empty">select an entity</div>
</aside> </aside>
+24 -7
View File
@@ -177,17 +177,34 @@ nav { display: flex; gap: 2px; }
/* ── MAIN LAYOUT ────────────────────────────────────── */ /* ── MAIN LAYOUT ────────────────────────────────────── */
main { main {
display: grid; display: grid;
grid-template-columns: 192px 1fr 400px; grid-template-columns: var(--rail-w, 192px) 4px 1fr 4px var(--peek-w, 400px);
overflow: hidden; overflow: hidden;
transition: grid-template-columns var(--t-base);
} }
main.hide-rail { grid-template-columns: 0px 1fr 400px; } main.resizing { transition: none; }
main.hide-peek { grid-template-columns: 192px 1fr 0px; } main:not(.resizing) { transition: grid-template-columns var(--t-base); }
main.hide-rail.hide-peek { grid-template-columns: 0px 1fr 0px; }
main.hide-rail #tag-rail { overflow: hidden; border-right: none; } main.hide-rail { grid-template-columns: 0px 0px 1fr 4px var(--peek-w, 400px); }
main.hide-peek #detail-pane { overflow: hidden; border-left: none; } main.hide-peek { grid-template-columns: var(--rail-w, 192px) 4px 1fr 0px 0px; }
main.hide-rail.hide-peek { grid-template-columns: 0px 0px 1fr 0px 0px; }
main.hide-rail #tag-rail { overflow: hidden; border-right: none; min-width: 0; }
main.hide-peek #detail-pane { overflow: hidden; border-left: none; min-width: 0; }
main.hide-rail .resize-handle[data-panel="rail"] { visibility: hidden; }
main.hide-peek .resize-handle[data-panel="peek"] { visibility: hidden; }
.resize-handle {
cursor: col-resize;
background: transparent;
transition: background var(--t-fast);
z-index: 10;
}
.resize-handle:hover,
.resize-handle.active {
background: var(--accent);
opacity: .4;
}
/* ── TAG RAIL ───────────────────────────────────────── */ /* ── TAG RAIL ───────────────────────────────────────── */
#tag-rail { #tag-rail {