From 1a41c5d06bb51907b752afa9c13c08dd1e82455a Mon Sep 17 00:00:00 2001
From: Robert Jaros <rjaros@finn.pl>
Date: Sat, 4 Nov 2017 12:47:52 +0100
Subject: Refactoring events handling

---
 src/main/kotlin/pl/treksoft/kvision/Showcase.kt    | 53 +++++++++++---
 src/main/kotlin/pl/treksoft/kvision/core/Widget.kt |  3 +-
 .../pl/treksoft/kvision/form/AbstractTextInput.kt  | 20 ++++--
 .../kotlin/pl/treksoft/kvision/form/CheckBox.kt    |  2 +
 .../kotlin/pl/treksoft/kvision/form/CheckInput.kt  | 20 +++---
 src/main/kotlin/pl/treksoft/kvision/form/Radio.kt  |  2 +
 src/main/kotlin/pl/treksoft/kvision/form/Select.kt |  2 +
 .../kotlin/pl/treksoft/kvision/form/SelectInput.kt | 80 ++++++++++++++++++++--
 src/main/kotlin/pl/treksoft/kvision/form/Text.kt   |  2 +
 .../kotlin/pl/treksoft/kvision/form/TextArea.kt    |  2 +
 .../pl/treksoft/kvision/form/TextAreaInput.kt      |  2 +-
 .../kotlin/pl/treksoft/kvision/snabbdom/Types.kt   | 14 +++-
 12 files changed, 173 insertions(+), 29 deletions(-)

(limited to 'src/main')

