diff options
author | Robert Jaros <rjaros@finn.pl> | 2017-11-06 00:20:25 +0100 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2017-11-06 00:20:25 +0100 |
commit | 1c742ec958a043519e58dcf53bb3b3c92bf3e94a (patch) | |
tree | 0d4804a58b0a59f28029c8f65897caf70e4ade35 /src/main/kotlin/pl/treksoft/kvision/form/select | |
parent | 2b0f687b17bdf0144262a20556446cfbf50a4f34 (diff) | |
download | kvision-1c742ec958a043519e58dcf53bb3b3c92bf3e94a.tar.gz kvision-1c742ec958a043519e58dcf53bb3b3c92bf3e94a.tar.bz2 kvision-1c742ec958a043519e58dcf53bb3b3c92bf3e94a.zip |
Ajax support for SelectInput
Diffstat (limited to 'src/main/kotlin/pl/treksoft/kvision/form/select')
5 files changed, 635 insertions, 0 deletions
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<StringPair>? = 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 <T : Widget> setEventListener(block: SnOn<T>.() -> Unit): Widget { + input.setEventListener(block) + return this + } + + override fun setEventListener(block: SnOn<Widget>.() -> 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<Widget>): 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<Widget> { + 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<StringPair>? = null, value: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + classes: Set<String> = 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<SelectInput> { + change = { + val v = getElementJQuery()?.`val`() + self.value = v?.let { + if (self.multiple) { + @Suppress("UNCHECKED_CAST") + val arr = it as? Array<String> + 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<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() { + 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<StringBoolPair> { + 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<StringPair> { + 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<StringPair>? = null, maxOptions: Int? = null, + disabled: Boolean = false, classes: Set<String> = 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<StringPair> { + 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<String> = 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<StringPair> { + 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 + } +} |