diff options
author | Robert Jaros <rjaros@finn.pl> | 2020-02-26 23:12:48 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2020-02-26 23:12:48 +0100 |
commit | 450d3d259cd3253f7cda8ab4baffb0a6d070d596 (patch) | |
tree | 25ff42e5e9e7d683ce5395fdd7159ed85244b1a3 | |
parent | 94f36f212a60654571728a1f34701d95453efc22 (diff) | |
download | kvision-450d3d259cd3253f7cda8ab4baffb0a6d070d596.tar.gz kvision-450d3d259cd3253f7cda8ab4baffb0a6d070d596.tar.bz2 kvision-450d3d259cd3253f7cda8ab4baffb0a6d070d596.zip |
Major changes in the event handling architecture. Support for multiple event handlers.
11 files changed, 61 insertions, 25 deletions
diff --git a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt index d6abc0f6..96277728 100644 --- a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt +++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt @@ -29,7 +29,6 @@ import kotlinx.serialization.list import kotlinx.serialization.stringify import org.w3c.dom.get import pl.treksoft.kvision.core.Container -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.remote.CallAgent import pl.treksoft.kvision.remote.HttpMethod import pl.treksoft.kvision.remote.JsonRpcRequest @@ -115,7 +114,7 @@ open class SelectRemoteInput<T : Any>( preserveSelected = ajaxOptions?.preserveSelected ?: true ) if (this.ajaxOptions?.emptyRequest == true) { - this.onEvent { + this.setInternalEventListener<SelectRemote<*>> { shownBsSelect = { val input = self.getElementJQuery()?.parent()?.find("input") input?.trigger("keyup", null) diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt index a96e614e..01b35ed2 100644 --- a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -28,7 +28,6 @@ import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.CssSize import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -150,7 +149,7 @@ open class SelectInput( init { setChildrenFromOptions() - this.onEvent { + this.setInternalEventListener<SelectInput> { change = { val v = getElementJQuery()?.`val`() self.value = v?.let { @@ -312,7 +311,8 @@ open class SelectInput( SelectDropdownAlign.AUTO -> { sn.add("data-dropdown-align-right" to "auto") } - else -> {} + else -> { + } } return sn } diff --git a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt index f195234a..3a52fed2 100644 --- a/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt +++ b/kvision-modules/kvision-bootstrap-spinner/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt @@ -27,7 +27,6 @@ import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -152,7 +151,7 @@ open class SpinnerInput( ButtonsType.HORIZONTAL -> this.addSurroundingCssClass("kv-spinner-btn-horizontal") } this.surroundingSpan = true - this.onEvent { + this.setInternalEventListener<SpinnerInput> { change = { self.changeValue() } diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index a7f8992a..3fa6cd36 100644 --- a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -28,7 +28,6 @@ import pl.treksoft.kvision.core.CssSize import pl.treksoft.kvision.core.ResString import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.html.Button import pl.treksoft.kvision.html.ButtonStyle import pl.treksoft.kvision.html.ButtonType @@ -356,7 +355,7 @@ internal class DropDownButton( init { this.id = id if (!forNavbar && !forDropDown) this.role = "button" - onEvent { + setInternalEventListener<DropDownButton> { click = { e -> if (parent?.parent is ContextMenu) { e.asDynamic().dropDownCM = true diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index e1871c6c..abff5e37 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -34,6 +34,7 @@ import pl.treksoft.kvision.i18n.I18n import pl.treksoft.kvision.i18n.I18n.trans import pl.treksoft.kvision.panel.Root import pl.treksoft.kvision.utils.SnOn +import pl.treksoft.kvision.utils.emptyOn import pl.treksoft.kvision.utils.hooks import pl.treksoft.kvision.utils.on import pl.treksoft.kvision.utils.snAttrs @@ -57,6 +58,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component internal val classes = classes.toMutableSet() internal val surroundingClasses: MutableSet<String> = mutableSetOf() internal val attributes: MutableMap<String, String> = mutableMapOf() + internal val internalListenersMap = mutableMapOf<String, MutableMap<Int, SnOn<Widget>.() -> Unit>>() internal val listenersMap = mutableMapOf<String, MutableMap<Int, SnOn<Widget>.() -> Unit>>() internal var listenerCounter: Int = 0 @@ -260,9 +262,19 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component * @return list of event handlers */ protected open fun getSnOn(): com.github.snabbdom.On? { - return if (listenersMap.isNotEmpty()) { - val handlers = on(eventTarget ?: this) - listenersMap.filter { it.value.isNotEmpty() }.forEach { (event, listeners) -> + val map = listenersMap.filter { it.key != "self" && it.value.isNotEmpty() }.toMutableMap() + internalListenersMap.filter { it.key != "self" && it.value.isNotEmpty() } + .forEach { (event, internalListeners) -> + val listeners = map[event] + if (listeners != null) { + listeners.putAll(internalListeners) + } else { + map[event] = internalListeners + } + } + return if (map.isNotEmpty()) { + val handlers = emptyOn() + map.forEach { (event, listeners) -> handlers.asDynamic()[event] = if (listeners.size == 1) { listeners.values.first() } else { @@ -302,6 +314,29 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component } /** + * @suppress + * Internal function + */ + @Suppress("UNCHECKED_CAST") + protected fun <T : Widget> setInternalEventListener(block: SnOn<T>.() -> Unit): Int { + val handlerCounter = listenerCounter++ + val blockAsWidget = block as SnOn<Widget>.() -> Unit + val handlers = on(this) + (handlers::apply)(blockAsWidget) + for (key: String in js("Object").keys(handlers)) { + val handler = handlers.asDynamic()[key] + val map = internalListenersMap[key] + if (map != null) { + map[handlerCounter] = handler + } else { + internalListenersMap[key] = mutableMapOf(handlerCounter to handler) + } + } + refresh() + return handlerCounter + } + + /** * Sets an event listener for current widget, keeping the actual type of component. * @param T widget type * @param block event handler @@ -320,7 +355,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent(), Component open fun <T : Widget> setEventListener(block: SnOn<T>.() -> Unit): Int { val handlerCounter = listenerCounter++ val blockAsWidget = block as SnOn<Widget>.() -> Unit - val handlers = js("{}") as SnOn<Widget> + val handlers = on(eventTarget ?: this) (handlers::apply)(blockAsWidget) for (key: String in js("Object").keys(handlers)) { val handler = handlers.asDynamic()[key] diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt index b5c3ead0..f93fd436 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt @@ -26,7 +26,6 @@ import org.w3c.dom.events.MouseEvent import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -53,7 +52,7 @@ abstract class CheckInput( ) : Widget(classes), FormInput { init { - this.onEvent { + this.setInternalEventListener<CheckInput> { click = { val v = getElementJQuery()?.prop("checked") as Boolean? self.value = (v == true) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt index b2471411..dc9ad7d4 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/range/RangeInput.kt @@ -27,7 +27,6 @@ import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -98,7 +97,7 @@ open class RangeInput( override var validationStatus: ValidationStatus? by refreshOnUpdate() init { - this.onEvent { + this.setInternalEventListener<RangeInput> { change = { self.changeValue() } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt index 796f653c..bc99f514 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SimpleSelectInput.kt @@ -25,7 +25,6 @@ import com.github.snabbdom.VNode import pl.treksoft.kvision.core.Container import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -92,7 +91,7 @@ open class SimpleSelectInput( init { setChildrenFromOptions() - this.onEvent { + this.setInternalEventListener<SimpleSelectInput> { change = { self.changeValue() } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt index 343129f3..192b33c8 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/text/AbstractTextInput.kt @@ -25,7 +25,6 @@ import com.github.snabbdom.VNode import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.StringPair import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.form.FormInput import pl.treksoft.kvision.form.InputSize import pl.treksoft.kvision.form.ValidationStatus @@ -43,7 +42,7 @@ abstract class AbstractTextInput( ) : Widget(classes), FormInput { init { - this.onEvent { + this.setInternalEventListener<AbstractTextInput> { input = { self.changeValue() } diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt b/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt index 4b02db5a..b1bb36a8 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/Root.kt @@ -29,7 +29,6 @@ import pl.treksoft.kvision.KVManager import pl.treksoft.kvision.core.StringBoolPair import pl.treksoft.kvision.core.Style import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.core.onEvent import pl.treksoft.kvision.utils.snClasses import pl.treksoft.kvision.utils.snOpt import kotlin.browser.document @@ -104,7 +103,7 @@ class Root : SimplePanel { fun addContextMenu(contextMenu: Widget) { contextMenus.add(contextMenu) contextMenu.parent = this - this.onEvent { + this.setInternalEventListener<Root> { click = { e -> @Suppress("UnsafeCastFromDynamic") if (!e.asDynamic().dropDownCM) contextMenu.hide() diff --git a/src/main/kotlin/pl/treksoft/kvision/utils/Snabbdom.kt b/src/main/kotlin/pl/treksoft/kvision/utils/Snabbdom.kt index 00d7e59a..768ce8aa 100644 --- a/src/main/kotlin/pl/treksoft/kvision/utils/Snabbdom.kt +++ b/src/main/kotlin/pl/treksoft/kvision/utils/Snabbdom.kt @@ -157,12 +157,21 @@ inline fun snOpt(noinline block: VNodeData.() -> Unit) = (vNodeData()::apply)(bl * Internal function. */ @Suppress("UnsafeCastFromDynamic") -internal fun on(widget: Widget): SnOn<Widget> { +internal fun on(target: Widget): SnOn<Widget> { val obj = js("{}") - obj["self"] = widget + obj["self"] = target return obj } +/** + * @suppress + * Internal function. + */ +@Suppress("UnsafeCastFromDynamic") +internal fun emptyOn(): SnOn<Widget> { + return js("{}") +} + @Suppress("UnsafeCastFromDynamic", "NOTHING_TO_INLINE") internal inline fun hooks(): Hooks { return js("{}") |