Thanks to visit codestin.com
Credit goes to github.com

Skip to content

WIP: qwik on-window: and on-document: #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 160 additions & 5 deletions packages/async-framework/loader/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ export class AsyncLoader {
private context: any;
private domRoot: Element | HTMLElement;
private config: AsyncLoaderConfig;

// TODO: Add new property to track global event handlers
private globalHandlers: Map<
string,
Set<{ element: Element; attrValue: string }>
> = new Map();

constructor(config: AsyncLoaderConfig) {
this.config = config;
this.context = config.context || {};
Expand Down Expand Up @@ -308,6 +315,36 @@ export class AsyncLoader {
},
true, // Use capturing phase to ensure the handler runs before other listeners
);
// TODO: figure out if we need to add document event listeners
// document.addEventListener(
// eventName,
// function newHandleContainerEvent(event) {
// // TODO: we may not need to parse the container anymore for the event type
// // when doing lazy event registration

// // Lazy parse the element for the event type before handling the event
// self.parseDocumentEvent(containerElement, eventName);
// // Handle the event when it occurs
// self.handleContainerEvent(containerElement, event);
// // console.log('setupContainerListeners: event handled', res);
// },
// true, // Use capturing phase to ensure the handler runs before other listeners
// );
window.addEventListener(
eventName,
function newHandleContainerEvent(event) {
// TODO: we may not need to parse the container anymore for the event type
// when doing lazy event registration

// Lazy parse the element for the event type before handling the event
self.parseWindowEvent(containerElement, eventName);
// Handle the event when it occurs
self.handleContainerEvent(containerElement, event);
// console.log('setupContainerListeners: event handled', res);
},
true, // Use capturing phase to ensure the handler runs before other listeners
);

});
success = true;
return success;
Expand All @@ -319,9 +356,10 @@ export class AsyncLoader {
// 2. Registering handler associations in the event map
// 3. Supporting lazy parsing for better performance
// This enables dynamic handler registration without requiring immediate processing of all elements.
parseContainerElement(containerElement, eventName) {
parseContainerElement(containerElement: Element, eventName: string) {
const self = this;
// Select elements with 'on:{event}' attributes for example 'on:click'

// Handle regular events (existing code)
const eventAttr = `${self.eventPrefix}${eventName}`;
const elements = self.query(
containerElement,
Expand All @@ -335,9 +373,35 @@ export class AsyncLoader {
self.addEventData(containerElement, eventName, element, eventAttrValue);
}
});

// Mark this event as processed for this container
// processedEvents.add(eventName);
}
parseWindowEvent(containerElement: Element, eventName: string) {
const self = this;
// Handle window events
const windowEventAttr = `on-window:${eventName}`;
const windowElements = self.query(
containerElement,
`[${escapeSelector(windowEventAttr)}]`,
);
windowElements.forEach(function (element: Element) {
const attrValue = element.getAttribute(windowEventAttr);
if (attrValue) {
self.setupGlobalEventHandler("window", eventName, element, attrValue);
}
});
}
parseDocumentEvent(containerElement: Element, eventName: string) {
const self = this;
const documentEventAttr = `on-document:${eventName}`;
const documentElements = self.query(
containerElement,
`[${escapeSelector(documentEventAttr)}]`,
);
documentElements.forEach(function (element: Element) {
const attrValue = element.getAttribute(documentEventAttr);
if (attrValue) {
self.setupGlobalEventHandler("document", eventName, element, attrValue);
}
});
}

// Registers event listeners for specific elements within a container
Expand Down Expand Up @@ -408,6 +472,7 @@ export class AsyncLoader {
// 4. Ensuring events reach all registered handlers
// This provides a reliable system for cross-component communication.
dispatch(eventName: string | CustomEvent, detail?: any) {
console.log('dispatch: dispatching event', eventName);
// create the custom event
let customEvent;
let success = false;
Expand Down Expand Up @@ -441,6 +506,14 @@ export class AsyncLoader {
},
true, // Use capturing phase to ensure the handler runs before other listeners
);
window.addEventListener(
eventName,
function newHandleContainerWindowEvent(event) {
console.log('dispatch: handling window event', eventName);
self.handleContainerEvent(containerElement, event);
},
true,
);
// add the event to the events array
}
// console.log('dispatch: parsing container elements for event', eventName);
Expand Down Expand Up @@ -647,4 +720,86 @@ export class AsyncLoader {
element = element.parentElement;
}
}

// Add new method to set up global event handlers
private setupGlobalEventHandler(
target: "window" | "document",
eventName: string,
element: Element,
attrValue: string,
) {
const key = `${target}:${eventName}`;
let handlers = this.globalHandlers.get(key);

if (!handlers) {
handlers = new Set();
this.globalHandlers.set(key, handlers);

// Add the actual event listener to window/document
const targetObj = target === "window" ? window : document;
targetObj.addEventListener(eventName, async (event) => {
// Get current handlers for this event
const currentHandlers = this.globalHandlers.get(key);
if (!currentHandlers) return;

// Execute all handlers
for (const handler of currentHandlers) {
if (!handler.element.isConnected) {
// Clean up disconnected elements
currentHandlers.delete(handler);
continue;
}

try {
const context = {
value: event instanceof CustomEvent ? event.detail : undefined,
attrValue: handler.attrValue,
dispatch: this.dispatch.bind(this),
element: handler.element,
event,
eventName,
handlers: this.handlerRegistry,
signals: this.signalRegistry,
container:
handler.element.closest(`[${this.containerAttribute}]`) ||
document.body,
module: undefined,
break: () => false,
preventDefault: () => event.preventDefault(),
stopPropagation: () => event.stopPropagation(),
target: event.target,
};

const res = this.handlerRegistry.handler(context);
if (isPromise(res)) {
await res;
}
} catch (error) {
console.error(
`Error in ${target} handler for ${eventName}:`,
error,
);
}
}
}, true);
}

// Add this element's handler to the set
handlers.add({ element, attrValue });
}

// Add cleanup method for global handlers
cleanup() {
// Clean up global event listeners
this.globalHandlers.forEach((handlers, key) => {
const [target, eventName] = key.split(":");
const targetObj = target === "window" ? window : document;
targetObj.removeEventListener(eventName, handlers as any);
});
this.globalHandlers.clear();

// Clean up container event listeners
this.containers.clear();
this.processedContainers = new WeakSet();
}
}