Files
michaelschiemer/resources/js/modules/spa-router/index.js
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

159 lines
5.3 KiB
JavaScript

import { Logger } from '../../core/logger.js';
import { SPARouter } from './SPARouter.js';
import { getAdaptiveTransition, fastTransition } from './transition-config.js';
/**
* SPA Router Module
*
* Provides Single Page Application navigation:
* - Intercepts internal links and loads content via AJAX
* - Updates only the <main> element, keeping header/footer unchanged
* - Manages browser history (back/forward buttons work)
* - Shows skeleton loading states during navigation
* - Progressive enhancement (falls back to normal navigation on errors)
*
* Features:
* - Automatic link interception for all internal links (href="/...")
* - Opt-out via data-spa="false"
* - Browser history management with pushState
* - Loading states and smooth transitions
* - Module re-initialization after content changes
* - Skeleton loading animation
*
* Backend Integration:
* - Sends X-SPA-Request header to signal SPA navigation
* - Backend can return only <main> content for SPA requests
* - Falls back to full page load if SPA request fails
*/
const SPARouterModule = {
name: 'spa-router',
router: null,
initialized: false,
init(config = {}) {
// Prevent multiple initialization
if (this.initialized && this.router) {
Logger.warn('[SPARouterModule] SPA Router already initialized, returning existing instance');
return this.router;
}
Logger.info('[SPARouterModule] Initializing SPA Router');
// Default configuration mit schnelleren Transitions
const defaultConfig = {
containerSelector: 'main',
linkSelector: 'a[href^="/"]',
excludeSelector: '[data-spa="false"], [download], [target="_blank"], [href^="mailto:"], [href^="tel:"], [href^="#"]',
enableSkeletonLoading: true,
...fastTransition, // Verwende schnelle Transitions als Standard
...getAdaptiveTransition() // Überschreibe mit adaptiven Einstellungen
};
const options = { ...defaultConfig, ...config };
// Initialize router
this.router = SPARouter.create(options);
this.initialized = true;
// Set up global access
if (typeof window !== 'undefined') {
window.spaRouter = this.router;
}
// Listen for module re-initialization events
document.addEventListener('spa:reinit-module', this.handleModuleReinit.bind(this));
// Listen for navigation events for debugging
document.addEventListener('spa:navigated', this.handleNavigation.bind(this));
Logger.info('[SPARouterModule] SPA Router initialized successfully');
return this.router;
},
handleModuleReinit(event) {
const { element, moduleName } = event.detail;
Logger.info(`[SPARouterModule] Re-initializing module: ${moduleName}`, element);
// This would need access to the main module system
// For now, we'll just log and let the main init system handle it
// Trigger a custom event that the main module system can listen to
const reinitEvent = new CustomEvent('module:reinit-needed', {
detail: { element, moduleName },
bubbles: true
});
document.dispatchEvent(reinitEvent);
},
handleNavigation(event) {
const { url, timestamp } = event.detail;
Logger.info(`[SPARouterModule] Navigation completed to: ${url}`);
// Re-run auto form enhancement for new content
if (typeof window.initAutoFormHandling === 'function') {
// Only re-initialize forms that are not already enhanced
setTimeout(() => {
window.initAutoFormHandling();
}, 100);
}
// Trigger analytics or other tracking if needed
if (typeof window.gtag === 'function') {
window.gtag('config', 'GA_TRACKING_ID', {
page_path: new URL(url).pathname
});
}
},
// Public API methods
navigateTo(url, title) {
if (this.router) {
return this.router.navigateTo(url, title);
}
Logger.warn('[SPARouterModule] Router not initialized');
},
getCurrentUrl() {
return this.router?.getCurrentUrl() || window.location.href;
},
isNavigating() {
return this.router?.isNavigating() || false;
},
destroy() {
if (this.router) {
this.router.destroy();
this.router = null;
}
this.initialized = false;
// Remove global reference
if (typeof window !== 'undefined' && window.spaRouter) {
delete window.spaRouter;
}
document.removeEventListener('spa:reinit-module', this.handleModuleReinit);
document.removeEventListener('spa:navigated', this.handleNavigation);
Logger.info('[SPARouterModule] SPA Router destroyed');
}
};
// Export the router class for direct usage
export { SPARouter };
// Export as default for module system
export default SPARouterModule;
// Export init function directly for compatibility with module system
export const init = SPARouterModule.init.bind(SPARouterModule);
// Also export named for direct usage
export { SPARouterModule };