@taiga-ui/event-plugins
Version:
This is a library for optimizing performance sensitive events and declarative preventDefault and stopPropagation
379 lines (359 loc) • 14.9 kB
JavaScript
import { EventManagerPlugin, EVENT_MANAGER_PLUGINS } from '@angular/platform-browser';
import { DOCUMENT } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, Injectable, InjectionToken } from '@angular/core';
class LongtapEvent extends CustomEvent {
constructor(type, { clientX, clientY, ...eventInitDict }) {
super(type, { ...eventInitDict, detail: { clientX, clientY } });
}
}
const isIos = ({ userAgent, maxTouchPoints }) => /ipad|iphone|ipod/i.test(userAgent) ||
(/^((?!chrome|android).)*safari/i.test(userAgent) && maxTouchPoints > 1);
const TAP_DELAY = 700;
const SAFE_NAVIGATOR = typeof navigator === 'undefined' ? null : navigator;
const MOVE_THRESHOLD = 15;
class LongtapEventPlugin extends EventManagerPlugin {
constructor() {
super(...arguments);
this.isIOS = !!SAFE_NAVIGATOR && isIos(SAFE_NAVIGATOR);
}
addEventListener(element, _event, handler) {
const removeLongtapEventPolyfill = this.isIOS
? this.listenTouchEvents(element)
: this.listenContextmenuEvent(element);
element.addEventListener('longtap', handler);
return () => {
removeLongtapEventPolyfill();
element.removeEventListener('longtap', handler);
};
}
supports(event) {
return event === 'longtap';
}
listenContextmenuEvent(element) {
return this.manager.addEventListener(element, 'contextmenu.prevent.stop', ({ clientX, clientY }) => {
this.dispatchLongtapEvent(element, clientX, clientY);
});
}
listenTouchEvents(element) {
let longTapTimeout = null;
let touchStartCoords = null;
const reset = () => {
clearTimeout(longTapTimeout);
touchStartCoords = null;
longTapTimeout = null;
};
const removeTouchstartListener = this.manager.addEventListener(element, 'touchstart.zoneless.passive', ({ touches }) => {
const touch = touches[0];
if (!touch) {
return;
}
const { clientX, clientY } = touch;
touchStartCoords = { clientX, clientY };
longTapTimeout = setTimeout(() => {
this.dispatchLongtapEvent(element, clientX, clientY);
reset();
}, TAP_DELAY);
});
const removeTouchmoveListener = this.manager.addEventListener(element, 'touchmove.zoneless.passive', ({ touches }) => {
const touch = touches[0];
if (!touch || !touchStartCoords) {
return;
}
const { clientX, clientY } = touch;
if (Math.hypot(clientX - touchStartCoords.clientX, clientY - touchStartCoords.clientY) <= MOVE_THRESHOLD) {
return;
}
reset();
});
const removeTouchcancelListener = this.manager.addEventListener(element, 'touchcancel.zoneless.passive', reset);
const removeTouchendListener = this.manager.addEventListener(element, 'touchend.zoneless.passive', reset);
return () => {
removeTouchstartListener();
removeTouchmoveListener();
removeTouchcancelListener();
removeTouchendListener();
};
}
dispatchLongtapEvent(element, clientX, clientY) {
element.dispatchEvent(new LongtapEvent('longtap', {
clientX,
clientY,
bubbles: false,
cancelable: false,
composed: false,
}));
}
}
class TimedEventPlugin extends EventManagerPlugin {
supports(event) {
return this.regExp.test(event);
}
getDelay(event) {
const match = this.regExp.exec(event);
if (!match?.groups) {
throw new Error(`Invalid event: ${event}`);
}
const { time, units } = match.groups;
switch (units) {
case 'ms':
return Number(time);
case 's':
return Number(time) * 1000;
default:
throw new Error(`Invalid event: ${event}`);
}
}
unwrap(event) {
return event.replace(this.regExp, '');
}
}
class DebounceEventPlugin extends TimedEventPlugin {
constructor() {
super(...arguments);
this.regExp = /\.debounce~(?<time>\d+)(?<units>ms|s)/;
}
addEventListener(element, eventName, handler) {
let timeout;
const unsubscribe = this.manager.addEventListener(element, this.unwrap(eventName), (event) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
handler(event);
}, this.getDelay(eventName));
});
return () => {
clearTimeout(timeout);
unsubscribe();
};
}
}
class AbstractEventPlugin extends EventManagerPlugin {
constructor() {
super(inject(DOCUMENT));
}
supports(event) {
return event.includes(this.modifier);
}
unwrap(event) {
return event
.split('.')
.filter((v) => !this.modifier.includes(v))
.join('.');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AbstractEventPlugin, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AbstractEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AbstractEventPlugin, decorators: [{
type: Injectable
}], ctorParameters: () => [] });
/// <reference types="@taiga-ui/tsconfig/ng-dev-mode" />
const GLOBAL_HANDLER = new InjectionToken(ngDevMode ? '[GLOBAL_HANDLER]: Global event target handler' : '', {
factory: () => {
const document = inject(DOCUMENT);
return (name) => name
.split('.')
.reduce((obj, prop) => obj?.[prop], document.defaultView);
},
});
class GlobalEventPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.handler = inject(GLOBAL_HANDLER);
this.modifier = '>';
}
addEventListener(_, event, handler) {
return this.manager.addEventListener(this.handler(event.split('>')[0]), event.split('>')?.[1] ?? '', handler);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GlobalEventPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GlobalEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GlobalEventPlugin, decorators: [{
type: Injectable
}] });
class OptionsEventPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = 'capture.once.passive';
}
supports(event) {
return event.includes('.') && !this.unwrap(event).includes('.');
}
addEventListener(element, event, handler) {
const unwrap = this.unwrap(event);
const capture = event.includes('.capture');
element.addEventListener(unwrap, handler, {
capture,
once: event.includes('.once'),
passive: event.includes('.passive'),
});
return () => element.removeEventListener(unwrap, handler, { capture });
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OptionsEventPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OptionsEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OptionsEventPlugin, decorators: [{
type: Injectable
}] });
class PreventEventPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = '.prevent';
}
addEventListener(element, event, handler) {
return this.manager.addEventListener(element, this.unwrap(event), (event) => {
event.preventDefault();
handler(event);
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PreventEventPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PreventEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: PreventEventPlugin, decorators: [{
type: Injectable
}] });
class ResizePlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = 'resize';
}
supports(event) {
return event === 'resize';
}
addEventListener(element, event, handler) {
if (typeof ResizeObserver === 'undefined' || !(element instanceof Element)) {
element.addEventListener(event, handler);
return () => element.removeEventListener(event, handler);
}
const observer = new ResizeObserver(handler);
observer.observe(element);
return () => observer.disconnect();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizePlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizePlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizePlugin, decorators: [{
type: Injectable
}] });
class SelfEventPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = '.self';
}
addEventListener(element, event, handler) {
return this.manager.addEventListener(element, this.unwrap(event), (event) => {
if (event.target === event.currentTarget) {
handler(event);
}
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SelfEventPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SelfEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SelfEventPlugin, decorators: [{
type: Injectable
}] });
class StopEventPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = '.stop';
}
addEventListener(element, event, handler) {
return this.manager.addEventListener(element, this.unwrap(event), (event) => {
event.stopPropagation();
handler(event);
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StopEventPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StopEventPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: StopEventPlugin, decorators: [{
type: Injectable
}] });
class ThrottleEventPlugin extends TimedEventPlugin {
constructor() {
super(...arguments);
this.regExp = /\.throttle~(?<time>\d+)(?<units>ms|s)/;
}
addEventListener(element, eventName, handler) {
let timeout;
const unsubscribe = this.manager.addEventListener(element, this.unwrap(eventName), (event) => {
if (timeout !== undefined) {
return;
}
handler(event);
timeout = setTimeout(() => {
timeout = undefined;
}, this.getDelay(eventName));
});
return () => {
clearTimeout(timeout);
unsubscribe();
};
}
}
class ZonelessPlugin extends AbstractEventPlugin {
constructor() {
super(...arguments);
this.modifier = '.zoneless';
}
addEventListener(element, event, handler) {
ZonelessPlugin.ngZone = this.manager.getZone();
return ZonelessPlugin.ngZone?.runOutsideAngular(() => this.manager.addEventListener(element, this.unwrap(event), handler));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ZonelessPlugin, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ZonelessPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ZonelessPlugin, decorators: [{
type: Injectable
}] });
const PLUGINS = [
ZonelessPlugin,
SelfEventPlugin,
GlobalEventPlugin,
OptionsEventPlugin,
PreventEventPlugin,
ResizePlugin,
StopEventPlugin,
LongtapEventPlugin,
DebounceEventPlugin,
ThrottleEventPlugin,
];
/**
* @deprecated: use {@link provideEventPlugins}
*/
const NG_EVENT_PLUGINS = PLUGINS.map((useClass) => ({
provide: EVENT_MANAGER_PLUGINS,
multi: true,
useClass,
}));
function provideEventPlugins() {
return NG_EVENT_PLUGINS;
}
function shouldCall(predicate) {
return (original, _context, desc) => {
const value = desc?.value || original;
const result = function (...args) {
if (!predicate.apply(this, args)) {
return;
}
if (ZonelessPlugin.ngZone) {
ZonelessPlugin.ngZone.run(() => value.apply(this, args));
}
else {
value.apply(this, args);
}
};
if (!desc) {
return result;
}
desc.value = result;
};
}
function asCallable(a) {
return a;
}
/**
* Generated bundle index. Do not edit.
*/
export { AbstractEventPlugin, DebounceEventPlugin, GLOBAL_HANDLER, GlobalEventPlugin, LongtapEvent, NG_EVENT_PLUGINS, OptionsEventPlugin, PreventEventPlugin, ResizePlugin, SelfEventPlugin, StopEventPlugin, ThrottleEventPlugin, TimedEventPlugin, ZonelessPlugin, asCallable, provideEventPlugins, shouldCall };
//# sourceMappingURL=taiga-ui-event-plugins.mjs.map