feat: DrawerComonent

This commit is contained in:
2025-09-16 14:35:09 +02:00
parent f9dc811239
commit 27f93959ff
5 changed files with 177 additions and 157 deletions

View File

@@ -11,10 +11,7 @@ class TopologyGraphComponent extends Component {
this.isInitialized = false;
// Drawer state for desktop reuse (same pattern as ClusterMembersComponent)
this.detailsDrawer = null;
this.detailsDrawerContent = null;
this.detailsDrawerBackdrop = null;
this.activeDrawerComponent = null;
this.drawer = new DrawerComponent();
// Tooltip for labels on hover
this.tooltipEl = null;
@@ -22,52 +19,16 @@ class TopologyGraphComponent extends Component {
// Determine desktop threshold
isDesktop() {
try { return window && window.innerWidth >= 1024; } catch (_) { return false; }
return this.drawer.isDesktop();
}
ensureDrawer() {
if (this.detailsDrawer) return;
// Backdrop
this.detailsDrawerBackdrop = document.createElement('div');
this.detailsDrawerBackdrop.className = 'details-drawer-backdrop';
document.body.appendChild(this.detailsDrawerBackdrop);
// Drawer
this.detailsDrawer = document.createElement('div');
this.detailsDrawer.className = 'details-drawer';
const header = document.createElement('div');
header.className = 'details-drawer-header';
header.innerHTML = `
<div class="drawer-title">Node Details</div>
<button class="drawer-close" aria-label="Close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
`;
this.detailsDrawer.appendChild(header);
this.detailsDrawerContent = document.createElement('div');
this.detailsDrawerContent.className = 'details-drawer-content';
this.detailsDrawer.appendChild(this.detailsDrawerContent);
document.body.appendChild(this.detailsDrawer);
const close = () => this.closeDrawer();
header.querySelector('.drawer-close').addEventListener('click', close);
this.detailsDrawerBackdrop.addEventListener('click', close);
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
}
openDrawerForNode(nodeData) {
this.ensureDrawer();
// Title from hostname and IP
// Get display name for drawer title
let displayName = 'Node Details';
try {
const hostname = nodeData.hostname || '';
const ip = nodeData.ip || '';
let displayName = 'Node Details';
if (hostname && ip) {
displayName = `${hostname} - ${ip}`;
@@ -76,43 +37,32 @@ class TopologyGraphComponent extends Component {
} else if (ip) {
displayName = ip;
}
const titleEl = this.detailsDrawer.querySelector('.drawer-title');
if (titleEl) titleEl.textContent = displayName;
} catch (_) {}
// Clear previous component
if (this.activeDrawerComponent && typeof this.activeDrawerComponent.unmount === 'function') {
try { this.activeDrawerComponent.unmount(); } catch (_) {}
}
this.detailsDrawerContent.innerHTML = '<div class="loading-details">Loading detailed information...</div>';
// Open drawer with content callback
this.drawer.openDrawer(displayName, (contentContainer, setActiveComponent) => {
// Mount NodeDetailsComponent
const nodeDetailsVM = new NodeDetailsViewModel();
const nodeDetailsComponent = new NodeDetailsComponent(contentContainer, nodeDetailsVM, this.eventBus);
setActiveComponent(nodeDetailsComponent);
// Mount NodeDetailsComponent
const nodeDetailsVM = new NodeDetailsViewModel();
const nodeDetailsComponent = new NodeDetailsComponent(this.detailsDrawerContent, nodeDetailsVM, this.eventBus);
this.activeDrawerComponent = nodeDetailsComponent;
const ip = nodeData.ip || nodeData.id;
nodeDetailsVM.loadNodeDetails(ip).then(() => {
nodeDetailsComponent.mount();
}).catch((error) => {
logger.error('Failed to load node details (topology drawer):', error);
this.detailsDrawerContent.innerHTML = `
<div class="error">
<strong>Error loading node details:</strong><br>
${this.escapeHtml(error.message)}
</div>
`;
const ip = nodeData.ip || nodeData.id;
nodeDetailsVM.loadNodeDetails(ip).then(() => {
nodeDetailsComponent.mount();
}).catch((error) => {
logger.error('Failed to load node details (topology drawer):', error);
contentContainer.innerHTML = `
<div class="error">
<strong>Error loading node details:</strong><br>
${this.escapeHtml(error.message)}
</div>
`;
});
});
// Open
this.detailsDrawer.classList.add('open');
this.detailsDrawerBackdrop.classList.add('visible');
}
closeDrawer() {
if (this.detailsDrawer) this.detailsDrawer.classList.remove('open');
if (this.detailsDrawerBackdrop) this.detailsDrawerBackdrop.classList.remove('visible');
this.drawer.closeDrawer();
}
// Tooltip helpers