type EventMap = HTMLElementEventMap;
type EventList = keyof EventMap;
/**
* `Event` with a strongly typed `target` known to exist.
*/
export interface EventOf extends Event {
target: TEl;
}
/**
* `CustomEvent` with a strongly typed `target` known to exist.
*/
export interface CustomEventOf extends CustomEvent {
target: TEl;
}
/**
* Emits a custom event with more convenient defaults.
*
* Gets type information from the global type {@link HTMLElementEventMap}
*/
export const emitEvent = (
el: HTMLElement,
name: R | K,
options?: T[R] extends CustomEvent ? CustomEventInit : CustomEventInit,
) => {
const event = new CustomEvent(
name,
{
bubbles: true,
cancelable: false,
composed: true,
detail: {},
...options,
},
);
el.dispatchEvent(event);
return event;
};
/**
* Waits for a specific event to be emitted from an element.
*
* Ignores events that bubble up from child elements.
*
* Gets type information from the global type {@link HTMLElementEventMap}
*/
export const waitForEvent = (
el: Window | HTMLElement,
eventName: R | K,
options?: {
bubbles?: boolean,
/** Predicate that can be used to terminate the waiting state. */
continue?: (ev: T[R] extends CustomEvent ? CustomEvent : T[R]) => boolean
},
) => {
type EventType = T[R] extends CustomEvent ? CustomEvent : T[R];
type EventReturnType = T[R] extends CustomEvent ? T[R]['detail'] | undefined : undefined;
return new Promise(resolve => {
const end = (details: EventReturnType) => {
el.removeEventListener(eventName, done as any);
resolve(details);
};
const done = (event: EventType) => {
if (options?.bubbles) {
if (event.currentTarget === window)
end(undefined);
if (options?.continue?.(event) ?? true)
end(undefined);
}
else {
if (event.target === el) {
if (options?.continue?.(event) ?? true) {
if (event instanceof CustomEvent)
end(event.detail);
else
end(undefined);
}
}
}
};
el.addEventListener(eventName, done as any);
});
};
/**
* Prevents default and stops propagation.
*
* @param immediate - By default all propagation is stopped. Pass `false` if you don't want to stop immediate propagation.
*/
export const setEventHandled = (e: Event, immediate = true) => {
e.preventDefault();
e.stopPropagation();
if (immediate)
e.stopImmediatePropagation();
};