Skip to content

Commit b8f1f66

Browse files
committed
Add component loader and improve router initialization and cleanup
1 parent 10fbee9 commit b8f1f66

File tree

3 files changed

+249
-15
lines changed

3 files changed

+249
-15
lines changed

public/js/components-loader.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Components Loader
3+
*
4+
* This file imports all web components used in the application
5+
* to ensure they are registered before they are used.
6+
*/
7+
8+
// Import all components
9+
import './components/pf-header.js';
10+
import './components/pf-footer.js';
11+
import './components/pf-hero.js';
12+
import './components/pf-dialog.js';
13+
import './components/language-switcher.js';
14+
import './components/api-key-manager.js';
15+
import './components/subscription-form.js';
16+
import './components/simple-counter.js';
17+
import './components/tab-container.js';
18+
import './components/document-editor.js';
19+
import './components/document-history.js';
20+
import './components/html-editor.js';
21+
import './components/markdown-editor.js';
22+
import './components/slides-editor.js';
23+
import './components/table-editor.js';
24+
import './components/state-example.js';
25+
import './components/about-section.js';
26+
27+
console.log('All components loaded and registered');

public/js/main.js

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,27 @@
33
*/
44
import { initI18n } from './i18n-setup.js';
55
import { createRouter, defineRoutes } from './router.js';
6+
import './components-loader.js'; // Import all components
7+
8+
// Initialize the application
9+
let initialized = false;
610

711
// Wait for DOM to be fully loaded before initializing
812
document.addEventListener('DOMContentLoaded', () => {
913
console.log('DOM fully loaded, initializing app');
10-
initApp();
14+
if (!initialized) {
15+
initialized = true;
16+
initApp();
17+
}
1118
});
1219

1320
// Also initialize immediately to handle direct navigation
1421
if (document.readyState === 'complete' || document.readyState === 'interactive') {
1522
console.log('Document already ready, initializing app');
16-
initApp();
23+
if (!initialized) {
24+
initialized = true;
25+
initApp();
26+
}
1727
}
1828

