From 62d0118f8d35c202babfff661e1bbff4d4465498 Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Sun, 9 Feb 2020 00:00:07 +0100 Subject: New TypeaheadRemote component module. --- build.gradle | 1 + .../build.gradle | 6 + .../package.json.d/project.info | 3 + .../treksoft/kvision/form/text/TypeaheadRemote.kt | 167 +++++++++++++++++++++ .../kvision/form/text/TypeaheadRemoteInput.kt | 100 ++++++++++++ .../webpack.config.d/css.js | 2 + settings.gradle | 1 + 7 files changed, 280 insertions(+) create mode 100644 kvision-modules/kvision-bootstrap-typeahead-remote/build.gradle create mode 100644 kvision-modules/kvision-bootstrap-typeahead-remote/package.json.d/project.info create mode 100644 kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemote.kt create mode 100644 kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemoteInput.kt create mode 100644 kvision-modules/kvision-bootstrap-typeahead-remote/webpack.config.d/css.js diff --git a/build.gradle b/build.gradle index db61ad2b..c0d156a6 100644 --- a/build.gradle +++ b/build.gradle @@ -180,6 +180,7 @@ dokka { 'kvision-modules/kvision-remote/src/main/kotlin', 'kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin', 'kvision-modules/kvision-tabulator-remote/src/main/kotlin', + 'kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin', 'kvision-modules/kvision-common/src/main/kotlin', 'kvision-modules/kvision-common-types/src/main/kotlin', 'kvision-modules/kvision-common-annotations/src/main/kotlin', diff --git a/kvision-modules/kvision-bootstrap-typeahead-remote/build.gradle b/kvision-modules/kvision-bootstrap-typeahead-remote/build.gradle new file mode 100644 index 00000000..cab403d5 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead-remote/build.gradle @@ -0,0 +1,6 @@ +apply from: "../shared.gradle" + +dependencies { + compile project(":kvision-modules:kvision-bootstrap-typeahead") + compile project(":kvision-modules:kvision-remote") +} diff --git a/kvision-modules/kvision-bootstrap-typeahead-remote/package.json.d/project.info b/kvision-modules/kvision-bootstrap-typeahead-remote/package.json.d/project.info new file mode 100644 index 00000000..15dc8333 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead-remote/package.json.d/project.info @@ -0,0 +1,3 @@ +{ + "description": "KVision Typeahead remote addon module" +} diff --git a/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemote.kt b/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemote.kt new file mode 100644 index 00000000..961b5dd0 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemote.kt @@ -0,0 +1,167 @@ +/* + * 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.text + +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.remote.KVServiceManager + +/** + * Form field typeahead component connected to the multiplatform service. + * + * @constructor + * @param serviceManager multiplatform service manager + * @param function multiplatform service method returning the list of options + * @param stateFunction a function to generate the state object passed with the remote request + * @param items the max number of items to display in the dropdown + * @param minLength the minimum character length needed before triggering dropdown + * @param delay a delay between lookups + * @param type text input type (default "text") + * @param value text input value + * @param name the name attribute of the generated HTML input element + * @param label label text bound to the input element + * @param rich determines if [label] can contain HTML code + */ +open class TypeaheadRemote( + serviceManager: KVServiceManager, + function: suspend T.(String?, String?) -> List, + private val stateFunction: (() -> String)? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, + label: String? = null, rich: Boolean = false +) : AbstractText(label, rich) { + + /** + * The max number of items to display in the dropdown + */ + var items + get() = input.items + set(value) { + input.items = value + } + + /** + * The minimum character length needed before triggering dropdown + */ + var minLength + get() = input.minLength + set(value) { + input.minLength = value + } + + /** + * Determines if hints should be shown as soon as the input gets focus. + */ + var showHintOnFocus + get() = input.showHintOnFocus + set(value) { + input.showHintOnFocus = value + } + + /** + * Determines if the first suggestion is selected automatically. + */ + var autoSelect + get() = input.autoSelect + set(value) { + input.autoSelect = value + } + + /** + * A delay between lookups. + */ + var delay + get() = input.ajaxOptions + set(value) { + input.ajaxOptions = value + } + + /** + * Determines if the menu is the same size as the input it is attached to. + */ + var fitToElement + get() = input.fitToElement + set(value) { + input.fitToElement = value + } + + /** + * Text input type. + */ + var type + get() = input.type + set(value) { + input.type = value + } + /** + * Determines if autocomplete is enabled for the input element. + */ + var autocomplete + get() = input.autocomplete + set(value) { + input.autocomplete = value + } + + final override val input: TypeaheadRemoteInput = + TypeaheadRemoteInput(serviceManager, function, stateFunction, items, minLength, delay, type, value).apply { + this.id = idc + this.name = name + } + + init { + @Suppress("LeakingThis") + input.eventTarget = this + this.addInternal(input) + this.addInternal(invalidFeedback) + } +} + +/** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ +fun Container.typeaheadRemote( + serviceManager: KVServiceManager, + function: suspend T.(String?, String?) -> List, + stateFunction: (() -> String)? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, + label: String? = null, rich: Boolean = false, init: (TypeaheadRemote.() -> Unit)? = null +): TypeaheadRemote { + val typeaheadRemote = TypeaheadRemote( + serviceManager, + function, + stateFunction, + items, + minLength, + delay, + type, + value, + name, + label, + rich + ).apply { + init?.invoke(this) + } + this.add(typeaheadRemote) + return typeaheadRemote +} diff --git a/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemoteInput.kt b/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemoteInput.kt new file mode 100644 index 00000000..e56468d1 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead-remote/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadRemoteInput.kt @@ -0,0 +1,100 @@ +/* + * 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.text + +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.list +import kotlinx.serialization.serializer +import kotlinx.serialization.stringify +import org.w3c.dom.get +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.remote.JsonRpcRequest +import pl.treksoft.kvision.remote.KVServiceManager +import pl.treksoft.kvision.utils.JSON +import kotlin.browser.window + +/** + * The Typeahead control connected to the multiplatform service. + * + * @constructor + * @param serviceManager multiplatform service manager + * @param function multiplatform service method returning the list of options + * @param stateFunction a function to generate the state object passed with the remote request + * @param items the max number of items to display in the dropdown + * @param minLength the minimum character length needed before triggering dropdown + * @param delay a delay between lookups + * @param type text input type (default "text") + * @param value text input value + * @param classes a set of CSS class names + */ +@UseExperimental(ImplicitReflectionSerializer::class) +open class TypeaheadRemoteInput( + serviceManager: KVServiceManager, + function: suspend T.(String?, String?) -> List, + private val stateFunction: (() -> String)? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, classes: Set = setOf() +) : TypeaheadInput(null, null, items, minLength, delay, type, value, classes) { + + private val kvUrlPrefix = window["kv_remote_url_prefix"] + private val urlPrefix: String = if (kvUrlPrefix != undefined) kvUrlPrefix else "" + + init { + val (url, method) = + serviceManager.getCalls()[function.toString().replace("\\s".toRegex(), "")] + ?: throw IllegalStateException("Function not specified!") + this.ajaxOptions = TaAjaxOptions( + urlPrefix + url, + preprocessQuery = { query -> + val state = stateFunction?.invoke() + JSON.plain.stringify(JsonRpcRequest(0, url, listOf(query, state))) + }, + preprocessData = { + JSON.plain.parse(String.serializer().list, it.result as String).toTypedArray() + }, + httpType = HttpType.valueOf(method.name) + ) + } +} + +/** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ +fun Container.typeaheadRemoteInput( + serviceManager: KVServiceManager, + function: suspend T.(String?, String?) -> List, + stateFunction: (() -> String)? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, classes: Set = setOf(), + init: (TypeaheadRemoteInput.() -> Unit)? = null +): TypeaheadRemoteInput { + val typeaheadRemoteInput = + TypeaheadRemoteInput( + serviceManager, function, stateFunction, items, minLength, delay, type, value, classes + ).apply { + init?.invoke(this) + } + this.add(typeaheadRemoteInput) + return typeaheadRemoteInput +} diff --git a/kvision-modules/kvision-bootstrap-typeahead-remote/webpack.config.d/css.js b/kvision-modules/kvision-bootstrap-typeahead-remote/webpack.config.d/css.js new file mode 100644 index 00000000..5d710d35 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead-remote/webpack.config.d/css.js @@ -0,0 +1,2 @@ +config.module.rules.push({ test: /\.css$/, loader: "style-loader!css-loader" }); + diff --git a/settings.gradle b/settings.gradle index 8b3b6cc0..be1b7b75 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ include 'kvision-modules:kvision-base', 'kvision-modules:kvision-bootstrap-select-remote', 'kvision-modules:kvision-bootstrap-dialog', 'kvision-modules:kvision-bootstrap-typeahead', + 'kvision-modules:kvision-bootstrap-typeahead-remote', 'kvision-modules:kvision-richtext', 'kvision-modules:kvision-handlebars', 'kvision-modules:kvision-i18n', -- cgit