class AppSidebar extends HTMLElement {
connectedCallback() {
if (this._initialized) return;
this._initialized = true;
this.className = 'sidebar';
this.innerHTML = `
<div class="sidebar-inner">
<nav class="sidebar-nav">
<button class="nav-item active" data-panel="search">検索</button>
<button class="nav-item" data-panel="docs">文書管理</button>
<button class="nav-item" data-panel="settings">設定</button>
</nav>
<div class="sidebar-bottom">
<div class="accordion">
<button class="accordion-header" aria-expanded="false">
INFO <span class="accordion-toggle">▸</span>
</button>
<div class="accordion-content" hidden>
<div style="color:var(--text-dim); margin-bottom:4px;">TelosDB v0.3.5</div>
<div style="color:var(--text-dim);">Minimal HUD Edition</div>
</div>
</div>
</div>
</div>
`;
// accordion behavior
const header = this.querySelector('.accordion-header');
const content = this.querySelector('.accordion-content');
if (header && content) {
header.addEventListener('click', () => {
const expanded = header.getAttribute('aria-expanded') === 'true';
header.setAttribute('aria-expanded', String(!expanded));
if (expanded) {
content.hidden = true;
header.querySelector('.accordion-toggle').textContent = '▸';
} else {
content.hidden = false;
header.querySelector('.accordion-toggle').textContent = '▾';
}
});
}
// Add a resizer handle to allow dragging to change sidebar width
const resizer = document.createElement('div');
resizer.className = 'sidebar-resizer';
this.appendChild(resizer);
let isResizing = false;
let startX = 0;
let startWidth = 0;
const onPointerMove = (e) => {
if (!isResizing) return;
const minW = 160;
const maxW = 520;
let newW = Math.max(minW, Math.min(maxW, e.clientX));
document.documentElement.style.setProperty('--sidebar-width', `${newW}px`);
};
const onPointerUp = () => {
if (!isResizing) return;
isResizing = false;
document.body.style.userSelect = '';
window.removeEventListener('pointermove', onPointerMove);
window.removeEventListener('pointerup', onPointerUp);
try { localStorage.setItem('telos_sidebar_width', getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width')); } catch (e) { }
};
resizer.addEventListener('pointerdown', (e) => {
isResizing = true;
startX = e.clientX;
const current = getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width').trim();
startWidth = parseInt(current) || this.offsetWidth || 244;
document.body.style.userSelect = 'none';
window.addEventListener('pointermove', onPointerMove);
window.addEventListener('pointerup', onPointerUp);
});
// Restore saved width from localStorage if present
try {
const saved = localStorage.getItem('telos_sidebar_width');
if (saved) document.documentElement.style.setProperty('--sidebar-width', saved.trim());
} catch (e) { }
// Navigation: dispatch custom event when a nav item is clicked
const navButtons = this.querySelectorAll('.sidebar-nav .nav-item');
navButtons.forEach(btn => {
btn.addEventListener('click', (ev) => {
const panel = btn.getAttribute('data-panel') || btn.textContent.trim();
// toggle active class
navButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// dispatch event to document so main panel can listen
const e = new CustomEvent('navigate-panel', { detail: panel, bubbles: true });
document.dispatchEvent(e);
});
});
}
}
customElements.define('app-sidebar', AppSidebar);