1929
/**
@@ -24,26 +34,70 @@ function initApp() {
2434
initI18n().then(() => {
2535
console.log('i18n initialized');
2636

27-
// Create and initialize the router
37+
// Create the router with initialization disabled
2838
const router = createRouter({
2939
rootElement: '#app',
30-
transitionDuration: 300
40+
transitionDuration: 300,
41+
disableAutoInit: true // Disable auto-initialization
3142
});
3243

33-
// Define routes
44+
// Define routes BEFORE initializing
45+
console.log('Defining routes before initialization');
3446
defineRoutes(router);
3547

3648
// Expose router globally
3749
window.router = router;
3850

39-
console.log('Application initialized successfully');
51+
// Manually initialize the router after routes are defined
52+
console.log('Manually initializing router');
53+
router.init();
54+
55+
// Wait a moment to ensure routes are registered and any loading state is cleared
56+
setTimeout(() => {
57+
// Reset loading state if needed
58+
if (router.loading) {
59+
console.log('Resetting router loading state before navigation');
60+
router.loading = false;
61+
}
62+
63+
// Navigate to the current path
64+
console.log('Navigating to:', window.location.pathname);
65+
router.navigate(window.location.pathname, false);
66+
67+
console.log('Application initialized successfully');
68+
}, 100); // Increased timeout to ensure loading state is cleared
4069
}).catch(error => {
4170
console.error('Error initializing i18n:', error);
4271

4372
// Initialize router anyway to allow basic navigation
44-
const router = createRouter();
73+
const router = createRouter({
74+
rootElement: '#app',
75+
transitionDuration: 300,
76+
disableAutoInit: true
77+
});
78+
79+
// Define routes BEFORE initializing
80+
console.log('Defining routes before initialization (fallback)');
4581
defineRoutes(router);
82+
4683
window.router = router;
84+
85+
// Manually initialize the router after routes are defined
86+
console.log('Manually initializing router (fallback)');
87+
router.init();
88+
89+
// Wait a moment to ensure routes are registered and any loading state is cleared
90+
setTimeout(() => {
91+
// Reset loading state if needed
92+
if (router.loading) {
93+
console.log('Resetting router loading state before navigation (fallback)');
94+
router.loading = false;
95+
}
96+
97+
// Navigate to the current path
98+
console.log('Navigating to:', window.location.pathname);
99+
router.navigate(window.location.pathname, false);
100+
}, 100); // Increased timeout to ensure loading state is cleared
47101
});
48102
}
49103

public/js/router.js

Lines changed: 161 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,30 +66,168 @@ async function loadPage(url) {
6666
}
6767
}
6868

69+
/**
70+
* Clean up any transition overlays
71+
*/
72+
function cleanupOverlays() {
73+
// Remove any transition overlays
74+
const overlays = document.querySelectorAll('.transition-overlay');
75+
console.log('Found transition overlays:', overlays.length);
76+
77+
overlays.forEach(overlay => {
78+
if (document.body.contains(overlay)) {
79+
console.log('Removing transition overlay');
80+
document.body.removeChild(overlay);
81+
}
82+
});
83+
84+
// Also check for any elements with opacity or visibility styles that might be leftover
85+
document.querySelectorAll('[style*="opacity: 0"]').forEach(el => {
86+
if (el.classList.contains('transition-overlay') || el.style.position === 'absolute') {
87+
console.log('Removing hidden element with opacity 0');
88+
if (el.parentNode) {
89+
el.parentNode.removeChild(el);
90+
}
91+
}
92+
});
93+
94+
// Remove the initial loading overlay if it exists
95+
const initialOverlay = document.getElementById('initial-loading-overlay');
96+
if (initialOverlay && initialOverlay.parentNode) {
97+
console.log('Removing initial loading overlay');
98+
initialOverlay.style.opacity = '0';
99+
setTimeout(() => {
100+
if (initialOverlay.parentNode) {
101+
initialOverlay.parentNode.removeChild(initialOverlay);
102+
}
103+
}, 150);
104+
}
105+
}
106+
69107
/**
70108
* Create and initialize the router
71109
* @param {Object} options - Router options
72110
* @returns {Router} Router instance
73111
*/
74112
export function createRouter(options = {}) {
113+
console.log('Creating router with options:', options);
114+
115+
// Create a custom fade transition that ensures overlays are cleaned up
116+
const customFade = transitions.fade({
117+
duration: options.transitionDuration || 300,
118+
onComplete: () => {
119+
// Clean up any overlays
120+
cleanupOverlays();
121+
122+
// Dispatch a custom event when the transition is complete
123+
document.dispatchEvent(new CustomEvent('spa-transition-end'));
124+
}
125+
});
126+
75127
// Create the router
76128
const router = new Router({
77129
rootElement: options.rootElement || '#app',
78-
transition: transitions.fade({
79-
duration: options.transitionDuration || 300,
80-
onComplete: () => {
81-
document.dispatchEvent(new CustomEvent('spa-transition-end'));
82-
}
83-
}),
130+
transition: customFade,
84131
renderer: renderer.createRenderer({
85132
translateContainer: localizer.translateContainer.bind(localizer),
86133
applyRTLToDocument: localizer.applyRTLToDocument.bind(localizer)
87134
}),
88-
errorHandler: renderer.createErrorHandler()
135+
errorHandler: (path) => {
136+
console.log('Custom error handler called for path:', path);
137+
138+
// Clean up any overlays immediately
139+
cleanupOverlays();
140+
141+
// Set up a safety interval to periodically check for and remove any overlays
142+
const safetyInterval = setInterval(cleanupOverlays, 500);
143+
144+
// Clear the safety interval after 3 seconds
145+
setTimeout(() => {
146+
clearInterval(safetyInterval);
147+
console.log('Safety interval cleared');
148+
}, 3000);
149+
150+
return `
151+
<pf-header></pf-header>
152+
<div class="content-container" style="display: flex; justify-content: center; align-items: center; min-height: 60vh;">
153+
<div class="error-page">
154+
<h1>404 - Page Not Found</h1>
155+
<p>The page "${path}" could not be found.</p>
156+
<a href="/" class="back-link">Go back to home</a>
157+
</div>
158+
</div>
159+
<pf-footer></pf-footer>
160+
`;
161+
}
89162
});
90163

164+
// Store the original init method
165+
const originalInit = router.init;
166+
167+
// Override the init method to do nothing if disableAutoInit is true
168+
if (options.disableAutoInit) {
169+
console.log('Auto-initialization disabled');
170+
router.init = function() {
171+
console.log('Manual initialization called');
172+
173+
// Add event listeners for popstate and clicks
174+
window.addEventListener('popstate', (e) => {
175+
console.log('Popstate event, navigating to:', window.location.pathname);
176+
this.navigate(window.location.pathname, false);
177+
});
178+
179+
// Intercept all clicks at the document level
180+
document.addEventListener('click', (e) => {
181+
// Skip if modifier keys are pressed
182+
if (e.metaKey || e.ctrlKey || e.shiftKey) return;
183+
184+
// Find anchor element in the event path
185+
const path = e.composedPath();
186+
let anchor = null;
187+
188+
for (let i = 0; i < path.length; i++) {
189+
if (path[i].tagName === 'A') {
190+
anchor = path[i];
191+
break;
192+
}
193+
}
194+
195+
// Skip if no anchor found
196+
if (!anchor) return;
197+
198+
// Get the href attribute
199+
const href = anchor.getAttribute('href');
200+
201+
// Skip if no href
202+
if (!href) return;
203+
204+
// Skip if it's an external link
205+
if (href.startsWith('http') || href.startsWith('//')) return;
206+
207+
// Skip if it has a target
208+
if (anchor.hasAttribute('target')) return;
209+
210+
// Skip if it's a download link
211+
if (anchor.hasAttribute('download')) return;
212+
213+
// Skip if it's an anchor link
214+
if (href.startsWith('#')) return;
215+
216+
// Prevent default behavior
217+
e.preventDefault();
218+
219+
// Navigate to the link
220+
this.navigate(href);
221+
222+
console.log('Intercepted click on link:', href);
223+
}, { capture: true });
224+
};
225+
}
226+
91227
// Add middleware for translations
92228
router.use(async (to, from, next) => {
229+
console.log(`Router middleware: from ${from || 'initial'} to ${to.path}`);
230+
93231
// Dispatch pre-navigation event
94232
document.dispatchEvent(new CustomEvent('pre-navigation', {
95233
detail: { fromPath: from || '', toPath: to.path }
@@ -105,9 +243,17 @@ export function createRouter(options = {}) {
105243
}, { once: true });
106244
});
107245

108-
// Override navigate method to dispatch events
246+
// Override navigate method to dispatch events and handle loading state
109247
const originalNavigate = router.navigate.bind(router);
110248
router.navigate = async function(path, params = {}) {
249+
console.log(`Custom navigate method called for path: ${path}`);
250+
251+
// Reset loading state if needed
252+
if (this.loading) {
253+
console.log('Resetting loading state before navigation');
254+
this.loading = false;
255+
}
256+
111257
document.dispatchEvent(new CustomEvent('pre-navigation', {
112258
detail: { fromPath: window.location.pathname, toPath: path }
113259
}));
@@ -123,6 +269,8 @@ export function createRouter(options = {}) {
123269
* @param {Router} router - Router instance
124270
*/
125271
export function defineRoutes(router) {
272+
console.log('Defining routes...');
273+
126274
// Define routes
127275
const routes = {
128276
'/': {
@@ -220,8 +368,13 @@ export function defineRoutes(router) {
220368
}
221369
});
222370

371+
console.log('Routes defined:', Object.keys(routes));
372+
223373
// Register routes
224374
router.registerRoutes(routes);
225375

376+
// Debug: Log registered routes
377+
console.log('Routes registered:', Object.keys(router.routes));
378+
226379
return router;
227380
}

0 commit comments

Comments
 (0)