633505e7dc
- Add quick modal, table, service status, and confirmation dialog components - Refactor all pages (certs, dhcp, proxy, etc.) to use new component patterns - Introduce refactor load utility and render guard for consistent UX - Add hoover documentation and update AGENTS.md, architecture, overview
62 lines
1.9 KiB
JavaScript
62 lines
1.9 KiB
JavaScript
/**
|
|
* Hoover — router.js
|
|
*
|
|
* Hash-based SPA router with reactive state (triggers re-render on
|
|
* navigation). Link component for client-side navigation.
|
|
*/
|
|
|
|
import { reactive } from './reactivity.js?v=6';
|
|
import { h } from './vdom.js?v=6';
|
|
|
|
/**
|
|
* Hash-based router.
|
|
*
|
|
* const router = createRouter({
|
|
* '/dashboard': () => h('#comp', { component: DashboardPage, key: '/dashboard' }, []),
|
|
* '/interfaces': () => h('#comp', { component: InterfacesPage, key: '/interfaces' }, []),
|
|
* '*': () => h('#comp', { component: NotFoundPage, key: '*' }, []),
|
|
* });
|
|
*
|
|
* Reactive `router.state.path` updates trigger re-renders automatically.
|
|
*/
|
|
export function createRouter(routes) {
|
|
const initialPath = location.hash.slice(1) || '/dashboard';
|
|
if (!location.hash) location.hash = initialPath;
|
|
|
|
const state = reactive({ path: initialPath });
|
|
|
|
window.addEventListener('hashchange', () => {
|
|
state.path = location.hash.slice(1) || '/dashboard';
|
|
});
|
|
|
|
const component = () => {
|
|
const handler = routes[state.path] || routes['*'];
|
|
if (!handler) {
|
|
return h('div', { class: 'card' },
|
|
h('div', { class: 'text-muted' }, `404 — Not found: ${state.path}`));
|
|
}
|
|
try {
|
|
return handler();
|
|
} catch (e) {
|
|
return h('div', { class: 'card' },
|
|
h('div', { class: 'text-muted' }, `Error: ${e.message || String(e)}`));
|
|
}
|
|
};
|
|
|
|
return { state, navigate: (p) => { location.hash = p; }, component };
|
|
}
|
|
|
|
/**
|
|
* Client-side navigation link component.
|
|
* Sets `location.hash` without full page navigation.
|
|
*/
|
|
export function Link(props) {
|
|
const { path, class: cls, children, ...rest } = props || {};
|
|
return h('a', {
|
|
href: '#' + path,
|
|
class: cls || '',
|
|
'on:click': (e) => { e.preventDefault(); location.hash = path; },
|
|
...rest,
|
|
}, children || []);
|
|
}
|