Declarative, functional, reactive abstractions for the DOM
yarn add @motorcycle/dom
# or
npm install --save @motorcycle/dom
All functions are curried!
A DOM source interface for objects to declaratively query the DOM.
interface DomSource {
query(cssSelector: CssSelector): DomSource
elements<El extends Element = Element>(): Stream<ReadonlyArray<El>>
events<Ev extends Event = Event>(eventType: StandardEvents, options?: EventListenerOptions): Stream<Ev>
cssSelectors(): ReadonlyArray<CssSelector>
}
Retrieves a list of all previously queried CSS selectors.
See an example
const queriedDomSource = domSource.query(`.myCssSelector`)
const cssSelectors = queriedDomSource.cssSelectors()
console.log(cssSelectors[0]) // .myCssSelector
See the code
cssSelectors(): ReadonlyArray<CssSelector>
}
export type CssSelector = string
Retrieves a stream of a list of elements matching previous queries.
NOTE: Elements will emit every single time the DOM is updated.
See an example
const queriedDomSource = domSource.query(`.myCssSelector`)
const elements$ = queriedDomSource.elements()
See the code
elements(): Stream<ReadonlyArray<A>>
DomSource.events<Ev extends Event = Event>(eventType: StandardEvents, options?: EventListenerOptions): Stream<Ev>
Retrieves a stream of events from elements matching previous queries.
DomSource.events
optionally takes a second parameter of EventListernerOptions
,
which specifies whether event listeners will listen to events during the
capturing phase. If not provided, all event listeners will use bubbling phase.
See an example
const queriedDomSource = domSource.query(`.myCssSelector`)
const clickEvent$: Stream<MouseEvent> = queriedDomSource.events<MouseEvent>('click')
See the code
events<Ev extends B = B>(eventType: StandardEvents, options?: EventListenerOptions): Stream<Ev>
Queries for elements and events for a specified CSS selector.
See an example
const queriedDomSource = domSource.query(`.myCssSelector`)
See the code
query<C extends A = A>(cssSelector: CssSelector): DomSource<C, B>
export type HistoryEffect = (history: History) => void
export type HistorySinks = {
readonly history$: Stream<HistoryEffect>
}
export type HistorySources<State = any> = {
readonly location$: Stream<Readonly<Location>>
readonly state$: Stream<State>
}
Standard event types defined by MDN. All browser should have these implemented the same.
export type StandardEvents =
// name - Event Types
| 'abort' // UIEvent, ProgressEvent, Event
| 'afterprint' // Event;
| 'animationend' // AnimationEvent
| 'animationiteration' // AnimationEvent
| 'animationstart' // AnimationEvent
| 'audioprocess' // AudioProcessingEvent
| 'audioend' // Event
| 'audiostart' // Event
| 'beforprint' // Event
| 'beforeunload' // BeforeUnloadEvent
| 'beginEvent' // TimeEvent
| 'blocked' // Event
| 'blur' // FocusEvent
| 'boundary' // SpeechsynthesisEvent
| 'cached' // Event
| 'canplay' // Event
| 'canplaythrough' // Event
| 'change' // Event
| 'chargingchange' // Event
| 'chargingtimechange' // Event
| 'checking' // Event
| 'click' // MouseEvent
| 'close' // Event
| 'complete' // Event, OfflineAudioCompletionEvent
| 'compositionend' // CompositionEvent
| 'compositionstart' // CompositionEvent
| 'compositionupdate' // CompositionEvent
| 'contextmenu' // MoustEvent
| 'copy' // ClipboardEvent
| 'cut' // ClipboardEvent
| 'dblclick' // MouseEvent
| 'devicechange' // Event
| 'devicelight' // DeviceLightEvent
| 'devicemotion' // DeviceMotionEvent
| 'deviceorientation' // DeviceOrientationEvent
| 'deviceproximity' // DeviceProximityEvent
| 'dischargingtimechange' // Event
| 'DOMActivate' // UIEvent
| 'DOMAttributeNameChanged' // MutationNameEvent
| 'DOMAttrModified' // Mutationevent
| 'DOMCharacterDataModified' // MutationEvent
| 'DOMContentLoaded' // Event
| 'DOMElementNamedChanged' // MutationNameEvent
| 'DOMNodeInserted' // MutationEvent
| 'DOMNodeInsertedIntoDocument' // MutationEvent
| 'DOMNodeRemoved' // MutationEvent
| 'DOMNodeRemovedFromDocument' // MutationEvent
| 'DOMSubtreeModified' // MutationEvent
| 'downloaded' // Event
| 'drag' // DragEvent
| 'dragend' // DragEvent
| 'dragenter' // DragEvent
| 'dragleave' // DragEvent
| 'dragover' // DragEvent
| 'dragstart' // DragEvent
| 'drop' // DragEvent
| 'durationchange' // Event
| 'emptied' // Event
| 'end' // Event, SpeechSynthesisEvent
| 'ended' // Event
| 'endEvent' // TimeEvent
| 'error' // UIEvent | ProgressEvent | Event
| 'focus' // FocusEvent
| 'fullscreenchange' // Event
| 'fullscreenerror' // Event
| 'gamepadconnected' // GamepadEvent
| 'gamepaddisconnected' // GamepadEvent
| 'gotpointercapture' // PointerEvent
| 'hashchange' // HashChangEvent
| 'lostpointercapture' // PointerEvent
| 'input' // event
| 'invalid' // Event
| 'keydown' // KeyboardEvent
| 'keypress' // KeyboardEvent
| 'keyup' // KeyboardEvent
| 'languagechange' // Event
| 'levelchange' // Event
| 'load' // UIEvent, ProgressEvent
| 'loadeddata' // Event
| 'loadedmetadata' // Event
| 'loadend' // ProgressEvent
| 'loadstart' // ProgressEvent
| 'mark' // SpeechSynthesisEvent
| 'message' // MessageEvent, ServiceWorkerMessageEvent, ExtendableMessageEvent
| 'mousedown' // MouseEvent
| 'mouseenter' // MouseEvent
| 'mouseleave' // MouseEvent
| 'mousemove' // MouseEvent
| 'mouseout' // MouseEvent
| 'mouseover' // Mouseevent
| 'nomatch' // SpeechRecognitionEvent
| 'notificationclick' // NotificationEvent
| 'noupdate' // event
| 'obsolete' // Event
| 'offline' // event
| 'online' // Event
| 'open' // event
| 'orientationchange' // Event
| 'pagehide' // PageTransitionEvent
| 'pageshow' // PageTransitionEvent
| 'paste' // ClipboardEvent
| 'pause' // Event, SpeechSynthesisEvent
| 'pointercancel' // PointerEvent
| 'pointerdown' // PointerEvent
| 'pointerenter' // PointerEvent
| 'pointerleave' // PointerEvent
| 'pointerlockchange' // Event
| 'pointerlockerror' // Event
| 'pointermove' // PointerEvent
| 'pointerout' // PointerEvent
| 'pointerover' // PointerEvent
| 'pointerup' // PointerEvent
| 'play' // Event
| 'playing' // Event
| 'popstate' // PopStateEvent
| 'progress' // ProgressEvent
| 'push' // PushEvent
| 'pushsubscriptionchange' // PushEvent
| 'ratechange' // Event
| 'readystatechange' // Event
| 'repeatEvent' // TimeEvent
| 'reset' // Event
| 'resize' // UIEvent
| 'resourcetimingbufferfull' // Performance
| 'result' // SpeechRecognitionEvent
| 'resume' // SpeechSynthesisEvent
| 'scroll' // UIEvent
| 'seeked' // Event
| 'seeking' // Event
| 'select' // UIEvent
| 'selectstart' // UIEvent
| 'selectionchange' // Event
| 'show' // MouseEvent
| 'soundend' // Event
| 'soundstart' // Event
| 'speechend' // Event
| 'speechstart' // Event
| 'stalled' // Event
| 'start' // SpeechSynthesisEvent
| 'storage' // StorageEvent
| 'submit' // Event
| 'success' // Event
| 'suspend' // Event
| 'SVGAbort' // SvgEvent
| 'SVGError' // SvgEvent
| 'SVGLoad' // SvgEvent
| 'SVGResize' // SvgEvent
| 'SVGScroll' // SvgEvent
| 'SVGUnload' // SvgEvent
| 'SVGZoom' // SvgEvent
| 'timeout' // ProgressEvent
| 'timeupdate' // Event
| 'touchcancel' // TouchEvent
| 'touchend' // TouchEvent
| 'touchenter' // TouchEvent
| 'touchleave' // TouchEvent
| 'touchmove' // TouchEvent
| 'touchstart' // TouchEvent ;
| 'transitionend' // Transitionevent
| 'unload' // UIEvent
| 'updateready' // Event
| 'upgradeneeded' // Event
| 'userproximity' // UserProximityEvent
| 'voiceschanged' // Event
| 'versionchange' // Event
| 'visibilitychange' // Event
| 'volumechange' // Event
| 'vrdisplayconnected' // Event
| 'vrdisplaydisconnected' // Event
| 'vrdisplaypresentchange' // Event
| 'waiting' // Event
| 'wheel' // WheelEvent
Takes in Stream\<Document\>
and produces a DocumentDomSource.
Stream\<Document\>
is required and allows for the developer to decide which
events cause the stream to emit.
See an example
import { createDocumentDomSource } from '@motorcycle/dom'
import { makeDomComponent } from '@motorcycle/mostly-dom'
import { constant } from '@motorcycle/stream'
import { UI } from './UI'
const element = document.querySelector('#app-container') as Element
const Dom = makeDomComponent(element)
function Effects(sinks) {
const { view$ } = sinks
const document$ = constant(document, view$)
const { dom } = Dom({ view$ })
return {
dom,
document: createDocumentDomSource(document$)
}
}
See the code
export function createDocumentDomSource(document$: Stream<Document>): DomSource<Document, Event> {
return new DocumentDomSource(document$)
}
Takes a stream of DOM Elements an returns a DomSource. This DomSource makes use of event delegation.
See an example
import { createDomSource } from '@motorcycle/dom'
const dom = createDomSource(element$)
See the code
export function createDomSource(element$: Stream<Element>): DomSource {
return new EventDelegationDomSource(element$, [])
}
Takes in Stream\<Window\>
and produces a WindowDomSource
.
Stream\<Document\>
is required and allows for the developer to decide which
events cause the stream to emit.
See an example
import { createWindowDomSource } from '@motorcycle/dom'
import { makeDomComponent } from '@motorcycle/mostly-dom'
import { constant } from '@motorcycle/stream'
import { UI } from './UI'
const element = document.querySelector('#app-container') as Element
const Dom = makeDomComponent(element)
function Effects(sinks) {
const { view$ } = sinks
const window$ = constant(window, view$)
const { dom } = Dom({ view$ })
return {
dom,
window: createWindowDomSource(window$)
}
}
See the code
export class WindowDomSource implements DomSource<Window, Event> {
public window$: Stream<Window>
constructor(window$: Stream<Window>) {
this.window$ = window$
}
public query(): DomSource<Window, Event> {
return this
}
public elements(): Stream<ReadonlyArray<Window>> {
return map(Array, this.window$)
}
public events<Ev extends Event = Event>(
eventType: StandardEvents,
options: EventListenerOptions = {}
): Stream<Ev> {
const { window$ } = this
const event$$ = map(window => new EventStream(eventType, window, options), window$)
return multicast(switchLatest(event$$))
}
public cssSelectors(): ReadonlyArray<CssSelector> {
return WINDOW_CSS_SELECTORS
}
}
Takes a DomSource and returns a stream of Array of elements matches previous queries.
See an example
import { DomSource, elements } from '@motorcycle/dom'
type Sources = { dom: DomSource }
function Component(sources: Sources) {
const { dom } = sources
const elements$ = elements(dom)
...
}
See the code
export function elements<A = Element, B = Event>(dom: DomSource<A, B>): Stream<ReadonlyArray<A>> {
return dom.elements()
}
Takes an event type and a DomSource and returns a stream of events.
See an example
import { events } from '@motorcycle/dom'
const click$ = events('click', dom)
See the code
export const events: Events = curry2(function<A = Element, B = Event>(
eventType: StandardEvents,
dom: DomSource<A, B>
): Stream<B> {
return dom.events<B>(eventType)
})
export interface Events {
<A = Element, B = Event>(eventType: StandardEvents, dom: DomSource<A, B>): Stream<B>
<A = Element, B = Event>(eventType: StandardEvents): (dom: DomSource<A, B>) => Stream<B>
(eventType: StandardEvents): <A = Element, B = Event>(dom: DomSource<A, B>) => Stream<B>
}
export const abortEvent = events<Element, UIEvent | ProgressEvent | Event>('abort')
export const afterPrintEvent = events<Element, Event>('afterprint')
export const animationEndEvent = events<Element, AnimationEvent>('animationend')
export const animationIterationEvent = events<Element, AnimationEvent>('animationiteration')
export const animationStartEvent = events<Element, AnimationEvent>('animationstart')
export const audioProcessEvent = events<Element, AudioProcessingEvent>('audioprocess')
export const audioEndEvent = events<Element, Event>('audioend')
export const audioStartEvent = events<Element, Event>('audiostart')
export const beforeUnloadEvent = events<Element, BeforeUnloadEvent>('beforeunload')
export const beginEvent = events<Element, Event>('beginEvent')
export const blockedEvent = events<Element, Event>('blocked')
export const blurEvent = events<Element, FocusEvent>('blur')
export const boundaryEvent = events<Element, SpeechSynthesisEvent>('boundary')
export const cachedEvent = events<Element, Event>('cached')
export const canPlayThroughEvent = events<Element, Event>('canplaythrough')
export const changeEvent = events<Element, Event>('change')
export const chargingChangeEvent = events<Element, Event>('chargingchange')
export const chargingTimeChangeEvent = events<Element, Event>('chargingtimechange')
export const checkingEvent = events<Element, Event>('checking')
export const clickEvent = events<Element, MouseEvent>('click')
export const closeEvent = events<Element, Event>('close')
export const completeEvent = events<Element, OfflineAudioCompletionEvent | Event>('complete')
export const compositionEndEvent = events<Element, CompositionEvent>('compositionend')
export const compositionStartEvent = events<Element, CompositionEvent>('compositionstart')
export const compositionUpdateEvent = events<Element, CompositionEvent>('compositionupdate')
export const contextMenuEvent = events<Element, MouseEvent>('contextmenu')
export const copyEvent = events<Element, ClipboardEvent>('copy')
export const cutEvent = events<Element, ClipboardEvent>('cut')
export const dblclickEvent = events<Element, MouseEvent>('dblclick')
export const deviceChangeEvent = events<Element, Event>('devicechange')
export const deviceLightEvent = events<Element, DeviceLightEvent>('devicelight')
export const deviceMotionEvent = events<Element, DeviceMotionEvent>('devicemotion')
export const deviceOrientationEvent = events<Element, DeviceOrientationEvent>('deviceorientation')
export const deviceProximityEvent = events<Element, DeviceMotionEvent>('deviceproximity')
export const dischargingTimeChangeEvent = events<Element, Event>('dischargingtimechange')
export const downloadedEvent = events<Element, Event>('downloaded')
export const dragEvent = events<Element, DragEvent>('drag')
export const dragEndEvent = events<Element, DragEvent>('dragend')
export const dragEnterEvent = events<Element, DragEvent>('dragenter')
export const dragLeaveEvent = events<Element, DragEvent>('dragleave')
export const dragOverEvent = events<Element, DragEvent>('dragover')
export const dragStartEvent = events<Element, DragEvent>('dragstart')
export const dropEvent = events<Element, DragEvent>('drop')
export const durationChangeEvent = events<Element, Event>('durationchange')
export const emptiedEvent = events<Element, Event>('emptied')
export const endEvent = events<Element, Event | SpeechSynthesisEvent>('end')
export const endedEvent = events<Element, Event>('ended')
export const focusEvent = events<Element, FocusEvent>('focus')
export const fullScreenChangeEvent = events<Element, Event>('fullscreenchange')
export const fullScreenErrorEvent = events<Element, Event>('fullscreenerror')
export const gamepadConnectedEvent = events<Element, GamepadEvent>('gamepadconnected')
export const gamepadDisconnectedEvent = events<Element, GamepadEvent>('gamepaddisconnected')
export const hashChangeEvent = events<Element, HashChangeEvent>('hashchange')
export const lostPointerCaptureEvent = events<Element, PointerEvent>('lostpointercapture')
export const inputEvent = events<Element, Event>('input')
export const invalidEvent = events<Element, Event>('invalid')
export const keyDownEvent = events<Element, KeyboardEvent>('keydown')
export const keyPressEvent = events<Element, KeyboardEvent>('keypress')
export const keyUpEvent = events<Element, KeyboardEvent>('keyup')
export const languageChangeEvent = events<Element, Event>('languagechange')
export const levelChangeEvent = events<Element, Event>('levelchange')
export const loadEvent = events<Element, UIEvent | ProgressEvent>('load')
export const loadedDataEvent = events<Element, Event>('loadeddata')
export const loadedMetadataEvent = events<Element, Event>('loadedmetadata')
export const loadEndEvent = events<Element, ProgressEvent>('loadend')
export const loadStartEvent = events<Element, ProgressEvent>('loadstart')
export const markEvent = events<Element, SpeechSynthesisEvent>('mark')
export const messageEvent = events<Element, MessageEvent | ServiceWorkerMessageEvent>('message')
export const mouseDownEvent = events<Element, MouseEvent>('mousedown')
export const mouseEnterEvent = events<Element, MouseEvent>('mouseenter')
export const mouseLeaveEvent = events<Element, MouseEvent>('mouseleave')
export const mouseMoveEvent = events<Element, MouseEvent>('mousemove')
export const mouseOutEvent = events<Element, MouseEvent>('mouseout')
export const mouseOverEvent = events<Element, MouseEvent>('mouseover')
export const noMatchEvent = events<Element, SpeechSynthesisEvent>('nomatch')
export const notificationClickEvent = events<Element, Event>('notificationclick')
export const noUpdateEvent = events<Element, Event>('noupdate')
export const obsoleteEvent = events<Element, Event>('obsolete')
export const offlineEvent = events<Element, Event>('offline')
export const onlineEvent = events<Element, Event>('online')
export const openEvent = events<Element, Event>('open')
export const orientationChangeEvent = events<Element, Event>('orientationchange')
export const pageShowEvent = events<Element, PageTransitionEvent>('pageshow')
export const pasteEvent = events<Element, ClipboardEvent>('paste')
export const pauseEvent = events<Element, Event | SpeechSynthesisEvent>('pause')
export const pointerCancelEvent = events<Element, PointerEvent>('pointercancel')
export const pointerDownEvent = events<Element, PointerEvent>('pointerdown')
export const pointerLockChangeEvent = events<Element, Event>('pointerlockchange')
export const pointerLockErrorEvent = events<Element, Event>('pointerlockerror')
export const pointerMoveEvent = events<Element, PointerEvent>('pointermove')
export const pointerOutEvent = events<Element, PointerEvent>('pointerout')
export const pointerOverEvent = events<Element, PointerEvent>('pointerover')
export const pointerUpEvent = events<Element, PointerEvent>('pointerup')
export const playEvent = events<Element, Event>('play')
export const playingEvent = events<Element, Event>('playing')
export const popStateEvent = events<Element, PopStateEvent>('popstate')
export const progressEvent = events<Element, ProgressEvent>('progress')
export const pushEvent = events<Element, Event>('push')
export const pushSubscriptionChangeEvent = events<Element, Event>('pushsubscriptionchange')
export const rateChangeEvent = events<Element, Event>('ratechange')
export const readyStateChangeEvent = events<Element, Event>('readystatechange')
export const resetEvent = events<Element, Event>('reset')
export const resizeEvent = events<Element, UIEvent>('resize')
export const resourceTimingBufferFullEvent = events<Element, Event>('resourcetimingbufferfull')
export const resultEvent = events<Element, Event>('result')
export const resumeEvent = events<Element, SpeechSynthesisEvent>('resume')
export const scrollEvent = events<Element, UIEvent>('scroll')
export const seekedEvent = events<Element, Event>('seeked')
export const seekingEvent = events<Element, Event>('seeking')
export const selectEvent = events<Element, UIEvent>('select')
export const selectStartEvent = events<Element, UIEvent>('selectstart')
export const selectionChangeEvent = events<Element, Event>('selectionchange')
export const showEvent = events<Element, MouseEvent>('show')
export const soundEndEvent = events<Element, Event>('soundend')
export const soundStartEvent = events<Element, Event>('soundstart')
export const speechEndEvent = events<Element, Event>('speechend')
export const stalledEvent = events<Element, Event>('stalled')
export const startEvent = events<Element, SpeechSynthesisEvent>('start')
export const storageEvent = events<Element, StorageEvent>('storage')
export const submitEvent = events<Element, Event>('submit')
export const successEvent = events<Element, Event>('success')
export const suspendEvent = events<Element, Event>('suspend')
export const timeoutEvent = events<Element, ProgressEvent>('timeout')
export const timeUpdateEvent = events<Element, Event>('timeupdate')
export const touchCancelEvent = events<Element, TouchEvent>('touchcancel')
export const touchEndEvent = events<Element, TouchEvent>('touchend')
export const touchEnterEvent = events<Element, TouchEvent>('touchenter')
export const touchLeaveEvent = events<Element, TouchEvent>('touchleave')
export const touchMoveEvent = events<Element, TouchEvent>('touchmove')
export const touchStartEvent = events<Element, TouchEvent>('touchstart')
export const transitionEndEvent = events<Element, TransitionEvent>('transitionend')
export const unloadEvent = events<Element, UIEvent>('unload')
export const updateReadyEvent = events<Element, Event>('updateready')
export const upgradeNeededEvent = events<Element, Event>('upgradeneeded')
export const userProximityEvent = events<Element, Event>('userproximity')
export const voicesChangedEvent = events<Element, Event>('voiceschanged')
export const versionChangeEvent = events<Element, Event>('versionchange')
export const visibilityChangeEvent = events<Element, Event>('visibilitychange')
export const volumeChangeEvent = events<Element, Event>('volumechange')
export const vrDisplayConnectedEvent = events<Element, Event>('vrdisplayconnected')
export const vrDisplayDisconnectedEvent = events<Element, Event>('vrdisplaydisconnected')
export const waitingEvent = events<Element, Event>('waiting')
export const wheelEvent = events<Element, WheelEvent>('wheel')
makeHistoryComponent<State = any>(location: Location, history: History): IOComponent<HistorySinks, HistorySources<State>>
Given implementations of the Location
and History
interfaces, it returns
an IOComponent function which facilitates performing side-effects with
the history API.
See an example
import { run } from '@motorcycle/run'
import { makeDomComponent, makeHistoryComponent } from '@motorcycle/dom'
import { UI } from './ui'
const rootElementSelector = '#app'
const element = document.querySelector(rootElementSelector)
if (!element) throw new Error(`Unable to find element by '${rootElementSelector}'`)
const Dom = makeDomComponent(element)
const History = makeHistoryComponent(location, history)
function IO(sinks) {
return {
...Dom(sinks),
...History(sinks),
}
}
run(UI, IO)
See the code
export function makeHistoryComponent<State = any>(
location: Location,
history: History
): IOComponent<HistorySinks, HistorySources<State>> {
const popState$: Stream<PopStateEvent> =
typeof window === void 0 ? empty() : new EventStream('popstate', window, {})
return function History(sinks: HistorySinks): HistorySources {
const { history$ } = sinks
const performHistoryEffect$ = multicast(tap(historyEffect => historyEffect(history), history$))
const updateSignal$ = multicast(merge<any>(performHistoryEffect$, popState$))
const location$ = hold(startWith(location, constant(location, updateSignal$)))
const state$ = hold(
map(({ state }) => state, startWith(history, constant(history, updateSignal$)))
)
drain(performHistoryEffect$)
return { location$, state$ }
}
}
A curried function for building more specific queries for elements.
See an example
import { DomSource, query, events } from '@motorcycle/dom'
type Sources = { dom: DomSource }
function Component(sources: Sources) {
const { dom } = sources
const button: DomSource = query('button', dom)
const event$ = events('click', button)
...
}
See the code
export const query: Query = curry2(function queryWrapper<A = Element, B = Event, C extends A = A>(
cssSelector: CssSelector,
domSource: DomSource<A, B>
): DomSource<C, B> {
return domSource.query<C>(cssSelector)
})
export interface Query {
<A = Element, B = Event, C = Element>(
cssSelector: CssSelector,
domSource: DomSource<A, B>
): DomSource<C, B>
<A = Element, B = Event, C = Element>(cssSelector: CssSelector): (
domSource: DomSource<A, B>
) => DomSource<C, B>
(cssSelector: CssSelector): <A = Element, B = Event, C = Element>(
domSource: DomSource<A, B>
) => DomSource<C, B>
}
Creates a new DomSource that will default to using
capture when using events()
.
See an example
import { useCapture, events } from '@motorcycle/dom'
export function Component(sources) {
const { dom } = sources
const click$ = events('click', useCapture(dom))
...
}
See the code
export function useCapture<A = Element, B = Event>(dom: DomSource<A, B>): DomSource<A, B> {
const useCaptureDomSource: DomSource<A, B> = {
query(cssSelector: CssSelector): DomSource<A, B> {
return dom.query(cssSelector)
},
elements(): Stream<ReadonlyArray<A>> {
return dom.elements()
},
events<Ev extends B = B>(
eventType: StandardEvents,
options: EventListenerOptions = { capture: true }
): Stream<Ev> {
return dom.events(eventType, options)
},
cssSelectors(): ReadonlyArray<CssSelector> {
return dom.cssSelectors()
},
}
return useCaptureDomSource
}