diff --git a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
index a0ea6881..41ceacd9 100644
--- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt
@@ -110,10 +110,50 @@ class Showcase : ApplicationBase() {
         val select5 = Select(listOf("a" to "Pierwsza", "b" to "Druga"), "a", label = "Lista wyboru")
         root.add(select5)
 
-        val select6 = Select(label = "Lista wyboru 2")
+        val text = Text(label = "To jest pole").apply {
+            placeholder = "Pole formularza"
+            maxlength = 5
+        }
+
+        val select6 = Select(label = "Lista wyboru 2", value = "b")
         select6.add(SelectOption("a", "Opcja 1"))
         select6.add(SelectOption("b", "Opcja 2"))
         select6.add(SelectOption("c", "Opcja 3"))
+        select6.setEventListener<Select> {
+            showBsSelect = { e ->
+                println("show")
+            }
+            shownBsSelect = { e ->
+                println("shown")
+            }
+            hideBsSelect = { e ->
+                println("hide")
+                e.detail.preventDefault()
+            }
+            hiddenBsSelect = { e ->
+                println("hidden")
+            }
+            renderedBsSelect= { e ->
+                println("rendered")
+            }
+            refreshedBsSelect = { e ->
+                println("refreshed")
+            }
+            loadedBsSelect= { e ->
+                println("loaded")
+            }
+            changedBsSelect = { e ->
+                println(e.detail.clickedIndex)
+                if (e.detail.clickedIndex == 0) {
+                    self.options = listOf("x" to "x", "y" to "y", "z" to "z")
+                    self.value = "y"
+                    text.value = "ole"
+                    textField.value = "ole2"
+                } else {
+                    self.add(SelectOption("x", "XXX"))
+                }
+            }
+        }
         root.add(select6)
 
         val container = SimplePanel(setOf("abc", "def"))
@@ -148,15 +188,14 @@ class Showcase : ApplicationBase() {
         textField2.size = INPUTSIZE.LARGE
         root.add(textField2)
 
-/*        val checkbox = CheckBox(true, label = "Kliknij aby <b>przetestować</b>", rich = true, circled = true,
-                style = CHECKBOXSTYLE.DANGER)
+        val checkbox = CheckBox(true, label = "Kliknij aby <b>przetestować</b>", rich = true)
         root.add(checkbox)
         checkbox.setEventListener<CheckBox> {
             click = { e ->
                 println("click" + self.value)
             }
             change = { e -> println("change" + self.value) }
-        }*/
+        }
 
 /*        val radio = Radio(true, name = "radios", label = "Opcja 1", inline = true,
                 style = RADIOSTYLE.DANGER, extraValue = "o1")
@@ -173,11 +212,6 @@ class Showcase : ApplicationBase() {
             }
             change = { e -> println("rchange" + self.value) }
         }*/
-
-        val text = Text(label = "To jest pole").apply {
-            placeholder = "Pole formularza"
-            maxlength = 5
-        }
         root.add(text)
 
         val textareainput = TextAreaInput(cols = 5, rows = 2, value = "To jest tekst\nTo jest <b>te</b></textarea>kst2").apply {
@@ -223,6 +257,7 @@ class Showcase : ApplicationBase() {
             click = {
                 console.log("x")
                 dd2.toggle()
+                checkbox.value = true
             }
         }
         root.add(ddbutton)
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
index 123ffc4c..46aa2a90 100644
--- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt
@@ -48,6 +48,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
             field = value
             refresh()
         }
+    internal var eventTarget: Widget? = null
 
     private var vnode: VNode? = null
 
@@ -144,7 +145,7 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() {
         return if (internalListeners.size > 0 || listeners.size > 0) {
             val internalHandlers = on(this)
             internalListeners.forEach { l -> (internalHandlers::apply)(l) }
-            val handlers = on(this)
+            val handlers = on(eventTarget ?: this)
             listeners.forEach { l -> (handlers::apply)(l) }
             if (internalHandlers.click != null) {
                 if (handlers.click == null) {
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt
index 84ad5661..8f0a09e0 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/AbstractTextInput.kt
@@ -4,21 +4,27 @@ import pl.treksoft.kvision.core.Widget
 import pl.treksoft.kvision.snabbdom.StringBoolPair
 import pl.treksoft.kvision.snabbdom.StringPair
 
-abstract class AbstractTextInput(override var value: String? = null,
+abstract class AbstractTextInput(value: String? = null,
                                  classes: Set<String> = setOf()) : Widget(classes + "form-control"), StringFormField {
+
     init {
-        this.setInternalEventListener {
+        this.setInternalEventListener<AbstractTextInput> {
             input = {
                 val v = getElementJQuery()?.`val`() as String?
                 if (v != null && v.isNotEmpty()) {
-                    value = v
+                    self.value = v
                 } else {
-                    value = null
+                    self.value = null
                 }
             }
         }
     }
 
+    override var value: String? = value
+        set(value) {
+            field = value
+            refreshState()
+        }
     @Suppress("LeakingThis")
     var startValue: String? = value
         set(value) {
@@ -96,4 +102,10 @@ abstract class AbstractTextInput(override var value: String? = null,
         }
         return sn
     }
+
+    private fun refreshState() {
+        value?.let {
+            getElementJQuery()?.`val`(it)
+        } ?: getElementJQueryD()?.`val`(null)
+    }
 }
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt b/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt
index f4598d4d..ecd016b2 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/CheckBox.kt
@@ -73,6 +73,8 @@ open class CheckBox(value: Boolean = false, label: String? = null,
     val flabel: FieldLabel = FieldLabel(idc, label, rich)
 
     init {
+        @Suppress("LeakingThis")
+        input.eventTarget = this
         this.addInternal(input)
         this.addInternal(flabel)
         counter++
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt
index 36e59e87..746b6e3d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/CheckInput.kt
@@ -10,22 +10,26 @@ enum class CHECKINPUTTYPE(val type: String) {
     RADIO("radio")
 }
 
-open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, override var value: Boolean = false,
+open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, value: Boolean = false,
                       classes: Set<String> = setOf()) : Widget(classes), BoolFormField {
 
     init {
-        this.setInternalEventListener {
+        this.setInternalEventListener<CheckInput> {
             click = {
                 val v = getElementJQuery()?.prop("checked") as Boolean?
-                value = (v == true)
+                self.value = (v == true)
             }
             change = {
                 val v = getElementJQuery()?.prop("checked") as Boolean?
-                value = (v == true)
+                self.value = (v == true)
             }
         }
     }
-
+    override var value: Boolean = value
+        set(value) {
+            field = value
+            refreshState()
+        }
     @Suppress("LeakingThis")
     var startValue: Boolean = value
         set(value) {
@@ -90,14 +94,14 @@ open class CheckInput(type: CHECKINPUTTYPE = CHECKINPUTTYPE.CHECKBOX, override v
     }
 
     override fun afterInsert(node: VNode) {
-        refreshCheckedState()
+        refreshState()
     }
 
     override fun afterPostpatch(node: VNode) {
-        refreshCheckedState()
+        refreshState()
     }
 
-    private fun refreshCheckedState() {
+    private fun refreshState() {
         val v = getElementJQuery()?.prop("checked") as Boolean?
         if (this.value != v) {
             getElementJQuery()?.prop("checked", this.value)
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt b/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt
index 3ee6edff..f4ca9c41 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/Radio.kt
@@ -81,6 +81,8 @@ open class Radio(value: Boolean = false, extraValue: String? = null, label: Stri
     val flabel: FieldLabel = FieldLabel(idc, label, rich)
 
     init {
+        @Suppress("LeakingThis")
+        input.eventTarget = this
         this.addInternal(input)
         this.addInternal(flabel)
         counter++
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/Select.kt
index a8759208..e099dd43 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/Select.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/Select.kt
@@ -96,6 +96,8 @@ open class Select(options: List<StringPair>? = null, value: String? = null,
 
     init {
         this.addInternal(flabel)
+        @Suppress("LeakingThis")
+        input.eventTarget = this
         this.addInternal(input)
         counter++
     }
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt
index bd67868d..68c3b57d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt
@@ -2,10 +2,12 @@ package pl.treksoft.kvision.form
 
 import com.github.snabbdom.VNode
 import pl.treksoft.kvision.core.CssSize
+import pl.treksoft.kvision.core.Widget
 import pl.treksoft.kvision.html.BUTTONSTYLE
 import pl.treksoft.kvision.panel.SimplePanel
 import pl.treksoft.kvision.snabbdom.StringBoolPair
 import pl.treksoft.kvision.snabbdom.StringPair
+import pl.treksoft.kvision.snabbdom.obj
 
 private val _aKVNULL = "#kvnull"
 
@@ -14,7 +16,7 @@ enum class SELECTWIDTHTYPE(val value: String) {
     FIT("fit")
 }
 
-class SelectInput(options: List<StringPair>? = null, override var value: String? = null,
+class SelectInput(options: List<StringPair>? = null, value: String? = null,
                   multiple: Boolean = false, classes: Set<String> = setOf()) : SimplePanel(classes), StringFormField {
 
     internal var options = options
@@ -23,6 +25,13 @@ class SelectInput(options: List<StringPair>? = null, override var value: String?
             setChildrenFromOptions()
         }
 
+    @Suppress("LeakingThis")
+    override var value: String? = value
+        set(value) {
+            field = value
+            refreshState()
+        }
+
     @Suppress("LeakingThis")
     var startValue: String? = value
         set(value) {
@@ -122,17 +131,42 @@ class SelectInput(options: List<StringPair>? = null, override var value: String?
         return kvh("select", childrenVNodes())
     }
 
+    override fun add(child: Widget): SimplePanel {
+        super.add(child)
+        refreshSelectInput()
+        return this
+    }
+
+    override fun addAll(children: List<Widget>): SimplePanel {
+        super.addAll(children)
+        refreshSelectInput()
+        return this
+    }
+
+    override fun remove(child: Widget): SimplePanel {
+        super.remove(child)
+        refreshSelectInput()
+        return this
+    }
+
+    override fun removeAll(): SimplePanel {
+        super.removeAll()
+        refreshSelectInput()
+        return this
+    }
+
     private fun setChildrenFromOptions() {
-        this.removeAll()
+        super.removeAll()
         if (emptyOption) {
-            this.add(SelectOption(_aKVNULL, ""))
+            super.add(SelectOption(_aKVNULL, ""))
         }
         options?.let {
             val c = it.map {
                 SelectOption(it.first, it.second)
             }
-            this.addAll(c)
+            super.addAll(c)
         }
+        this.refreshSelectInput()
     }
 
     override fun getSnClass(): List<StringBoolPair> {
@@ -144,6 +178,11 @@ class SelectInput(options: List<StringPair>? = null, override var value: String?
         return cl
     }
 
+    fun refreshSelectInput() {
+        getElementJQueryD()?.selectpicker("refresh")
+        refreshState()
+    }
+
     @Suppress("ComplexMethod")
     override fun getSnAttrs(): List<StringPair> {
         val sn = super.getSnAttrs().toMutableList()
@@ -193,12 +232,43 @@ class SelectInput(options: List<StringPair>? = null, override var value: String?
     @Suppress("UnsafeCastFromDynamic")
     override fun afterInsert(node: VNode) {
         getElementJQueryD()?.selectpicker("render")
+        this.getElementJQuery()?.on("show.bs.select", { e, _ ->
+            this.dispatchEvent("showBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("shown.bs.select", { e, _ ->
+            this.dispatchEvent("shownBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("hide.bs.select", { e, _ ->
+            this.dispatchEvent("hideBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("hidden.bs.select", { e, _ ->
+            this.dispatchEvent("hiddenBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("loaded.bs.select", { e, _ ->
+            this.dispatchEvent("loadedBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("rendered.bs.select", { e, _ ->
+            this.dispatchEvent("renderedBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQuery()?.on("refreshed.bs.select", { e, _ ->
+            this.dispatchEvent("refreshedBsSelect", obj({ detail = e }))
+        })
+        this.getElementJQueryD()?.on("changed.bs.select", { e, cIndex: Int ->
+            e["clickedIndex"] = cIndex
+            this.dispatchEvent("changedBsSelect", obj({ detail = e }))
+        })
+        refreshState()
+    }
+
+    @Suppress("UnsafeCastFromDynamic")
+    private fun refreshState() {
         value?.let {
             if (multiple) {
                 getElementJQueryD()?.selectpicker("val", it.split(",").toTypedArray())
             } else {
                 getElementJQueryD()?.selectpicker("val", it)
             }
-        }
+        } ?: getElementJQueryD()?.selectpicker("val", null)
     }
+
 }
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Text.kt b/src/main/kotlin/pl/treksoft/kvision/form/Text.kt
index 01816195..211cb1f6 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/Text.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/Text.kt
@@ -18,6 +18,8 @@ open class Text(type: TEXTINPUTTYPE = TEXTINPUTTYPE.TEXT, value: String? = null,
     final override val input: TextInput = TextInput(type, value).apply { id = idc }
 
     init {
+        @Suppress("LeakingThis")
+        input.eventTarget = this
         this.addInternal(input)
     }
 }
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/TextArea.kt b/src/main/kotlin/pl/treksoft/kvision/form/TextArea.kt
index 9b65a306..28bbdb48 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/TextArea.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/TextArea.kt
@@ -22,6 +22,8 @@ open class TextArea(cols: Int? = null, rows: Int? = null, value: String? = null,
     final override val input: TextAreaInput = TextAreaInput(cols, rows, value).apply { id = idc }
 
     init {
+        @Suppress("LeakingThis")
+        input.eventTarget = this
         this.addInternal(input)
     }
 }
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/TextAreaInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/TextAreaInput.kt
index 5b8dd5b5..9f27bd16 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/TextAreaInput.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/TextAreaInput.kt
@@ -23,7 +23,7 @@ class TextAreaInput(cols: Int? = null, rows: Int? = null, value: String? = null,
         }
 
     override fun render(): VNode {
-        return value?.let {
+        return startValue?.let {
             kvh("textarea", arrayOf(it))
         } ?: kvh("textarea")
     }
diff --git a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
index f1a79a76..ecd3316e 100644
--- a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt
@@ -21,9 +21,13 @@ fun obj(init: dynamic.() -> Unit): dynamic {
 @Suppress("UnsafeCastFromDynamic")
 private fun vNodeData(): VNodeData = js("({})")
 
+interface KvJQueryEventObject : JQueryEventObject {
+    val clickedIndex: Int
+}
+
 @Suppress("UnsafeCastFromDynamic")
 class KvEvent(type: String, eventInitDict: CustomEventInit) : CustomEvent(type, eventInitDict) {
-    override val detail: JQueryEventObject = obj({})
+    override val detail: KvJQueryEventObject = obj({})
 }
 
 interface BtOn : On {
@@ -37,6 +41,14 @@ interface BtOn : On {
     var hiddenBsModal: ((KvEvent) -> kotlin.Unit)?
     var dragSplitPanel: ((KvEvent) -> kotlin.Unit)?
     var dragEndSplitPanel: ((KvEvent) -> kotlin.Unit)?
+    var showBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var shownBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var hideBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var hiddenBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var loadedBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var renderedBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var refreshedBsSelect: ((KvEvent) -> kotlin.Unit)?
+    var changedBsSelect: ((KvEvent) -> kotlin.Unit)?
 }
 
 interface SnOn<T> : BtOn {
-- 
cgit