Files
michaelschiemer/resources/js/core/ClickManager.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

151 lines
3.9 KiB
JavaScript

// modules/core/click-manager.js
import { Logger } from './logger.js';
import { useEvent } from './useEvent.js';
import { navigateTo } from "./navigateTo";
import { LinkPrefetcher } from './LinkPrefetcher.js';
let callback = null;
let unsubscribes = [];
let prefetcher = null;
function isInternal(link) {
return link.origin === location.origin;
}
function handleClick(e) {
const link = e.target.closest('a');
if (!link || e.defaultPrevented) return;
const href = link.getAttribute('href');
if (!href || href.startsWith('#')) return;
// Skip conditions
if (
link.target === '_blank' ||
link.hasAttribute('download') ||
link.getAttribute('rel')?.includes('external') ||
link.hasAttribute('data-skip')
) {
Logger.info(`[click-manager] skipped: ${href}`);
return;
}
if (isInternal(link)) {
e.preventDefault();
const cached = prefetcher ? prefetcher.getCached(href) : null;
const options = {
viewTransition: link.hasAttribute('data-view-transition'),
replace: link.hasAttribute('data-replace'),
modal: link.hasAttribute('data-modal'),
prefetched: cached !== null,
data: cached,
};
Logger.info(`[click-manager] internal: ${href}`, options);
if(options.modal) {
callback?.(href, link, options);
} else {
navigateTo(href, options);
}
}
}
function handleMouseOver(e) {
const link = e.target.closest('a[href]');
if (!link || !prefetcher) return;
prefetcher.handleHover(link);
}
function handleMouseOut(e) {
const link = e.target.closest('a[href]');
if (!link || !prefetcher) return;
prefetcher.handleMouseLeave();
}
function handlePopState() {
const href = location.pathname;
Logger.info(`[click-manager] popstate: ${href}`);
navigateTo(href, { replace: true });
}
export function getPrefetched(href) {
return prefetcher ? prefetcher.getCached(href) : null;
}
export function prefetchHref(href) {
if (!href || !prefetcher) return;
prefetcher.prefetch(href, { priority: 'high' });
}
export function prefetchUrls(urls) {
if (!prefetcher) return;
prefetcher.prefetchEager(urls);
}
export function init(onNavigate, prefetchOptions = {}) {
callback = onNavigate;
// Initialize prefetcher with options
prefetcher = new LinkPrefetcher({
strategies: ['hover', 'visible'],
hoverDelay: 150,
observerMargin: '50px',
maxCacheSize: 20,
cacheTTL: 60000,
...prefetchOptions
});
unsubscribes = [
useEvent(document, 'click', handleClick),
useEvent(document, 'mouseover', handleMouseOver),
useEvent(document, 'mouseout', handleMouseOut),
useEvent(window, 'popstate', handlePopState),
];
// Observe all initial links if visible strategy is enabled
if (prefetchOptions.strategies?.includes('visible') !== false) {
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
prefetcher.observeLinks();
});
} else {
prefetcher.observeLinks();
}
}
// Support for eager prefetching via data attributes
document.querySelectorAll('a[data-prefetch="eager"]').forEach(link => {
const href = link.getAttribute('href');
if (href) {
prefetcher.prefetch(href, { priority: 'high' });
}
});
Logger.info('[click-manager] ready with prefetching');
}
export function destroy() {
callback = null;
if (prefetcher) {
prefetcher.destroy();
prefetcher = null;
}
unsubscribes.forEach(unsub => unsub());
unsubscribes = [];
Logger.info('[click-manager] destroyed');
}
// Export prefetcher for advanced usage
export function getPrefetcher() {
return prefetcher;
}