From 0b78b92b1b2eef6a7146f1ec1c8d5f16f6ca3c6f Mon Sep 17 00:00:00 2001 From: "christian.hausknecht" Date: Thu, 18 Apr 2024 18:35:06 +0200 Subject: [PATCH] Repairs event handling with focus of manipulation and filtering - enables filtering and manipulating events immediately after occurrence due to specialized event factories. Prevent `Flow` based delay we had until now. - simplifies `Listener`-type. It remains more or less as a marker type, in order to dispatch the convenience functions to grab values out of specific DOM elements. - get rid of unnecessary convenience functions - remove `@ignore` from one test case as the cause is now fixed - add a new dedicated documentation chapter for event handling including `Key`-API --- .../jsMain/kotlin/dev/fritz2/core/events.kt | 3902 +++++++++++++++-- .../jsMain/kotlin/dev/fritz2/core/listener.kt | 112 +- .../src/jsMain/kotlin/dev/fritz2/core/tags.kt | 9 +- .../jsTest/kotlin/dev/fritz2/core/events.kt | 83 +- .../headless/components/checkboxGroup.kt | 15 +- .../fritz2/headless/components/disclosure.kt | 4 +- .../dev/fritz2/headless/components/listbox.kt | 2 +- .../dev/fritz2/headless/components/menu.kt | 4 +- .../dev/fritz2/headless/components/popOver.kt | 2 +- .../dev/fritz2/headless/components/switch.kt | 13 +- .../fritz2/headless/components/tabGroup.kt | 22 +- .../dev/fritz2/headless/components/toast.kt | 8 +- .../fritz2/headless/foundation/OpenClose.kt | 21 +- www/src/pages/docs/30_Render HTML.md | 4 +- www/src/pages/docs/45_EventHandling.md | 452 ++ 15 files changed, 4176 insertions(+), 477 deletions(-) create mode 100644 www/src/pages/docs/45_EventHandling.md diff --git a/core/src/jsMain/kotlin/dev/fritz2/core/events.kt b/core/src/jsMain/kotlin/dev/fritz2/core/events.kt index 25cc64a67..0f84b8196 100644 --- a/core/src/jsMain/kotlin/dev/fritz2/core/events.kt +++ b/core/src/jsMain/kotlin/dev/fritz2/core/events.kt @@ -21,808 +21,4062 @@ import org.w3c.xhr.ProgressEvent */ interface WithEvents { + companion object { + private const val AFTERPRINT = "afterprint" + private const val BEFOREPRINT = "beforeprint" + private const val BEFOREUNLOAD = "beforeunload" + private const val BLUR = "blur" + private const val CANPLAY = "canplay" + private const val CANPLAYTHROUGH = "canplaythrough" + private const val CHANGE = "change" + private const val CLICK = "click" + private const val CONTEXTMENU = "contextmenu" + private const val COPY = "copy" + private const val CUT = "cut" + private const val DBLCLICK = "dblclick" + private const val DRAG = "drag" + private const val DRAGEND = "dragend" + private const val DRAGENTER = "dragenter" + private const val DRAGLEAVE = "dragleave" + private const val DRAGOVER = "dragover" + private const val DRAGSTART = "dragstart" + private const val DROP = "drop" + private const val DURATIONCHANGE = "durationchange" + private const val ENDED = "ended" + private const val FOCUS = "focus" + private const val FOCUSIN = "focusin" + private const val FOCUSOUT = "focusout" + private const val FULLSCREENCHANGE = "fullscreenchange" + private const val FULLSCREENERROR = "fullscreenerror" + private const val HASHCHANGE = "hashchange" + private const val INPUT = "input" + private const val INVALID = "invalid" + private const val KEYDOWN = "keydown" + private const val KEYPRESS = "keypress" + private const val KEYUP = "keyup" + private const val LOAD = "load" + private const val LOADEDDATA = "loadeddata" + private const val LOADEDMETADATA = "loadedmetadata" + private const val MOUSEENTER = "mouseenter" + private const val MOUSELEAVE = "mouseleave" + private const val MOUSEMOVE = "mousemove" + private const val MOUSEOVER = "mouseover" + private const val MOUSEOUT = "mouseout" + private const val MOUSEUP = "mouseup" + private const val OFFLINE = "offline" + private const val ONLINE = "online" + private const val OPEN = "open" + private const val PAGEHIDE = "pagehide" + private const val PAGESHOW = "pageshow" + private const val PASTE = "paste" + private const val LOADSTART = "loadstart" + private const val MESSAGE = "message" + private const val MOUSEDOWN = "mousedown" + private const val PAUSE = "pause" + private const val PLAY = "play" + private const val PLAYING = "playing" + private const val POPSTATE = "popstate" + private const val PROGRESS = "progress" + private const val RATECHANGE = "ratechange" + private const val RESIZE = "resize" + private const val RESET = "reset" + private const val SCROLL = "scroll" + private const val SEARCH = "search" + private const val SEEKED = "seeked" + private const val SEEKING = "seeking" + private const val SELECT = "select" + private const val SHOW = "show" + private const val STALLED = "stalled" + private const val STORAGE = "storage" + private const val SUBMIT = "submit" + private const val SUSPEND = "suspend" + private const val TIMEUPDATE = "timeupdate" + private const val TOGGLE = "toggle" + private const val TOUCHCANCEL = "touchcancel" + private const val TOUCHEND = "touchend" + private const val TOUCHMOVE = "touchmove" + private const val TOUCHSTART = "touchstart" + private const val UNLOAD = "unload" + private const val VOLUMECHANGE = "volumechange" + private const val WAITING = "waiting" + private const val WHEEL = "wheel" + private const val ABORT = "abort" + } + /** * Creates an [Listener] for the given event [eventName]. * - * @param eventName of the [Event] to listen for + * @param eventName the [DOM-API name](https://developer.mozilla.org/en-US/docs/Web/API/Element#events) of an event. + * Can be a custom name. + * @param capture if `true`, activates capturing mode, else remains in `bubble` mode (default) + * @param selector optional lambda expression to select specific events with option to manipulate it + * (e.g. `preventDefault` or `stopPropagation`). + * + * @return a [Listener]-object, which is more or less a [Flow] of the specific `Event`-type. */ - fun subscribe(eventName: String, capture: Boolean = false, init: Event.() -> Unit = {}): Listener + fun subscribe( + eventName: String, + capture: Boolean = false, + selector: X.() -> Boolean = { true } + ): Listener /** * occurs when the loading of a media is aborted */ - val aborts get() = subscribe("abort") + val aborts: Listener get() = subscribe(ABORT) /** - * occurs when a page has started printing, or if the print dialogue box has been closed + * occurs when the loading of a media is aborted + * + * @param init expression to manipulate the event dispatching like calling `stopPropagation` or similar DOM-API. + * + * @return a [Listener] that emits [Event]s on its [Flow] */ - val afterprints get() = subscribe("afterprint") + fun aborts(init: Event.() -> Unit): Listener = subscribe(ABORT) { init(); true } /** - * occurs when a page is about to be printed + * occurs when the loading of a media is aborted + * + * @param selector expression to evaluate, which specific event should be emitted to the [Flow]. It is also + * possible and encouraged to manipulate the event dispatching by calling `stopPropagation` or similar DOM-API. + * + * @return a [Listener] that emits [Event]s on its [Flow] */ - val beforeprints get() = subscribe("beforeprint") + fun abortsIf(selector: Event.() -> Boolean): Listener = subscribe(ABORT, selector = selector) /** - * occurs before the document is about to be unloaded + * occurs when a page has started printing, or if the print dialogue box has been closed */ - val beforeunloads get() = subscribe("beforeunload") + val afterprints: Listener get() = subscribe(AFTERPRINT) /** - * occurs when an element loses focus + * occurs when a page has started printing, or if the print dialogue box has been closed + * + * @param init expression to manipulate the event dispatching like calling `stopPropagation` or similar DOM-API. + * + * @return a [Listener] that emits [Event]s on its [Flow] */ - val blurs get() = subscribe("blur") + fun afterprints(init: Event.() -> Unit): Listener = subscribe(AFTERPRINT) { init(); true } /** - * occurs when the browser can start playing the media (when it has buffered enough to begin) + * occurs when a page has started printing, or if the print dialogue box has been closed + * + * @param selector expression to evaluate, which specific event should be emitted to the [Flow]. It is also + * possible and encouraged to manipulate the event dispatching by calling `stopPropagation` or similar DOM-API. + * + * @return a [Listener] that emits [Event]s on its [Flow] */ - val canplays get() = subscribe("canplay") + fun afterprintsIf(selector: Event.() -> Boolean): Listener = subscribe(AFTERPRINT, selector = selector) /** - * occurs when the browser can play through the media without stopping for buffering + * occurs when a page is about to be printed */ - val canplaythroughs get() = subscribe("canplaythrough") + val beforeprints: Listener get() = subscribe(BEFOREPRINT) /** - * occurs when the content of a form element, the selection, or the checked state have changed - * (for ``, `