82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
|
|
import {normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform';
|
|
import {NgZone} from '@angular/core';
|
|
|
|
/** Options used to bind a passive capturing event. */
|
|
const passiveCapturingEventOptions = normalizePassiveListenerOptions({
|
|
passive: true,
|
|
capture: true,
|
|
});
|
|
|
|
/** Manages events through delegation so that as few event handlers as possible are bound. */
|
|
export class RippleEventManager {
|
|
private _events = new Map<string, Map<HTMLElement, Set<EventListenerObject>>>();
|
|
|
|
/** Adds an event handler. */
|
|
addHandler(ngZone: NgZone, name: string, element: HTMLElement, handler: EventListenerObject) {
|
|
const handlersForEvent = this._events.get(name);
|
|
|
|
if (handlersForEvent) {
|
|
const handlersForElement = handlersForEvent.get(element);
|
|
|
|
if (handlersForElement) {
|
|
handlersForElement.add(handler);
|
|
} else {
|
|
handlersForEvent.set(element, new Set([handler]));
|
|
}
|
|
} else {
|
|
this._events.set(name, new Map([[element, new Set([handler])]]));
|
|
|
|
ngZone.runOutsideAngular(() => {
|
|
document.addEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);
|
|
});
|
|
}
|
|
}
|
|
|
|
/** Removes an event handler. */
|
|
removeHandler(name: string, element: HTMLElement, handler: EventListenerObject) {
|
|
const handlersForEvent = this._events.get(name);
|
|
|
|
if (!handlersForEvent) {
|
|
return;
|
|
}
|
|
|
|
const handlersForElement = handlersForEvent.get(element);
|
|
|
|
if (!handlersForElement) {
|
|
return;
|
|
}
|
|
|
|
handlersForElement.delete(handler);
|
|
|
|
if (handlersForElement.size === 0) {
|
|
handlersForEvent.delete(element);
|
|
}
|
|
|
|
if (handlersForEvent.size === 0) {
|
|
this._events.delete(name);
|
|
document.removeEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);
|
|
}
|
|
}
|
|
|
|
/** Event handler that is bound and which dispatches the events to the different targets. */
|
|
private _delegateEventHandler = (event: Event) => {
|
|
const target = _getEventTarget(event);
|
|
|
|
if (target) {
|
|
this._events.get(event.type)?.forEach((handlers, element) => {
|
|
if (element === target || element.contains(target as Node)) {
|
|
handlers.forEach(handler => handler.handleEvent(event));
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}
|