feat(gateway-ui): replace legacy CSS with Tailwind base and responsive router
Reduce style.css from 1740 lines to ~120 lines covering only what Tailwind cannot express (scrollbars, keyframes, stateful toggles). Rewrite app.js router for responsive nav with desktop sidebar active states, mobile pill indicators, and dual connection status updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+49
-13
@@ -2,6 +2,7 @@
|
||||
* Flynn SPA Router
|
||||
*
|
||||
* Hash-based routing with page lifecycle management.
|
||||
* Drives both desktop sidebar and mobile pill navigation.
|
||||
*/
|
||||
import { getClient } from './lib/ws-client.js';
|
||||
import { registerPwaServiceWorker } from './lib/pwa.js';
|
||||
@@ -19,8 +20,7 @@ export function navigate(path) {
|
||||
}
|
||||
|
||||
function getPath() {
|
||||
const hash = window.location.hash.slice(1) || '/';
|
||||
return hash;
|
||||
return window.location.hash.slice(1) || '/';
|
||||
}
|
||||
|
||||
async function render() {
|
||||
@@ -28,7 +28,7 @@ async function render() {
|
||||
const page = routes.get(path);
|
||||
|
||||
if (!page) {
|
||||
contentEl.innerHTML = '<div class="page-error"><h2>Page not found</h2></div>';
|
||||
contentEl.innerHTML = '<div class="p-8 text-center text-zinc-500">Page not found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,17 +37,22 @@ async function render() {
|
||||
currentPage.teardown();
|
||||
}
|
||||
|
||||
// Update nav
|
||||
// Update desktop sidebar active state
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
link.classList.toggle('active', link.getAttribute('href') === `#${path}`);
|
||||
});
|
||||
|
||||
// Update mobile pill active state
|
||||
document.querySelectorAll('.page-pill').forEach(pill => {
|
||||
pill.classList.toggle('active', pill.dataset.hash === `#${path}`);
|
||||
});
|
||||
|
||||
// Render new page
|
||||
currentPage = page;
|
||||
contentEl.innerHTML = '';
|
||||
|
||||
const pageEl = document.createElement('div');
|
||||
pageEl.className = 'page';
|
||||
pageEl.className = 'page max-w-7xl mx-auto px-4 md:px-0';
|
||||
contentEl.appendChild(pageEl);
|
||||
|
||||
await page.render(pageEl, getClient());
|
||||
@@ -56,23 +61,54 @@ async function render() {
|
||||
export function initRouter() {
|
||||
contentEl = document.getElementById('content');
|
||||
window.addEventListener('hashchange', render);
|
||||
|
||||
// Wire mobile pill click handlers
|
||||
document.querySelectorAll('.page-pill').forEach(pill => {
|
||||
pill.addEventListener('click', () => {
|
||||
window.location.hash = pill.dataset.hash;
|
||||
});
|
||||
});
|
||||
|
||||
void registerPwaServiceWorker().catch(() => undefined);
|
||||
render();
|
||||
}
|
||||
|
||||
// Connection status indicator
|
||||
// Connection status indicator — updates both desktop and mobile dots
|
||||
export function initStatusIndicator() {
|
||||
const statusEl = document.getElementById('conn-status');
|
||||
const connDot = document.getElementById('conn-dot');
|
||||
const connText = document.getElementById('conn-text');
|
||||
const connDotMobile = document.getElementById('conn-dot-mobile');
|
||||
const client = getClient();
|
||||
|
||||
const textMap = {
|
||||
connected: 'Connected',
|
||||
connecting: 'Connecting...',
|
||||
disconnected: 'Disconnected',
|
||||
locked: 'Locked',
|
||||
};
|
||||
|
||||
const colorMap = {
|
||||
connected: 'bg-green-500',
|
||||
connecting: 'bg-amber-500',
|
||||
disconnected: 'bg-red-500',
|
||||
locked: 'bg-red-500',
|
||||
};
|
||||
|
||||
const baseDotClasses = 'w-2 h-2 rounded-full shrink-0';
|
||||
|
||||
function applyStatus(status) {
|
||||
const text = textMap[status] || 'Disconnected';
|
||||
const color = colorMap[status] || 'bg-zinc-600';
|
||||
|
||||
if (connText) connText.textContent = text;
|
||||
if (connDot) connDot.className = `${baseDotClasses} ${color}`;
|
||||
if (connDotMobile) connDotMobile.className = `${baseDotClasses} ${color}`;
|
||||
}
|
||||
|
||||
client.onStatusChange((status) => {
|
||||
statusEl.textContent = status === 'connected' ? 'Connected' :
|
||||
status === 'connecting' ? 'Connecting...' : 'Disconnected';
|
||||
statusEl.className = `conn-status ${status}`;
|
||||
applyStatus(status);
|
||||
});
|
||||
|
||||
// Set initial status
|
||||
statusEl.textContent = client.status === 'connected' ? 'Connected' :
|
||||
client.status === 'connecting' ? 'Connecting...' : 'Disconnected';
|
||||
statusEl.className = `conn-status ${client.status}`;
|
||||
applyStatus(client.status);
|
||||
}
|
||||
|
||||
+68
-1689
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user