From 6b53324c97bfc80ed14dfca6a5dbc879950715b9 Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Thu, 3 Oct 2019 19:03:21 +0200 Subject: Upgrade to Bootstrap 4. Upgrade to Font Awesome 5. Restructure modules. --- .../kotlin/pl/treksoft/kvision/KVManagerSelect.kt | 53 +++ .../pl/treksoft/kvision/form/select/AjaxOptions.kt | 140 ++++++++ .../pl/treksoft/kvision/form/select/Select.kt | 285 ++++++++++++++++ .../pl/treksoft/kvision/form/select/SelectInput.kt | 375 +++++++++++++++++++++ .../treksoft/kvision/form/select/SelectOptGroup.kt | 123 +++++++ .../treksoft/kvision/form/select/SelectOption.kt | 170 ++++++++++ 6 files changed, 1146 insertions(+) create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt (limited to 'kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft') diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt new file mode 100644 index 00000000..3617c417 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt @@ -0,0 +1,53 @@ +/* + * 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 + +internal val kVManagerSelectInit = KVManagerSelect.init() + +/** + * Internal singleton object which initializes and configures KVision select module. + */ +internal object KVManagerSelect { + internal const val AJAX_REQUEST_DELAY = 300 + internal const val KVNULL = "#kvnull" + + init { + require("bootstrap-select/dist/css/bootstrap-select.min.css") + require("bootstrap-select/dist/js/bootstrap-select.min.js") + require("./js/locales/bootstrap-select/bootstrap-select-i18n.min.js") + require("ajax-bootstrap-select/dist/css/ajax-bootstrap-select.min.css") + require("ajax-bootstrap-select/dist/js/ajax-bootstrap-select.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.de-DE.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.es-ES.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.fr-FR.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.it-IT.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ja-JP.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ko-KR.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.nl-NL.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pl-PL.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pt-BR.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ru-RU.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.tr-TR.min.js") + } + + internal fun init() {} +} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.kt new file mode 100644 index 00000000..c088f68d --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/AjaxOptions.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 pl.treksoft.jquery.JQueryXHR +import pl.treksoft.kvision.KVManagerSelect.AJAX_REQUEST_DELAY +import pl.treksoft.kvision.KVManagerSelect.KVNULL +import pl.treksoft.kvision.i18n.I18n +import pl.treksoft.kvision.utils.obj + +/** + * HTTP protocol type for the AJAX call. + */ +enum class HttpType(internal val type: String) { + GET("GET"), + POST("POST") +} + +/** + * Data type for the AJAX call. + */ +enum class DataType(internal val type: String) { + JSON("json"), + JSONP("jsonp"), + XML("xml"), + TEXT("text"), + SCRIPT("script") +} + +/** + * Data class for AJAX options. + * + * @constructor + * @param url the url address + * @param preprocessData + * [AjaxBootstrapSelect preprocessOption](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionspreprocessdata) + * option + * @param beforeSend + * [JQuery ajax.beforeSend](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param data + * [JQuery ajax.data](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param httpType + * [JQuery ajax.type](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param minLength + * [AjaxBootstrapSelect minLength](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsminlength) option + * @param cache + * [AjaxBootstrapSelect cache](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionscache) option + * @param clearOnEmpty + * [AjaxBootstrapSelect clearOnEmpty](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsclearonempty) option + * @param clearOnError + * [AjaxBootstrapSelect clearOnError](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsclearonerror) option + * @param emptyRequest + * [AjaxBootstrapSelect emptyRequest](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsemptyrequest) option + * @param requestDelay + * [AjaxBootstrapSelect requestDelay](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsrequestdelay) option + * @param restoreOnError + * [AjaxBootstrapSelect restoreOnError](https://github.com/truckingsim/Ajax-Bootstrap-Select#optionsrestoreonerror) + * option + */ +data class AjaxOptions( + val url: String? = null, + val preprocessData: ((dynamic) -> dynamic)? = null, + val beforeSend: ((JQueryXHR, dynamic) -> dynamic)? = null, + val data: 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 requestDelay: Int = AJAX_REQUEST_DELAY, + val restoreOnError: Boolean = false, + val preserveSelected: Boolean = false, + val processData: Boolean = false +) + +/** + * Convert AjaxOptions to JavaScript JSON object. + * @param emptyOption add an empty position as the first select option + * @return JSON object + */ +fun AjaxOptions.toJs(emptyOption: Boolean): dynamic { + val procData = { data: dynamic -> + val processedData = this.preprocessData?.invoke(data) ?: data + if (emptyOption) { + val ret = mutableListOf(obj { + this.value = KVNULL + this.text = "" + }) + @Suppress("UnsafeCastFromDynamic") + ret.addAll((processedData as Array).asList()) + ret.toTypedArray() + } else { + processedData + } + } + val language = I18n.language + return obj { + this.ajax = obj { + this.url = url ?: "/" + this.method = httpType.type + this.dataType = dataType.type + this.data = data + this.beforeSend = beforeSend + this.contentType = "application/json" + } + this.preprocessData = procData + this.minLength = minLength + this.cache = cache + this.clearOnEmpty = clearOnEmpty + this.clearOnError = clearOnError + this.emptyRequest = emptyRequest + this.preserveSelected = preserveSelected + this.requestDelay = requestDelay + this.restoreOnError = restoreOnError + this.langCode = language + this.processData = processData + this.preserveSelectedOrder = true + } +} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt new file mode 100644 index 00000000..4b2505d2 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/Select.kt @@ -0,0 +1,285 @@ +/* + * 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.StringPair +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.utils.SnOn + +/** + * The form field component for Select control. + * + * The select control can be populated directly from *options* parameter or manually by adding + * [SelectOption] or [SelectOptGroup] components to the container. + * + * @constructor + * @param options an optional list of options (value to label pairs) for the select control + * @param value selected value + * @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 (AJAX) data source + * @param label label text bound to the input element + * @param rich determines if [label] can contain HTML code + */ +@Suppress("TooManyFunctions") +open class Select( + options: List? = null, value: String? = null, name: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, + rich: Boolean = false +) : SimplePanel(setOf("form-group")), StringFormControl { + + /** + * A list of options (value to label pairs) for the select control. + */ + var options + get() = input.options + set(value) { + input.options = value + } + /** + * 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 + } + /** + * Additional options for remote (AJAX) data source. + */ + var ajaxOptions + get() = input.ajaxOptions + set(value) { + input.ajaxOptions = value + } + /** + * Maximal number of selected options. + */ + var maxOptions + get() = input.maxOptions + set(value) { + input.maxOptions = value + } + /** + * Determines if live search is available. + */ + var liveSearch + get() = input.liveSearch + set(value) { + input.liveSearch = 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_select_$counter" + final override val input: SelectInput = SelectInput( + options, value, 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 { + val cl = super.getSnClass().toMutableList() + if (validatorError != null) { + cl.add("text-danger" to true) + } + return cl + } + + @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: Component): SimplePanel { + input.add(child) + return this + } + + override fun addAll(children: List): 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 { + 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 Container.select( + options: List? = null, value: String? = null, name: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, + rich: Boolean = false, init: (Select.() -> Unit)? = null + ): Select { + val select = Select(options, value, name, multiple, ajaxOptions, label, rich).apply { init?.invoke(this) } + this.add(select) + return select + } + } +} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt new file mode 100644 index 00000000..84eccaf7 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -0,0 +1,375 @@ +/* + * 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 com.github.snabbdom.VNode +import pl.treksoft.kvision.KVManagerSelect.KVNULL +import pl.treksoft.kvision.core.Component +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.core.CssSize +import pl.treksoft.kvision.core.StringBoolPair +import pl.treksoft.kvision.core.StringPair +import pl.treksoft.kvision.form.FormInput +import pl.treksoft.kvision.form.InputSize +import pl.treksoft.kvision.form.ValidationStatus +import pl.treksoft.kvision.html.ButtonStyle +import pl.treksoft.kvision.panel.SimplePanel +import pl.treksoft.kvision.utils.asString +import pl.treksoft.kvision.utils.obj + +/** + * Select width types. See [Bootstrap Select width](http://silviomoreto.github.io/bootstrap-select/examples/#width). + */ +enum class SelectWidthType(internal val value: String) { + AUTO("auto"), + FIT("fit") +} + +/** + * The basic component for Select control. + * + * The select control can be populated directly from *options* parameter or manually by adding + * [SelectOption] or [SelectOptGroup] components to the container. + * + * @constructor + * @param options an optional list of options (value to label pairs) for the select control + * @param value selected value + * @param multiple allows multiple value selection (multiple values are comma delimited) + * @param ajaxOptions additional options for remote (AJAX) data source + * @param classes a set of CSS class names + */ +@Suppress("TooManyFunctions") +open class SelectInput( + options: List? = null, value: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + classes: Set = setOf() +) : SimplePanel(classes), FormInput { + + /** + * A list of options (value to label pairs) for the select control. + */ + var options by refreshOnUpdate(options) { setChildrenFromOptions() } + /** + * A value of the selected option. + */ + var value by refreshOnUpdate(value) { refreshState() } + /** + * The name attribute of the generated HTML select element. + */ + override var name: String? by refreshOnUpdate() + /** + * Determines if multiple value selection is allowed. + */ + var multiple by refreshOnUpdate(multiple) + /** + * Additional options for remote (AJAX) data source. + */ + var ajaxOptions by refreshOnUpdate(ajaxOptions) { + if (it != null) { + liveSearch = true + } + refresh() + } + /** + * Maximal number of selected options. + */ + var maxOptions: Int? by refreshOnUpdate() + /** + * Determines if live search is available. + */ + var liveSearch by refreshOnUpdate(false) + /** + * The placeholder for the select control. + */ + var placeholder: String? by refreshOnUpdate() + /** + * The style of the select control. + */ + var style: ButtonStyle? by refreshOnUpdate() + /** + * The width of the select control. + */ + var selectWidth: CssSize? by refreshOnUpdate() + /** + * The width type of the select control. + */ + var selectWidthType: SelectWidthType? by refreshOnUpdate() + /** + * Determines if an empty option is automatically generated. + */ + var emptyOption by refreshOnUpdate(false) { setChildrenFromOptions() } + /** + * Determines if the field is disabled. + */ + override var disabled by refreshOnUpdate(false) + /** + * Determines if the select is automatically focused. + */ + var autofocus: Boolean? by refreshOnUpdate() + /** + * The size of the input. + */ + override var size: InputSize? by refreshOnUpdate() + /** + * The validation status of the input. + */ + override var validationStatus: ValidationStatus? by refreshOnUpdate() + + init { + setChildrenFromOptions() + this.setInternalEventListener { + change = { + val v = getElementJQuery()?.`val`() + self.value = v?.let { + calculateValue(it) + } + } + } + } + + private fun calculateValue(v: Any): String? { + return if (this.multiple) { + @Suppress("UNCHECKED_CAST") + val arr = v as? Array + if (arr != null && arr.isNotEmpty()) { + arr.filter { it.isNotEmpty() }.joinToString(",") + } else { + null + } + } else { + val vs = v as String + if (KVNULL == vs || vs.isEmpty()) { + null + } else { + vs + } + } + } + + override fun render(): VNode { + return render("select", childrenVNodes()) + } + + override fun add(child: Component): SimplePanel { + super.add(child) + refreshSelectInput() + return this + } + + override fun addAll(children: List): SimplePanel { + super.addAll(children) + refreshSelectInput() + return this + } + + override fun remove(child: Component): SimplePanel { + super.remove(child) + refreshSelectInput() + return this + } + + override fun removeAll(): SimplePanel { + super.removeAll() + refreshSelectInput() + return this + } + + private fun setChildrenFromOptions() { + if (ajaxOptions == null) { + super.removeAll() + if (emptyOption) { + super.add(SelectOption(KVNULL, "")) + } + options?.let { + val c = it.map { + SelectOption(it.first, it.second) + } + super.addAll(c) + } + } + this.refreshSelectInput() + } + + /** + * Opens dropdown with options. + */ + open fun showOptions() { + getElementJQueryD()?.selectpicker("show") + } + + /** + * Hides dropdown with options. + */ + open fun hideOptions() { + getElementJQueryD()?.selectpicker("hide") + } + + /** + * Toggles visibility of dropdown with options. + */ + open fun toggleOptions() { + getElementJQueryD()?.selectpicker("toggle") + } + + override fun getSnClass(): List { + val cl = super.getSnClass().toMutableList() + cl.add("selectpicker" to true) + validationStatus?.let { + cl.add(it.className to true) + } + size?.let { + cl.add(it.className to true) + } + return cl + } + + protected fun refreshSelectInput() { + getElementJQueryD()?.selectpicker("refresh") + refreshState() + getElementJQueryD()?.trigger("change")?.data("AjaxBootstrapSelect")?.list?.cache = {} + } + + @Suppress("ComplexMethod") + override fun getSnAttrs(): List { + val sn = super.getSnAttrs().toMutableList() + name?.let { + sn.add("name" to it) + } + if (multiple) { + sn.add("multiple" to "multiple") + } + maxOptions?.let { + sn.add("data-max-options" to "" + it) + } + if (liveSearch) { + sn.add("data-live-search" to "true") + } + placeholder?.let { + sn.add("title" to translate(it)) + } + autofocus?.let { + if (it) { + sn.add("autofocus" to "autofocus") + } + } + if (disabled) { + sn.add("disabled" to "disabled") + } + 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.asString()) + } + return sn + } + + @Suppress("UnsafeCastFromDynamic") + override fun afterInsert(node: VNode) { + ajaxOptions?.let { + getElementJQueryD()?.selectpicker("render").ajaxSelectPicker(it.toJs(emptyOption)) + } ?: 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() { + if (ajaxOptions == null) { + value?.let { + if (multiple) { + getElementJQueryD()?.selectpicker("val", it.split(",").toTypedArray()) + } else { + getElementJQueryD()?.selectpicker("val", it) + } + } ?: getElementJQueryD()?.selectpicker("val", null) + } + } + + /** + * Makes the input element focused. + */ + override fun focus() { + getElementJQuery()?.focus() + } + + /** + * Makes the input element blur. + */ + override fun blur() { + getElementJQuery()?.blur() + } + + companion object { + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun Container.selectInput( + options: List? = null, value: String? = null, + multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + classes: Set = setOf(), init: (SelectInput.() -> Unit)? = null + ): SelectInput { + val selectInput = SelectInput(options, value, multiple, ajaxOptions, classes).apply { init?.invoke(this) } + this.add(selectInput) + return selectInput + } + } +} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt new file mode 100644 index 00000000..3f07a9bf --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt @@ -0,0 +1,123 @@ +/* + * 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 com.github.snabbdom.VNode +import pl.treksoft.kvision.core.StringPair +import pl.treksoft.kvision.panel.SimplePanel + +/** + * The helper container for adding option groups to [Select]. + * + * The option group can be populated directly from *options* parameter or manually by adding + * [SelectOption] components to the container. + * + * @constructor + * @param label the label of the group + * @param options an optional list of options (label to value pairs) for the group + * @param maxOptions maximal number of selected options in the group + * @param disabled renders a disabled group + * @param classes a set of CSS class names + */ +open class SelectOptGroup( + label: String, options: List? = null, maxOptions: Int? = null, + disabled: Boolean = false, classes: Set = setOf() +) : SimplePanel(classes) { + /** + * A label for the group. + */ + var label by refreshOnUpdate(label) + /** + * A list of options (label to value pairs) for the group. + */ + var options by refreshOnUpdate(options) { setChildrenFromOptions() } + /** + * Maximal number of selected options in the group. + */ + var maxOptions by refreshOnUpdate(maxOptions) + /** + * Determines if the group is disabled. + */ + var disabled by refreshOnUpdate(disabled) + + init { + setChildrenFromOptions() + } + + override fun render(): VNode { + return render("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 translate(label)) + maxOptions?.let { + sn.add("data-max-options" to "" + it) + } + if (disabled) { + sn.add("disabled" to "disabled") + } + return sn + } + + companion object { + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun Select.selectOptGroup( + label: String, options: List? = null, maxOptions: Int? = null, + disabled: Boolean = false, classes: Set = setOf(), init: (SelectOptGroup.() -> Unit)? = null + ): SelectOptGroup { + val selectOptGroup = + SelectOptGroup(label, options, maxOptions, disabled, classes).apply { init?.invoke(this) } + this.add(selectOptGroup) + return selectOptGroup + } + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun SelectInput.selectOptGroup( + label: String, options: List? = null, maxOptions: Int? = null, + disabled: Boolean = false, classes: Set = setOf(), init: (SelectOptGroup.() -> Unit)? = null + ): SelectOptGroup { + val selectOptGroup = + SelectOptGroup(label, options, maxOptions, disabled, classes).apply { init?.invoke(this) } + this.add(selectOptGroup) + return selectOptGroup + } + } +} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt new file mode 100644 index 00000000..91c269a8 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt @@ -0,0 +1,170 @@ +/* + * 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 com.github.snabbdom.VNode +import pl.treksoft.kvision.core.StringPair +import pl.treksoft.kvision.core.Widget + +/** + * The helper component for adding options to [Select] or [SelectOptGroup]. + * + * @constructor + * @param value the value of the option + * @param label the label of the option + * @param subtext the small subtext after the label of the option + * @param icon the icon before the label of the option + * @param divider renders this option as a divider + * @param disabled renders a disabled option + * @param classes a set of CSS class names + */ +open class SelectOption( + value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, + divider: Boolean = false, disabled: Boolean = false, selected: Boolean = false, + classes: Set = setOf() +) : Widget(classes) { + + /** + * The value of the option. + */ + var value by refreshOnUpdate(value) + /** + * The label of the option. + */ + var label by refreshOnUpdate(label) + /** + * The subtext after the label of the option. + */ + var subtext by refreshOnUpdate(subtext) + /** + * The icon before the label of the option. + */ + var icon by refreshOnUpdate(icon) + /** + * Determines if the option should be rendered as divider. + */ + var divider by refreshOnUpdate(divider) + /** + * Determines if the option should be disabled. + */ + var disabled by refreshOnUpdate(disabled) + /** + * Determines if the option is selected. + */ + var selected by refreshOnUpdate(selected) + + override fun render(): VNode { + return if (!divider) { + render("option", arrayOf(translate(label) ?: value)) + } else { + render("option") + } + } + + @Suppress("ComplexMethod") + 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 translate(it)) + } + icon?.let { + sn.add("data-icon" to it) + } + if (disabled) { + sn.add("disabled" to "disabled") + } + if (selected) { + sn.add("selected" to "selected") + } + } else { + sn.add("data-divider" to "true") + } + return sn + } + + companion object { + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun Select.selectOption( + value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, + divider: Boolean = false, disabled: Boolean = false, selected: Boolean = false, + classes: Set = setOf(), init: (SelectOption.() -> Unit)? = null + ): SelectOption { + val selectOption = + SelectOption(value, label, subtext, icon, divider, disabled, selected, classes).apply { + init?.invoke( + this + ) + } + this.add(selectOption) + return selectOption + } + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun SelectInput.selectOption( + value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, + divider: Boolean = false, disabled: Boolean = false, selected: Boolean = false, + classes: Set = setOf(), init: (SelectOption.() -> Unit)? = null + ): SelectOption { + val selectOption = + SelectOption(value, label, subtext, icon, divider, disabled, selected, classes).apply { + init?.invoke( + this + ) + } + this.add(selectOption) + return selectOption + } + + /** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ + fun SelectOptGroup.selectOption( + value: String? = null, label: String? = null, subtext: String? = null, icon: String? = null, + divider: Boolean = false, disabled: Boolean = false, selected: Boolean = false, + classes: Set = setOf(), init: (SelectOption.() -> Unit)? = null + ): SelectOption { + val selectOption = + SelectOption(value, label, subtext, icon, divider, disabled, selected, classes).apply { + init?.invoke( + this + ) + } + this.add(selectOption) + return selectOption + } + + } +} -- cgit From 8427632d0ba7e31e7a0802d2eed7f151ef174a4b Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Sat, 5 Oct 2019 15:28:28 +0200 Subject: Updated Select form control. --- .../kvision-bootstrap-select/build.gradle | 4 +- .../kotlin/pl/treksoft/kvision/KVManagerSelect.kt | 5 + .../ajax-bootstrap-select.cs-CZ.min.js | 23 ++ .../ajax-bootstrap-select.sr-SP.min.js | 23 ++ .../bootstrap-select/bootstrap-select-i18n.min.js | 2 +- .../pl/treksoft/kvision/KVManagerBootstrap.kt | 1 - .../src/main/resources/css/kvbootstrap.css | 295 -------------------- src/main/kotlin/pl/treksoft/kvision/KVManager.kt | 4 +- src/main/resources/css/style.css | 310 ++++++++++++++++++++- 9 files changed, 365 insertions(+), 302 deletions(-) create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.cs-CZ.min.js create mode 100644 kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.sr-SP.min.js delete mode 100644 kvision-modules/kvision-bootstrap/src/main/resources/css/kvbootstrap.css (limited to 'kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft') diff --git a/kvision-modules/kvision-bootstrap-select/build.gradle b/kvision-modules/kvision-bootstrap-select/build.gradle index 83bcd627..7c10515f 100644 --- a/kvision-modules/kvision-bootstrap-select/build.gradle +++ b/kvision-modules/kvision-bootstrap-select/build.gradle @@ -7,8 +7,8 @@ dependencies { kotlinFrontend { npm { - dependency("bootstrap-select", "1.12.4") - dependency("ajax-bootstrap-select", "1.4.3") + dependency("bootstrap-select", "1.13.11") + dependency("ajax-bootstrap-select", "1.4.5") } } diff --git a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt index 3617c417..09f92a0d 100644 --- a/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt +++ b/kvision-modules/kvision-bootstrap-select/src/main/kotlin/pl/treksoft/kvision/KVManagerSelect.kt @@ -36,6 +36,7 @@ internal object KVManagerSelect { require("./js/locales/bootstrap-select/bootstrap-select-i18n.min.js") require("ajax-bootstrap-select/dist/css/ajax-bootstrap-select.min.css") require("ajax-bootstrap-select/dist/js/ajax-bootstrap-select.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.cs-CZ.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.de-DE.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.es-ES.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.fr-FR.min.js") @@ -46,7 +47,11 @@ internal object KVManagerSelect { require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pl-PL.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.pt-BR.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.ru-RU.min.js") + require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.sr-SP.min.js") require("./js/locales/ajax-bootstrap-select/ajax-bootstrap-select.tr-TR.min.js") + js("$.fn.selectpicker.Constructor.BootstrapVersion = '4';") + js("$.fn.selectpicker.Constructor.DEFAULTS.styleBase = 'form-control';"); + js("$.fn.selectpicker.Constructor.DEFAULTS.style = '';") } internal fun init() {} diff --git a/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.cs-CZ.min.js b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.cs-CZ.min.js new file mode 100644 index 00000000..994014a9 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.cs-CZ.min.js @@ -0,0 +1,23 @@ +/*! + * Ajax Bootstrap Select + * + * Extends existing [Bootstrap Select] implementations by adding the ability to search via AJAX requests as you type. Originally for CROSCON. + * + * @version 1.4.5 + * @author Adam Heim - https://github.com/truckingsim + * @link https://github.com/truckingsim/Ajax-Bootstrap-Select + * @copyright 2019 Adam Heim + * @license Released under the MIT license. + * + * Contributors: + * Mark Carver - https://github.com/markcarver + * + * Last build: 2019-04-23 12:18:55 PM EDT + */ +!(function ($) { + +/*! + * Czech translation for the "cs-CZ" and "cs" language codes. + * Martin Brettschneider + */ +$.fn.ajaxSelectPicker.locale["cs-CZ"]={currentlySelected:"Aktuálně vybrané",emptyTitle:"Vyberte a začněte psát",errorText:"Výsledky nelze načíst",searchPlaceholder:"Vyhledat...",statusInitialized:"Začněte psát hledaný výraz",statusNoResults:"Žádné výsledky",statusSearching:"Vyhledávání...",statusTooShort:"Zadejte více znaků"},$.fn.ajaxSelectPicker.locale.cs=$.fn.ajaxSelectPicker.locale["cs-CZ"];})(jQuery); diff --git a/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.sr-SP.min.js b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.sr-SP.min.js new file mode 100644 index 00000000..4536c620 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/ajax-bootstrap-select/ajax-bootstrap-select.sr-SP.min.js @@ -0,0 +1,23 @@ +/*! + * Ajax Bootstrap Select + * + * Extends existing [Bootstrap Select] implementations by adding the ability to search via AJAX requests as you type. Originally for CROSCON. + * + * @version 1.4.5 + * @author Adam Heim - https://github.com/truckingsim + * @link https://github.com/truckingsim/Ajax-Bootstrap-Select + * @copyright 2019 Adam Heim + * @license Released under the MIT license. + * + * Contributors: + * Mark Carver - https://github.com/markcarver + * + * Last build: 2019-04-23 12:18:55 PM EDT + */ +!(function ($) { + +/*! + * Serbian translation for the "sr-SP" and "sr" language codes. + * Miroslav Maksimovic + */ +$.fn.ajaxSelectPicker.locale["sr-SP"]={currentlySelected:"Trenutno izabrano",emptyTitle:"Izaberite i pocnite kucati",errorText:"Nemoguce dobiti rezultate",searchPlaceholder:"Pretrazi...",statusInitialized:"Pocnite kucati kljucnu rijec",statusNoResults:"Nema rezultata",statusSearching:"Trazim...",statusTooShort:"Molimo unesite vise znakova"},$.fn.ajaxSelectPicker.locale.ru=$.fn.ajaxSelectPicker.locale["sr-SP"];})(jQuery); diff --git a/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/bootstrap-select/bootstrap-select-i18n.min.js b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/bootstrap-select/bootstrap-select-i18n.min.js index 4428d3c0..877071ce 100644 --- a/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/bootstrap-select/bootstrap-select-i18n.min.js +++ b/kvision-modules/kvision-bootstrap-select/src/main/resources/js/locales/bootstrap-select/bootstrap-select-i18n.min.js @@ -1 +1 @@ -!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){!function(a){a.fn.selectpicker.defaults={noneSelectedText:"",noneResultsText:"",countSelectedText:function(a,b){return 1==a?"... ({n})":"... ({n})"},maxOptionsText:function(a,b){return[1==a?"🛇":"🛇",1==b?"🛇":"🛇"]},selectAllText:"++",deselectAllText:"--",multipleSeparator:", "}}(a)}); \ No newline at end of file +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){!function(a){a.fn.selectpicker.defaults={noneSelectedText:" ",noneResultsText:"",countSelectedText:function(a,b){return 1==a?"... ({n})":"... ({n})"},maxOptionsText:function(a,b){return[1==a?"🛇":"🛇",1==b?"🛇":"🛇"]},selectAllText:"++",deselectAllText:"--",multipleSeparator:", "}}(a)}); \ No newline at end of file diff --git a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt index b7a44115..78365281 100644 --- a/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt +++ b/kvision-modules/kvision-bootstrap/src/main/kotlin/pl/treksoft/kvision/KVManagerBootstrap.kt @@ -33,7 +33,6 @@ internal object KVManagerBootstrap { init { require("bootstrap/dist/js/bootstrap.bundle.min.js") require("awesome-bootstrap-checkbox") - require("./css/kvbootstrap.css") } private val elementResizeEvent = require("element-resize-event") diff --git a/kvision-modules/kvision-bootstrap/src/main/resources/css/kvbootstrap.css b/kvision-modules/kvision-bootstrap/src/main/resources/css/kvbootstrap.css deleted file mode 100644 index 6f99a26c..00000000 --- a/kvision-modules/kvision-bootstrap/src/main/resources/css/kvbootstrap.css +++ /dev/null @@ -1,295 +0,0 @@ -.input-group.date.is-invalid~.invalid-feedback { - display: block; -} - -/* - -.bootstrap-touchspin .input-group-btn-vertical> .input-sm { - padding: 7px 10px; - height: 6px; -} - -.bootstrap-touchspin .input-group-btn-vertical> .input-lg { - height: 24px; -} - -.kv-spinner-btn-none .input-group-btn-vertical { - display: none; -} - -.kv-spinner-btn-none .form-control { - border-radius: 4px !important; -} - -.kv-spinner-btn-vertical .form-control { - border-radius: 4px 0px 0px 4px !important; -}*/ - -.form-check { - padding-left: 0.5rem; -} - -.form-check-input.form-control-sm, .form-check-input.form-control-lg { - height: inherit; -} - -.kv-radiogroup-inline label.control-label { - vertical-align: top; - margin-right: .75rem; - margin-bottom: 0px; -} -.row.kv-radiogroup-inline label.control-label { - margin-right: 0px; -} - -.row.kv-radiogroup-inline .kv-radiogroup-container, .row.kv-radiogroup .kv-radiogroup-container { - margin-left: -15px; -} - -.kv-radiogroup-inline .kv-radiogroup-container { - display: inline-flex; -} - -.kv-radiogroup-container.is-invalid~.invalid-feedback { - display: block; -} - -.form-check-inline { - margin-left: 3px; -} - -.form-check-inline.form-check { - padding-left: 0px; -} - -.form-horizontal.container-fluid { - width: inherit; -} - -.form-inline .form-group { - margin-right: 6px; -} - -.form-inline .form-group .control-label { - margin-right: 6px; -} - -.form-inline .form-check.form-group { - margin-left: 6px; -} - -.kv-form-condensed .form-group { - margin-bottom: 0.5rem; -} - -.kv-window.modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0,0,0,.5); - box-shadow: 0 5px 15px rgba(0,0,0,.5); - border-radius: 0px; -} - -.kv-window .modal-header { - height: 40px; - padding: 5px 15px 5px 15px; - align-items: center; -} - -.kv-window .modal-header button.close { - width: 24px; - height: 24px; - margin: 0px; - padding: 0px; -} - -.kv-window .modal-header .modal-title { - white-space: nowrap; -} - -.kv-window .modal-header .window-icon { - margin-right: 6px; -} - -.kv-window .kv-window-icons-container { - display: flex; -} - -.kv-preview-thumb .btn, .kv-zoom-actions .btn, .file-zoom-dialog .floating-buttons .btn { - padding: 5px 8px; -} - -.file-drop-zone.clickable:hover { - border: 1px dashed #999; -} - -.file-drop-zone.clickable:focus { - border: 1px solid #5acde2; -} - -.nav.tabs-top { - flex-wrap: nowrap; -} - -ul.tabs-top { - overflow-x: auto; - overflow-y: hidden; - display: flex; -} - -ul.tabs-top > li { - float:none; - flex-shrink: 0; -} - -.kv-tab-close { - margin-left: 10px; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: 0.2; -} - -.kv-tab-close:hover, .kv-tab-close:focus { - cursor: pointer; - filter: alpha(opacity=50); - opacity: 0.5; -} - -/*select.form-control, .tabulator-row .tabulator-cell.tabulator-editing select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: transparent none no-repeat; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAKCAYAAABblxXYAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUKFyIn4IjqJgAAAENJREFUKM/l0LERACEQQlGsiTa2px1aokGugNNAx8wfMy8AeLoBALYjaTqoKkga2+gKPgF/2Q7JkEx359oftu+C7/UBCUIcVQz0PvcAAAAASUVORK5CYII='); - background-position: right center; - cursor: pointer; -} - -select.form-control:hover { - background-color: #e6e6e6; -} - -select.form-control option { - background-color: white; -} - -select.input-sm { - line-height: inherit; -}*/ - -.abc-checkbox input[type="checkbox"]:checked+label::after, -.abc-checkbox input[type="radio"]:checked+label::after { - font-family: "Font Awesome 5 Pro", "Font Awesome 5 Free"; - content: "\f00c"; - font-weight: 900; -} - -.abc-checkbox label::before { - top: 0px; -} -.abc-checkbox label::after { - top: 0px; -} - -.abc-radio label::before { - top: 0px; -} - -.abc-radio label::after { - top: 3px; -} - -.abc-checkbox.form-check-inline label::before { - top: 2px; -} - -.abc-checkbox.form-check-inline label::after { - top: 2px; -} - -.abc-radio.form-check-inline label::before { - top: 2px; -} - -.abc-radio.form-check-inline label::after { - top: 5px; -} - -.abc-checkbox label.col-form-label-lg::before { - top: 10px; -} -.abc-checkbox label.col-form-label-lg::after { - top: 10px; -} - -.abc-radio label.col-form-label-lg::before { - top: 10px; -} - -.abc-radio label.col-form-label-lg::after { - top: 13px; -} - -.abc-checkbox.form-check-inline label.col-form-label-lg::before { - top: 15px; -} - -.abc-checkbox.form-check-inline label.col-form-label-lg::after { - top: 15px; -} - -.abc-radio.form-check-inline label.col-form-label-lg::before { - top: 15px; -} - -.abc-radio.form-check-inline label.col-form-label-lg::after { - top: 18px; -} - -/*! - * bootstrap-vertical-tabs - v1.2.2 - * https://dbtek.github.io/bootstrap-vertical-tabs - * 2016-12-02 - * Copyright (c) 2016 İsmail Demirbilek - * License: MIT - */ -.nav-tabs.tabs-left, .nav-tabs.tabs-right { - border-bottom: none; - padding-top: 2px; -} -.nav-tabs.tabs-left { - border-right: 1px solid #dee2e6; -} -.nav-tabs.tabs-right { - border-left: 1px solid #dee2e6; -} -.nav-tabs.tabs-left>li.nav-item, .nav-tabs.tabs-right>li.nav-item { - float: none; - margin-bottom: 2px; -} -.nav-tabs.tabs-left>li.nav-item { - margin-right: -1px; -} -.nav-tabs.tabs-right>li.nav-item { - margin-left: -1px; -} -.nav-tabs.tabs-left>li.nav-item>a.nav-link.active, -.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:hover, -.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:focus { - border-bottom-color: #dee2e6; - border-right-color: transparent; -} -.nav-tabs.tabs-right>li.nav-item>a.nav-link.active, -.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:hover, -.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:focus { - border-bottom: 1px solid #dee2e6; - border-left-color: transparent; -} -.nav-tabs.tabs-left>li.nav-item>a.nav-link { - border-radius: 4px 0 0 4px; - margin-right: 0; - display:block; -} -.nav-tabs.tabs-right>li.nav-item>a.nav-link { - border-radius: 0 4px 4px 0; - margin-right: 0; -} diff --git a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt index 9772f808..0ba694eb 100644 --- a/src/main/kotlin/pl/treksoft/kvision/KVManager.kt +++ b/src/main/kotlin/pl/treksoft/kvision/KVManager.kt @@ -46,11 +46,11 @@ external fun require(name: String): dynamic object KVManager { init { try { - require("kvision-bootstrap").pl.treksoft.kvision.KVManagerBootstrap + require("kvision-bootstrap-css").pl.treksoft.kvision.KVManagerBootstrapCss } catch (e: Throwable) { } try { - require("kvision-bootstrap-css").pl.treksoft.kvision.KVManagerBootstrapCss + require("kvision-bootstrap").pl.treksoft.kvision.KVManagerBootstrap } catch (e: Throwable) { } try { diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css index 7323772b..ace7da8c 100644 --- a/src/main/resources/css/style.css +++ b/src/main/resources/css/style.css @@ -38,7 +38,6 @@ overflow: auto; } - .splitter-vertical { flex: 0 0 auto; width: 9px; @@ -76,3 +75,312 @@ trix-toolbar .trix-button-group { border-right: 1px solid #1d68cd !important; padding: 2px !important; } + +.input-group.date.is-invalid~.invalid-feedback { + display: block; +} + +.input-group.date { + padding-left: 0px; + padding-right: 0px; +} + +.bootstrap-select .dropdown-toggle.btn-default:focus { + outline: none !important; +} + +.bootstrap-select .dropdown-toggle::after { + margin-left: -1em !important; +} + +.form-inline .bootstrap-select .form-control { + min-width: 200px; +} + +/*.bootstrap-select.form-control { + border: 1px solid #ced4da !important; +} + +.bootstrap-select.form-control:focus { + border-color: #80bdff !important; +}*/ + +/* + +.bootstrap-touchspin .input-group-btn-vertical> .input-sm { + padding: 7px 10px; + height: 6px; +} + +.bootstrap-touchspin .input-group-btn-vertical> .input-lg { + height: 24px; +} + +.kv-spinner-btn-none .input-group-btn-vertical { + display: none; +} + +.kv-spinner-btn-none .form-control { + border-radius: 4px !important; +} + +.kv-spinner-btn-vertical .form-control { + border-radius: 4px 0px 0px 4px !important; +}*/ + +.kv-radiogroup-inline label.control-label { + vertical-align: top; + margin-right: .75rem; + margin-bottom: 0px; +} +.row.kv-radiogroup-inline label.control-label { + margin-right: 0px; +} + +.row.kv-radiogroup-inline .kv-radiogroup-container, .row.kv-radiogroup .kv-radiogroup-container { + margin-left: -15px; +} + +.kv-radiogroup-inline .kv-radiogroup-container { + display: inline-flex; +} + +.kv-radiogroup-container.is-invalid~.invalid-feedback { + display: block; +} + +.form-check { + padding-left: 0.5rem; +} + +.form-check-input.form-control-sm, .form-check-input.form-control-lg { + height: inherit; +} + +.form-check-inline { + margin-left: 3px; +} + +.form-check-inline.form-check { + padding-left: 0px; +} + +.form-horizontal.container-fluid { + width: inherit; +} + +.form-inline .form-group { + margin-right: 6px; +} + +.form-inline .form-group .control-label { + margin-right: 6px; +} + +.form-inline .form-check.form-group { + margin-left: 6px; +} + +.kv-form-condensed .form-group { + margin-bottom: 0.5rem; +} + +.kv-window.modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0,0,0,.5); + box-shadow: 0 5px 15px rgba(0,0,0,.5); + border-radius: 0px; +} + +.kv-window .modal-header { + height: 40px; + padding: 5px 15px 5px 15px; + align-items: center; +} + +.kv-window .modal-header button.close { + width: 24px; + height: 24px; + margin: 0px; + padding: 0px; +} + +.kv-window .modal-header .modal-title { + white-space: nowrap; +} + +.kv-window .modal-header .window-icon { + margin-right: 6px; +} + +.kv-window .kv-window-icons-container { + display: flex; +} + +.kv-preview-thumb .btn, .kv-zoom-actions .btn, .file-zoom-dialog .floating-buttons .btn { + padding: 5px 8px; +} + +.file-drop-zone.clickable:hover { + border: 1px dashed #999; +} + +.file-drop-zone.clickable:focus { + border: 1px solid #5acde2; +} + +.nav.tabs-top { + flex-wrap: nowrap; +} + +ul.tabs-top { + overflow-x: auto; + overflow-y: hidden; + display: flex; +} + +ul.tabs-top > li { + float:none; + flex-shrink: 0; +} + +.kv-tab-close { + margin-left: 10px; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: 0.2; +} + +.kv-tab-close:hover, .kv-tab-close:focus { + cursor: pointer; + filter: alpha(opacity=50); + opacity: 0.5; +} + +select.form-control, .tabulator-row .tabulator-cell.tabulator-editing select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: transparent none no-repeat; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAKCAYAAABblxXYAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUKFyIn4IjqJgAAAENJREFUKM/l0LERACEQQlGsiTa2px1aokGugNNAx8wfMy8AeLoBALYjaTqoKkga2+gKPgF/2Q7JkEx359oftu+C7/UBCUIcVQz0PvcAAAAASUVORK5CYII='); + background-position: right center; + cursor: pointer; +} + +.abc-checkbox input[type="checkbox"]:checked+label::after, +.abc-checkbox input[type="radio"]:checked+label::after { + font-family: "Font Awesome 5 Pro", "Font Awesome 5 Free"; + content: "\f00c"; + font-weight: 900; +} + +.abc-checkbox label::before { + top: 0px; +} +.abc-checkbox label::after { + top: 0px; +} + +.abc-radio label::before { + top: 0px; +} + +.abc-radio label::after { + top: 3px; +} + +.abc-checkbox.form-check-inline label::before { + top: 2px; +} + +.abc-checkbox.form-check-inline label::after { + top: 2px; +} + +.abc-radio.form-check-inline label::before { + top: 2px; +} + +.abc-radio.form-check-inline label::after { + top: 5px; +} + +.abc-checkbox label.col-form-label-lg::before { + top: 10px; +} +.abc-checkbox label.col-form-label-lg::after { + top: 10px; +} + +.abc-radio label.col-form-label-lg::before { + top: 10px; +} + +.abc-radio label.col-form-label-lg::after { + top: 13px; +} + +.abc-checkbox.form-check-inline label.col-form-label-lg::before { + top: 15px; +} + +.abc-checkbox.form-check-inline label.col-form-label-lg::after { + top: 15px; +} + +.abc-radio.form-check-inline label.col-form-label-lg::before { + top: 15px; +} + +.abc-radio.form-check-inline label.col-form-label-lg::after { + top: 18px; +} + +/*! + * bootstrap-vertical-tabs - v1.2.2 + * https://dbtek.github.io/bootstrap-vertical-tabs + * 2016-12-02 + * Copyright (c) 2016 İsmail Demirbilek + * License: MIT + */ +.nav-tabs.tabs-left, .nav-tabs.tabs-right { + border-bottom: none; + padding-top: 2px; +} +.nav-tabs.tabs-left { + border-right: 1px solid #dee2e6; +} +.nav-tabs.tabs-right { + border-left: 1px solid #dee2e6; +} +.nav-tabs.tabs-left>li.nav-item, .nav-tabs.tabs-right>li.nav-item { + float: none; + margin-bottom: 2px; +} +.nav-tabs.tabs-left>li.nav-item { + margin-right: -1px; +} +.nav-tabs.tabs-right>li.nav-item { + margin-left: -1px; +} +.nav-tabs.tabs-left>li.nav-item>a.nav-link.active, +.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:hover, +.nav-tabs.tabs-left>li.nav-item>a.nav-link.active:focus { + border-bottom-color: #dee2e6; + border-right-color: transparent; +} +.nav-tabs.tabs-right>li.nav-item>a.nav-link.active, +.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:hover, +.nav-tabs.tabs-right>li.nav-item>a.nav-link.active:focus { + border-bottom: 1px solid #dee2e6; + border-left-color: transparent; +} +.nav-tabs.tabs-left>li.nav-item>a.nav-link { + border-radius: 4px 0 0 4px; + margin-right: 0; + display:block; +} +.nav-tabs.tabs-right>li.nav-item>a.nav-link { + border-radius: 0 4px 4px 0; + margin-right: 0; +} -- cgit