mirror of
https://github.com/logos-blockchain/logos-blockchain-block-explorer-template.git
synced 2026-01-02 13:13:10 +00:00
65 lines
2.4 KiB
JavaScript
65 lines
2.4 KiB
JavaScript
import { h } from 'preact';
|
|
import { useEffect, useState } from 'preact/hooks';
|
|
|
|
export default function AppRouter({ routes }) {
|
|
const [match, setMatch] = useState(() => resolveRoute(location.pathname, routes));
|
|
|
|
useEffect(() => {
|
|
const handlePopState = () => setMatch(resolveRoute(location.pathname, routes));
|
|
|
|
const handleLinkClick = (event) => {
|
|
// Only intercept unmodified left-clicks
|
|
if (event.defaultPrevented || event.button !== 0) return;
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
|
|
const anchor = event.target.closest?.('a[href]');
|
|
if (!anchor) return;
|
|
|
|
// Respect explicit navigation hints
|
|
if (anchor.target && anchor.target !== '_self') return;
|
|
if (anchor.hasAttribute('download')) return;
|
|
if (anchor.getAttribute('rel')?.includes('external')) return;
|
|
if (anchor.dataset.external === 'true' || anchor.dataset.noRouter === 'true') return;
|
|
|
|
const href = anchor.getAttribute('href');
|
|
if (!href) return;
|
|
|
|
// Allow in-page, mailto, and other schemes to pass through
|
|
if (href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return;
|
|
|
|
// Different origin → let the browser handle it
|
|
if (anchor.origin !== location.origin) return;
|
|
|
|
// Likely a static asset (e.g., ".css", ".png") → let it pass
|
|
if (/\.[a-z0-9]+($|\?)/i.test(href)) return;
|
|
|
|
event.preventDefault();
|
|
history.pushState({}, '', href);
|
|
setMatch(resolveRoute(location.pathname, routes));
|
|
};
|
|
|
|
window.addEventListener('popstate', handlePopState);
|
|
document.addEventListener('click', handleLinkClick);
|
|
|
|
return () => {
|
|
window.removeEventListener('popstate', handlePopState);
|
|
document.removeEventListener('click', handleLinkClick);
|
|
};
|
|
}, [routes]);
|
|
|
|
const View = match?.view ?? NotFound;
|
|
return h(View, { params: match?.params ?? [] });
|
|
}
|
|
|
|
function resolveRoute(pathname, routes) {
|
|
for (const route of routes) {
|
|
const result = pathname.match(route.pattern);
|
|
if (result) return { view: route.view, params: result.slice(1) };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function NotFound() {
|
|
return h('main', { class: 'wrap' }, h('h1', null, 'Not found'));
|
|
}
|