Files
michaelschiemer/resources/js/core/router.js

139 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// modules/core/router.js
import { init as initClickManager } from './ClickManager.js';
import { navigateTo } from './navigateTo.js';
const routes = new Map();
const wildcards = [];
const guards = new Map();
let currentRoute = null;
let layoutCallback = null;
let metaCallback = null;
/**
* Registriert eine neue Route mit optionalem Handler
* @param {string} path - Pfad der Route
* @param {Function} handler - Callback bei Treffer
*/
export function defineRoute(path, handler) {
if (path.includes('*')) {
wildcards.push({ pattern: new RegExp('^' + path.replace('*', '.*') + '$'), handler });
} else {
routes.set(path, handler);
}
}
/**
* Definiert einen Guard für eine Route
* @param {string} path - Pfad
* @param {Function} guard - Guard-Funktion (return false = block)
*/
export function guardRoute(path, guard) {
guards.set(path, guard);
}
/**
* Gibt die aktuell aktive Route zurück
*/
export function getRouteContext() {
return currentRoute;
}
/**
* Setzt eine Callback-Funktion für dynamische Layout-Switches
*/
export function onLayoutSwitch(fn) {
layoutCallback = fn;
}
/**
* Setzt eine Callback-Funktion für Meta-Daten (z.B. title, theme)
*/
export function onMetaUpdate(fn) {
metaCallback = fn;
}
function matchRoute(href) {
if (routes.has(href)) return routes.get(href);
for (const entry of wildcards) {
if (entry.pattern.test(href)) return entry.handler;
}
return null;
}
function runGuard(href) {
const guard = guards.get(href);
return guard ? guard(href) !== false : true;
}
function extractMetaFromHTML(html) {
const temp = document.createElement('div');
temp.innerHTML = html;
const metaTags = {};
temp.querySelectorAll('[data-meta]').forEach(el => {
for (const attr of el.attributes) {
if (attr.name.startsWith('data-meta-')) {
const key = attr.name.replace('data-meta-', '');
metaTags[key] = attr.value;
}
}
})
//const title = temp.querySelector('[data-meta-title]')?.getAttribute('data-meta-title');
//const theme = temp.querySelector('[data-meta-theme]')?.getAttribute('data-meta-theme');
return { metaTags };
}
function animateLayoutSwitch(type) {
document.body.dataset.layout = type;
document.body.classList.add('layout-transition');
setTimeout(() => document.body.classList.remove('layout-transition'), 300);
}
/**
* Startet den Router
*/
export function startRouter() {
initClickManager((href, link, options) => {
if (!runGuard(href)) return;
if (options.modal) {
const handler = matchRoute(href);
currentRoute = { href, modal: true, link, options };
handler?.(currentRoute);
layoutCallback?.(currentRoute);
metaCallback?.(currentRoute);
} else {
navigateTo(href, {
...options,
onUpdate: (html) => {
const container = document.querySelector('main');
if (container) container.innerHTML = html;
const routeHandler = matchRoute(href);
currentRoute = { href, html, modal: false };
const meta = extractMetaFromHTML(html);
if (meta.title) document.title = meta.title;
if (meta.theme) document.documentElement.style.setProperty('--theme-color', meta.theme);
routeHandler?.(currentRoute);
layoutCallback?.(currentRoute);
metaCallback?.(currentRoute);
}
});
}
});
// Bei Seitenstart erste Route prüfen
window.addEventListener('DOMContentLoaded', () => {
const href = location.pathname;
const routeHandler = matchRoute(href);
currentRoute = { href, modal: false };
routeHandler?.(currentRoute);
layoutCallback?.(currentRoute);
metaCallback?.(currentRoute);
});
}
export { animateLayoutSwitch, extractMetaFromHTML };