diff options
author | Robert Jaros <rjaros@finn.pl> | 2019-10-07 09:58:14 +0200 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2019-10-07 09:58:14 +0200 |
commit | 04ac8542c218b7ce5199350f0880e8f7cb4252b6 (patch) | |
tree | 4f96d1c3bb8281289b96e2b11eecc404a3c98788 /kvision-modules/kvision-bootstrap-select-remote/src | |
parent | 6678eec9799681b09e5ac85de1a39596d56de22f (diff) | |
parent | 6b14906f0e35dc522bd1c1a44682d728315cf619 (diff) | |
download | kvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.tar.gz kvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.tar.bz2 kvision-04ac8542c218b7ce5199350f0880e8f7cb4252b6.zip |
Merge branch 'bs4'
Diffstat (limited to 'kvision-modules/kvision-bootstrap-select-remote/src')
2 files changed, 416 insertions, 0 deletions
diff --git a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt new file mode 100644 index 00000000..de823280 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.form.select + +import pl.treksoft.kvision.core.Component +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.StringBoolPair +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.form.FieldLabel +import pl.treksoft.kvision.form.InvalidFeedback +import pl.treksoft.kvision.form.StringFormControl +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.remote.KVServiceManager +import pl.treksoft.kvision.remote.RemoteOption +import pl.treksoft.kvision.utils.SnOn + +/** + * The form field component for SelectRemote control. + * + * @constructor + * @param value selected value + * @param serviceManager multiplatform service manager + * @param function multiplatform service method returning the list of options + * @param name the name attribute of the generated HTML input element + * @param multiple allows multiple value selection (multiple values are comma delimited) + * @param ajaxOptions additional options for remote data source + * @param label label text bound to the input element + * @param rich determines if [label] can contain HTML code + */ +@Suppress("TooManyFunctions") +open class SelectRemote<T : Any>( + value: String? = null, + serviceManager: KVServiceManager<T>, + function: T.(String?, String?) -> List<RemoteOption>, + name: String? = null, + multiple: Boolean = false, + ajaxOptions: AjaxOptions? = null, + label: String? = null, + rich: Boolean = false +) : SimplePanel(setOf("form-group")), StringFormControl { + /** + * A value of the selected option. + */ + override var value + get() = input.value + set(value) { + input.value = value + } + /** + * Determines if multiple value selection is allowed. + */ + var multiple + get() = input.multiple + set(value) { + input.multiple = value + } + /** + * Maximal number of selected options. + */ + var maxOptions + get() = input.maxOptions + set(value) { + input.maxOptions = value + } + /** + * The placeholder for the select control. + */ + var placeholder + get() = input.placeholder + set(value) { + input.placeholder = value + } + /** + * The style of the select control. + */ + var style + get() = input.style + set(value) { + input.style = value + } + /** + * The width of the select control. + */ + var selectWidth + get() = input.selectWidth + set(value) { + input.selectWidth = value + } + /** + * The width type of the select control. + */ + var selectWidthType + get() = input.selectWidthType + set(value) { + input.selectWidthType = value + } + /** + * Determines if an empty option is automatically generated. + */ + var emptyOption + get() = input.emptyOption + set(value) { + input.emptyOption = value + } + /** + * Determines if the select is automatically focused. + */ + var autofocus + get() = input.autofocus + set(value) { + input.autofocus = value + } + /** + * The label text bound to the select element. + */ + var label + get() = flabel.content + set(value) { + flabel.content = value + } + /** + * Determines if [label] can contain HTML code. + */ + var rich + get() = flabel.rich + set(value) { + flabel.rich = value + } + + private val idc = "kv_form_SelectRemote_$counter" + final override val input: SelectRemoteInput<T> = SelectRemoteInput( + value, serviceManager, function, multiple, ajaxOptions, + setOf("form-control") + ).apply { + this.id = idc + this.name = name + } + final override val flabel: FieldLabel = FieldLabel(idc, label, rich) + final override val invalidFeedback: InvalidFeedback = InvalidFeedback().apply { visible = false } + + init { + @Suppress("LeakingThis") + input.eventTarget = this + this.addInternal(flabel) + this.addInternal(input) + this.addInternal(invalidFeedback) + counter++ + } + + override fun getSnClass(): List<StringBoolPair> { + val cl = super.getSnClass().toMutableList() + if (validatorError != null) { + cl.add("text-danger" to true) + } + return cl + } + + @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: Component): SimplePanel { + input.add(child) + return this + } + + override fun addAll(children: List<Component>): SimplePanel { + input.addAll(children) + return this + } + + override fun remove(child: Component): SimplePanel { + input.remove(child) + return this + } + + override fun removeAll(): SimplePanel { + input.removeAll() + return this + } + + override fun getChildren(): List<Component> { + return input.getChildren() + } + + /** + * Opens dropdown with options. + */ + open fun showOptions() { + input.showOptions() + } + + /** + * Hides dropdown with options. + */ + open fun hideOptions() { + input.hideOptions() + } + + /** + * Toggles visibility of dropdown with options. + */ + open fun toggleOptions() { + input.toggleOptions() + } + + override fun focus() { + input.focus() + } + + override fun blur() { + input.blur() + } + + companion object { + internal var counter = 0 + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun <T : Any> Container.selectRemote( + value: String? = null, + serviceManager: KVServiceManager<T>, + function: T.(String?, String?) -> List<RemoteOption>, name: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, + rich: Boolean = false, init: (SelectRemote<T>.() -> Unit)? = null + ): SelectRemote<T> { + val selectRemote = + SelectRemote( + value, + serviceManager, + function, + name, + multiple, + ajaxOptions, + label, + rich + ).apply { init?.invoke(this) } + this.add(selectRemote) + return selectRemote + } + } +} 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 new file mode 100644 index 00000000..4c891d30 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.form.select + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.asDeferred +import kotlinx.coroutines.launch +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.list +import kotlinx.serialization.stringify +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.remote.CallAgent +import pl.treksoft.kvision.remote.HttpMethod +import pl.treksoft.kvision.remote.JsonRpcRequest +import pl.treksoft.kvision.remote.KVServiceManager +import pl.treksoft.kvision.remote.RemoteOption +import pl.treksoft.kvision.utils.JSON +import pl.treksoft.kvision.utils.obj + +external fun decodeURIComponent(encodedURI: String): String + +/** + * The Select control connected to the multiplatform service. + * + * @constructor + * @param value selected value + * @param serviceManager multiplatform service manager + * @param function multiplatform service method returning the list of options + * @param multiple allows multiple value selection (multiple values are comma delimited) + * @param ajaxOptions additional options for remote data source + * @param classes a set of CSS class names + */ +@UseExperimental(ImplicitReflectionSerializer::class) +open class SelectRemoteInput<T : Any>( + value: String? = null, + serviceManager: KVServiceManager<T>, + function: T.(String?, String?) -> List<RemoteOption>, + multiple: Boolean = false, + ajaxOptions: AjaxOptions? = null, + classes: Set<String> = setOf() +) : SelectInput(null, value, multiple, null, classes) { + init { + val (url, method) = + serviceManager.getCalls()[function.toString().replace("\\s".toRegex(), "")] + ?: throw IllegalStateException("Function not specified!") + val data = obj { + q = "{{{q}}}" + } + val tempAjaxOptions = ajaxOptions ?: AjaxOptions() + this.ajaxOptions = tempAjaxOptions.copy(url = url, preprocessData = { + @Suppress("UnsafeCastFromDynamic") + JSON.plain.parse(RemoteOption.serializer().list, it.result as String).map { + obj { + this.value = it.value + if (it.text != null) this.text = it.text + if (it.className != null) this.`class` = it.className + if (it.disabled) this.disabled = true + if (it.divider) this.divider = true + this.data = obj { + if (it.subtext != null) this.subtext = it.subtext + if (it.icon != null) this.icon = it.icon + if (it.content != null) this.content = it.content + } + } + }.toTypedArray() + }, data = data, beforeSend = { _, b -> + @Suppress("UnsafeCastFromDynamic") + val q = decodeURIComponent(b.data.substring(2)) + b.data = JSON.plain.stringify(JsonRpcRequest(0, url, listOf(q, this.value))) + true + }, httpType = HttpType.valueOf(method.name), cache = false, preserveSelected = true) + if (value != null) { + GlobalScope.launch { + val callAgent = CallAgent() + val initials = callAgent.remoteCall( + url, + JSON.plain.stringify(JsonRpcRequest(0, url, listOf(null, value))), + HttpMethod.POST + ).asDeferred().await() + JSON.plain.parse(RemoteOption.serializer().list, initials.result as String).map { + add(SelectOption(it.value, it.text, selected = true)) + } + this@SelectRemoteInput.refreshSelectInput() + } + } + if (this.ajaxOptions?.emptyRequest == true) { + this.setInternalEventListener<SelectRemote<*>> { + shownBsSelect = { + val input = self.getElementJQuery()?.parent()?.find("input") + input?.trigger("keyup", null) + input?.hide(0) + } + } + + } + } + + companion object { + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun <T : Any> Container.selectRemoteInput( + value: String? = null, + serviceManager: KVServiceManager<T>, + function: T.(String?, String?) -> List<RemoteOption>, + multiple: Boolean = false, + ajaxOptions: AjaxOptions? = null, + classes: Set<String> = setOf(), init: (SelectRemoteInput<T>.() -> Unit)? = null + ): SelectRemoteInput<T> { + val selectRemoteInput = + SelectRemoteInput(value, serviceManager, function, multiple, ajaxOptions, classes).apply { + init?.invoke(this) + } + this.add(selectRemoteInput) + return selectRemoteInput + } + } + +} |