// Reusable Drawer Component for desktop slide-in panels class DrawerComponent { constructor() { if (window.__sharedDrawerInstance) { return window.__sharedDrawerInstance; } this.detailsDrawer = null; this.detailsDrawerContent = null; this.activeDrawerComponent = null; this.onCloseCallback = null; window.__sharedDrawerInstance = this; } // Determine if we should use desktop drawer behavior isDesktop() { try { return window && window.innerWidth >= 1024; // desktop threshold } catch (_) { return false; } } ensureDrawer() { if (this.detailsDrawer) return; // Create drawer this.detailsDrawer = document.createElement('div'); this.detailsDrawer.className = 'details-drawer'; // Header with actions and close button const header = document.createElement('div'); header.className = 'details-drawer-header'; header.innerHTML = `
Node Details
`; this.detailsDrawer.appendChild(header); // Content container this.detailsDrawerContent = document.createElement('div'); this.detailsDrawerContent.className = 'details-drawer-content'; this.detailsDrawer.appendChild(this.detailsDrawerContent); // Terminal panel container (positioned left of details drawer) this.terminalPanelContainer = document.createElement('div'); this.terminalPanelContainer.className = 'terminal-panel-container'; document.body.appendChild(this.terminalPanelContainer); document.body.appendChild(this.detailsDrawer); // Close handlers const close = () => this.closeDrawer(); header.querySelector('.drawer-close').addEventListener('click', close); const terminalBtn = header.querySelector('.drawer-terminal-btn'); if (terminalBtn) { terminalBtn.addEventListener('click', (e) => { e.stopPropagation(); try { const nodeIp = this.activeDrawerComponent && this.activeDrawerComponent.viewModel && this.activeDrawerComponent.viewModel.get('nodeIp'); if (!window.TerminalPanel) return; const panel = window.TerminalPanel; const wasMinimized = panel.isMinimized; panel.open(this.terminalPanelContainer, nodeIp); if (nodeIp && panel._updateTitle) { panel._updateTitle(nodeIp); } if (wasMinimized && panel.restore) { panel.restore(); } } catch (err) { console.error('Failed to open terminal:', err); } }); } document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); }); } openDrawer(title, contentCallback, errorCallback, onCloseCallback) { this.ensureDrawer(); this.onCloseCallback = onCloseCallback; // Set drawer title const titleEl = this.detailsDrawer.querySelector('.drawer-title'); if (titleEl) { titleEl.textContent = title; } // Clear previous component if any if (this.activeDrawerComponent && typeof this.activeDrawerComponent.unmount === 'function') { try { this.activeDrawerComponent.unmount(); } catch (_) {} } this.detailsDrawerContent.innerHTML = '
Loading detailed information...
'; // Execute content callback try { contentCallback(this.detailsDrawerContent, (component) => { this.activeDrawerComponent = component; }); } catch (error) { logger.error('Failed to load drawer content:', error); if (errorCallback) { errorCallback(error); } else { this.detailsDrawerContent.innerHTML = `
Error loading content:
${this.escapeHtml ? this.escapeHtml(error.message) : error.message}
`; } } // Open drawer this.detailsDrawer.classList.add('open'); // Inform terminal container that the drawer is open for alignment if (this.terminalPanelContainer) { this.terminalPanelContainer.classList.add('drawer-open'); } } closeDrawer() { if (this.detailsDrawer) this.detailsDrawer.classList.remove('open'); if (this.terminalPanelContainer) { this.terminalPanelContainer.classList.remove('drawer-open'); } // Call close callback if provided if (this.onCloseCallback) { this.onCloseCallback(); this.onCloseCallback = null; } } // Clean up drawer elements destroy() { if (this.detailsDrawer && this.detailsDrawer.parentNode) { this.detailsDrawer.parentNode.removeChild(this.detailsDrawer); } this.detailsDrawer = null; this.detailsDrawerContent = null; this.activeDrawerComponent = null; } // Helper method for HTML escaping (can be overridden) escapeHtml(text) { if (typeof text !== 'string') return text; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } } window.DrawerComponent = DrawerComponent;