@cycle/dom
Version:
The standard DOM Driver for Cycle.js, based on Snabbdom
81 lines (75 loc) • 2.19 kB
text/typescript
import {Stream, Producer, Listener} from 'xstream';
export type Predicate = (ev: any) => boolean;
export type PreventDefaultOpt = boolean | Predicate | Comparator;
export type Comparator = {[key: string]: any};
export function fromEvent(
element: Element | Document,
eventName: string,
useCapture = false,
preventDefault: PreventDefaultOpt = false,
passive = false
): Stream<Event> {
let next: ((e: Event) => void) | null = null;
return Stream.create<Event>({
start: function start(listener: Listener<Event>) {
if (preventDefault) {
next = function _next(event: Event) {
preventDefaultConditional(event, preventDefault);
listener.next(event);
};
} else {
next = function _next(event: Event) {
listener.next(event);
};
}
element.addEventListener(eventName, next, {
capture: useCapture,
passive,
});
},
stop: function stop() {
element.removeEventListener(eventName, next as any, useCapture);
next = null;
},
} as Producer<Event>);
}
function matchObject(matcher: object, obj: object): boolean {
const keys = Object.keys(matcher);
const n = keys.length;
for (let i = 0; i < n; i++) {
const k = keys[i];
if (typeof matcher[k] === 'object' && typeof obj[k] === 'object') {
if (!matchObject(matcher[k], obj[k])) {
return false;
}
} else if (matcher[k] !== obj[k]) {
return false;
}
}
return true;
}
export function preventDefaultConditional(
event: any,
preventDefault: PreventDefaultOpt
): void {
if (preventDefault) {
if (typeof preventDefault === 'boolean') {
event.preventDefault();
} else if (isPredicate(preventDefault)) {
if (preventDefault(event)) {
event.preventDefault();
}
} else if (typeof preventDefault === 'object') {
if (matchObject(preventDefault, event)) {
event.preventDefault();
}
} else {
throw new Error(
'preventDefault has to be either a boolean, predicate function or object'
);
}
}
}
function isPredicate(fn: any): fn is Predicate {
return typeof fn === 'function';
}