From 1c742ec958a043519e58dcf53bb3b3c92bf3e94a Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Mon, 6 Nov 2017 00:20:25 +0100 Subject: Ajax support for SelectInput --- src/main/kotlin/pl/treksoft/kvision/Showcase.kt | 36 ++- .../kotlin/pl/treksoft/kvision/core/KVManager.kt | 2 + .../pl/treksoft/kvision/dropdown/DropDown.kt | 8 +- src/main/kotlin/pl/treksoft/kvision/form/Select.kt | 160 ----------- .../kotlin/pl/treksoft/kvision/form/SelectInput.kt | 286 -------------------- .../pl/treksoft/kvision/form/SelectOptGroup.kt | 60 ----- .../pl/treksoft/kvision/form/SelectOption.kt | 79 ------ .../pl/treksoft/kvision/form/select/AjaxOptions.kt | 36 +++ .../pl/treksoft/kvision/form/select/Select.kt | 162 +++++++++++ .../pl/treksoft/kvision/form/select/SelectInput.kt | 298 +++++++++++++++++++++ .../treksoft/kvision/form/select/SelectOptGroup.kt | 60 +++++ .../treksoft/kvision/form/select/SelectOption.kt | 79 ++++++ src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt | 10 +- .../kotlin/pl/treksoft/kvision/panel/SplitPanel.kt | 4 +- .../kotlin/pl/treksoft/kvision/snabbdom/Types.kt | 2 +- 15 files changed, 682 insertions(+), 600 deletions(-) delete mode 100644 src/main/kotlin/pl/treksoft/kvision/form/Select.kt delete mode 100644 src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt delete mode 100644 src/main/kotlin/pl/treksoft/kvision/form/SelectOptGroup.kt delete mode 100644 src/main/kotlin/pl/treksoft/kvision/form/SelectOption.kt create mode 100644 src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt create mode 100644 src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt create mode 100644 src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt create mode 100644 src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt create mode 100644 src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt (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 7ff2f1fe..985f9f98 100644 --- a/src/main/kotlin/pl/treksoft/kvision/Showcase.kt +++ b/src/main/kotlin/pl/treksoft/kvision/Showcase.kt @@ -7,7 +7,19 @@ import pl.treksoft.kvision.data.DataComponent import pl.treksoft.kvision.data.DataContainer import pl.treksoft.kvision.dropdown.DD.* import pl.treksoft.kvision.dropdown.DropDown -import pl.treksoft.kvision.form.* +import pl.treksoft.kvision.form.CheckBox +import pl.treksoft.kvision.form.INPUTSIZE +import pl.treksoft.kvision.form.TEXTINPUTTYPE +import pl.treksoft.kvision.form.Text +import pl.treksoft.kvision.form.TextArea +import pl.treksoft.kvision.form.TextAreaInput +import pl.treksoft.kvision.form.TextInput +import pl.treksoft.kvision.form.select.AjaxOptions +import pl.treksoft.kvision.form.select.SELECTWIDTHTYPE +import pl.treksoft.kvision.form.select.Select +import pl.treksoft.kvision.form.select.SelectInput +import pl.treksoft.kvision.form.select.SelectOptGroup +import pl.treksoft.kvision.form.select.SelectOption import pl.treksoft.kvision.html.* import pl.treksoft.kvision.html.TAG.DIV import pl.treksoft.kvision.html.TAG.H1 @@ -16,6 +28,7 @@ import pl.treksoft.kvision.modal.Confirm import pl.treksoft.kvision.modal.Modal import pl.treksoft.kvision.panel.* import pl.treksoft.kvision.routing.routing +import pl.treksoft.kvision.snabbdom.obj import pl.treksoft.kvision.utils.perc import pl.treksoft.kvision.utils.px @@ -134,13 +147,13 @@ class Showcase : ApplicationBase() { hiddenBsSelect = { e -> println("hidden") } - renderedBsSelect= { e -> + renderedBsSelect = { e -> println("rendered") } refreshedBsSelect = { e -> println("refreshed") } - loadedBsSelect= { e -> + loadedBsSelect = { e -> println("loaded") } changedBsSelect = { e -> @@ -157,6 +170,23 @@ class Showcase : ApplicationBase() { } root.add(select6) + val select7 = SelectInput().apply { + ajaxOptions = AjaxOptions("https://api.github.com/search/repositories", processData = { + it.items.map { item -> + obj { + this.value = item.id + this.text = item.name + this.data = obj { + this.subtext = item.owner.login + } + } + } + }, processParams = obj { + q = "{{{q}}}" + }) + } + root.add(select7) + val container = SimplePanel(setOf("abc", "def")) val h1 = Tag(H1, "To jest test pisania tekstu", false, null, classes = setOf("test", "test2")) container.add(h1) diff --git a/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt b/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt index f603d944..a1f48982 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/KVManager.kt @@ -21,6 +21,8 @@ object KVManager { private val bootstrapSelectCss = require("bootstrap-select/dist/css/bootstrap-select.min.css") private val bootstrapSelect = require("bootstrap-select") private val bootstrapSelectI18n = require("./js/bootstrap-select-i18n.min.js") + private val bootstrapSelectAjaxCss = require("ajax-bootstrap-select/dist/css/ajax-bootstrap-select.min.css") + private val bootstrapSelectAjax = require("ajax-bootstrap-select/dist/js/ajax-bootstrap-select.min.js") private val sdPatch = Snabbdom.init(arrayOf(classModule, attributesModule, propsModule, styleModule, eventListenersModule, datasetModule)) diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index ade2937b..3f6ca828 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -139,17 +139,17 @@ open class DropDown(text: String, elements: List? = null, icon: Stri override fun afterInsert(node: VNode) { this.getElementJQuery()?.on("show.bs.dropdown", { e, _ -> if (!list.visible) list.visible = true - this.dispatchEvent("showBsDropdown", obj({ detail = e })) + this.dispatchEvent("showBsDropdown", obj { detail = e }) }) this.getElementJQuery()?.on("shown.bs.dropdown", { e, _ -> - this.dispatchEvent("shownBsDropdown", obj({ detail = e })) + this.dispatchEvent("shownBsDropdown", obj { detail = e }) }) this.getElementJQuery()?.on("hide.bs.dropdown", { e, _ -> - this.dispatchEvent("hideBsDropdown", obj({ detail = e })) + this.dispatchEvent("hideBsDropdown", obj { detail = e }) }) this.getElementJQuery()?.on("hidden.bs.dropdown", { e, _ -> if (list.visible) list.visible = false - this.dispatchEvent("hiddenBsDropdown", obj({ detail = e })) + this.dispatchEvent("hiddenBsDropdown", obj { detail = e }) }) } diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/Select.kt deleted file mode 100644 index 4ed04cc8..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/Select.kt +++ /dev/null @@ -1,160 +0,0 @@ -package pl.treksoft.kvision.form - -import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.snabbdom.SnOn -import pl.treksoft.kvision.snabbdom.StringPair - -open class Select(options: List? = null, value: String? = null, - multiple: Boolean = false, label: String? = null, - rich: Boolean = false) : SimplePanel(setOf("form-group")), StringFormField { - - var options - get() = input.options - set(value) { - input.options = value - } - override var value - get() = input.value - set(value) { - input.value = value - } - var startValue - get() = input.startValue - set(value) { - input.startValue = value - } - var name - get() = input.name - set(value) { - input.name = value - } - var multiple - get() = input.multiple - set(value) { - input.multiple = value - } - var maxOptions - get() = input.maxOptions - set(value) { - input.maxOptions = value - } - var liveSearch - get() = input.liveSearch - set(value) { - input.liveSearch = value - } - var placeholder - get() = input.placeholder - set(value) { - input.placeholder = value - } - var style - get() = input.style - set(value) { - input.style = value - } - var selectWidth - get() = input.selectWidth - set(value) { - input.selectWidth = value - } - var selectWidthType - get() = input.selectWidthType - set(value) { - input.selectWidthType = value - } - var emptyOption - get() = input.emptyOption - set(value) { - input.emptyOption = value - } - override var disabled - get() = input.disabled - set(value) { - input.disabled = value - } - var label - get() = flabel.text - set(value) { - flabel.text = value - } - var rich - get() = flabel.rich - set(value) { - flabel.rich = value - } - override var size - get() = input.size - set(value) { - input.size = value - } - - private val idc = "kv_form_select_" + counter - val input: SelectInput = SelectInput(options, value, multiple, setOf("form-control")).apply { id = idc } - val flabel: FieldLabel = FieldLabel(idc, label, rich) - - init { - this.addInternal(flabel) - @Suppress("LeakingThis") - input.eventTarget = this - this.addInternal(input) - counter++ - } - - companion object { - var counter = 0 - } - - @Suppress("UNCHECKED_CAST") - override fun setEventListener(block: SnOn.() -> Unit): Widget { - input.setEventListener(block) - return this - } - - override fun setEventListener(block: SnOn.() -> Unit): Widget { - input.setEventListener(block) - return this - } - - override fun removeEventListeners(): Widget { - input.removeEventListeners() - return this - } - - override fun add(child: Widget): SimplePanel { - input.add(child) - return this - } - - override fun addAll(children: List): SimplePanel { - input.addAll(children) - return this - } - - override fun remove(child: Widget): SimplePanel { - input.remove(child) - return this - } - - override fun removeAll(): SimplePanel { - input.removeAll() - return this - } - - override fun getChildren(): List { - return input.getChildren() - } - - open fun showOptions() { - input.showOptions() - } - - open fun hideOptions() { - input.hideOptions() - } - - open fun toggleOptions() { - input.toggleOptions() - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt deleted file mode 100644 index 7cacd422..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/SelectInput.kt +++ /dev/null @@ -1,286 +0,0 @@ -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" - -enum class SELECTWIDTHTYPE(val value: String) { - AUTO("auto"), - FIT("fit") -} - -open class SelectInput(options: List? = null, value: String? = null, - multiple: Boolean = false, classes: Set = setOf()) : SimplePanel(classes), StringFormField { - - internal var options = options - set(value) { - field = value - setChildrenFromOptions() - } - - @Suppress("LeakingThis") - override var value: String? = value - set(value) { - field = value - refreshState() - } - - @Suppress("LeakingThis") - var startValue: String? = value - set(value) { - field = value - this.value = value - refresh() - } - var name: String? = null - set(value) { - field = value - refresh() - } - var multiple: Boolean = multiple - set(value) { - field = value - refresh() - } - var maxOptions: Int? = null - set(value) { - field = value - refresh() - } - var liveSearch: Boolean = false - set(value) { - field = value - refresh() - } - var placeholder: String? = null - set(value) { - field = value - refresh() - } - var style: BUTTONSTYLE? = null - set(value) { - field = value - refresh() - } - var selectWidth: CssSize? = null - set(value) { - field = value - refresh() - } - var selectWidthType: SELECTWIDTHTYPE? = null - set(value) { - field = value - refresh() - } - var emptyOption: Boolean = false - set(value) { - field = value - setChildrenFromOptions() - } - override var disabled: Boolean = false - set(value) { - field = value - refresh() - } - var autofocus: Boolean? = null - set(value) { - field = value - refresh() - } - override var size: INPUTSIZE? = null - set(value) { - field = value - refresh() - } - - init { - setChildrenFromOptions() - this.setInternalEventListener { - change = { - val v = getElementJQuery()?.`val`() - self.value = v?.let { - if (self.multiple) { - @Suppress("UNCHECKED_CAST") - val arr = it as? Array - if (arr != null && arr.isNotEmpty()) { - arr.joinToString() - } else { - null - } - } else { - val vs = it as String - if (_aKVNULL == vs) { - null - } else { - vs - } - } - } - } - } - } - - override fun render(): VNode { - return kvh("select", childrenVNodes()) - } - - override fun add(child: Widget): SimplePanel { - super.add(child) - refreshSelectInput() - return this - } - - override fun addAll(children: List): 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() { - super.removeAll() - if (emptyOption) { - super.add(SelectOption(_aKVNULL, "")) - } - options?.let { - val c = it.map { - SelectOption(it.first, it.second) - } - super.addAll(c) - } - this.refreshSelectInput() - } - - open fun showOptions() { - getElementJQueryD()?.selectpicker("show") - } - - open fun hideOptions() { - getElementJQueryD()?.selectpicker("hide") - } - - open fun toggleOptions() { - getElementJQueryD()?.selectpicker("toggle") - } - - override fun getSnClass(): List { - val cl = super.getSnClass().toMutableList() - cl.add("selectpicker" to true) - size?.let { - cl.add(it.className to true) - } - return cl - } - - fun refreshSelectInput() { - getElementJQueryD()?.selectpicker("refresh") - refreshState() - } - - @Suppress("ComplexMethod") - override fun getSnAttrs(): List { - val sn = super.getSnAttrs().toMutableList() - name?.let { - sn.add("name" to it) - } - if (multiple) { - sn.add("multiple" to "true") - } - maxOptions?.let { - sn.add("data-max-options" to "" + it) - } - if (liveSearch) { - sn.add("data-live-search" to "true") - } - placeholder?.let { - sn.add("title" to it) - } - autofocus?.let { - if (it) { - sn.add("autofocus" to "autofocus") - } - } - if (disabled) { - sn.add("disabled" to "true") - } - val btnStyle = style?.className ?: "btn-default" - when (size) { - INPUTSIZE.LARGE -> { - sn.add("data-style" to "$btnStyle btn-lg") - } - INPUTSIZE.SMALL -> { - sn.add("data-style" to "$btnStyle btn-sm") - } - else -> { - sn.add("data-style" to btnStyle) - } - } - selectWidthType?.let { - sn.add("data-width" to it.value) - } ?: selectWidth?.let { - sn.add("data-width" to it.first.toString() + it.second.unit) - } - return sn - } - - @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/SelectOptGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/SelectOptGroup.kt deleted file mode 100644 index 513077ef..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/SelectOptGroup.kt +++ /dev/null @@ -1,60 +0,0 @@ -package pl.treksoft.kvision.form - -import com.github.snabbdom.VNode -import pl.treksoft.kvision.panel.SimplePanel -import pl.treksoft.kvision.snabbdom.StringPair - -open class SelectOptGroup(label: String, options: List? = null, maxOptions: Int? = null, - disabled: Boolean = false, classes: Set = setOf()) : SimplePanel(classes) { - - var label: String = label - set(value) { - field = value - refresh() - } - private var options = options - set(value) { - field = value - setChildrenFromOptions() - } - var maxOptions: Int? = maxOptions - set(value) { - field = value - refresh() - } - var disabled: Boolean = disabled - set(value) { - field = value - refresh() - } - - init { - setChildrenFromOptions() - } - - override fun render(): VNode { - return kvh("optgroup", childrenVNodes()) - } - - private fun setChildrenFromOptions() { - this.removeAll() - options?.let { - val c = it.map { - SelectOption(it.first, it.second) - } - this.addAll(c) - } - } - - override fun getSnAttrs(): List { - val sn = super.getSnAttrs().toMutableList() - sn.add("label" to label) - maxOptions?.let { - sn.add("data-max-options" to "" + it) - } - if (disabled) { - sn.add("disabled" to "true") - } - return sn - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/SelectOption.kt b/src/main/kotlin/pl/treksoft/kvision/form/SelectOption.kt deleted file mode 100644 index f3be295c..00000000 --- a/src/main/kotlin/pl/treksoft/kvision/form/SelectOption.kt +++ /dev/null @@ -1,79 +0,0 @@ -package pl.treksoft.kvision.form - -import com.github.snabbdom.VNode -import pl.treksoft.kvision.core.Widget -import pl.treksoft.kvision.snabbdom.StringPair - -open class SelectOption(value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, - divider: Boolean = false, disabled: Boolean = false, - classes: Set = setOf()) : Widget(classes) { - - var value: String? = value - set(value) { - field = value - refresh() - } - - var label: String? = label - set(value) { - field = value - refresh() - } - - var subtext: String? = subtext - set(value) { - field = value - refresh() - } - - var icon: String? = icon - set(value) { - field = value - refresh() - } - - var divider: Boolean = divider - set(value) { - field = value - refresh() - } - - var disabled: Boolean = disabled - set(value) { - field = value - refresh() - } - - override fun render(): VNode { - return if (!divider) { - kvh("option", arrayOf(label ?: value)) - } else { - kvh("option") - } - } - - override fun getSnAttrs(): List { - val sn = super.getSnAttrs().toMutableList() - if (!divider) { - value?.let { - sn.add("value" to it) - } - subtext?.let { - sn.add("data-subtext" to it) - } - icon?.let { - if (it.startsWith("fa-")) { - sn.add("data-icon" to "fa $it") - } else { - sn.add("data-icon" to "glyphicon-$it") - } - } - if (disabled) { - sn.add("disabled" to "true") - } - } else { - sn.add("data-divider" to "true") - } - return sn - } -} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt new file mode 100644 index 00000000..8dbb5f65 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt @@ -0,0 +1,36 @@ +package pl.treksoft.kvision.form.select + +import pl.treksoft.kvision.snabbdom.obj + +enum class HttpType(val type: String) { + GET("GET"), + POST("POST") +} + +enum class DataType(val type: String) { + JSON("json"), + JSONP("jsonp"), + XML("xml"), + TEXT("text"), + SCRIPT("script") +} + +data class AjaxOptions(val url: String, val processData: (dynamic) -> dynamic, + val processParams: dynamic = null, val httpType: HttpType = HttpType.GET, + val dataType: DataType = DataType.JSON, val minLength: Int = 0, + val cache: Boolean = true, val clearOnEmpty: Boolean = true, val clearOnError: Boolean = true, + val emptyRequest: Boolean = false, val preserveSelected: Boolean = true, + val requestDelay: Int = 300, val restoreOnError: Boolean = false) + +fun AjaxOptions.toJs(): dynamic { + return obj { + this.ajax = obj { + this.url = url + this.type = httpType.type + this.data = processParams + } + this.preprocessData = processData + } +} + +data class AjaxData(val value: String, val text: String? = null) diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt new file mode 100644 index 00000000..321e3a70 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt @@ -0,0 +1,162 @@ +package pl.treksoft.kvision.form.select + +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.form.FieldLabel +import pl.treksoft.kvision.form.StringFormField +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.snabbdom.SnOn +import pl.treksoft.kvision.snabbdom.StringPair + +open class Select(options: List? = null, value: String? = null, + multiple: Boolean = false, label: String? = null, + rich: Boolean = false) : SimplePanel(setOf("form-group")), StringFormField { + + var options + get() = input.options + set(value) { + input.options = value + } + override var value + get() = input.value + set(value) { + input.value = value + } + var startValue + get() = input.startValue + set(value) { + input.startValue = value + } + var name + get() = input.name + set(value) { + input.name = value + } + var multiple + get() = input.multiple + set(value) { + input.multiple = value + } + var maxOptions + get() = input.maxOptions + set(value) { + input.maxOptions = value + } + var liveSearch + get() = input.liveSearch + set(value) { + input.liveSearch = value + } + var placeholder + get() = input.placeholder + set(value) { + input.placeholder = value + } + var style + get() = input.style + set(value) { + input.style = value + } + var selectWidth + get() = input.selectWidth + set(value) { + input.selectWidth = value + } + var selectWidthType + get() = input.selectWidthType + set(value) { + input.selectWidthType = value + } + var emptyOption + get() = input.emptyOption + set(value) { + input.emptyOption = value + } + override var disabled + get() = input.disabled + set(value) { + input.disabled = value + } + var label + get() = flabel.text + set(value) { + flabel.text = value + } + var rich + get() = flabel.rich + set(value) { + flabel.rich = value + } + override var size + get() = input.size + set(value) { + input.size = value + } + + private val idc = "kv_form_select_" + counter + val input: SelectInput = SelectInput(options, value, multiple, null, setOf("form-control")).apply { id = idc } + val flabel: FieldLabel = FieldLabel(idc, label, rich) + + init { + this.addInternal(flabel) + @Suppress("LeakingThis") + input.eventTarget = this + this.addInternal(input) + counter++ + } + + companion object { + var counter = 0 + } + + @Suppress("UNCHECKED_CAST") + override fun setEventListener(block: SnOn.() -> Unit): Widget { + input.setEventListener(block) + return this + } + + override fun setEventListener(block: SnOn.() -> Unit): Widget { + input.setEventListener(block) + return this + } + + override fun removeEventListeners(): Widget { + input.removeEventListeners() + return this + } + + override fun add(child: Widget): SimplePanel { + input.add(child) + return this + } + + override fun addAll(children: List): SimplePanel { + input.addAll(children) + return this + } + + override fun remove(child: Widget): SimplePanel { + input.remove(child) + return this + } + + override fun removeAll(): SimplePanel { + input.removeAll() + return this + } + + override fun getChildren(): List { + return input.getChildren() + } + + open fun showOptions() { + input.showOptions() + } + + open fun hideOptions() { + input.hideOptions() + } + + open fun toggleOptions() { + input.toggleOptions() + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt new file mode 100644 index 00000000..7522ef98 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -0,0 +1,298 @@ +package pl.treksoft.kvision.form.select + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.core.CssSize +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.form.INPUTSIZE +import pl.treksoft.kvision.form.StringFormField +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" + +enum class SELECTWIDTHTYPE(val value: String) { + AUTO("auto"), + FIT("fit") +} + +open class SelectInput(options: List? = null, value: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + classes: Set = setOf()) : SimplePanel(classes), StringFormField { + + internal var options = options + set(value) { + field = value + setChildrenFromOptions() + } + + @Suppress("LeakingThis") + override var value: String? = value + set(value) { + field = value + refreshState() + } + + @Suppress("LeakingThis") + var startValue: String? = value + set(value) { + field = value + this.value = value + refresh() + } + var name: String? = null + set(value) { + field = value + refresh() + } + var multiple: Boolean = multiple + set(value) { + field = value + refresh() + } + var ajaxOptions: AjaxOptions? = ajaxOptions + set(value) { + field = value + if (value != null) liveSearch = true + refresh() + } + var maxOptions: Int? = null + set(value) { + field = value + refresh() + } + var liveSearch: Boolean = false + set(value) { + field = value + refresh() + } + var placeholder: String? = null + set(value) { + field = value + refresh() + } + var style: BUTTONSTYLE? = null + set(value) { + field = value + refresh() + } + var selectWidth: CssSize? = null + set(value) { + field = value + refresh() + } + var selectWidthType: SELECTWIDTHTYPE? = null + set(value) { + field = value + refresh() + } + var emptyOption: Boolean = false + set(value) { + field = value + setChildrenFromOptions() + } + override var disabled: Boolean = false + set(value) { + field = value + refresh() + } + var autofocus: Boolean? = null + set(value) { + field = value + refresh() + } + override var size: INPUTSIZE? = null + set(value) { + field = value + refresh() + } + + init { + setChildrenFromOptions() + this.setInternalEventListener { + change = { + val v = getElementJQuery()?.`val`() + self.value = v?.let { + if (self.multiple) { + @Suppress("UNCHECKED_CAST") + val arr = it as? Array + if (arr != null && arr.isNotEmpty()) { + arr.joinToString() + } else { + null + } + } else { + val vs = it as String + if (_aKVNULL == vs) { + null + } else { + vs + } + } + } + } + } + } + + override fun render(): VNode { + return kvh("select", childrenVNodes()) + } + + override fun add(child: Widget): SimplePanel { + super.add(child) + refreshSelectInput() + return this + } + + override fun addAll(children: List): 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() { + super.removeAll() + if (emptyOption) { + super.add(SelectOption(_aKVNULL, "")) + } + options?.let { + val c = it.map { + SelectOption(it.first, it.second) + } + super.addAll(c) + } + this.refreshSelectInput() + } + + open fun showOptions() { + getElementJQueryD()?.selectpicker("show") + } + + open fun hideOptions() { + getElementJQueryD()?.selectpicker("hide") + } + + open fun toggleOptions() { + getElementJQueryD()?.selectpicker("toggle") + } + + override fun getSnClass(): List { + val cl = super.getSnClass().toMutableList() + cl.add("selectpicker" to true) + size?.let { + cl.add(it.className to true) + } + return cl + } + + fun refreshSelectInput() { + getElementJQueryD()?.selectpicker("refresh") + refreshState() + } + + @Suppress("ComplexMethod") + override fun getSnAttrs(): List { + val sn = super.getSnAttrs().toMutableList() + name?.let { + sn.add("name" to it) + } + if (multiple) { + sn.add("multiple" to "true") + } + maxOptions?.let { + sn.add("data-max-options" to "" + it) + } + if (liveSearch) { + sn.add("data-live-search" to "true") + } + placeholder?.let { + sn.add("title" to it) + } + autofocus?.let { + if (it) { + sn.add("autofocus" to "autofocus") + } + } + if (disabled) { + sn.add("disabled" to "true") + } + val btnStyle = style?.className ?: "btn-default" + when (size) { + INPUTSIZE.LARGE -> { + sn.add("data-style" to "$btnStyle btn-lg") + } + INPUTSIZE.SMALL -> { + sn.add("data-style" to "$btnStyle btn-sm") + } + else -> { + sn.add("data-style" to btnStyle) + } + } + selectWidthType?.let { + sn.add("data-width" to it.value) + } ?: selectWidth?.let { + sn.add("data-width" to it.first.toString() + it.second.unit) + } + return sn + } + + @Suppress("UnsafeCastFromDynamic") + override fun afterInsert(node: VNode) { + ajaxOptions?.let { + getElementJQueryD()?.selectpicker("render").ajaxSelectPicker(it.toJs()) + } ?: 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/select/SelectOptGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt new file mode 100644 index 00000000..38a578fe --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt @@ -0,0 +1,60 @@ +package pl.treksoft.kvision.form.select + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.snabbdom.StringPair + +open class SelectOptGroup(label: String, options: List? = null, maxOptions: Int? = null, + disabled: Boolean = false, classes: Set = setOf()) : SimplePanel(classes) { + + var label: String = label + set(value) { + field = value + refresh() + } + private var options = options + set(value) { + field = value + setChildrenFromOptions() + } + var maxOptions: Int? = maxOptions + set(value) { + field = value + refresh() + } + var disabled: Boolean = disabled + set(value) { + field = value + refresh() + } + + init { + setChildrenFromOptions() + } + + override fun render(): VNode { + return kvh("optgroup", childrenVNodes()) + } + + private fun setChildrenFromOptions() { + this.removeAll() + options?.let { + val c = it.map { + SelectOption(it.first, it.second) + } + this.addAll(c) + } + } + + override fun getSnAttrs(): List { + val sn = super.getSnAttrs().toMutableList() + sn.add("label" to label) + maxOptions?.let { + sn.add("data-max-options" to "" + it) + } + if (disabled) { + sn.add("disabled" to "true") + } + return sn + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt new file mode 100644 index 00000000..3ff44c61 --- /dev/null +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt @@ -0,0 +1,79 @@ +package pl.treksoft.kvision.form.select + +import com.github.snabbdom.VNode +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.snabbdom.StringPair + +open class SelectOption(value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, + divider: Boolean = false, disabled: Boolean = false, + classes: Set = setOf()) : Widget(classes) { + + var value: String? = value + set(value) { + field = value + refresh() + } + + var label: String? = label + set(value) { + field = value + refresh() + } + + var subtext: String? = subtext + set(value) { + field = value + refresh() + } + + var icon: String? = icon + set(value) { + field = value + refresh() + } + + var divider: Boolean = divider + set(value) { + field = value + refresh() + } + + var disabled: Boolean = disabled + set(value) { + field = value + refresh() + } + + override fun render(): VNode { + return if (!divider) { + kvh("option", arrayOf(label ?: value)) + } else { + kvh("option") + } + } + + override fun getSnAttrs(): List { + val sn = super.getSnAttrs().toMutableList() + if (!divider) { + value?.let { + sn.add("value" to it) + } + subtext?.let { + sn.add("data-subtext" to it) + } + icon?.let { + if (it.startsWith("fa-")) { + sn.add("data-icon" to "fa $it") + } else { + sn.add("data-icon" to "glyphicon-$it") + } + } + if (disabled) { + sn.add("disabled" to "true") + } + } else { + sn.add("data-divider" to "true") + } + return sn + } +} diff --git a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt index a4e14d5c..77e40349 100644 --- a/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt +++ b/src/main/kotlin/pl/treksoft/kvision/modal/Modal.kt @@ -1,13 +1,13 @@ package pl.treksoft.kvision.modal import com.github.snabbdom.VNode -import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.core.Root import pl.treksoft.kvision.core.Widget import pl.treksoft.kvision.helpers.CloseIcon import pl.treksoft.kvision.html.Button import pl.treksoft.kvision.html.TAG import pl.treksoft.kvision.html.Tag +import pl.treksoft.kvision.panel.SimplePanel import pl.treksoft.kvision.snabbdom.StringBoolPair import pl.treksoft.kvision.snabbdom.StringPair import pl.treksoft.kvision.snabbdom.obj @@ -134,17 +134,17 @@ open class Modal(caption: String? = null, closeButton: Boolean = true, backdrop = if (escape) "true" else "static" }) this.getElementJQuery()?.on("show.bs.modal", { e, _ -> - this.dispatchEvent("showBsModal", obj({ detail = e })) + this.dispatchEvent("showBsModal", obj { detail = e }) }) this.getElementJQuery()?.on("shown.bs.modal", { e, _ -> - this.dispatchEvent("shownBsModal", obj({ detail = e })) + this.dispatchEvent("shownBsModal", obj { detail = e }) }) this.getElementJQuery()?.on("hide.bs.modal", { e, _ -> - this.dispatchEvent("hideBsModal", obj({ detail = e })) + this.dispatchEvent("hideBsModal", obj { detail = e }) }) this.getElementJQuery()?.on("hidden.bs.modal", { e, _ -> this.visible = false - this.dispatchEvent("hiddenBsModal", obj({ detail = e })) + this.dispatchEvent("hiddenBsModal", obj { detail = e }) }) } diff --git a/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt b/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt index 02811639..b8c9c580 100644 --- a/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt +++ b/src/main/kotlin/pl/treksoft/kvision/panel/SplitPanel.kt @@ -32,7 +32,7 @@ open class SplitPanel(private val direction: DIRECTION = DIRECTION.VERTICAL, onDrag = lok@ { e: JQueryEventObject, _: JQuery, newWidth: Int, newHeight: Int, _: dynamic -> e.asDynamic()["newWidth"] = newWidth e.asDynamic()["newHeight"] = newHeight - self.dispatchEvent("dragSplitPanel", obj({ detail = e })) + self.dispatchEvent("dragSplitPanel", obj { detail = e }) return@lok !e.isDefaultPrevented() } onDragEnd = { e: JQueryEventObject, el: JQuery, _: dynamic -> @@ -41,7 +41,7 @@ open class SplitPanel(private val direction: DIRECTION = DIRECTION.VERTICAL, } else { children[0].width = el.width().toInt() to px } - self.dispatchEvent("dragEndSplitPanel", obj({ detail = e })) + self.dispatchEvent("dragEndSplitPanel", obj { detail = e }) } }) } diff --git a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt index ecd3316e..3ed50967 100644 --- a/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt +++ b/src/main/kotlin/pl/treksoft/kvision/snabbdom/Types.kt @@ -27,7 +27,7 @@ interface KvJQueryEventObject : JQueryEventObject { @Suppress("UnsafeCastFromDynamic") class KvEvent(type: String, eventInitDict: CustomEventInit) : CustomEvent(type, eventInitDict) { - override val detail: KvJQueryEventObject = obj({}) + override val detail: KvJQueryEventObject = obj {} } interface BtOn : On { -- cgit